mirror of
https://github.com/corda/corda.git
synced 2025-04-07 11:27:01 +00:00
Blob inspector: add support for hex and base64 inputs.
Also rename the file to make it easier to navigate to it in IntelliJ by keyboard.
This commit is contained in:
parent
21754e4323
commit
20c03d2196
@ -8,25 +8,48 @@ by allowing the contents of a binary blob file (or URL end-point) to be output i
|
||||
|
||||
The latest version of the tool can be downloaded from `here <https://www.corda.net/downloads/>`_.
|
||||
|
||||
To run simply pass in the file or URL as the first parameter:
|
||||
To run simply pass in the file or URL as the first parameter::
|
||||
|
||||
``java -jar blob-inspector.jar <file or URL>``
|
||||
java -jar blob-inspector.jar <file or URL>
|
||||
|
||||
Use the ``--help`` flag for a full list of command line options.
|
||||
|
||||
When inspecting your custom data structures, there's no need to include the jars containing the class definitions for them
|
||||
in the classpath. The blob inspector (or rather the serialization framework) is able to synthesis any classes found in the
|
||||
in the classpath. The blob inspector (or rather the serialization framework) is able to synthesize any classes found in the
|
||||
blob that aren't on the classpath.
|
||||
|
||||
SerializedBytes
|
||||
~~~~~~~~~~~~~~~
|
||||
Supported formats
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
One thing to note is that the binary blob may contain embedded ``SerializedBytes`` objects. Rather than printing these
|
||||
out as a Base64 string, the blob inspector will first materialise them into Java objects and then output those. You will
|
||||
see this when dealing with classes such as ``SignedData`` or other structures that attach a signature, such as the
|
||||
``nodeInfo-*`` files or the ``network-parameters`` file in the node's directory. For example, the output of a node-info
|
||||
file may look like:
|
||||
The inspector can read **input data** in three formats: raw binary, hex encoded text and base64 encoded text. For instance
|
||||
if you have retrieved your binary data and it looks like this::
|
||||
|
||||
636f7264610100000080c562000000000001d0000030720000000300a3226e65742e636f7264613a38674f537471464b414a5055...
|
||||
|
||||
then you have hex encoded data. If it looks like this it's base64 encoded::
|
||||
|
||||
Y29yZGEBAAAAgMViAAAAAAAB0AAAMHIAAAADAKMibmV0LmNvcmRhOjhnT1N0cUZLQUpQVWVvY2Z2M1NlU1E9PdAAACc1AAAAAgCjIm5l...
|
||||
|
||||
And if it looks like something vomited over your screen it's raw binary. You don't normally need to care about these
|
||||
differences because the tool will try every format until it works.
|
||||
|
||||
Something that's useful to know about Corda's format is that it always starts with the word "corda" in binary. Try
|
||||
hex decoding 636f726461 using the `online hex decoder tool here <https://convertstring.com/EncodeDecode/HexDecode>`_
|
||||
to see for yourself.
|
||||
|
||||
**Output data** can be in either a slightly extended form of YaML or JSON. YaML (Yet another markup language) is a bit
|
||||
easier to read for humans and is the default. JSON can of course be parsed by any JSON library in any language.
|
||||
|
||||
.. note:: One thing to note is that the binary blob may contain embedded ``SerializedBytes`` objects. Rather than printing these
|
||||
out as a Base64 string, the blob inspector will first materialise them into Java objects and then output those. You will
|
||||
see this when dealing with classes such as ``SignedData`` or other structures that attach a signature, such as the
|
||||
``nodeInfo-*`` files or the ``network-parameters`` file in the node's directory.
|
||||
|
||||
|
||||
Example
|
||||
~~~~~~~
|
||||
|
||||
Here's what a node-info file from the node's data directory may look like:
|
||||
|
||||
**-\\-format=YAML**
|
||||
::
|
||||
|
@ -6,6 +6,7 @@ apply plugin: 'com.jfrog.artifactory'
|
||||
dependencies {
|
||||
compile project(':client:jackson')
|
||||
compile "info.picocli:picocli:$picocli_version"
|
||||
compile "com.google.guava:guava:$guava_version"
|
||||
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
||||
|
@ -2,6 +2,7 @@ package net.corda.blobinspector
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.google.common.io.BaseEncoding
|
||||
import com.jcabi.manifests.Manifests
|
||||
import net.corda.client.jackson.JacksonSupport
|
||||
import net.corda.core.internal.isRegularFile
|
||||
@ -43,18 +44,21 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "Blob Inspector",
|
||||
name = "blob-inspector",
|
||||
versionProvider = CordaVersionProvider::class,
|
||||
mixinStandardHelpOptions = true, // add --help and --version options,
|
||||
mixinStandardHelpOptions = true, // add --help and --version options,
|
||||
showDefaultValues = true,
|
||||
description = ["Inspect AMQP serialised binary blobs"]
|
||||
description = ["Convert AMQP serialised binary blobs to text"]
|
||||
)
|
||||
class BlobInspector : Runnable {
|
||||
@Parameters(index = "0", paramLabel = "SOURCE", description = ["URL or file path to the blob"], converter = [SourceConverter::class])
|
||||
var source: URL? = null
|
||||
private var source: URL? = null
|
||||
|
||||
@Option(names = ["--format"], paramLabel = "type", description = ["Output format. Possible values: [YAML, JSON]"])
|
||||
private var formatType: FormatType = FormatType.YAML
|
||||
private var formatType: OutputFormatType = OutputFormatType.YAML
|
||||
|
||||
@Option(names = ["--input-format"], paramLabel = "type", description = ["Input format. If the file can't be decoded with the given value it's auto-detected, so you should never normally need to specify this. Possible values: [BINARY, HEX, BASE64]"])
|
||||
private var inputFormatType: InputFormatType = InputFormatType.BINARY
|
||||
|
||||
@Option(names = ["--full-parties"],
|
||||
description = ["Display the owningKey and certPath properties of Party and PartyAndReference objects respectively"])
|
||||
@ -73,15 +77,12 @@ class BlobInspector : Runnable {
|
||||
System.setProperty("logLevel", "trace")
|
||||
}
|
||||
|
||||
val bytes = source!!.readBytes().run {
|
||||
require(size > amqpMagic.size) { "Insufficient bytes for AMQP blob" }
|
||||
sequence()
|
||||
}
|
||||
|
||||
require(bytes.take(amqpMagic.size) == amqpMagic) { "Not an AMQP blob" }
|
||||
val inputBytes = source!!.readBytes()
|
||||
val bytes = parseToBinaryRelaxed(inputFormatType, inputBytes)
|
||||
?: throw IllegalArgumentException("Error: this input does not appear to be encoded in Corda's AMQP extended format, sorry.")
|
||||
|
||||
if (schema) {
|
||||
val envelope = DeserializationInput.getEnvelope(bytes)
|
||||
val envelope = DeserializationInput.getEnvelope(bytes.sequence())
|
||||
out.println(envelope.schema)
|
||||
out.println()
|
||||
out.println(envelope.transformsSchema)
|
||||
@ -89,8 +90,8 @@ class BlobInspector : Runnable {
|
||||
}
|
||||
|
||||
val factory = when (formatType) {
|
||||
FormatType.YAML -> YAMLFactory()
|
||||
FormatType.JSON -> JsonFactory()
|
||||
OutputFormatType.YAML -> YAMLFactory()
|
||||
OutputFormatType.JSON -> JsonFactory()
|
||||
}
|
||||
|
||||
val mapper = JacksonSupport.createNonRpcMapper(factory, fullParties)
|
||||
@ -105,6 +106,32 @@ class BlobInspector : Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseToBinaryRelaxed(format: InputFormatType, inputBytes: ByteArray): ByteArray? {
|
||||
// Try the format the user gave us first, then try the others.
|
||||
return parseToBinary(format, inputBytes) ?: parseToBinary(InputFormatType.HEX, inputBytes)
|
||||
?: parseToBinary(InputFormatType.BASE64, inputBytes) ?: parseToBinary(InputFormatType.BINARY, inputBytes)
|
||||
}
|
||||
|
||||
private fun parseToBinary(format: InputFormatType, inputBytes: ByteArray): ByteArray? {
|
||||
try {
|
||||
val bytes = when (format) {
|
||||
InputFormatType.BINARY -> inputBytes
|
||||
InputFormatType.HEX -> BaseEncoding.base16().decode(String(inputBytes).trim().toUpperCase())
|
||||
InputFormatType.BASE64 -> BaseEncoding.base64().decode(String(inputBytes).trim())
|
||||
}
|
||||
require(bytes.size > amqpMagic.size) { "Insufficient bytes for AMQP blob" }
|
||||
return if (bytes.copyOf(amqpMagic.size).contentEquals(amqpMagic.bytes)) {
|
||||
if (verbose)
|
||||
println("Parsing input as $format")
|
||||
bytes
|
||||
} else {
|
||||
null // Not an AMQP blob.
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
return null // Failed to parse in some other way.
|
||||
}
|
||||
}
|
||||
|
||||
private fun initialiseSerialization() {
|
||||
// Deserialise with the lenient carpenter as we only care for the AMQP field getters
|
||||
_contextSerializationEnv.set(SerializationEnvironmentImpl(
|
||||
@ -121,6 +148,7 @@ private object AMQPInspectorSerializationScheme : AbstractAMQPSerializationSchem
|
||||
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean {
|
||||
return magic == amqpMagic
|
||||
}
|
||||
|
||||
override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
|
||||
override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
|
||||
}
|
||||
@ -146,5 +174,6 @@ private class CordaVersionProvider : IVersionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private enum class FormatType { YAML, JSON }
|
||||
private enum class OutputFormatType { YAML, JSON }
|
||||
private enum class InputFormatType { BINARY, HEX, BASE64 }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user