-
Notifications
You must be signed in to change notification settings - Fork 230
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
improve error handling #11070
improve error handling #11070
Changes from all commits
bcc94d1
e3f6fc4
d3db6ef
e06360d
e6070aa
65d5c9a
741be79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
/** @file Exo for @see {prepareTransactionFeedKit} */ | ||
import { makeTracer } from '@agoric/internal'; | ||
import { prepareDurablePublishKit } from '@agoric/notifier'; | ||
import { keyEQ, M } from '@endo/patterns'; | ||
import { Fail, quote } from '@endo/errors'; | ||
import { keyEQ, M } from '@endo/patterns'; | ||
import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js'; | ||
import { defineInertInvitation } from '../utils/zoe.js'; | ||
import { prepareOperatorKit } from './operator-kit.js'; | ||
|
@@ -64,8 +65,14 @@ export const stateShape = { | |
pending: M.remotable(), | ||
risks: M.remotable(), | ||
}; | ||
harden(stateShape); | ||
|
||
/** | ||
* A TransactionFeed is responsible for finding quorum among oracles. | ||
* | ||
* It receives attestations, records their evidence, and when enough oracles | ||
* agree, publishes the results for the advancer to act on. | ||
* | ||
* @param {Zone} zone | ||
* @param {ZCF} zcf | ||
*/ | ||
|
@@ -148,18 +155,20 @@ export const prepareTransactionFeedKit = (zone, zcf) => { | |
|
||
/** @param {string} operatorId */ | ||
removeOperator(operatorId) { | ||
const { operators } = this.state; | ||
const { operators, pending, risks } = this.state; | ||
trace('removeOperator', operatorId); | ||
const operatorKit = operators.get(operatorId); | ||
operatorKit.admin.disable(); | ||
operators.delete(operatorId); | ||
pending.delete(operatorId); | ||
risks.delete(operatorId); | ||
Comment on lines
+163
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose this change is covered by the revised |
||
}, | ||
}, | ||
operatorPowers: { | ||
/** | ||
* Add evidence from an operator. | ||
* | ||
* NB: the operatorKit is responsible for | ||
* NB: the operatorKit is responsible for revoking access. | ||
* | ||
* @param {CctpTxEvidence} evidence | ||
* @param {RiskAssessment} riskAssessment | ||
|
@@ -169,10 +178,6 @@ export const prepareTransactionFeedKit = (zone, zcf) => { | |
const { operators, pending, risks } = this.state; | ||
trace('attest', operatorId, evidence); | ||
|
||
// TODO https://github.com/Agoric/agoric-sdk/pull/10720 | ||
// TODO validate that it's a valid for Fast USDC before accepting | ||
// E.g. that the `recipientAddress` is the FU settlement account and that | ||
// the EUD is a chain supported by FU. | ||
const { txHash } = evidence; | ||
|
||
// accept the evidence | ||
|
@@ -192,6 +197,29 @@ export const prepareTransactionFeedKit = (zone, zcf) => { | |
const found = [...pending.values()].filter(store => | ||
store.has(txHash), | ||
); | ||
|
||
{ | ||
let lastEvidence; | ||
for (const store of found) { | ||
const next = store.get(txHash); | ||
if (lastEvidence && !keyEQ(lastEvidence, next)) { | ||
// Ignore conflicting evidence, but treat it as an error | ||
// because it should never happen and needs to be prevented | ||
// from happening again. | ||
trace( | ||
'🚨 conflicting evidence for', | ||
txHash, | ||
':', | ||
lastEvidence, | ||
'!=', | ||
next, | ||
); | ||
Fail`conflicting evidence for ${quote(txHash)}`; | ||
} | ||
lastEvidence = next; | ||
} | ||
} | ||
|
||
const minAttestations = Math.ceil(operators.getSize() / 2); | ||
trace( | ||
'transaction', | ||
|
@@ -202,48 +230,37 @@ export const prepareTransactionFeedKit = (zone, zcf) => { | |
minAttestations, | ||
'necessary attestations', | ||
); | ||
|
||
if (found.length < minAttestations) { | ||
// wait for more | ||
return; | ||
} | ||
|
||
let lastEvidence; | ||
for (const store of found) { | ||
const next = store.get(txHash); | ||
if (lastEvidence && !keyEQ(lastEvidence, next)) { | ||
// Ignore conflicting evidence, but treat it as an error | ||
// because it should never happen and needs to be prevented | ||
// from happening again. | ||
trace( | ||
'🚨 conflicting evidence for', | ||
txHash, | ||
':', | ||
lastEvidence, | ||
'!=', | ||
next, | ||
); | ||
Fail`conflicting evidence for ${quote(txHash)}`; | ||
} | ||
lastEvidence = next; | ||
} | ||
|
||
const riskStores = [...risks.values()].filter(store => | ||
store.has(txHash), | ||
); | ||
// take the union of risks identified from all operators | ||
const risksIdentified = allRisksIdentified(riskStores, txHash); | ||
|
||
// sufficient agreement, so remove from pending risks, then publish | ||
for (const store of found) { | ||
store.delete(txHash); | ||
// Publish at the threshold of agreement | ||
if (found.length === minAttestations) { | ||
// take the union of risks identified from all operators | ||
const risksIdentified = allRisksIdentified(riskStores, txHash); | ||
trace('publishing evidence', evidence, risksIdentified); | ||
publisher.publish({ | ||
evidence, | ||
risk: { risksIdentified }, | ||
}); | ||
return; | ||
} | ||
for (const store of riskStores) { | ||
store.delete(txHash); | ||
|
||
if (found.length === pending.getSize()) { | ||
turadg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// all have reported so clean up | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so clean-up depends on availability of all operators. doesn't seem ideal, but seems workable for the coming release. |
||
for (const store of found) { | ||
store.delete(txHash); | ||
} | ||
for (const store of riskStores) { | ||
store.delete(txHash); | ||
} | ||
} | ||
trace('publishing evidence', evidence, risksIdentified); | ||
publisher.publish({ | ||
evidence, | ||
risk: { risksIdentified }, | ||
}); | ||
}, | ||
}, | ||
public: { | ||
|
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 wonder if we should be using...
I don't see any other use of it in
agoric-sdk
production code, though.