Skip to content

Commit

Permalink
fix: handle multiple wallets with the same FQDN PE-5473
Browse files Browse the repository at this point in the history
It's possible for multiple wallets to associate themselves with the same
FQDN. When that happens we need to fail wallets that do not match the
wallet returned by the gateway and pass the one that does. In order to
include this information in the report this change replaces
`expectedWallet` with `expectedWallets` and updates the contract
interactions creation logic to ensure all non-matching wallets in
`expectedWallets` are marked as failed.

NOTE: The `expectedWallet` => `expectedWallets` change may be a breaking
change for some report consumers. Since we are still in testing this
seems reasonable. After launch we will do our best to avoid any breaking
changes or at the very least deprecate first and make the breaking
change.
  • Loading branch information
djwhitt committed Jan 24, 2024
1 parent a60bb85 commit 82045f6
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 23 deletions.
14 changes: 8 additions & 6 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ components:
Info:
type: object
properties:
wallet: { '$ref': '#/components/schemas/ArweaveWallet' }
contractId: { '$ref': '#/components/schemas/ArweaveId' }
wallet: { '$ref': '#/components/schemas/ArweaveWallet' }
contractId: { '$ref': '#/components/schemas/ArweaveId' }
OwnershipAssessment:
type: object
properties:
expectedWallet: { '$ref': '#/components/schemas/ArweaveWallet' }
observedWallet: { '$ref': '#/components/schemas/ArweaveWallet' }
expectedWallets:
type: array
items: { '$ref': '#/components/schemas/ArweaveWallet' }
observedWallet: { '$ref': '#/components/schemas/ArweaveWallet' }
failureReason: { type: string }
pass: { '$ref': '#/components/schemas/Evaluation' }
pass: { '$ref': '#/components/schemas/Evaluation' }
ArnsAssessment:
type: object
properties:
Expand Down Expand Up @@ -85,7 +87,7 @@ components:
properties:
formatVersion: { type: integer }
observerAddress: { '$ref': '#/components/schemas/ArweaveAddress' }
generatedAt: { '$ref': '#/components/schemas/Timestamp' }
generatedAt: { '$ref': '#/components/schemas/Timestamp' }
gatewayAssessments: { '$ref': '#/components/schemas/GatewayAssessments' }
required:
- observerAddress
Expand Down
27 changes: 18 additions & 9 deletions src/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ function getArnsResolution({

async function assessOwnership({
host,
expectedWallet,
expectedWallets,
}: {
host: string;
expectedWallet: string;
expectedWallets: string[];
}): Promise<OwnershipAssessment> {
try {
const url = `https://${host}/ar-io/info`;
Expand All @@ -125,30 +125,32 @@ async function assessOwnership({
})
.json<any>();
if (resp?.wallet) {
if (resp.wallet !== expectedWallet) {
if (!expectedWallets.includes(resp.wallet)) {
return {
expectedWallet,
expectedWallets,
observedWallet: null,
failureReason: `Wallet mismatch: expected ${expectedWallet} but found ${resp.wallet}`,
failureReason: `Wallet mismatch: expected one of ${expectedWallets.join(
', ',
)} but found ${resp.wallet}`,
pass: false,
};
} else {
return {
expectedWallet,
expectedWallets,
observedWallet: resp.wallet,
pass: true,
};
}
}
return {
expectedWallet,
expectedWallets,
observedWallet: null,
failureReason: `No wallet found`,
pass: false,
};
} catch (error: any) {
return {
expectedWallet,
expectedWallets,
observedWallet: null,
failureReason: error?.message as string,
pass: false,
Expand Down Expand Up @@ -302,12 +304,19 @@ export class Observer {
// Assess gateway
const gatewayAssessments: GatewayAssessments = {};
const gatewayHosts = await this.observedGatewayHostList.getHosts();

// Create map of FQDN => hosts to handle duplicates
const hostWallets: { [key: string]: string[] } = {};
gatewayHosts.forEach((host) => {
(hostWallets[host.fqdn] ||= []).push(host.wallet);
});

await pMap(
gatewayHosts,
async (host) => {
const ownershipAssessment = await assessOwnership({
host: host.fqdn,
expectedWallet: host.wallet,
expectedWallets: hostWallets[host.fqdn].sort(),
});

const [prescribedAssessments, chosenAssessments] = await Promise.all([
Expand Down
16 changes: 9 additions & 7 deletions src/store/contract-report-sink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,17 @@ export function getFailedGatewaySummaryFromReport(
const failedGatewaySummary: Set<string> = new Set();
Object.values(observerReport.gatewayAssessments).forEach(
(gatewayAssessment) => {
// Check if the pass property is false
if (gatewayAssessment.pass === false) {
failedGatewaySummary.add(
gatewayAssessment.ownershipAssessment.expectedWallet,
);
}
// Add expected wallets that do not match the observed wallet to the failed set
gatewayAssessment.ownershipAssessment.expectedWallets.forEach((wallet) => {

Check failure on line 38 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Insert `⏎········`
if (gatewayAssessment.ownershipAssessment.observedWallet != wallet) {

Check failure on line 39 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Insert `··`

Check failure on line 39 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Expected '!==' and instead saw '!='
console.log('adding wallet to failed set', wallet);

Check failure on line 40 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Insert `··`
failedGatewaySummary.add(wallet);

Check failure on line 41 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Insert `··`
}

Check failure on line 42 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Insert `··`
console.log('observed wallet', gatewayAssessment.ownershipAssessment.observedWallet);

Check failure on line 43 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Replace `console.log('observed·wallet',·gatewayAssessment.ownershipAssessment.observedWallet` with `··console.log(⏎············'observed·wallet',⏎············gatewayAssessment.ownershipAssessment.observedWallet,⏎··········`
});

Check failure on line 44 in src/store/contract-report-sink.ts

View workflow job for this annotation

GitHub Actions / build (lint:check)

Replace `}` with `··},⏎······`
},
);
return [...failedGatewaySummary];
return [...failedGatewaySummary].sort();
}

function splitArrayBySize(array: string[], maxSizeInBytes: number): string[][] {
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export interface ObserversSource {
//

export interface OwnershipAssessment {
expectedWallet: string;
expectedWallets: string[];
observedWallet: string | null;
failureReason?: string;
pass: boolean;
Expand Down

0 comments on commit 82045f6

Please sign in to comment.