For infinite number of reasons it might be interesting to access transaction history stored on the Tangle. I tried to do that myself, because I was interested in how many addresses are at risk because of reuse, but right now I'm stuck and I don't know how to move on. Here is what I did so far.
- I don't run a full node, so I downloaded the Tangle database from http://iota.partners (step 6. Database)
- I studied IRI source code a bit and discovered it's using RocksDB as the database engine, so I created a new java project and referenced RocksJava and IRI.
Struggled for a while how to actually make it work, but I was finally able to read some data from the database. Here is my source code so far (I was just trying to make it work, don't expect any pretty coding here):
RocksDB.loadLibrary(); final String databasePath = "d:\\tmp\\mainnetdb\\"; final List<String> columnFamilyNames = Arrays.asList("transaction", "transaction-metadata", "milestone", "stateDiff", "address", "approvee", "bundle", "tag"); final List<ColumnFamilyDescriptor> myColumnFamilyDescriptors = columnFamilyNames.stream() .map((x) -> new ColumnFamilyDescriptor(x.getBytes())).collect(Collectors.toList()); final List<ColumnFamilyDescriptor> columnFamilyDescriptors = new ArrayList<>(); columnFamilyDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)); columnFamilyDescriptors.addAll(myColumnFamilyDescriptors); // a list which will hold the handles for the column families once the db is // opened final List<ColumnFamilyHandle> columnFamilyHandles = new ArrayList<>(); try (final DBOptions options = new DBOptions().setCreateIfMissing(false) .setCreateMissingColumnFamilies(false)) { try (RocksDB db = RocksDB.openReadOnly(options, databasePath, columnFamilyDescriptors, columnFamilyHandles)) { try { for (int f = 0; f < columnFamilyHandles.size(); f++) { ColumnFamilyHandle cfh = columnFamilyHandles.get(f); RocksIterator it = db.newIterator(cfh); int i = 0; for (it.seekToFirst(); it.isValid(); it.next()) { i++; } if (f > 0) { // skip default System.out.println(columnFamilyNames.get(f - 1) + ": " + i); } } } finally { db.close(); } } finally { options.close(); } }
This is supposed to iterate over column families (similar to tables in relational db) and print the number of items in each of them. The output I'm getting is:
transaction: 19 transaction-metadata: 22 milestone: 13661 stateDiff: 0 address: 0 approvee: 0 bundle: 0 tag: 0
For the transactions that are actually present, I'm able to call https://github.com/iotaledger/iri/blob/dev/src/main/java/com/iota/iri/model/Transaction.java#L91 to deserialize the
byte[]
and convert it to usable data, but ...
Why are there so few transactions? The database is around 9-10 GB, so I would expect that to be the entire Tangle history, or am I wrong? Am I missing something here? Or am I accessing the database incorrectly?
PROGRESS: So after checking out the source for Python IOTA library here: https://github.com/mlouielu/iota-python/blob/dev/iotapy/storage/providers/rocksdb.py#L70 (that code is actually not used, but anyway ...) I tried to set merge operators through ColumnFamilyOptions:
final ColumnFamilyOptions cfo = new ColumnFamilyOptions().setMergeOperatorName("stringappend");
final List<ColumnFamilyDescriptor> myColumnFamilyDescriptors = columnFamilyNames.stream()
.map((x) -> new ColumnFamilyDescriptor(x.getBytes(), cfo)).collect(Collectors.toList());
and now it's producing different output for these column families:
address: 19
approvee: 25
bundle: 31
tag: 20