Skip to content

Utilities

A handful of helpers turn up in nearly every application that works with Transactions and scripts. They live in Utils, Sha256Hash, Base58 and VarInt.

byte[] bytes = Utils.HEX.decode("deadbeef"); // lower-case hex, Guava BaseEncoding
String hex = Utils.HEX.encode(bytes);
byte[] reversed = Utils.reverseBytes(bytes); // for switching between internal and display byte order

Utils.HEX is a Guava BaseEncoding, and writes lower-case hex. decode() throws an IllegalArgumentException for a string of odd length, or one with characters that are not hex.

Hashes such as Transaction ids and block hashes are shown with their bytes reversed. reverseBytes() is what we use to go between the internal order and the display order.

byte[] data = "hello".getBytes(StandardCharsets.UTF_8);
byte[] sha256 = Sha256Hash.hash(data);
byte[] doubleSha256 = Sha256Hash.hashTwice(data); // the hash behind txids and block hashes
byte[] hash160 = Utils.sha256hash160(data); // RIPEMD160(SHA256(data)), as in Addresses

In the preceding example:

  • Sha256Hash.hash() is a single SHA-256,
  • Sha256Hash.hashTwice() is the double SHA-256 behind Transaction ids, block hashes and Signatures,
  • Utils.sha256hash160() is RIPEMD-160 over SHA-256, the 20-byte hash inside a P2PKH Address and Locking Script.

Sha256Hash is also a class in its own right, wrapping a 32-byte hash. Sha256Hash.wrap(hex) and toString() read and write it as hex.

byte[] payload = Utils.HEX.decode("94837d2d5d6106aa97db38957dcc294181ee91e9");
// A version byte, the payload and a 4-byte checksum. It is the encoding Addresses and WIF keys use.
String encoded = Base58.encodeChecked(111, payload);
byte[] decoded = Base58.decodeChecked(encoded); // version byte followed by the payload

decodeChecked() verifies the checksum, and throws an AddressFormatException if it does not match. Base58.encode() and Base58.decode() do the same without a version byte or checksum. For Addresses and WIF keys we use the Address and key classes, which call these for us.

Reading and writing variable-length integers

Section titled “Reading and writing variable-length integers”

Serialised Transactions and blocks write counts and lengths as variable-length integers. Values below 0xfd take one byte, and larger ones take a marker byte followed by 2, 4 or 8 bytes.

// Bitcoin's variable-length integer, used for counts and lengths in serialised data.
byte[] encoded = new VarInt(515).encode();
long value = new VarInt(encoded, 0).value;

getSizeInBytes() tells us how many bytes an encoded value takes, which is handy when one parses a format by hand.