Merge branch 'develop' into 'feature/api_limit_error_msg'

# Conflicts:
#   libraries/app/database_api.cpp
This commit is contained in:
blockc p 2020-10-19 00:38:59 +00:00
commit 913a477d90
153 changed files with 16165 additions and 675 deletions

127
.clang-format Normal file
View 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
View file

@ -11,6 +11,7 @@ moc_*
hardfork.hpp
build_xc
data
CMakeDoxyfile.in
build

View file

@ -1,5 +1,9 @@
include:
- template: Code-Quality.gitlab-ci.yml
- template: Dependency-Scanning.gitlab-ci.yml
- template: License-Scanning.gitlab-ci.yml
- template: SAST.gitlab-ci.yml
- template: Secret-Detection.gitlab-ci.yml
stages:
- build
@ -8,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
View 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@

View file

@ -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" )

View file

@ -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 \
@ -50,13 +52,14 @@ WORKDIR /peerplays-core
# Compile Peerplays
RUN \
BOOST_ROOT=$HOME/boost_1_67_0 && \
git submodule sync --recursive && \
git submodule update --init --recursive && \
mkdir build && \
mkdir build/release && \
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 && \

View file

@ -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

View file

@ -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" )

View file

@ -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:

View file

@ -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;
@ -228,6 +246,9 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
uint32_t api_limit_lookup_committee_member_accounts = 1000;
uint32_t api_limit_get_trade_history = 100;
uint32_t api_limit_get_trade_history_by_sequence = 100;
// Account Role
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
//private:
const account_object* get_account_from_string( const std::string& name_or_id,
@ -583,11 +604,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());
@ -708,7 +729,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)
@ -798,7 +819,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));
@ -1159,7 +1180,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
@ -1167,7 +1188,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
@ -1817,6 +1838,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 //
@ -1836,6 +2064,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() );
@ -1878,6 +2107,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) );
@ -2227,7 +2466,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>();
@ -2254,7 +2493,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) {
@ -2930,6 +3169,23 @@ vector<offer_history_object> database_api_impl::get_offer_history_by_bidder(cons
return result;
}
vector<account_role_object> database_api::get_account_roles_by_owner(account_id_type owner) const
{
return my->get_account_roles_by_owner(owner);
}
vector<account_role_object> database_api_impl::get_account_roles_by_owner(account_id_type owner) const
{
const auto &idx_aro = _db.get_index_type<account_role_index>().indices().get<by_owner>();
auto idx_aro_range = idx_aro.equal_range(owner);
vector<account_role_object> result;
for (auto itr = idx_aro_range.first; itr != idx_aro_range.second; ++itr)
{
result.push_back(*itr);
}
return result;
}
//////////////////////////////////////////////////////////////////////
// //
// Private methods //
@ -3028,7 +3284,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)

View file

@ -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;

View file

@ -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>
@ -52,6 +55,7 @@
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
@ -599,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
@ -829,6 +929,11 @@ class database_api
vector<offer_history_object> get_offer_history_by_issuer(const offer_history_id_type lower_id, const account_id_type issuer_account_id, uint32_t limit) const;
vector<offer_history_object> get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const;
vector<offer_history_object> get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const;
//////////////////
// ACCOUNT ROLE //
//////////////////
vector<account_role_object> get_account_roles_by_owner(account_id_type owner) const;
private:
std::shared_ptr< database_api_impl > my;
};
@ -937,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
@ -999,4 +1122,7 @@ FC_API(graphene::app::database_api,
(get_offer_history_by_issuer)
(get_offer_history_by_item)
(get_offer_history_by_bidder)
// Account Roles
(get_account_roles_by_owner)
)

12
libraries/chain/CMakeLists.txt Normal file → Executable file
View file

@ -124,6 +124,18 @@ add_library( graphene_chain
offer_evaluator.cpp
nft_evaluator.cpp
protocol/nft.cpp
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}

View file

@ -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." );

View file

