use of com.amazon.ion.IonStruct in project amazon-qldb-dmv-sample-java by aws-samples.
the class GetRevision method queryRegistrationsByVin.
/**
* Query the registration history for the given VIN.
*
* @param txn
* The {@link TransactionExecutor} for lambda execute.
* @param vin
* The unique VIN to query.
* @return a list of {@link IonStruct} representing the registration history.
* @throws IllegalStateException if failed to convert parameters into {@link IonValue}
*/
public static List<IonStruct> queryRegistrationsByVin(final TransactionExecutor txn, final String vin) {
log.info(String.format("Let's query the 'VehicleRegistration' table for VIN: %s...", vin));
log.info("Let's query the 'VehicleRegistration' table for VIN: {}...", vin);
final String query = String.format("SELECT * FROM _ql_committed_%s WHERE data.VIN = ?", Constants.VEHICLE_REGISTRATION_TABLE_NAME);
try {
final List<IonValue> parameters = Collections.singletonList(Constants.MAPPER.writeValueAsIonValue(vin));
final Result result = txn.execute(query, parameters);
List<IonStruct> list = ScanTable.toIonStructs(result);
log.info(String.format("Found %d document(s)!", list.size()));
return list;
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
use of com.amazon.ion.IonStruct in project amazon-qldb-dmv-sample-java by aws-samples.
the class GetRevision method verifyRegistration.
/**
* Verify each version of the registration for the given VIN.
*
* @param driver
* A QLDB driver.
* @param ledgerName
* The ledger to get digest from.
* @param vin
* VIN to query the revision history of a specific registration with.
* @throws Exception if failed to verify digests.
* @throws AssertionError if document revision verification failed.
*/
public static void verifyRegistration(final QldbDriver driver, final String ledgerName, final String vin) throws Exception {
log.info(String.format("Let's verify the registration with VIN=%s, in ledger=%s.", vin, ledgerName));
try {
log.info("First, let's get a digest.");
GetDigestResult digestResult = GetDigest.getDigest(ledgerName);
ValueHolder digestTipAddress = digestResult.getDigestTipAddress();
byte[] digestBytes = Verifier.convertByteBufferToByteArray(digestResult.getDigest());
log.info("Got a ledger digest. Digest end address={}, digest={}.", QldbStringUtils.toUnredactedString(digestTipAddress), Verifier.toBase64(digestBytes));
log.info(String.format("Next, let's query the registration with VIN=%s. " + "Then we can verify each version of the registration.", vin));
List<IonStruct> documentsWithMetadataList = new ArrayList<>();
driver.execute(txn -> {
documentsWithMetadataList.addAll(queryRegistrationsByVin(txn, vin));
});
log.info("Registrations queried successfully!");
log.info(String.format("Found %s revisions of the registration with VIN=%s.", documentsWithMetadataList.size(), vin));
for (IonStruct ionStruct : documentsWithMetadataList) {
QldbRevision document = QldbRevision.fromIon(ionStruct);
log.info(String.format("Let's verify the document: %s", document));
log.info("Let's get a proof for the document.");
GetRevisionResult proofResult = getRevision(ledgerName, document.getMetadata().getId(), digestTipAddress, document.getBlockAddress());
final IonValue proof = Constants.MAPPER.writeValueAsIonValue(proofResult.getProof());
final IonReader reader = IonReaderBuilder.standard().build(proof);
reader.next();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IonWriter writer = SYSTEM.newBinaryWriter(baos);
writer.writeValue(reader);
writer.close();
baos.flush();
baos.close();
byte[] byteProof = baos.toByteArray();
log.info(String.format("Got back a proof: %s", Verifier.toBase64(byteProof)));
boolean verified = Verifier.verify(document.getHash(), digestBytes, proofResult.getProof().getIonText());
if (!verified) {
throw new AssertionError("Document revision is not verified!");
} else {
log.info("Success! The document is verified");
}
byte[] alteredDigest = Verifier.flipRandomBit(digestBytes);
log.info(String.format("Flipping one bit in the digest and assert that the document is NOT verified. " + "The altered digest is: %s", Verifier.toBase64(alteredDigest)));
verified = Verifier.verify(document.getHash(), alteredDigest, proofResult.getProof().getIonText());
if (verified) {
throw new AssertionError("Expected document to not be verified against altered digest.");
} else {
log.info("Success! As expected flipping a bit in the digest causes verification to fail.");
}
byte[] alteredDocumentHash = Verifier.flipRandomBit(document.getHash());
log.info(String.format("Flipping one bit in the document's hash and assert that it is NOT verified. " + "The altered document hash is: %s.", Verifier.toBase64(alteredDocumentHash)));
verified = Verifier.verify(alteredDocumentHash, digestBytes, proofResult.getProof().getIonText());
if (verified) {
throw new AssertionError("Expected altered document hash to not be verified against digest.");
} else {
log.info("Success! As expected flipping a bit in the document hash causes verification to fail.");
}
}
} catch (Exception e) {
log.error("Failed to verify digests.", e);
throw e;
}
log.info(String.format("Finished verifying the registration with VIN=%s in ledger=%s.", vin, ledgerName));
}
use of com.amazon.ion.IonStruct in project amazon-qldb-dmv-sample-java by aws-samples.
the class JournalS3ExportReader method getDataFileKeysFromManifest.
/**
* Given the S3Object to the completed manifest file, return the keys
* which are part of this export request.
*
* @param s3Object
* A {@link S3Object}.
* @return a list of data file keys containing the chunk of {@link JournalBlock}.
*/
private static List<String> getDataFileKeysFromManifest(final S3Object s3Object) {
IonReader ionReader = IonReaderBuilder.standard().build(s3Object.getObjectContent());
// Read the data
ionReader.next();
List<String> keys = new ArrayList<>();
IonStruct ionStruct = (IonStruct) SYSTEM.newValue(ionReader);
IonList ionKeysList = (IonList) ionStruct.get("keys");
ionKeysList.forEach(key -> keys.add(((IonString) key).stringValue()));
return keys;
}
use of com.amazon.ion.IonStruct in project amazon-qldb-dmv-sample-java by aws-samples.
the class QldbRevision method fromIon.
/**
* Constructs a new {@link QldbRevision} from an {@link IonStruct}.
*
* The specified {@link IonStruct} must include the following fields
*
* - blockAddress -- a {@link BlockAddress},
* - metadata -- a {@link RevisionMetadata},
* - hash -- the document's hash calculated by QLDB,
* - data -- an {@link IonStruct} containing user data in the document.
*
* If any of these fields are missing or are malformed, then throws {@link IllegalArgumentException}.
*
* If the document hash calculated from the members of the specified {@link IonStruct} does not match
* the hash member of the {@link IonStruct} then throws {@link IllegalArgumentException}.
*
* @param ionStruct
* The {@link IonStruct} that contains a {@link QldbRevision} object.
* @return the converted {@link QldbRevision} object.
* @throws IOException if failed to parse parameter {@link IonStruct}.
*/
public static QldbRevision fromIon(final IonStruct ionStruct) throws IOException {
try {
BlockAddress blockAddress = Constants.MAPPER.readValue(ionStruct.get("blockAddress"), BlockAddress.class);
IonBlob hash = (IonBlob) ionStruct.get("hash");
IonStruct metadataStruct = (IonStruct) ionStruct.get("metadata");
IonStruct data = (IonStruct) ionStruct.get("data");
if (hash == null || data == null) {
throw new IllegalArgumentException("Document is missing required fields");
}
verifyRevisionHash(metadataStruct, data, hash.getBytes());
RevisionMetadata metadata = RevisionMetadata.fromIon(metadataStruct);
return new QldbRevision(blockAddress, metadata, hash.getBytes(), data);
} catch (ClassCastException e) {
log.error("Failed to parse ion document");
throw new IllegalArgumentException("Document members are not of the correct type", e);
}
}
use of com.amazon.ion.IonStruct in project amazon-qldb-dmv-sample-java by aws-samples.
the class StreamJournal method streamRecordsToJournalBlocks.
private static List<JournalBlock> streamRecordsToJournalBlocks() {
Map<ByteBuffer, QldbRevision> revisionsByHash = new HashMap<>();
recordBuffer.stream().filter(record -> record.getRecordType().equals("REVISION_DETAILS")).forEach(record -> {
try {
Revision revision = ((RevisionDetailsRecord) record.getPayload()).getRevision();
byte[] revisionHash = revision.getHash();
revisionsByHash.put(wrap(revisionHash).asReadOnlyBuffer(), QldbRevision.fromIon((IonStruct) Constants.MAPPER.writeValueAsIonValue(revision)));
} catch (IOException e) {
throw new IllegalArgumentException("Could not map RevisionDetailsRecord to QldbRevision.", e);
}
});
return recordBuffer.stream().filter(streamRecord -> streamRecord.getRecordType().equals("BLOCK_SUMMARY")).map(streamRecord -> (BlockSummaryRecord) streamRecord.getPayload()).distinct().map(blockSummaryRecord -> blockSummaryRecordToJournalBlock(blockSummaryRecord, revisionsByHash)).sorted(Comparator.comparingLong(o -> o.getBlockAddress().getSequenceNo())).collect(Collectors.toList());
}
Aggregations