Bigchaindb Immutable Log API

Doğukan HAN
5 min readNov 20, 2022

--

Bigchaindb is a blockchain structuctured database system helps to create blockchain based applications.

Facts of bigchaindb

You can access here https://docs.bigchaindb.com/en/latest/about-bigchaindb.html

  1. One can store arbitrary data (including encrypted data) in a BigchainDB network, within limits: there’s a maximum transaction size. Every transaction has a metadata section which can store almost any Unicode string (up to some maximum length). Similarly, every CREATE transaction has an asset.data section which can store almost any Unicode string.
  2. The data stored in certain BigchainDB transaction fields must not be encrypted, e.g. public keys and amounts. BigchainDB doesn’t offer private transactions akin to Zcoin.
  3. Once data has been stored in a BigchainDB network, it’s best to assume it can’t be change or deleted.
  4. Every node in a BigchainDB network has a full copy of all the stored data.
  5. Every node in a BigchainDB network can read all the stored data.
  6. Everyone with full access to a BigchainDB node (e.g. the sysadmin of a node) can read all the data stored on that node.
  7. Everyone given access to a node via the BigchainDB HTTP API can find and read all the data stored by BigchainDB. The list of people with access might be quite short.
  8. If the connection between an external user and a BigchainDB node isn’t encrypted (using HTTPS, for example), then a wiretapper can read all HTTP requests and responses in transit.
  9. If someone gets access to plaintext (regardless of where they got it), then they can (in principle) share it with the whole world. One can make it difficult for them to do that, e.g. if it is a lot of data and they only get access inside a secure room where they are searched as they leave the room.

There are many ways to run bigchaindb but the easiest one is using docker. Run bigchaindb in docker with this commands.

$ docker pull bigchaindb/bigchaindb:all-in-one

$ docker run \
--detach \
--name bigchaindb \
--publish 9984:9984 \
--publish 9985:9985 \
--publish 27017:27017 \
--publish 26657:26657 \
--volume $HOME/bigchaindb_docker/mongodb/data/db:/data/db \
--volume $HOME/bigchaindb_docker/mongodb/data/configdb:/data/configdb \
--volume $HOME/bigchaindb_docker/tendermint:/tendermint \
bigchaindb/bigchaindb:all-in-one

Add this dependency to the project so we can access bigchaindb easly.

<dependency>
<groupId>com.bigchaindb</groupId>
<artifactId>bigchaindb-driver</artifactId>
<version>${bigchaindb.version}</version>
</dependency>

Log class

public class Log implements Serializable {

private String id;

private String title;

private String content;
}

Create A LogRepository

@Repository
public interface LogRepository {
Log save(Log log);

List<Log> search(String searchKey);
}

Creating restful controller

@RequestMapping("/log")
@RestController
public class LogController {

private final LogRepository repository;

public LogController(LogRepository repository) {
this.repository = repository;
}

@PostMapping
public Log saveLog(@RequestBody Log log) {
return repository.save(log);
}

@GetMapping("/{searchKey}")
public List<Log> list(@PathVariable("searchKey") String searchKey) {
return repository.search(searchKey);
}
}

Create Implementation of LogRepository

@Component
public class LogRepositoryImpl implements LogRepository {
}

Define keys and create a connection in logrepository

private static final Logger LOGGER = LoggerFactory.getLogger(LogRepositoryImpl.class);
private static final String PUBLIC_KEY = "302a300506032b657003210033c43dc2180936a2a9138a05f06c892d2fb1cfda4562cbc35373bf13cd8ed373";
private static final String PRIVATE_KEY = "302e020100300506032b6570042204206f6b0cd095f1e83fc5f08bffb79c7c8a30e77a3ab65f4bc659026b76394fcea8";


private KeyPair keyPair;

public void setKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
}

public LogRepositoryImpl() {
buildKeys();
BigchainDbConfigBuilder
.baseUrl("http://localhost:9984/")
.setup();
}

Implementation of saving asset

  @Override
public Log save(Log log) {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> assetData = mapper.convertValue(log, Map.class);
Transaction createTransaction = BigchainDbTransactionBuilder
.init()
.addAssets(assetData, TreeMap.class)
.operation(Operations.CREATE)
.buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate())
.sendTransaction();
return log;
}

Implementation of searching in assets.