@ -0,0 +1,162 @@
#include <graphene/chain/account_role_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/rbac_hardfork_visitor.hpp>
namespace graphene
{
namespace chain
{
void_result account_role_create_evaluator::do_evaluate(const account_role_create_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(d);
rbac_operation_hardfork_visitor arvtor(now);
for (const auto &op_type : op.allowed_operations)
{
arvtor(op_type);
}
for (const auto &acc : op.whitelisted_accounts)
{
acc(d);
}
FC_ASSERT(op.valid_to > now, "valid_to expiry should be in future");
FC_ASSERT((op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry");
const auto &ar_idx = d.get_index_type<account_role_index>().indices().get<by_owner>();
auto aro_range = ar_idx.equal_range(op.owner);
FC_ASSERT(std::distance(aro_range.first, aro_range.second) < d.get_global_properties().parameters.account_roles_max_per_account(), "Max account roles that can be created by one owner is reached");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
object_id_type account_role_create_evaluator::do_apply(const account_role_create_operation &op)
{
try
{
database &d = db();
return d.create<account_role_object>([&op](account_role_object &obj) mutable {
obj.owner = op.owner;
obj.name = op.name;
obj.metadata = op.metadata;
obj.allowed_operations = op.allowed_operations;
obj.whitelisted_accounts = op.whitelisted_accounts;
obj.valid_to = op.valid_to;
})
.id;
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result account_role_update_evaluator::do_evaluate(const account_role_update_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(d);
const account_role_object &aobj = op.account_role_id(d);
FC_ASSERT(aobj.owner == op.owner, "Only owner account can update account role object");
for (const auto &op_type : op.allowed_operations_to_remove)
{
FC_ASSERT(aobj.allowed_operations.find(op_type) != aobj.allowed_operations.end(),
"Cannot remove non existent operation");
}
for (const auto &acc : op.accounts_to_remove)
{
FC_ASSERT(aobj.whitelisted_accounts.find(acc) != aobj.whitelisted_accounts.end(),
"Cannot remove non existent account");
}
rbac_operation_hardfork_visitor arvtor(now);
for (const auto &op_type : op.allowed_operations_to_add)
{
arvtor(op_type);
}
FC_ASSERT((aobj.allowed_operations.size() + op.allowed_operations_to_add.size() - op.allowed_operations_to_remove.size()) > 0, "Allowed operations should be positive");
for (const auto &acc : op.accounts_to_add)
{
acc(d);
}
FC_ASSERT((aobj.whitelisted_accounts.size() + op.accounts_to_add.size() - op.accounts_to_remove.size()) > 0, "Accounts should be positive");
if (op.valid_to)
{
FC_ASSERT(*op.valid_to > now, "valid_to expiry should be in future");
FC_ASSERT((*op.valid_to - now) <= fc::seconds(d.get_global_properties().parameters.account_roles_max_lifetime()), "Validity of the account role beyond max expiry");
}
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result account_role_update_evaluator::do_apply(const account_role_update_operation &op)
{
try
{
database &d = db();
const account_role_object &aobj = op.account_role_id(d);
d.modify(aobj, [&op](account_role_object &obj) {
if (op.name)
obj.name = *op.name;
if (op.metadata)
obj.metadata = *op.metadata;
obj.allowed_operations.insert(op.allowed_operations_to_add.begin(), op.allowed_operations_to_add.end());
obj.whitelisted_accounts.insert(op.accounts_to_add.begin(), op.accounts_to_add.end());
for (const auto &op_type : op.allowed_operations_to_remove)
obj.allowed_operations.erase(op_type);
for (const auto &acc : op.accounts_to_remove)
obj.whitelisted_accounts.erase(acc);
if (op.valid_to)
obj.valid_to = *op.valid_to;
});
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result account_role_delete_evaluator::do_evaluate(const account_role_delete_operation &op)
{
try
{
const database &d = db();
auto now = d.head_block_time();
FC_ASSERT(now >= HARDFORK_NFT_TIME, "Not allowed until NFT HF");
op.owner(d);
const account_role_object &aobj = op.account_role_id(d);
FC_ASSERT(aobj.owner == op.owner, "Only owner account can delete account role object");
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
void_result account_role_delete_evaluator::do_apply(const account_role_delete_operation &op)
{
try
{
database &d = db();
const account_role_object &aobj = op.account_role_id(d);
d.remove(aobj);
return void_result();
}
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene

View file

@ -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 );

View file

@ -4,37 +4,13 @@
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/rbac_hardfork_visitor.hpp>
namespace graphene
{
namespace chain
{
struct rbac_operation_hardfork_visitor
{
typedef void result_type;
const fc::time_point_sec block_time;
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
void operator()(int op_type) const
{
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
switch (op_type)
{
case operation::tag<custom_permission_create_operation>::value:
case operation::tag<custom_permission_update_operation>::value:
case operation::tag<custom_permission_delete_operation>::value:
case operation::tag<custom_account_authority_create_operation>::value:
case operation::tag<custom_account_authority_update_operation>::value:
case operation::tag<custom_account_authority_delete_operation>::value:
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permission not allowed on this operation yet!");
break;
default:
FC_ASSERT(op_type < first_allowed_op, "Custom permission not allowed on this operation!");
}
}
};
void_result create_custom_account_authority_evaluator::do_evaluate(const custom_account_authority_create_operation &op)
{
try
@ -149,4 +125,4 @@ void_result delete_custom_account_authority_evaluator::do_apply(const custom_acc
}
} // namespace chain
} // namespace graphene
} // namespace graphene

View file

@ -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();
@ -789,12 +800,14 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
auto get_custom = [&]( account_id_type id, const operation& op ) {
auto get_active = [this]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [this]( account_id_type id ) { return &id(*this).owner; };
auto get_custom = [this]( account_id_type id, const operation& op ) {
return get_account_custom_authorities(id, op);
};
trx.verify_authority( chain_id, get_active, get_owner, get_custom, get_global_properties().parameters.max_authority_depth );
trx.verify_authority( chain_id, get_active, get_owner, get_custom,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(head_block_time()),
get_global_properties().parameters.max_authority_depth );
}
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is

View file

@ -30,6 +30,9 @@
#include <graphene/chain/custom_permission_object.hpp>
#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>
@ -192,4 +195,126 @@ bool database::item_locked(const nft_id_type &item) const
auto items_itr = market_items._locked_items.find(item);
return (items_itr != market_items._locked_items.end());
}
bool database::account_role_valid(const account_role_object &aro, account_id_type account, optional<int> op_type) const
{
return (aro.valid_to > head_block_time()) &&
(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());
}
} }

View file

@ -52,6 +52,7 @@
#include <graphene/chain/custom_permission_object.hpp>
#include <graphene/chain/custom_account_authority_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/nft_object.hpp>
@ -60,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>
@ -85,6 +93,13 @@
#include <graphene/chain/custom_account_authority_evaluator.hpp>
#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>
@ -186,6 +201,9 @@ const uint8_t offer_object::type_id;
const uint8_t offer_history_object::space_id;
const uint8_t offer_history_object::type_id;
const uint8_t account_role_object::space_id;
const uint8_t account_role_object::type_id;
void database::initialize_evaluators()
{
_operation_evaluators.resize(255);
@ -275,6 +293,28 @@ void database::initialize_evaluators()
register_evaluator<nft_safe_transfer_from_evaluator>();
register_evaluator<nft_approve_evaluator>();
register_evaluator<nft_set_approval_for_all_evaluator>();
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()
@ -291,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 > >();
@ -323,6 +364,15 @@ 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 > >();
@ -339,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 > >();
@ -353,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 > >();
}
@ -997,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;

View file

@ -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, &current_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;
});
@ -941,14 +1333,23 @@ void clear_expired_custom_account_authorities(database& db)
}
}
void clear_expired_account_roles(database& db)
{
const auto& arindex = db.get_index_type<account_role_index>().indices().get<by_expiration>();
while(!arindex.empty() && arindex.begin()->valid_to < db.head_block_time())
{
db.remove(*arindex.begin());
}
}
// Schedules payouts from a dividend distribution account to the current holders of the
// 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,
@ -957,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 =
@ -968,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()));
@ -1028,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
@ -1059,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
@ -1067,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
@ -1086,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;
@ -1095,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())));
}
@ -1111,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));
}
@ -1124,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)
{
@ -1137,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;
}
@ -1152,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) {
@ -1166,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;
@ -1238,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;
@ -1268,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))
@ -1305,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
@ -1350,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
@ -1362,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;
@ -1381,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;
@ -1394,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();
@ -1407,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;
@ -1430,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));
}
@ -1444,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())
@ -1465,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();
@ -1480,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();
@ -1508,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;
@ -1619,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;
}
@ -1634,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();
@ -1668,6 +2157,38 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
p.pending_parameters->extensions.value.gpos_subperiod = p.parameters.extensions.value.gpos_subperiod;
if( !p.pending_parameters->extensions.value.gpos_vesting_lockin_period.valid() )
p.pending_parameters->extensions.value.gpos_vesting_lockin_period = p.parameters.extensions.value.gpos_vesting_lockin_period;
if( !p.pending_parameters->extensions.value.rbac_max_permissions_per_account.valid() )
p.pending_parameters->extensions.value.rbac_max_permissions_per_account = p.parameters.extensions.value.rbac_max_permissions_per_account;
if( !p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime.valid() )
p.pending_parameters->extensions.value.rbac_max_account_authority_lifetime = p.parameters.extensions.value.rbac_max_account_authority_lifetime;
if( !p.pending_parameters->extensions.value.rbac_max_authorities_per_permission.valid() )
p.pending_parameters->extensions.value.rbac_max_authorities_per_permission = p.parameters.extensions.value.rbac_max_authorities_per_permission;
if( !p.pending_parameters->extensions.value.account_roles_max_per_account.valid() )
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;
if( !p.pending_parameters->extensions.value.son_account.valid() )
p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account;
if( !p.pending_parameters->extensions.value.btc_asset.valid() )
p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset;
if( !p.pending_parameters->extensions.value.maximum_son_count.valid() )
p.pending_parameters->extensions.value.maximum_son_count = p.parameters.extensions.value.maximum_son_count;
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
}
@ -1717,8 +2238,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
// Ideally we have to do this after every block but that leads to longer block applicaiton/replay times.
// So keep it here as it is not critical. valid_to check ensures
// these custom account auths are not usable.
// these custom account auths and account roles are not usable.
clear_expired_custom_account_authorities(*this);
clear_expired_account_roles(*this);
// process_budget needs to run at the bottom because
// it needs to know the next_maintenance_time
process_budget();

