-
Notifications
You must be signed in to change notification settings - Fork 366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
INDY-1944: Audit ledger support #1091
Conversation
ashcherbakov
commented
Feb 15, 2019
•
edited
edited
- Added audit ledger
- Integrated it into consensus protocol
- validate audit txn root hash from Primary
- TBD: move all ledger creation methods from node.py into a separate helper
- TBD: move all catchup-related callbacks from node.py into a separate helper
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
refactor replica unit tests Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
…nto audit-ledger
plenum/server/node.py
Outdated
@@ -3487,6 +3529,9 @@ def onBatchRejected(self, ledger_id): | |||
self.get_req_handler(ledger_id).onBatchRejected() | |||
else: | |||
logger.debug('{} did not know how to handle for ledger {}'.format(self, ledger_id)) | |||
|
|||
self.audit_handler.post_batch_rejected() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ledger_id required. (probable test fault reason)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thank you
@@ -140,3 +140,16 @@ def reset_uncommitted(self): | |||
self.uncommittedTxns = [] | |||
self.uncommittedRootHash = None | |||
self.uncommittedTree = None | |||
|
|||
def get_uncommitted_txns(self): | |||
return self.uncommittedTxns |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -13,4 +13,5 @@ class PlenumTransactions(Transactions): | |||
# components. | |||
NODE = "0" | |||
NYM = "1" | |||
AUDIT = "2" |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -13,4 +13,5 @@ class PlenumTransactions(Transactions): | |||
# components. | |||
NODE = "0" | |||
NYM = "1" | |||
AUDIT = "2" | |||
GET_TXN = "3" |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -10,7 +10,7 @@ def __init__(self, database_manager: DatabaseManager, ledger_id): | |||
self.database_manager = database_manager | |||
self.ledger_id = ledger_id | |||
|
|||
def commit_batch(self, txn_count, state_root, txn_root, pp_time, prev_result): | |||
def commit_batch(self, ledger_id, txn_count, state_root, txn_root, pp_time, prev_result=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also prev_result
is a bit ambiguos name: when I first looked at it I though it is result of previous run of this handler, and only after looking at calling code it became clear that it is result of previous call to another handler. This is pretty subjective though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will be a better name by your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prev_handler_result?
plenum/server/database_manager.py
Outdated
@property | ||
def ledgers(self): | ||
# TODO: change this. Too inefficient to build dict every time | ||
return dict((lid, db.ledger) for lid, db in self.databases.items()) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -10,7 +10,7 @@ def __init__(self, database_manager: DatabaseManager, ledger_id): | |||
self.database_manager = database_manager | |||
self.ledger_id = ledger_id |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
check_audit_ledger_updated(audit_size_initial, txnPoolNodeSet, | ||
audit_txns_added=3) | ||
for node in txnPoolNodeSet: | ||
check_audit_txn(txn=node.auditLedger.getBySeqNo(node.auditLedger.size - 2), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
audit_size_initial + 1
would be a bit more intuitive here
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
…lenum into audit-ledger
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Fix problem with CatchupRep splitting and catchup tests
…oesn't have any audit txns Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't finish review - probably there will be more comments. I really like what is being done here, there is only one issue that probably needs immediate attention, all others comments are quite minor or even just some rants.
self.ledger.append_txns_metadata([txn], three_pc_batch.pp_time) | ||
|
||
# 3. Add to the Ledger | ||
self.ledger.appendTxns([txn]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a change request for this code, but - do we really need to have separate methods to append txns and txns metadata, why not one atomic method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Will be changed in the next PR.
if lid == AUDIT_LEDGER_ID: | ||
continue | ||
# 2. ledger size | ||
txn[AUDIT_TXN_LEDGERS_SIZE][str(lid)] = ledger.uncommitted_size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to convert ledger id to string here, why not just store integer?
self.__fill_ledger_root_hash(txn, three_pc_batch, lid, last_audit_txn) | ||
|
||
# 4. state root hash | ||
txn[AUDIT_TXN_STATE_ROOT][str(three_pc_batch.ledger_id)] = Ledger.hashToStr(three_pc_batch.state_root) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason why it's different from how ledger root hashes are handled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
- state is never updated without ledger, so it's sufficient to have refs to previous updates in ledger part only.
- this is done like to simplify future support of combined state trie
|
||
# 1. ledger is changed in this batch => root_hash | ||
if lid == target_ledger_id: | ||
txn[AUDIT_TXN_LEDGER_ROOT][str(lid)] = Ledger.hashToStr(three_pc_batch.txn_root) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have ledger id as integer everywhere, why convert it to string when storing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's done as string to be able to support "all" key when we combine all the states into one.
But even in this case we can use -1
.
So, agree. Will be changed in a PR.
pass | ||
|
||
@abstractmethod | ||
def post_batch_rejected(self): | ||
def post_batch_rejected(self, ledger_id, prev_handler_result=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong here, but do we really need ledger_id
here (and in commit_batch
as well)? We are just rejecting (or committing) last applied batch, but we should have already received this batch from post_batch_applied
if I understand logic correctly. Furthermore, these parameters are not used in AuditBatchHandler
despite it actually handles multiple ledgers.
Also names look a bit misleading - post_batch_applied
and post_batch_rejected
look like either of them could be called, while in fact flow is more like post_batch_applied
followed by either commit_batch
or post_batch_rejected
. So name like reject_batch
looks a bit better. Again - this applies only if I really understand logic correctly, and even if so - this might be out of scope of this PR, since this comment relates to new request handlers, not audit ledger itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Will be done in next PR.
@@ -11,7 +11,7 @@ def __init__(self): | |||
self.databases = {} # type: Dict[int, Database] | |||
self.stores = {} | |||
|
|||
def register_new_database(self, lid, ledger: Ledger, state: State): | |||
def register_new_database(self, lid, ledger: Ledger, state: State = None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def register_new_database(self, lid, ledger: Ledger, state: State = None): | |
def register_new_database(self, lid, ledger: Ledger, state: Optional[State] = None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Will be done in next PR.
# 3. VALIDATE APPLIED | ||
invalid_from_pp = invalid_index_serializer.deserialize(pre_prepare.discarded) | ||
why_not_applied = self._validate_applied_pre_prepare(pre_prepare, | ||
reqs, invalid_indices, invalid_from_pp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hooray, _apply_pre_prepare
monstrosity is finally split into smaller chunks! Thanks a lot! 🎉
if digest != pre_prepare.digest: | ||
if self.isMaster: | ||
revert() | ||
return PP_APPLY_WRONG_DIGEST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small note - this check can be done before PREPREPARE is event applied. I remember we have some kind of static validation for messages (mostly schema checking) - can we move this check to that part somehow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Not in the scope of this work, but will be done in a separate PR.
def post_apply_batch(self, ledger_id, state_root): | ||
handlers = self.batch_handlers.get(ledger_id, None) | ||
# TODO: pass PrePrepare here | ||
def post_apply_batch(self, three_pc_batch): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you think that PrePrepare
would be better here? three_pc_batch
looks good enough here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, old TODO comment added before three_pc_batch
was added. The comment will be removed in the next PR.
_, txn_count = self.tracker.reject_batch() | ||
self.ledger.discardTxns(txn_count) | ||
|
||
def commit_batch(self, ledger_id, txn_count, state_root, txn_root, pp_time, prev_result=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def commit_batch(self, ledger_id, txn_count, state_root, txn_root, pp_time, prev_result=None): | |
def commit_batch(self, ledger_id, txn_count, state_root, txn_root, pp_time, prev_handler_result=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1