@Override
public List<Log> search(String searchKey) {
List<Log> logList = new ArrayList<>();

Assets assets = AssetsApi.getAssets(searchKey);
List<Asset> assetsList = assets.getAssets();
assetsList.forEach(asset -> {
Log log = stringToObject(tojson(asset.getData()), Log.class);
logList.add(log);
});

return logList;
}

Other utility function in this file

private void buildKeys() {
try {
X509EncodedKeySpec encoded = new X509EncodedKeySpec(Utils.hexToBytes(PUBLIC_KEY));
EdDSAPublicKey keyIn = new EdDSAPublicKey(encoded);
PKCS8EncodedKeySpec encodedP = new PKCS8EncodedKeySpec(Utils.hexToBytes(PRIVATE_KEY));
EdDSAPrivateKey keyPr = new EdDSAPrivateKey(encodedP);
setKeyPair(new KeyPair(keyIn, keyPr));
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
}
}


private String tojson(Object obj) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
return "";
}
}

public static <T> T stringToObject(String s, Class<T> clazz) {
return new Gson().fromJson(s, clazz);
}

Full content of LogRepository.

package com.bmukorera.tutorial.bigchainspringboot.repository;

import com.bigchaindb.api.AssetsApi;
import com.bigchaindb.builders.BigchainDbConfigBuilder;
import com.bigchaindb.builders.BigchainDbTransactionBuilder;
import com.bigchaindb.constants.Operations;
import com.bigchaindb.model.Asset;
import com.bigchaindb.model.Assets;
import com.bigchaindb.model.Transaction;
import com.bmukorera.tutorial.bigchainspringboot.model.Log;
import com.bmukorera.tutorial.bigchainspringboot.repository.exception.TransactionException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.security.KeyPair;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

@Component
public class LogRepositoryImpl implements LogRepository {
private static final Logger LOGGER = LoggerFactory.getLogger(LogRepositoryImpl.class);
private static final String PUBLIC_KEY = "302a300506032b657003210033c43dc2180936a2a9138a05f06c892d2fb1cfda4562cbc35373bf13cd8ed373";
private static final String PRIVATE_KEY = "302e020100300506032b6570042204206f6b0cd095f1e83fc5f08bffb79c7c8a30e77a3ab65f4bc659026b76394fcea8";


private KeyPair keyPair;

public void setKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
}

public LogRepositoryImpl() {
buildKeys();
BigchainDbConfigBuilder
.baseUrl("http://localhost:9984/")
.setup();
}

@Override
public Log save(Log log) {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> assetData = mapper.convertValue(log, Map.class);
try {
Transaction createTransaction = BigchainDbTransactionBuilder
.init()
.addAssets(assetData, TreeMap.class)
.operation(Operations.CREATE)
.buildAndSign((EdDSAPublicKey) keyPair.getPublic(), (EdDSAPrivateKey) keyPair.getPrivate())
.sendTransaction();
if (LOGGER.isDebugEnabled()) {
LOGGER.info("transaction created \n{}", createTransaction);
}
} catch (IOException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
throw new TransactionException(e);
}
return log;
}

@Override
public List<Log> search(String searchKey) {
List<Log> logList = new ArrayList<>();
try {
Assets assets = AssetsApi.getAssets(searchKey);
List<Asset> assetsList = assets.getAssets();
assetsList.forEach(asset -> {
try {
Log log = stringToObject(tojson(asset.getData()), Log.class);
logList.add(log);
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("not instance of Log {}", e.getMessage());
}
}
});
} catch (IOException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
throw new TransactionException(e);
}
return logList;
}

/* builds the KeyPair required for accessing and saving transaction to the blockchain using the public and private keys defined*/
private void buildKeys() {
try {
X509EncodedKeySpec encoded = new X509EncodedKeySpec(Utils.hexToBytes(PUBLIC_KEY));
EdDSAPublicKey keyIn = new EdDSAPublicKey(encoded);
PKCS8EncodedKeySpec encodedP = new PKCS8EncodedKeySpec(Utils.hexToBytes(PRIVATE_KEY));
EdDSAPrivateKey keyPr = new EdDSAPrivateKey(encodedP);
setKeyPair(new KeyPair(keyIn, keyPr));
} catch (Exception e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
}
}


private String tojson(Object obj) {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.error("{}", e);
}
return "";
}
}

public static <T> T stringToObject(String s, Class<T> clazz) {
return new Gson().fromJson(s, clazz);
}
}

Testing

Creating a log(assest)

Searching inside assets

Resources

https://github.com/bmukorera/bigchain_springboot_tutorial

--

--