View file

@ -40,6 +40,7 @@
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/transaction_object.hpp>
#include <graphene/chain/impacted.hpp>
#include <graphene/chain/hardfork.hpp>
using namespace fc;
@ -49,8 +50,13 @@ using namespace graphene::chain;
struct get_impacted_account_visitor
{
flat_set<account_id_type>& _impacted;
get_impacted_account_visitor( flat_set<account_id_type>& impact ):_impacted(impact) {}
typedef void result_type;
bool _ignore_custom_op_reqd_auths;
get_impacted_account_visitor( flat_set<account_id_type>& impact, bool ignore_custom_operation_required_auths )
: _impacted( impact ), _ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
using result_type = void;
void operator()( const transfer_operation& op )
{
@ -136,7 +142,7 @@ struct get_impacted_account_visitor
{
vector<authority> other;
for( const auto& proposed_op : op.proposed_ops )
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other );
operation_get_required_authorities( proposed_op.op, _impacted, _impacted, other, _ignore_custom_op_reqd_auths );
for( auto& o : other )
add_authority_accounts( _impacted, o );
}
@ -344,22 +350,85 @@ struct get_impacted_account_visitor
void operator()( const finalize_offer_operation& op ) {
_impacted.insert( op.fee_paying_account );
}
void operator()( const account_role_create_operation& op ){
_impacted.insert( op.owner );
}
void operator()( const account_role_update_operation& op ){
_impacted.insert( op.owner );
}
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 )
{
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
get_impacted_account_visitor vtor = get_impacted_account_visitor( result, ignore_custom_operation_required_auths );
op.visit( vtor );
}
void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result )
{
void graphene::chain::transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
for( const auto& op : tx.operations )
operation_get_impacted_accounts( op, result );
operation_get_impacted_accounts( op, result, ignore_custom_operation_required_auths );
}
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts )
{
void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accounts, bool ignore_custom_operation_required_auths ) {
if( obj->id.space() == protocol_ids )
{
switch( (object_type)obj->id.type() )
@ -406,12 +475,14 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case proposal_object_type:{
const auto& aobj = dynamic_cast<const proposal_object*>(obj);
assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts );
transaction_get_impacted_accounts( aobj->proposed_transaction, accounts,
ignore_custom_operation_required_auths);
break;
} case operation_history_object_type:{
const auto& aobj = dynamic_cast<const operation_history_object*>(obj);
assert( aobj != nullptr );
operation_get_impacted_accounts( aobj->op, accounts );
operation_get_impacted_accounts( aobj->op, accounts,
ignore_custom_operation_required_auths);
break;
} case withdraw_permission_object_type:{
const auto& aobj = dynamic_cast<const withdraw_permission_object*>(obj);
@ -432,6 +503,30 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case balance_object_type:{
/** these are free from any accounts */
break;
} case account_role_type:{
const auto& aobj = dynamic_cast<const account_role_object*>(obj);
assert( aobj != nullptr );
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;
}
}
}
@ -462,7 +557,8 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
} case impl_transaction_object_type:{
const auto& aobj = dynamic_cast<const transaction_object*>(obj);
assert( aobj != nullptr );
transaction_get_impacted_accounts( aobj->trx, accounts );
transaction_get_impacted_accounts( aobj->trx, accounts,
ignore_custom_operation_required_auths);
break;
} case impl_blinded_balance_object_type:{
const auto& aobj = dynamic_cast<const blinded_balance_object*>(obj);
@ -507,6 +603,7 @@ void database::notify_changed_objects()
if( _undo_db.enabled() )
{
const auto& head_undo = _undo_db.head();
auto chain_time = head_block_time();
// New
if( !new_objects.empty() )
@ -518,7 +615,8 @@ void database::notify_changed_objects()
new_ids.push_back(item);
auto obj = find_object(item);
if(obj != nullptr)
get_relevant_accounts(obj, new_accounts_impacted);
get_relevant_accounts(obj, new_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
}
GRAPHENE_TRY_NOTIFY( new_objects, new_ids, new_accounts_impacted)
@ -532,7 +630,8 @@ void database::notify_changed_objects()
for( const auto& item : head_undo.old_values )
{
changed_ids.push_back(item.first);
get_relevant_accounts(item.second.get(), changed_accounts_impacted);
get_relevant_accounts(item.second.get(), changed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
}
GRAPHENE_TRY_NOTIFY( changed_objects, changed_ids, changed_accounts_impacted)
@ -549,7 +648,8 @@ void database::notify_changed_objects()
removed_ids.emplace_back( item.first );
auto obj = item.second.get();
removed.emplace_back( obj );
get_relevant_accounts(obj, removed_accounts_impacted);
get_relevant_accounts(obj, removed_accounts_impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time));
}
GRAPHENE_TRY_NOTIFY( removed_objects, removed_ids, removed, removed_accounts_impacted)

View file

@ -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 );
}
} }

View file

@ -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 );

View file

@ -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;
}

View file

@ -1,4 +1,4 @@
// 5050_1 HARDFORK Thursday, 16 April 2020 19:00:00 GMT
// 5050_1 HARDFORK Wednesday, 15 April 2020 20:00:00 GMT
#ifndef HARDFORK_5050_1_TIME
#define HARDFORK_5050_1_TIME (fc::time_point_sec( 1587063600 ))
#define HARDFORK_5050_1_TIME (fc::time_point_sec( 1586980800 ))
#endif

View file

@ -0,0 +1,6 @@
// #210 Check authorities on custom_operation
#ifndef HARDFORK_CORE_210_TIME
#define HARDFORK_CORE_210_TIME (fc::time_point_sec(1893456000)) // Jan 1 00:00:00 2030 (Not yet scheduled)
// Bugfix: pre-HF 210, custom_operation's required_auths field was ignored.
#define MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) (chain_time <= HARDFORK_CORE_210_TIME)
#endif

View file

@ -1,4 +1,4 @@
// NFT HARDFORK Wednesday, 20-May-20 00:00:00 UTC
// NFT HARDFORK Sat, 15-Aug-20 00:00:00 UTC
#ifndef HARDFORK_NFT_TIME
#define HARDFORK_NFT_TIME (fc::time_point_sec( 1589932800 ))
#define HARDFORK_NFT_TIME (fc::time_point_sec( 1597449600 ))
#endif

View 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

View file

@ -0,0 +1,35 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace chain {
class account_role_create_evaluator : public evaluator<account_role_create_evaluator>
{
public:
typedef account_role_create_operation operation_type;
void_result do_evaluate( const account_role_create_operation& o );
object_id_type do_apply( const account_role_create_operation& o );
};
class account_role_update_evaluator : public evaluator<account_role_update_evaluator>
{
public:
typedef account_role_update_operation operation_type;
void_result do_evaluate( const account_role_update_operation& o );
void_result do_apply( const account_role_update_operation& o );
};
class account_role_delete_evaluator : public evaluator<account_role_delete_evaluator>
{
public:
typedef account_role_delete_operation operation_type;
void_result do_evaluate( const account_role_delete_operation& o );
void_result do_apply( const account_role_delete_operation& o );
};
} } // graphene::chain

View file

