Skip to content
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

Merged
merged 35 commits into from
Feb 22, 2019

Conversation

ashcherbakov
Copy link
Contributor

@ashcherbakov ashcherbakov commented Feb 15, 2019

  • 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>
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>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
@@ -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()
Copy link
Contributor

@ArtObr ArtObr Feb 18, 2019

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)

Copy link
Contributor Author

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.

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

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

@@ -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):
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prev_handler_result?

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

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

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),
Copy link
Member

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

Toktar and others added 12 commits February 19, 2019 10:39
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Signed-off-by: ashcherbakov <alexander.sherbakov@dsr-corporation.com>
Signed-off-by: toktar <renata.toktar@dsr-corporation.com>
Fix problem with CatchupRep splitting and catchup 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>
…oesn't have any audit txns

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>
@ashcherbakov ashcherbakov changed the title [WIP] INDY-1944: Audit ledger support INDY-1944: Audit ledger support Feb 21, 2019
Copy link
Member

@skhoroshavin skhoroshavin left a 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])
Copy link
Member

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?

Copy link
Contributor Author

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
Copy link
Member

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)
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

  1. state is never updated without ledger, so it's sufficient to have refs to previous updates in ledger part only.
  2. 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)
Copy link
Member

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?

Copy link
Contributor Author

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):
Copy link
Member

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.

Copy link
Contributor Author

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):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def register_new_database(self, lid, ledger: Ledger, state: State = None):
def register_new_database(self, lid, ledger: Ledger, state: Optional[State] = None):

Copy link
Contributor Author

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)
Copy link
Member

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
Copy link
Member

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?

Copy link
Contributor Author

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):
Copy link
Member

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.

Copy link
Contributor Author

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):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@ashcherbakov ashcherbakov merged commit 5a53794 into hyperledger:master Feb 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants