in src/governance/ccf-app/js/src/endpoints/documents.ts [267:392]
export function getAcceptedDocument(
request: ccfapp.Request<GetAcceptedDocumentRequest>
):
| ccfapp.Response<GetAcceptedDocumentResponse>
| ccfapp.Response<ErrorResponse> {
const id = request.params.documentId;
const requestBody = request.body.json();
if (!requestBody.attestation) {
return {
statusCode: 400,
body: new ErrorResponse(
"AttestationMissing",
"Attestation payload must be supplied."
)
};
}
if (!requestBody.encrypt) {
return {
statusCode: 400,
body: new ErrorResponse(
"EncryptionMissing",
"Encrypt payload must be supplied."
)
};
}
// Validate attestation report.
const contractId = request.params.contractId;
let snpAttestationResult: SnpAttestationResult;
try {
snpAttestationResult = verifySnpAttestation(
contractId,
requestBody.attestation
);
} catch (e) {
return {
statusCode: 400,
body: new ErrorResponse("VerifySnpAttestationFailed", e.message)
};
}
// Then validate the report data value.
try {
verifyReportData(snpAttestationResult, requestBody.encrypt.publicKey);
} catch (e) {
return {
statusCode: 400,
body: new ErrorResponse("ReportDataMismatch", e.message)
};
}
// Only accepted documents are exposed.
if (!acceptedDocumentsStore.has(id)) {
return {
statusCode: 404,
body: new ErrorResponse(
"DocumentNotFound",
"A document with the specified id was not found or has not been accepted."
)
};
}
const documentItem = acceptedDocumentsStore.get(id);
if (contractId != documentItem.contractId) {
// Something is amiss. The values should match.
return {
statusCode: 400,
body: new ErrorResponse(
"ContractIdMismatch",
`The contractId value specified in the URL ${contractId} and that in the document ${documentItem.contractId} don't match.`
)
};
}
// Attestation report and report data values are verified.
// Wrap the document with the encryption key before returning it.
const seqno = acceptedDocumentsStore.getVersionOfPreviousWrite(id);
const view = ccf.consensus.getViewForSeqno(seqno);
if (view == null) {
return {
statusCode: 503,
body: new ErrorResponse(
"ViewNotKnown",
"View for given sequence number not known to the node at this time."
)
};
}
const version = view + "." + seqno;
const finalVotes: { memberId: string; vote: boolean }[] = [];
if (documentItem.finalVotes !== undefined) {
for (const [memberId, vote] of Object.entries(documentItem.finalVotes)) {
finalVotes.push({
memberId: memberId,
vote: vote === true ? true : false
});
}
}
const body: GetDocumentResponse = {
id: id,
version: version,
state: "Accepted",
contractId: documentItem.contractId,
data: documentItem.data,
proposalId: documentItem.proposalId,
finalVotes: finalVotes
};
const wrapAlgo = {
name: "RSA-OAEP-AES-KWP",
aesKeySize: 256
} as RsaOaepAesKwpParams;
const wrapped: ArrayBuffer = ccf.crypto.wrapKey(
ccf.jsonCompatibleToBuf(body),
ccf.strToBuf(Base64.decode(requestBody.encrypt.publicKey)),
wrapAlgo
);
const wrappedBase64 = Base64.fromUint8Array(new Uint8Array(wrapped));
return {
statusCode: 200,
body: {
value: wrappedBase64
}
};
}