@ -0,0 +1,49 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/object.hpp>
#include <graphene/db/generic_index.hpp>
namespace graphene
{
namespace chain
{
using namespace graphene::db;
class account_role_object : public abstract_object<account_role_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = account_role_type;
account_id_type owner;
std::string name;
std::string metadata;
flat_set<int> allowed_operations;
flat_set<account_id_type> whitelisted_accounts;
time_point_sec valid_to;
};
struct by_owner;
struct by_expiration;
using account_role_multi_index_type = multi_index_container<
account_role_object,
indexed_by<
ordered_unique< tag<by_id>,
member<object, object_id_type, &object::id>
>,
ordered_non_unique< tag<by_owner>,
member<account_role_object, account_id_type, &account_role_object::owner>
>,
ordered_unique< tag<by_expiration>,
composite_key<account_role_object,
member<account_role_object, time_point_sec, &account_role_object::valid_to>,
member<object, object_id_type, &object::id>>
>
>
>;
using account_role_index = generic_index<account_role_object, account_role_multi_index_type>;
} // namespace chain
} // namespace graphene
FC_REFLECT_DERIVED(graphene::chain::account_role_object, (graphene::db::object),
(owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to))

View file

@ -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;
@ -74,7 +76,9 @@ FC_REFLECT(graphene::chain::budget_record,
(total_budget)
(witness_budget)
(worker_budget)
(son_budget)
(leftover_worker_funds)
(leftover_son_funds)
(supply_delta)
)

View file

@ -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
@ -243,4 +253,7 @@
#define NFT_TOKEN_MIN_LENGTH 3
#define NFT_TOKEN_MAX_LENGTH 15
#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH
#define NFT_URI_MAX_LENGTH GRAPHENE_MAX_URL_LENGTH
#define ACCOUNT_ROLES_MAX_PER_ACCOUNT 20 // Max 20 roles can be created by a resource owner
#define ACCOUNT_ROLES_MAX_LIFETIME 365*24*60*60 // 1 Year

View file

@ -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();
@ -283,6 +303,14 @@ namespace graphene { namespace chain {
uint64_t get_random_bits( uint64_t bound );
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;
@ -531,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:
@ -575,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;

View file

@ -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 )

View file

@ -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)
)

View file

@ -30,13 +30,12 @@
namespace graphene { namespace chain {
void operation_get_impacted_accounts(
const graphene::chain::operation& op,
fc::flat_set<graphene::chain::account_id_type>& result );
void operation_get_impacted_accounts( const graphene::chain::operation& op,
fc::flat_set<graphene::chain::account_id_type>& result,
bool ignore_custom_operation_required_auths );
void transaction_get_impacted_accounts(
const graphene::chain::transaction& tx,
fc::flat_set<graphene::chain::account_id_type>& result
);
void transaction_get_impacted_accounts( const graphene::chain::transaction& tx,
fc::flat_set<graphene::chain::account_id_type>& result,
bool ignore_custom_operation_required_auths );
} } // graphene::app
} } // graphene::app

View file

@ -20,6 +20,7 @@ namespace graphene { namespace chain {
optional<uint16_t> revenue_split;
bool is_transferable = false;
bool is_sellable = true;
optional<account_role_id_type> account_role;
};
class nft_object : public abstract_object<nft_object>
@ -95,7 +96,8 @@ FC_REFLECT_DERIVED( graphene::chain::nft_metadata_object, (graphene::db::object)
(revenue_partner)
(revenue_split)
(is_transferable)
(is_sellable) )
(is_sellable)
(account_role) )
FC_REFLECT_DERIVED( graphene::chain::nft_object, (graphene::db::object),
(nft_metadata_id)

View file

@ -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:

View file

@ -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;

View file

@ -0,0 +1,82 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene
{
namespace chain
{
struct account_role_create_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type owner;
std::string name;
std::string metadata;
flat_set<int> allowed_operations;
flat_set<account_id_type> whitelisted_accounts;
time_point_sec valid_to;
extensions_type extensions;
account_id_type fee_payer() const { return owner; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct account_role_update_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type owner;
account_role_id_type account_role_id;
optional<std::string> name;
optional<std::string> metadata;
flat_set<int> allowed_operations_to_add;
flat_set<int> allowed_operations_to_remove;
flat_set<account_id_type> accounts_to_add;
flat_set<account_id_type> accounts_to_remove;
optional<time_point_sec> valid_to;
extensions_type extensions;
account_id_type fee_payer() const { return owner; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const;
};
struct account_role_delete_operation : public base_operation
{
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type owner;
account_role_id_type account_role_id;
extensions_type extensions;
account_id_type fee_payer() const { return owner; }
void validate() const;
share_type calculate_fee(const fee_parameters_type &k) const { return k.fee; }
};
} // namespace chain
} // namespace graphene
FC_REFLECT(graphene::chain::account_role_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
FC_REFLECT(graphene::chain::account_role_update_operation::fee_parameters_type, (fee)(price_per_kbyte))
FC_REFLECT(graphene::chain::account_role_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::account_role_create_operation, (fee)(owner)(name)(metadata)(allowed_operations)(whitelisted_accounts)(valid_to)(extensions))
FC_REFLECT(graphene::chain::account_role_update_operation, (fee)(owner)(account_role_id)(name)(metadata)(allowed_operations_to_add)(allowed_operations_to_remove)(accounts_to_add)(accounts_to_remove)(valid_to)(extensions))
FC_REFLECT(graphene::chain::account_role_delete_operation, (fee)(owner)(account_role_id)(owner)(extensions))

View file

@ -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>
@ -52,6 +53,21 @@ namespace graphene { namespace chain {
optional < uint16_t > rbac_max_permissions_per_account = RBAC_MAX_PERMISSIONS_PER_ACCOUNT;
optional < uint32_t > rbac_max_account_authority_lifetime = RBAC_MAX_ACCOUNT_AUTHORITY_LIFETIME;
optional < uint16_t > rbac_max_authorities_per_permission = RBAC_MAX_AUTHS_PER_PERMISSION;
/* 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 < uint64_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 = GRAPHENE_NULL_ACCOUNT;
optional < asset_id_type > btc_asset = asset_id_type();
optional < uint16_t > maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS
};
struct chain_parameters
@ -101,7 +117,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 */
@ -152,6 +168,45 @@ namespace graphene { namespace chain {
inline uint16_t rbac_max_authorities_per_permission()const {
return extensions.value.rbac_max_authorities_per_permission.valid() ? *extensions.value.rbac_max_authorities_per_permission : RBAC_MAX_AUTHS_PER_PERMISSION;
}
inline uint16_t account_roles_max_per_account()const {
return extensions.value.account_roles_max_per_account.valid() ? *extensions.value.account_roles_max_per_account : ACCOUNT_ROLES_MAX_PER_ACCOUNT;
}
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 uint64_t son_pay_max()const {
return extensions.value.son_pay_max.valid() ? *extensions.value.son_pay_max : SON_PAY_MAX;
}
inline uint32_t son_pay_time()const {
return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME;
}
inline uint32_t son_deregister_time()const {
return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME;
}
inline uint32_t son_heartbeat_frequency()const {
return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY;
}
inline uint32_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();
}
inline uint16_t maximum_son_count()const {
return extensions.value.maximum_son_count.valid() ? *extensions.value.maximum_son_count : GRAPHENE_DEFAULT_MAX_SONS;
}
};
} } // graphene::chain
@ -172,6 +227,19 @@ FC_REFLECT( graphene::chain::parameter_extension,
(rbac_max_permissions_per_account)
(rbac_max_account_authority_lifetime)
(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)
(maximum_son_count)
)
FC_REFLECT( graphene::chain::chain_parameters,

View file

@ -50,6 +50,9 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return payer; }
void validate()const;
share_type calculate_fee(const fee_parameters_type& k)const;
void get_required_active_authorities( flat_set<account_id_type>& auths )const {
auths.insert( required_auths.begin(), required_auths.end() );
}
};
} } // namespace graphene::chain

View file

@ -20,6 +20,7 @@ struct custom_account_authority_create_operation : public base_operation
time_point_sec valid_from;
time_point_sec valid_to;
account_id_type owner_account;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -38,6 +39,7 @@ struct custom_account_authority_update_operation : public base_operation
optional<time_point_sec> new_valid_from;
optional<time_point_sec> new_valid_to;
account_id_type owner_account;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -54,6 +56,7 @@ struct custom_account_authority_delete_operation : public base_operation
asset fee;
custom_account_authority_id_type auth_id;
account_id_type owner_account;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -64,10 +67,10 @@ struct custom_account_authority_delete_operation : public base_operation
} // namespace graphene
FC_REFLECT(graphene::chain::custom_account_authority_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
FC_REFLECT(graphene::chain::custom_account_authority_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_create_operation, (fee)(permission_id)(operation_type)(valid_from)(valid_to)(owner_account)(extensions))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_update_operation, (fee)(auth_id)(new_valid_from)(new_valid_to)(owner_account)(extensions))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account))
FC_REFLECT(graphene::chain::custom_account_authority_delete_operation, (fee)(auth_id)(owner_account)(extensions))

View file

@ -18,6 +18,7 @@ struct custom_permission_create_operation : public base_operation
account_id_type owner_account;
string permission_name;
authority auth;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -35,6 +36,7 @@ struct custom_permission_update_operation : public base_operation
custom_permission_id_type permission_id;
optional<authority> new_auth;
account_id_type owner_account;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -51,6 +53,7 @@ struct custom_permission_delete_operation : public base_operation
asset fee;
custom_permission_id_type permission_id;
account_id_type owner_account;
extensions_type extensions;
account_id_type fee_payer() const { return owner_account; }
void validate() const;
@ -61,10 +64,10 @@ struct custom_permission_delete_operation : public base_operation
} // namespace graphene
FC_REFLECT(graphene::chain::custom_permission_create_operation::fee_parameters_type, (fee)(price_per_kbyte))
FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth))
FC_REFLECT(graphene::chain::custom_permission_create_operation, (fee)(owner_account)(permission_name)(auth)(extensions))
FC_REFLECT(graphene::chain::custom_permission_update_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account))
FC_REFLECT(graphene::chain::custom_permission_update_operation, (fee)(permission_id)(new_auth)(owner_account)(extensions))
FC_REFLECT(graphene::chain::custom_permission_delete_operation::fee_parameters_type, (fee))
FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account))
FC_REFLECT(graphene::chain::custom_permission_delete_operation, (fee)(permission_id)(owner_account)(extensions))

View file

@ -21,6 +21,9 @@ namespace graphene { namespace chain {
optional<uint16_t> revenue_split;
bool is_transferable = false;
bool is_sellable = true;
// Accounts Role
optional<account_role_id_type> account_role;
extensions_type extensions;
account_id_type fee_payer()const { return owner; }
void validate() const;
@ -29,7 +32,11 @@ namespace graphene { namespace chain {
struct nft_metadata_update_operation : public base_operation
{
struct fee_parameters_type { uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; };
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type owner;
@ -41,6 +48,9 @@ namespace graphene { namespace chain {
optional<uint16_t> revenue_split;
optional<bool> is_transferable;
optional<bool> is_sellable;
// Accounts Role
optional<account_role_id_type> account_role;
extensions_type extensions;
account_id_type fee_payer()const { return owner; }
void validate() const;
@ -63,6 +73,7 @@ namespace graphene { namespace chain {
account_id_type approved;
vector<account_id_type> approved_operators;
std::string token_uri;
extensions_type extensions;
account_id_type fee_payer()const { return payer; }
void validate() const;
@ -84,6 +95,7 @@ namespace graphene { namespace chain {
account_id_type to;
nft_id_type token_id;
string data;
extensions_type extensions;
account_id_type fee_payer()const { return operator_; }
share_type calculate_fee(const fee_parameters_type &k) const;
@ -98,6 +110,7 @@ namespace graphene { namespace chain {
account_id_type approved;
nft_id_type token_id;
extensions_type extensions;
account_id_type fee_payer()const { return operator_; }
share_type calculate_fee(const fee_parameters_type &k) const;
@ -112,6 +125,7 @@ namespace graphene { namespace chain {
account_id_type operator_;
bool approved;
extensions_type extensions;
account_id_type fee_payer()const { return owner; }
share_type calculate_fee(const fee_parameters_type &k) const;
@ -126,10 +140,9 @@ FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation::fee_parameters_ty
FC_REFLECT( graphene::chain::nft_approve_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) )
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) )
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) )
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) )
FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) )
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) )
FC_REFLECT( graphene::chain::nft_metadata_create_operation, (fee) (owner) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
FC_REFLECT( graphene::chain::nft_metadata_update_operation, (fee) (owner) (nft_metadata_id) (name) (symbol) (base_uri) (revenue_partner) (revenue_split) (is_transferable) (is_sellable) (account_role) (extensions) )
FC_REFLECT( graphene::chain::nft_mint_operation, (fee) (payer) (nft_metadata_id) (owner) (approved) (approved_operators) (token_uri) (extensions) )
FC_REFLECT( graphene::chain::nft_safe_transfer_from_operation, (fee) (operator_) (from) (to) (token_id) (data) (extensions) )
FC_REFLECT( graphene::chain::nft_approve_operation, (fee) (operator_) (approved) (token_id) (extensions) )
FC_REFLECT( graphene::chain::nft_set_approval_for_all_operation, (fee) (owner) (operator_) (approved) (extensions) )

View file

@ -49,6 +49,13 @@
#include <graphene/chain/protocol/custom_account_authority.hpp>
#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 {
@ -155,7 +162,29 @@ namespace graphene { namespace chain {
nft_mint_operation,
nft_safe_transfer_from_operation,
nft_approve_operation,
nft_set_approval_for_all_operation
nft_set_approval_for_all_operation,
account_role_create_operation,
account_role_update_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
@ -166,10 +195,11 @@ namespace graphene { namespace chain {
*
* @return a set of required authorities for @ref op
*/
void operation_get_required_authorities( const operation& op,
void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other );
vector<authority>& other,
bool ignore_custom_operation_required_auths );
void operation_validate( const operation& op );

View file

@ -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) )

View file

@ -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) )

View 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) )

View file

@ -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) )

View file

@ -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) )

View file

@ -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) )

View file

@ -112,7 +112,10 @@ namespace graphene { namespace chain {
return results;
}
void get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const;
void get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const;
};
/**
@ -142,6 +145,7 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const;
@ -150,6 +154,7 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/**
@ -158,13 +163,13 @@ namespace graphene { namespace chain {
* some cases where get_required_signatures() returns a
* non-minimal set.
*/
set<public_key_type> minimize_required_signatures(
const chain_id_type& chain_id,
const flat_set<public_key_type>& available_keys,
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const;
@ -198,10 +203,11 @@ namespace graphene { namespace chain {
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false,
bool allow_committee = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),
const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>());
const flat_set<account_id_type>& owner_approvals = flat_set<account_id_type>() );
/**
* @brief captures the result of evaluating the operations contained in the transaction

View file

@ -176,6 +176,14 @@ namespace graphene { namespace chain {
offer_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 ///< Sentry value which contains the number of different object types
};
@ -205,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;
@ -241,6 +251,14 @@ namespace graphene { namespace chain {
class offer_object;
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;
@ -272,6 +290,14 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, offer_object_type, offer_object> offer_id_type;
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;
@ -296,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;
@ -325,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;
@ -459,6 +489,14 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(offer_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,
@ -487,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 )
@ -535,6 +575,15 @@ FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type )
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, )

View file

@ -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 )

View file

@ -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 )

View file

@ -0,0 +1,69 @@
#pragma once
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene
{
namespace chain
{
struct rbac_operation_hardfork_visitor
{
typedef void result_type;
const fc::time_point_sec block_time;
rbac_operation_hardfork_visitor(const fc::time_point_sec bt) : block_time(bt) {}
void operator()(int op_type) const
{
int first_allowed_op = operation::tag<custom_permission_create_operation>::value;
switch (op_type)
{
case operation::tag<son_create_operation>::value:
case operation::tag<son_update_operation>::value:
case operation::tag<son_deregister_operation>::value:
case operation::tag<son_heartbeat_operation>::value:
case operation::tag<son_report_down_operation>::value:
case operation::tag<son_maintenance_operation>::value:
case operation::tag<son_wallet_recreate_operation>::value:
case operation::tag<son_wallet_update_operation>::value:
case operation::tag<son_wallet_deposit_create_operation>::value:
case operation::tag<son_wallet_deposit_process_operation>::value:
case operation::tag<son_wallet_withdraw_create_operation>::value:
case operation::tag<son_wallet_withdraw_process_operation>::value:
case operation::tag<sidechain_address_add_operation>::value:
case operation::tag<sidechain_address_update_operation>::value:
case operation::tag<sidechain_address_delete_operation>::value:
case operation::tag<sidechain_transaction_create_operation>::value:
case operation::tag<sidechain_transaction_sign_operation>::value:
case operation::tag<sidechain_transaction_send_operation>::value:
case operation::tag<sidechain_transaction_settle_operation>::value:
FC_ASSERT(block_time >= HARDFORK_SON_TIME, "Custom permissions and roles not allowed on this operation yet!");
break;
case operation::tag<custom_permission_create_operation>::value:
case operation::tag<custom_permission_update_operation>::value:
case operation::tag<custom_permission_delete_operation>::value:
case operation::tag<custom_account_authority_create_operation>::value:
case operation::tag<custom_account_authority_update_operation>::value:
case operation::tag<custom_account_authority_delete_operation>::value:
case operation::tag<offer_operation>::value:
case operation::tag<bid_operation>::value:
case operation::tag<cancel_offer_operation>::value:
case operation::tag<finalize_offer_operation>::value:
case operation::tag<nft_metadata_create_operation>::value:
case operation::tag<nft_metadata_update_operation>::value:
case operation::tag<nft_mint_operation>::value:
case operation::tag<nft_safe_transfer_from_operation>::value:
case operation::tag<nft_approve_operation>::value:
case operation::tag<nft_set_approval_for_all_operation>::value:
case operation::tag<account_role_create_operation>::value:
case operation::tag<account_role_update_operation>::value:
case operation::tag<account_role_delete_operation>::value:
FC_ASSERT(block_time >= HARDFORK_NFT_TIME, "Custom permissions and roles not allowed on this operation yet!");
break;
default:
FC_ASSERT(op_type >= operation::tag<transfer_operation>::value && op_type < first_allowed_op, "Custom permissions and roles not allowed on this operation!");
}
}
};
} // namespace chain
} // namespace graphene

View file

@ -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

View file

@ -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) )

View 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) )

View file

@ -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

View file

@ -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) )

View 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

View 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) )

View 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)
)

View file

@ -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) )

View file

@ -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

View file

@ -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) )

View file

@ -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

View 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) )

View file

@ -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

View file

@ -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) )

View file

@ -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),

View file

@ -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);

View file

@ -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::extensions.value.maximum_son_count )::value_type,
/* 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::extensions.value.maximum_son_count )::value_type,
/* 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 )

View file

@ -1,5 +1,7 @@
#include <graphene/chain/nft_evaluator.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
@ -18,6 +20,10 @@ void_result nft_metadata_create_evaluator::do_evaluate( const nft_metadata_creat
(*op.revenue_partner)(db());
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
}
if(op.account_role) {
const auto& ar_obj = (*op.account_role)(db());
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -32,6 +38,7 @@ object_id_type nft_metadata_create_evaluator::do_apply( const nft_metadata_creat
obj.revenue_split = op.revenue_split;
obj.is_transferable = op.is_transferable;
obj.is_sellable = op.is_sellable;
obj.account_role = op.account_role;
});
return new_nft_metadata_object.id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -57,6 +64,10 @@ void_result nft_metadata_update_evaluator::do_evaluate( const nft_metadata_updat
(*op.revenue_partner)(db());
FC_ASSERT(*op.revenue_split >= 0 && *op.revenue_split <= GRAPHENE_100_PERCENT, "Revenue split percent invalid");
}
if(op.account_role) {
const auto& ar_obj = (*op.account_role)(db());
FC_ASSERT(ar_obj.owner == op.owner, "Only the Account Role created by the owner can be attached");
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -77,6 +88,8 @@ void_result nft_metadata_update_evaluator::do_apply( const nft_metadata_update_o
obj.is_transferable = *op.is_transferable;
if( op.is_sellable.valid() )
obj.is_sellable = *op.is_sellable;
if( op.account_role.valid() )
obj.account_role = op.account_role;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -144,6 +157,17 @@ void_result nft_safe_transfer_from_evaluator::do_evaluate( const nft_safe_transf
const auto& nft_meta_obj = itr_nft->nft_metadata_id(db());
FC_ASSERT( nft_meta_obj.is_transferable == true, "NFT is not transferable");
if (nft_meta_obj.account_role)
{
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
if(ar_itr != ar_idx.end())
{
FC_ASSERT(db().account_role_valid(*ar_itr, op.operator_, get_type()), "Account role not valid");
FC_ASSERT(db().account_role_valid(*ar_itr, op.to), "Account role not valid");
}
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }

View file

@ -2,6 +2,8 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/offer_object.hpp>
#include <graphene/chain/nft_object.hpp>
#include <graphene/chain/protocol/operations.hpp>
#include <graphene/chain/account_role_object.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>
@ -38,6 +40,15 @@ namespace graphene
}
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable");
if (nft_meta_obj.account_role)
{
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
if(ar_itr != ar_idx.end())
{
FC_ASSERT(db().account_role_valid(*ar_itr, op.issuer, get_type()), "Account role not valid");
}
}
}
FC_ASSERT(op.offer_expiration_date > d.head_block_time(), "Expiration should be in future");
FC_ASSERT(op.fee.amount >= 0, "Invalid fee");
@ -100,6 +111,18 @@ namespace graphene
FC_ASSERT(!is_approved, "Bidder cannot already be an approved account of the item");
FC_ASSERT(!is_approved_operator, "Bidder cannot already be an approved operator of the item");
}
const auto &nft_meta_obj = nft_obj.nft_metadata_id(d);
FC_ASSERT(nft_meta_obj.is_sellable == true, "NFT is not sellable");
if (nft_meta_obj.account_role)
{
const auto &ar_idx = db().get_index_type<account_role_index>().indices().get<by_id>();
auto ar_itr = ar_idx.find(*nft_meta_obj.account_role);
if(ar_itr != ar_idx.end())
{
FC_ASSERT(db().account_role_valid(*ar_itr, op.bidder, get_type()), "Account role not valid");
}
}
}
FC_ASSERT(op.bid_price.asset_id == offer.minimum_price.asset_id, "Asset type mismatch");
@ -335,4 +358,4 @@ namespace graphene
FC_CAPTURE_AND_RETHROW((op))
}
} // namespace chain
} // namespace graphene
} // namespace graphene

View file

@ -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 {
@ -196,6 +197,41 @@ struct proposal_operation_hardfork_visitor
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "nft_set_approval_for_all_operation not allowed yet!" );
}
void operator()(const account_role_create_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_create_operation not allowed yet!" );
}
void operator()(const account_role_update_operation &v) const {
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_update_operation not allowed yet!" );
}
void operator()(const account_role_delete_operation &v) const {
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 {
@ -204,19 +240,38 @@ struct proposal_operation_hardfork_visitor
}
};
void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o)
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();
auto block_time = d.head_block_time();
proposal_operation_hardfork_visitor vtor( d.head_block_time() );
proposal_operation_hardfork_visitor vtor( block_time );
vtor( o );
const auto& global_parameters = d.get_global_properties().parameters;
FC_ASSERT( o.expiration_time > d.head_block_time(), "Proposal has already expired on creation." );
FC_ASSERT( o.expiration_time <= d.head_block_time() + global_parameters.maximum_proposal_lifetime,
FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." );
FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime,
"Proposal expiration time is too far in the future.");
FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - d.head_block_time()),
FC_ASSERT( !o.review_period_seconds || fc::seconds(*o.review_period_seconds) < (o.expiration_time - block_time),
"Proposal review period must be less than its overall lifetime." );
{
@ -225,7 +280,8 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
vector<authority> other;
for( auto& op : o.proposed_ops )
{
operation_get_required_authorities(op.op, auths, auths, other);
operation_get_required_authorities( op.op, auths, auths, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(block_time) );
}
FC_ASSERT( other.size() == 0 ); // TODO: what about other???
@ -248,18 +304,19 @@ void_result proposal_create_evaluator::do_evaluate(const proposal_create_operati
}
}
for( const op_wrapper& op : o.proposed_ops )
for (const op_wrapper& op : o.proposed_ops)
_proposed_trx.operations.push_back(op.op);
_proposed_trx.validate();
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
object_id_type proposal_create_evaluator::do_apply(const proposal_create_operation& o)
object_id_type proposal_create_evaluator::do_apply( const proposal_create_operation& o )
{ try {
database& d = db();
auto chain_time = d.head_block_time();
const proposal_object& proposal = d.create<proposal_object>([&](proposal_object& proposal) {
const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {
_proposed_trx.expiration = o.expiration_time;
proposal.proposed_transaction = _proposed_trx;
proposal.proposer = o.fee_paying_account;
@ -273,7 +330,8 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
// TODO: consider caching values from evaluate?
for( auto& op : _proposed_trx.operations )
operation_get_required_authorities(op, required_active, proposal.required_owner_approvals, other);
operation_get_required_authorities( op, required_active, proposal.required_owner_approvals, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(chain_time) );
//All accounts which must provide both owner and active authority should be omitted from the active authority set;
//owner authority approval implies active authority approval.
@ -282,10 +340,16 @@ object_id_type proposal_create_evaluator::do_apply(const proposal_create_operati
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) ) }
void_result proposal_update_evaluator::do_evaluate(const proposal_update_operation& o)
void_result proposal_update_evaluator::do_evaluate( const proposal_update_operation& o )
{ try {
database& d = db();

View file

@ -24,12 +24,13 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
bool proposal_object::is_authorized_to_execute(database& db) const
bool proposal_object::is_authorized_to_execute( database& db ) const
{
transaction_evaluation_state dry_run_eval(&db);
transaction_evaluation_state dry_run_eval( &db );
try {
verify_authority( proposed_transaction.operations,
@ -38,6 +39,7 @@ bool proposal_object::is_authorized_to_execute(database& db) const
[&]( account_id_type id ){ return &id(db).owner; },
[&]( account_id_type id, const operation& op ){
return db.get_account_custom_authorities(id, op); },
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ),
db.get_global_properties().parameters.max_authority_depth,
true, /* allow committee */
available_active_approvals,

View file

@ -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

View file

@ -0,0 +1,61 @@
#include <graphene/chain/protocol/account_role.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene
{
namespace chain
{
void account_role_create_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(allowed_operations.size() > 0, "Allowed operations should be positive");
FC_ASSERT(whitelisted_accounts.size() > 0, "Whitelisted accounts should be positive");
}
void account_role_update_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
for (auto aop : allowed_operations_to_add)
{
FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid");
FC_ASSERT(allowed_operations_to_remove.find(aop) == allowed_operations_to_remove.end(),
"Cannot add and remove allowed operation at the same time.");
}
for (auto aop : allowed_operations_to_remove)
{
FC_ASSERT(aop >= 0 && aop < operation::count(), "operation_type is not valid");
FC_ASSERT(allowed_operations_to_add.find(aop) == allowed_operations_to_add.end(),
"Cannot add and remove allowed operation at the same time.");
}
for (auto acc : accounts_to_add)
{
FC_ASSERT(accounts_to_remove.find(acc) == accounts_to_remove.end(),
"Cannot add and remove accounts at the same time.");
}
for (auto acc : accounts_to_remove)
{
FC_ASSERT(accounts_to_add.find(acc) == accounts_to_add.end(),
"Cannot add and remove accounts at the same time.");
}
}
void account_role_delete_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
}
share_type account_role_create_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte);
}
share_type account_role_update_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee + calculate_data_fee(fc::raw::pack_size(*this), k.price_per_kbyte);
}
} // namespace chain
} // namespace graphene

View file

@ -45,7 +45,6 @@ void nft_metadata_create_operation::validate() const
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(is_valid_nft_token_name(name), "Invalid NFT name provided");
FC_ASSERT(is_valid_nft_token_name(symbol), "Invalid NFT symbol provided");
FC_ASSERT(base_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI");
}
void nft_metadata_update_operation::validate() const
@ -55,14 +54,11 @@ void nft_metadata_update_operation::validate() const
FC_ASSERT(is_valid_nft_token_name(*name), "Invalid NFT name provided");
if(symbol)
FC_ASSERT(is_valid_nft_token_name(*symbol), "Invalid NFT symbol provided");
if(base_uri)
FC_ASSERT((*base_uri).length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Base URI");
}
void nft_mint_operation::validate() const
{
FC_ASSERT(fee.amount >= 0, "Fee must not be negative");
FC_ASSERT(token_uri.length() <= NFT_URI_MAX_LENGTH, "Invalid NFT Token URI");
}
share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_type &k) const
@ -72,7 +68,7 @@ share_type nft_metadata_create_operation::calculate_fee(const fee_parameters_typ
share_type nft_metadata_update_operation::calculate_fee(const fee_parameters_type &k) const
{
return k.fee;
return k.fee + calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
}
share_type nft_mint_operation::calculate_fee(const fee_parameters_type &k) const

View file

@ -53,25 +53,38 @@ struct operation_validator
struct operation_get_required_auth
{
typedef void result_type;
using result_type = void;
flat_set<account_id_type>& active;
flat_set<account_id_type>& owner;
vector<authority>& other;
bool ignore_custom_op_reqd_auths;
operation_get_required_auth( flat_set<account_id_type>& a,
flat_set<account_id_type>& own,
vector<authority>& oth ):active(a),owner(own),other(oth){}
flat_set<account_id_type>& own,
vector<authority>& oth,
bool ignore_custom_operation_required_auths )
: active( a ), owner( own ), other( oth ),
ignore_custom_op_reqd_auths( ignore_custom_operation_required_auths )
{}
template<typename T>
void operator()( const T& v )const
{
void operator()( const T& v ) const {
active.insert( v.fee_payer() );
v.get_required_active_authorities( active );
v.get_required_owner_authorities( owner );
v.get_required_active_authorities( active );
v.get_required_owner_authorities( owner );
v.get_required_authorities( other );
}
void operator()( const custom_operation& op ) const {
active.insert( op.fee_payer() );
if( !ignore_custom_op_reqd_auths ) {
op.get_required_active_authorities( active );
op.get_required_owner_authorities( owner );
op.get_required_authorities( other );
}
}
};
void operation_validate( const operation& op )
@ -79,12 +92,13 @@ void operation_validate( const operation& op )
op.visit( operation_validator() );
}
void operation_get_required_authorities( const operation& op,
void operation_get_required_authorities( const operation& op,
flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other )
vector<authority>& other,
bool ignore_custom_operation_required_auths )
{
op.visit( operation_get_required_auth( active, owner, other ) );
op.visit( operation_get_required_auth( active, owner, other, ignore_custom_operation_required_auths ) );
}
} } // namespace graphene::chain

View file

@ -95,10 +95,13 @@ void transaction::set_reference_block( const block_id_type& reference_block )
ref_block_prefix = reference_block._hash[1];
}
void transaction::get_required_authorities( flat_set<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const
void transaction::get_required_authorities( flat_set<account_id_type>& active,
flat_set<account_id_type>& owner,
vector<authority>& other,
bool ignore_custom_operation_required_auths )const
{
for( const auto& op : operations )
operation_get_required_authorities( op, active, owner, other );
for ( const auto& op : operations )
operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths );
}
@ -249,8 +252,9 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion_depth,
bool allow_committe,
bool allow_committee,
const flat_set<account_id_type>& active_aprovals,
const flat_set<account_id_type>& owner_approvals )
{ try {
@ -276,7 +280,8 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
for( const auto& op : ops ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other );
operation_get_required_authorities( op, operation_required_active, required_owner, other,
ignore_custom_operation_required_auths );
auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) {
@ -289,7 +294,7 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
}
if( !allow_committe )
if( !allow_committee )
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" );
@ -349,6 +354,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_authorities,
uint32_t max_recursion_depth )const
{
flat_set<account_id_type> required_active;
@ -370,7 +376,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
for( const auto& op : operations ) {
flat_set<account_id_type> operation_required_active;
operation_get_required_authorities( op, operation_required_active, required_owner, other );
operation_get_required_authorities( op, operation_required_active, required_owner, other, ignore_custom_operation_required_authorities );
auto itr = operation_required_active.begin();
while ( itr != operation_required_active.end() ) {
@ -383,7 +389,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
required_active.insert( operation_required_active.begin(), operation_required_active.end() );
}
for( const auto& auth : other )
for (const auto& auth : other)
s.check_authority(&auth);
for( auto& owner : required_owner )
s.check_authority( get_owner( owner ) );
@ -407,10 +413,12 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion
) const
{
set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom, max_recursion );
set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner, get_custom,
ignore_custom_operation_required_auths, max_recursion );
flat_set< public_key_type > result( s.begin(), s.end() );
for( const public_key_type& k : s )
@ -418,7 +426,8 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
result.erase( k );
try
{
graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom, max_recursion );
graphene::chain::verify_authority( operations, result, get_active, get_owner, get_custom,
ignore_custom_operation_required_auths, max_recursion );
continue; // element stays erased if verify_authority is ok
}
catch( const tx_missing_owner_auth& e ) {}
@ -434,6 +443,7 @@ void signed_transaction::verify_authority(
const std::function<const authority*(account_id_type)>& get_active,
const std::function<const authority*(account_id_type)>& get_owner,
const std::function<vector<authority>(account_id_type, const operation&)>& get_custom,
bool ignore_custom_operation_required_auths,
uint32_t max_recursion )const
{ try {
graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, get_custom, max_recursion );

View 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

View 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

View 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

View 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));
}
}}

View 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

View 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

View 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

View file

@ -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)

View file

@ -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 \
{ \

View file

@ -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 )

View file

@ -33,6 +33,7 @@
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/operation_history_object.hpp>
#include <graphene/chain/transaction_evaluation_state.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/thread/thread.hpp>
@ -123,12 +124,15 @@ void account_history_plugin_impl::update_account_histories( const signed_block&
// get the set of accounts this operation applies to
flat_set<account_id_type> impacted;
vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here
// fee payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() );
else
graphene::chain::operation_get_impacted_accounts( op.op, impacted );
graphene::chain::operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) );
if( op.op.which() == operation::tag< lottery_end_operation >::value )
{
auto lop = op.op.get< lottery_end_operation >();

View file

@ -25,7 +25,7 @@
#include <graphene/elasticsearch/elasticsearch_plugin.hpp>
#include <graphene/chain/impacted.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/chain/hardfork.hpp>
#include <curl/curl.h>
namespace graphene { namespace elasticsearch {
@ -157,12 +157,15 @@ bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b
// get the set of accounts this operation applies to
flat_set<account_id_type> impacted;
vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other ); // fee_payer is added here
// fee_payer is added here
operation_get_required_authorities( op.op, impacted, impacted, other,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( op.result.get<object_id_type>() );
else
graphene::chain::operation_get_impacted_accounts( op.op, impacted );
operation_get_impacted_accounts( op.op, impacted,
MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) );
for( auto& a : other )
for( auto& item : a.account_auths )

View 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" )

View 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

View file

@ -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

Some files were not shown because too many files have changed in this diff Show more