diff --git a/pom.xml b/pom.xml
index 7a781439..e9cbf1d4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,6 +7,13 @@
 	<packaging>jar</packaging>
 	<properties>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<bouncycastle.version>1.60</bouncycastle.version>
+		<hsqldb.version>r5836</hsqldb.version>
+		<jetty.version>9.4.11.v20180605</jetty.version>
+		<jersey.version>2.27</jersey.version>
+		<log4j.version>2.11.0</log4j.version>
+		<slf4j.version>1.7.12</slf4j.version>
+		<swagger-api.version>2.0.6</swagger-api.version>
 		<swagger-ui.version>3.19.0</swagger-ui.version>
 	</properties>
 	<build>
@@ -156,105 +163,144 @@
 		</repository>
 	</repositories>
 	<dependencies>
+		<!-- HSQLDB for repository -->
 		<dependency>
 			<groupId>org.hsqldb</groupId>
 			<artifactId>hsqldb</artifactId>
-			<version>r5836</version>
+			<version>${hsqldb.version}</version>
 		</dependency>
+		<!-- CIYAM AT (automated transactions) -->
+		<dependency>
+			<groupId>org.ciyam</groupId>
+			<artifactId>at</artifactId>
+			<version>1.0</version>
+		</dependency>
+		<!-- Bitcoin support -->
+		<dependency>
+			<groupId>org.bitcoinj</groupId>
+			<artifactId>bitcoinj-core</artifactId>
+			<version>0.14.7</version>
+		</dependency>
+		<!-- Utilities -->
 		<dependency>
 			<groupId>com.googlecode.json-simple</groupId>
 			<artifactId>json-simple</artifactId>
 			<version>1.1.1</version>
 		</dependency>
-		<dependency>
-			<groupId>com.google.guava</groupId>
-			<artifactId>guava</artifactId>
-			<version>25.0-jre</version>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.logging.log4j</groupId>
-			<artifactId>log4j-core</artifactId>
-			<version>2.11.0</version>
-		</dependency>
-		<dependency>
-			<groupId>org.apache.logging.log4j</groupId>
-			<artifactId>log4j-api</artifactId>
-			<version>2.11.0</version>
-		</dependency>
-		<dependency>
-			<groupId>commons-net</groupId>
-			<artifactId>commons-net</artifactId>
-			<version>3.3</version>
-		</dependency>
-		<dependency>
-			<groupId>org.glassfish.jersey.core</groupId>
-			<artifactId>jersey-server</artifactId>
-			<version>2.27</version>
-		</dependency>
-		<dependency>
-			<groupId>javax.servlet</groupId>
-			<artifactId>javax.servlet-api</artifactId>
-			<version>4.0.1</version>
-		</dependency>
-		<dependency>
-			<groupId>org.eclipse.jetty</groupId>
-			<artifactId>jetty-server</artifactId>
-			<version>9.4.11.v20180605</version>
-			<classifier>config</classifier>
-		</dependency>
-		<dependency>
-			<groupId>org.glassfish.jersey.containers</groupId>
-			<artifactId>jersey-container-servlet-core</artifactId>
-			<version>2.27</version>
-		</dependency>
-		<dependency>
-			<groupId>org.eclipse.jetty</groupId>
-			<artifactId>jetty-servlet</artifactId>
-			<version>9.4.11.v20180605</version>
-			<type>jar</type>
-		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-servlets -->
-		<dependency>
-			<groupId>org.eclipse.jetty</groupId>
-			<artifactId>jetty-servlets</artifactId>
-			<version>9.4.11.v20180605</version>
-		</dependency>
-		<dependency>
-			<groupId>org.glassfish.jersey.inject</groupId>
-			<artifactId>jersey-hk2</artifactId>
-			<version>2.27</version>
-		</dependency>
-		<dependency>
-			<groupId>io.swagger.core.v3</groupId>
-			<artifactId>swagger-jaxrs2</artifactId>
-			<version>2.0.4</version>
-		</dependency>
-		<dependency>
-			<groupId>io.swagger.core.v3</groupId>
-			<artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
-			<version>2.0.4</version>
-		</dependency>
 		<dependency>
 			<groupId>org.apache.commons</groupId>
 			<artifactId>commons-text</artifactId>
 			<version>1.4</version>
 		</dependency>
+		<dependency>
+			<groupId>commons-net</groupId>
+			<artifactId>commons-net</artifactId>
+			<version>3.3</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>25.0-jre</version>
+		</dependency>
+		<!-- Logging: log4j2 -->
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-core</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-api</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<!-- Logging: slf4j used by Jetty -->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>${slf4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-simple</artifactId>
+			<version>${slf4j.version}</version>
+		</dependency>
+		<!-- Servlet related -->
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<version>4.0.1</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.mail</groupId>
+			<artifactId>mail</artifactId>
+			<version>1.5.0-b01</version>
+		</dependency>
+		<!-- Jetty -->
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-server</artifactId>
+			<version>${jetty.version}</version>
+			<classifier>config</classifier>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-servlet</artifactId>
+			<version>${jetty.version}</version>
+			<type>jar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-servlets</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-rewrite</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		<!-- Jersey -->
+		<dependency>
+			<groupId>org.glassfish.jersey.core</groupId>
+			<artifactId>jersey-server</artifactId>
+			<version>${jersey.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.jersey.containers</groupId>
+			<artifactId>jersey-container-servlet-core</artifactId>
+			<version>${jersey.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.jersey.inject</groupId>
+			<artifactId>jersey-hk2</artifactId>
+			<version>${jersey.version}</version>
+		</dependency>
 		<dependency>
 			<groupId>org.glassfish.jersey.media</groupId>
 			<artifactId>jersey-media-moxy</artifactId>
-			<version>2.27</version>
+			<version>${jersey.version}</version>
 		</dependency>
 		<dependency>
-			<groupId>org.ciyam</groupId>
-			<artifactId>at</artifactId>
-			<version>1.0</version>
+			<groupId>org.glassfish.jersey.media</groupId>
+			<artifactId>jersey-media-multipart</artifactId>
+			<version>${jersey.version}</version>
+		</dependency>
+		<!-- Swagger OpenAPI implementation -->
+		<dependency>
+			<groupId>io.swagger.core.v3</groupId>
+			<artifactId>swagger-jaxrs2</artifactId>
+			<version>${swagger-api.version}</version>
 		</dependency>
 		<dependency>
-			<groupId>org.hsqldb</groupId>
-			<artifactId>sqltool</artifactId>
-			<version>2.4.1</version>
-			<scope>test</scope>
+			<groupId>io.swagger.core.v3</groupId>
+			<artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
+			<version>${swagger-api.version}</version>
 		</dependency>
+		<dependency>
+			<groupId>org.webjars</groupId>
+			<artifactId>swagger-ui</artifactId>
+			<version>${swagger-ui.version}</version>
+		</dependency>
+		<!-- Testing -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
 			<artifactId>junit-jupiter-engine</artifactId>
@@ -265,48 +311,11 @@
 			<artifactId>hamcrest-library</artifactId>
 			<version>1.3</version>
 		</dependency>
-		<dependency>
-			<groupId>org.glassfish.jersey.media</groupId>
-			<artifactId>jersey-media-multipart</artifactId>
-			<version>2.27</version>
-		</dependency>
-		<dependency>
-			<groupId>javax.mail</groupId>
-			<artifactId>mail</artifactId>
-			<version>1.5.0-b01</version>
-		</dependency>
-		<dependency>
-			<groupId>org.webjars</groupId>
-			<artifactId>swagger-ui</artifactId>
-			<version>${swagger-ui.version}</version>
-		</dependency>
-		<dependency>
-			<groupId>org.eclipse.jetty</groupId>
-			<artifactId>jetty-rewrite</artifactId>
-			<version>9.4.11.v20180605</version>
-		</dependency>
-		<dependency>
-			<groupId>org.bitcoinj</groupId>
-			<artifactId>bitcoinj-core</artifactId>
-			<version>0.14.7</version>
-		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
+		<!-- BouncyCastle for crypto, including TLS secure networking -->
 		<dependency>
 			<groupId>org.bouncycastle</groupId>
 			<artifactId>bcprov-jdk15on</artifactId>
-			<version>1.60</version>
-		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-api</artifactId>
-			<version>1.7.12</version>
-		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
-		<dependency>
-			<groupId>org.slf4j</groupId>
-			<artifactId>slf4j-simple</artifactId>
-			<version>1.7.12</version>
+			<version>${bouncycastle.version}</version>
 		</dependency>
 	</dependencies>
 </project>
\ No newline at end of file
diff --git a/src/api/AddressesResource.java b/src/api/AddressesResource.java
index dad51a40..9d335e68 100644
--- a/src/api/AddressesResource.java
+++ b/src/api/AddressesResource.java
@@ -81,8 +81,6 @@ public class AddressesResource {
 	public String getLastReference(
 		@Parameter(description = "a base58-encoded address", required = true) @PathParam("address") String address
 	) {
-		Security.checkApiCallAllowed("GET addresses/lastreference", request);
-
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -130,8 +128,6 @@ public class AddressesResource {
 		}
 	)
 	public String getLastReferenceUnconfirmed(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/lastreference", request);
-
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -177,8 +173,6 @@ public class AddressesResource {
 		}
 	)
 	public boolean validate(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/validate", request);
-
 		return Crypto.isValidAddress(address);
 	}
 	
@@ -208,8 +202,6 @@ public class AddressesResource {
 		}
 	)
 	public BigDecimal getGeneratingBalanceOfAddress(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/generatingbalance", request);
-
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -250,8 +242,6 @@ public class AddressesResource {
 		}
 	)
 	public BigDecimal getGeneratingBalance(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/balance", request);
-		
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -291,8 +281,6 @@ public class AddressesResource {
 		}
 	)
 	public BigDecimal getAssetBalance(@PathParam("assetid") long assetid, @PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/assetbalance", request);
-		
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -332,8 +320,6 @@ public class AddressesResource {
 		}
 	)
 	public List<AccountBalanceData> getAssets(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/assets", request);
-		
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -372,8 +358,6 @@ public class AddressesResource {
 		}
 	)
 	public String getGeneratingBalance(@PathParam("address") String address, @PathParam("confirmations") int confirmations) {
-		Security.checkApiCallAllowed("GET addresses/balance", request);
-		
 		throw new UnsupportedOperationException();
 	}
 
@@ -403,8 +387,6 @@ public class AddressesResource {
 		}
 	)
 	public String getPublicKey(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET addresses/publickey", request);
-		
 		throw new UnsupportedOperationException();
 	}
 	
diff --git a/src/api/AdminResource.java b/src/api/AdminResource.java
index 0f8a006d..ffb238f4 100644
--- a/src/api/AdminResource.java
+++ b/src/api/AdminResource.java
@@ -1,7 +1,8 @@
 package api;
 
-import globalization.Translator;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.extensions.Extension;
 import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
 import io.swagger.v3.oas.annotations.media.Content;
@@ -30,16 +31,14 @@ public class AdminResource {
 	@Context
 	HttpServletRequest request;
 
-	private static final long startTime = System.currentTimeMillis();
-
-	private ApiErrorFactory apiErrorFactory;
-
-	public AdminResource() {
-		this(new ApiErrorFactory(Translator.getInstance()));
-	}
-
-	public AdminResource(ApiErrorFactory apiErrorFactory) {
-		this.apiErrorFactory = apiErrorFactory;
+	@GET
+	@Path("/dud")
+	@Parameter(name = "blockSignature", description = "Block signature", schema = @Schema(type = "string", format = "byte", minLength = 84, maxLength=88))
+	@Parameter(in = ParameterIn.QUERY, name = "limit", description = "Maximum number of entries to return", schema = @Schema(type = "integer", defaultValue = "10"))
+	@Parameter(in = ParameterIn.QUERY, name = "offset", description = "Starting entry in results", schema = @Schema(type = "integer"))
+	@Parameter(in = ParameterIn.QUERY, name = "includeTransactions", description = "Include associated transactions in results", schema = @Schema(type = "boolean"))
+	public String globalParameters() {
+		return "";
 	}
 
 	@GET
@@ -65,9 +64,7 @@ public class AdminResource {
 		}
 	)
 	public String uptime() {
-		Security.checkApiCallAllowed("GET admin/uptime", request);
-
-		return Long.toString(System.currentTimeMillis() - startTime);
+		return Long.toString(System.currentTimeMillis() - Controller.startTime);
 	}
 
 	@GET
@@ -93,16 +90,16 @@ public class AdminResource {
 		}
 	)
 	public String shutdown() {
-		Security.checkApiCallAllowed("GET admin/stop", request);
+		Security.checkApiCallAllowed(request);
 
 		new Thread(new Runnable() {
 			@Override
 			public void run() {
 				Controller.shutdown();
 			}
-		}); // disabled for now: .start();
+		}).start();
 
-		return "false";
+		return "true";
 	}
 
 }
diff --git a/src/api/AnnotationPostProcessor.java b/src/api/AnnotationPostProcessor.java
index f34413fe..f4b2f22a 100644
--- a/src/api/AnnotationPostProcessor.java
+++ b/src/api/AnnotationPostProcessor.java
@@ -6,23 +6,31 @@ import globalization.Translator;
 import io.swagger.v3.core.converter.ModelConverters;
 import io.swagger.v3.jaxrs2.Reader;
 import io.swagger.v3.jaxrs2.ReaderListener;
-import io.swagger.v3.oas.models.media.Content;
+import io.swagger.v3.oas.models.Components;
 import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.Operation;
 import io.swagger.v3.oas.models.PathItem;
 import io.swagger.v3.oas.models.examples.Example;
 import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.media.Content;
 import io.swagger.v3.oas.models.media.MediaType;
 import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.Parameter;
 import io.swagger.v3.oas.models.responses.ApiResponse;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 
 public class AnnotationPostProcessor implements ReaderListener {
 
+	private static final Logger LOGGER = LogManager.getLogger(AnnotationPostProcessor.class);
+
 	private class ContextInformation {
 		public String path;
 		public Map<String, String> keys;
@@ -41,10 +49,22 @@ public class AnnotationPostProcessor implements ReaderListener {
 	}
 	
 	@Override
-	public void beforeScan(Reader reader, OpenAPI openAPI) {}
+	public void beforeScan(Reader reader, OpenAPI openAPI) {
+		LOGGER.info("beforeScan");
+	}
 
 	@Override
 	public void afterScan(Reader reader, OpenAPI openAPI) {
+		LOGGER.info("afterScan");
+
+		// Populate Components section with reusable parameters, like "limit" and "offset"
+		// We take the reusable parameters from AdminResource.globalParameters path "/admin/dud"
+		Components components = openAPI.getComponents();
+		PathItem globalParametersPathItem = openAPI.getPaths().get("/admin/dud");
+		if (globalParametersPathItem != null)
+			for (Parameter parameter : globalParametersPathItem.getGet().getParameters())
+				components.addParameters(parameter.getName(), parameter);
+
 		// use context path and keys from "x-translation" extension annotations
 		// to translate supported annotations and finally remove "x-translation" extensions
 		Info resourceInfo = openAPI.getInfo();
diff --git a/src/api/ApiService.java b/src/api/ApiService.java
index f68453e0..2389eff6 100644
--- a/src/api/ApiService.java
+++ b/src/api/ApiService.java
@@ -31,10 +31,12 @@ public class ApiService {
 		this.resources.add(AdminResource.class);
 		this.resources.add(BlocksResource.class);
 		this.resources.add(TransactionsResource.class);
-		this.resources.add(BlockExplorerResource.class);
-		this.resources.add(OpenApiResource.class); // swagger
-		this.resources.add(ApiDefinition.class); // for API definition
-		this.resources.add(AnnotationPostProcessor.class); // for API resource annotations
+		this.resources.add(UtilsResource.class);
+
+		this.resources.add(BlockExplorerResource.class); // block-explorer.html
+		this.resources.add(OpenApiResource.class); // Swagger/OpenAPI
+		this.resources.add(ApiDefinition.class); // API info
+		this.resources.add(AnnotationPostProcessor.class); // For API resource annotations
 		ResourceConfig config = new ResourceConfig(this.resources);
 
 		// Create RPC server
diff --git a/src/api/BlockExplorerResource.java b/src/api/BlockExplorerResource.java
index e8720c11..6944ced0 100644
--- a/src/api/BlockExplorerResource.java
+++ b/src/api/BlockExplorerResource.java
@@ -7,12 +7,11 @@ import java.nio.file.Files;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
+
+import io.swagger.v3.oas.annotations.Operation;
 
 @Path("/")
-@Produces({ MediaType.TEXT_HTML })
 public class BlockExplorerResource {
 
 	@Context
@@ -23,6 +22,7 @@ public class BlockExplorerResource {
 
 	@GET
 	@Path("/block-explorer.html")
+	@Operation(hidden = true)
 	public String getBlockExplorer() {
 		try {
 			byte[] htmlBytes = Files.readAllBytes(FileSystems.getDefault().getPath("block-explorer.html"));
diff --git a/src/api/BlocksResource.java b/src/api/BlocksResource.java
index dfbfed37..271ad9a7 100644
--- a/src/api/BlocksResource.java
+++ b/src/api/BlocksResource.java
@@ -3,6 +3,7 @@ package api;
 import data.block.BlockData;
 import globalization.Translator;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.extensions.Extension;
 import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
 import io.swagger.v3.oas.annotations.media.Content;
@@ -11,19 +12,23 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 
 import java.math.BigDecimal;
+import java.util.Base64;
+
 import javax.servlet.http.HttpServletRequest;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+
+import api.models.BlockWithTransactions;
 import qora.block.Block;
 import repository.DataException;
 import repository.Repository;
 import repository.RepositoryManager;
-import utils.Base58;
 
 @Path("blocks")
 @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
@@ -50,7 +55,7 @@ public class BlocksResource {
 	@GET
 	@Path("/{signature}")
 	@Operation(
-		summary = "Fetch block using base58 signature",
+		summary = "Fetch block using base64 signature",
 		description = "returns the block that matches the given signature",
 		extensions = {
 			@Extension(name = "translation", properties = {
@@ -64,7 +69,7 @@ public class BlocksResource {
 		responses = {
 			@ApiResponse(
 				description = "the block",
-				content = @Content(schema = @Schema(implementation = BlockData.class)),
+				content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -73,34 +78,23 @@ public class BlocksResource {
 			)
 		}
 	)
-	public BlockData getBlock(@PathParam("signature") String signature) {
-		Security.checkApiCallAllowed("GET blocks", request);
-
-		// decode signature
+	public BlockWithTransactions getBlock(@PathParam("signature") String signature, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
+		// Decode signature
 		byte[] signatureBytes;
-		try
-		{
-			signatureBytes = Base58.decode(signature);
-		}
-		catch(Exception e)
-		{
-            throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
+		try {
+			signatureBytes = Base64.getDecoder().decode(signature);
+		} catch (NumberFormatException e) {
+			throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
 		}
 
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
-				
-			// check if block exists
-			if(blockData == null)
-				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
-
-			return blockData;
-
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
+			return new BlockWithTransactions(repository, blockData, includeTransactions);
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -115,7 +109,7 @@ public class BlocksResource {
 		responses = {
 			@ApiResponse(
 				description = "the block",
-				content = @Content(schema = @Schema(implementation = BlockData.class)),
+				content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -124,18 +118,15 @@ public class BlocksResource {
 			)
 		}
 	)
-	public BlockData getFirstBlock() {
-		Security.checkApiCallAllowed("GET blocks/first", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().fromHeight(1);
-			return blockData;
-
+	public BlockWithTransactions getFirstBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().fromHeight(1);
+			return new BlockWithTransactions(repository, blockData, includeTransactions);
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -150,7 +141,7 @@ public class BlocksResource {
 		responses = {
 			@ApiResponse(
 				description = "the block",
-				content = @Content(schema = @Schema(implementation = BlockData.class)),
+				content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -159,24 +150,21 @@ public class BlocksResource {
 			)
 		}
 	)
-	public BlockData getLastBlock() {
-		Security.checkApiCallAllowed("GET blocks/last", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().getLastBlock();
-			return blockData;
-
+	public BlockWithTransactions getLastBlock(@Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().getLastBlock();
+			return new BlockWithTransactions(repository, blockData, includeTransactions);
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
 	@Path("/child/{signature}")
 	@Operation(
-		summary = "Fetch child block using base58 signature of parent block",
+		summary = "Fetch child block using base64 signature of parent block",
 		description = "returns the child block of the block that matches the given signature",
 		extensions = {
 			@Extension(name = "translation", properties = {
@@ -190,7 +178,7 @@ public class BlocksResource {
 		responses = {
 			@ApiResponse(
 				description = "the block",
-				content = @Content(schema = @Schema(implementation = BlockData.class)),
+				content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -199,13 +187,11 @@ public class BlocksResource {
 			)
 		}
 	)
-	public BlockData getChild(@PathParam("signature") String signature) {
-		Security.checkApiCallAllowed("GET blocks/child", request);
-
-		// decode signature
+	public BlockWithTransactions getChild(@PathParam("signature") String signature, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
+		// Decode signature
 		byte[] signatureBytes;
 		try {
-			signatureBytes = Base58.decode(signature);
+			signatureBytes = Base64.getDecoder().decode(signature);
 		} catch (NumberFormatException e) {
 			throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
 		}
@@ -213,17 +199,17 @@ public class BlocksResource {
 		try (final Repository repository = RepositoryManager.getRepository()) {
 			BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
 
-			// check if block exists
+			// Check block exists
 			if(blockData == null)
 				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
 
 			BlockData childBlockData = repository.getBlockRepository().fromReference(signatureBytes);
 
-			// check if child exists
+			// Check child exists
 			if(childBlockData == null)
 				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
 
-			return childBlockData;
+			return new BlockWithTransactions(repository, childBlockData, includeTransactions);
 		} catch (ApiException e) {
 			throw e;
 		} catch (DataException e) {
@@ -252,18 +238,15 @@ public class BlocksResource {
 		}
 	)
 	public BigDecimal getGeneratingBalance() {
-		Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().getLastBlock();
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().getLastBlock();
 			Block block = new Block(repository, blockData);
 			return block.calcNextBlockGeneratingBalance();
-			
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -292,34 +275,28 @@ public class BlocksResource {
 		}
 	)
 	public BigDecimal getGeneratingBalance(@PathParam("signature") String signature) {
-		Security.checkApiCallAllowed("GET blocks/generatingbalance", request);
-
-		// decode signature
+		// Decode signature
 		byte[] signatureBytes;
-		try
-		{
-			signatureBytes = Base58.decode(signature);
-		}
-		catch(Exception e)
-		{
-            throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
+		try {
+			signatureBytes = Base64.getDecoder().decode(signature);
+		} catch (NumberFormatException e) {
+			throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
 		}
 
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
-				
-			// check if block exists
-			if(blockData == null)
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
+
+			// Check block exists
+			if (blockData == null)
 				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
 
 			Block block = new Block(repository, blockData);
 			return block.calcNextBlockGeneratingBalance();
-			
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -343,17 +320,14 @@ public class BlocksResource {
 		}
 	)
 	public long getTimePerBlock() {
-		Security.checkApiCallAllowed("GET blocks/time", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().getLastBlock();
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().getLastBlock();
 			return Block.calcForgingDelay(blockData.getGeneratingBalance());
-
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -377,8 +351,6 @@ public class BlocksResource {
 		}
 	)
 	public long getTimePerBlock(@PathParam("generating") BigDecimal generatingbalance) {
-		Security.checkApiCallAllowed("GET blocks/time", request);
-
 		return Block.calcForgingDelay(generatingbalance);
 	}
 
@@ -403,16 +375,13 @@ public class BlocksResource {
 		}
 	)
 	public int getHeight() {
-		Security.checkApiCallAllowed("GET blocks/height", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            return repository.getBlockRepository().getBlockchainHeight();
-
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			return repository.getBlockRepository().getBlockchainHeight();
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -441,33 +410,27 @@ public class BlocksResource {
 		}
 	)
 	public int getHeight(@PathParam("signature") String signature) {
-		Security.checkApiCallAllowed("GET blocks/height", request);
-
-		// decode signature
+		// Decode signature
 		byte[] signatureBytes;
-		try
-		{
-			signatureBytes = Base58.decode(signature);
-		}
-		catch(Exception e)
-		{
-            throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
+		try {
+			signatureBytes = Base64.getDecoder().decode(signature);
+		} catch (NumberFormatException e) {
+			throw this.apiErrorFactory.createError(ApiError.INVALID_SIGNATURE, e);
 		}
 
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
-				
-			// check if block exists
-			if(blockData == null)
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().fromSignature(signatureBytes);
+
+			// Check block exists
+			if (blockData == null)
 				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
 
 			return blockData.getHeight();
-			
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
 
 	@GET
@@ -486,7 +449,7 @@ public class BlocksResource {
 		responses = {
 			@ApiResponse(
 				description = "the block",
-				content = @Content(schema = @Schema(implementation = BlockData.class)),
+				content = @Content(schema = @Schema(implementation = BlockWithTransactions.class)),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -495,22 +458,15 @@ public class BlocksResource {
 			)
 		}
 	)
-	public BlockData getbyHeight(@PathParam("height") int height) {
-		Security.checkApiCallAllowed("GET blocks/byheight", request);
-
-        try (final Repository repository = RepositoryManager.getRepository()) {
-            BlockData blockData = repository.getBlockRepository().fromHeight(height);
-				
-			// check if block exists
-			if(blockData == null)
-				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
-
-			return blockData;
-			
+	public BlockWithTransactions getbyHeight(@PathParam("height") int height, @Parameter(ref = "includeTransactions") @QueryParam("includeTransactions") boolean includeTransactions) {
+		try (final Repository repository = RepositoryManager.getRepository()) {
+			BlockData blockData = repository.getBlockRepository().fromHeight(height);
+			return new BlockWithTransactions(repository, blockData, includeTransactions);
 		} catch (ApiException e) {
 			throw e;
-		} catch (Exception e) {
-            throw this.apiErrorFactory.createError(ApiError.UNKNOWN, e);
-        }
+		} catch (DataException e) {
+			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
+		}
 	}
+
 }
diff --git a/src/api/Security.java b/src/api/Security.java
index a7599d48..2d981b2c 100644
--- a/src/api/Security.java
+++ b/src/api/Security.java
@@ -1,12 +1,22 @@
 package api;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
 import javax.servlet.http.HttpServletRequest;
 
 public class Security {
 
-	public static void checkApiCallAllowed(final String messageToDisplay, HttpServletRequest request) {
-		// TODO     
+	// TODO: replace with proper authentication
+	public static void checkApiCallAllowed(HttpServletRequest request) {
+		InetAddress remoteAddr;
+		try {
+			remoteAddr = InetAddress.getByName(request.getRemoteAddr());
+		} catch (UnknownHostException e) {
+			throw ApiErrorFactory.getInstance().createError(ApiError.UNAUTHORIZED);
+		}
 
-		// throw this.apiErrorFactory.createError(ApiError.UNAUTHORIZED);
+		if (!remoteAddr.isLoopbackAddress())
+			throw ApiErrorFactory.getInstance().createError(ApiError.UNAUTHORIZED);
 	}
 }
diff --git a/src/api/TransactionsResource.java b/src/api/TransactionsResource.java
index d396d3c3..a7ba240b 100644
--- a/src/api/TransactionsResource.java
+++ b/src/api/TransactionsResource.java
@@ -2,6 +2,8 @@ package api;
 
 import globalization.Translator;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.extensions.Extension;
 import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
 import io.swagger.v3.oas.annotations.media.ArraySchema;
@@ -19,9 +21,12 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 
+import data.transaction.GenesisTransactionData;
+import data.transaction.PaymentTransactionData;
 import data.transaction.TransactionData;
 import repository.DataException;
 import repository.Repository;
@@ -56,6 +61,9 @@ public class TransactionsResource {
 	@Operation(
 		summary = "Fetch transactions involving address",
 		description = "Returns list of transactions",
+		parameters = {
+				@Parameter(in = ParameterIn.PATH, name = "address", description = "Account's address", schema = @Schema(type = "string"))
+		},
 		extensions = {
 			@Extension(name = "translation", properties = {
 					@ExtensionProperty(name="path", value="GET block:signature"),
@@ -77,9 +85,7 @@ public class TransactionsResource {
 			)
 		}
 	)
-	public List<TransactionData> getAddressTransactions(@PathParam("address") String address) {
-		Security.checkApiCallAllowed("GET transactions/address", request);
-
+	public List<TransactionData> getAddressTransactions(@PathParam("address") String address, @Parameter(ref = "limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
 		if (!Crypto.isValidAddress(address))
 			throw this.apiErrorFactory.createError(ApiError.INVALID_ADDRESS);
 
@@ -89,6 +95,9 @@ public class TransactionsResource {
 			List<byte[]> signatures = txRepo.getAllSignaturesInvolvingAddress(address);
 
 			// Pagination would take effect here (or as part of the repository access)
+			int fromIndex = Integer.min(offset, signatures.size());
+			int toIndex = limit == 0 ? signatures.size() : Integer.min(fromIndex + limit, signatures.size());
+			signatures = signatures.subList(fromIndex, toIndex);
 
 			// Expand signatures to transactions
 			List<TransactionData> transactions = new ArrayList<TransactionData>(signatures.size());
@@ -101,13 +110,12 @@ public class TransactionsResource {
 		} catch (DataException e) {
 			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
 		}
-
 	}
 
 	@GET
 	@Path("/block/{signature}")
 	@Operation(
-		summary = "Fetch transactions via block signature",
+		summary = "Fetch transactions using block signature",
 		description = "Returns list of transactions",
 		extensions = {
 			@Extension(name = "translation", properties = {
@@ -121,7 +129,9 @@ public class TransactionsResource {
 		responses = {
 			@ApiResponse(
 				description = "list of transactions",
-				content = @Content(array = @ArraySchema(schema = @Schema(implementation = TransactionData.class))),
+				content = @Content(array = @ArraySchema(schema = @Schema(
+							oneOf = { GenesisTransactionData.class, PaymentTransactionData.class }
+						))),
 				extensions = {
 					@Extension(name = "translation", properties = {
 						@ExtensionProperty(name="description.key", value="success_response:description")
@@ -130,9 +140,7 @@ public class TransactionsResource {
 			)
 		}
 	)
-	public List<TransactionData> getBlockTransactions(@PathParam("signature") String signature) {
-		Security.checkApiCallAllowed("GET transactions/block", request);
-
+	public List<TransactionData> getBlockTransactions(@PathParam("signature") String signature, @Parameter(ref = "limit") @QueryParam("limit") int limit, @Parameter(ref = "offset") @QueryParam("offset") int offset) {
 		// decode signature
 		byte[] signatureBytes;
 		try {
@@ -148,13 +156,17 @@ public class TransactionsResource {
 			if(transactions == null)
 				throw this.apiErrorFactory.createError(ApiError.BLOCK_NO_EXISTS);
 
+			// Pagination would take effect here (or as part of the repository access)
+			int fromIndex = Integer.min(offset, transactions.size());
+			int toIndex = limit == 0 ? transactions.size() : Integer.min(fromIndex + limit, transactions.size());
+			transactions = transactions.subList(fromIndex, toIndex);
+
 			return transactions;
 		} catch (ApiException e) {
 			throw e;
 		} catch (DataException e) {
 			throw this.apiErrorFactory.createError(ApiError.REPOSITORY_ISSUE, e);
 		}
-
 	}
 
 }
diff --git a/src/api/UtilsResource.java b/src/api/UtilsResource.java
new file mode 100644
index 00000000..ba77a707
--- /dev/null
+++ b/src/api/UtilsResource.java
@@ -0,0 +1,66 @@
+package api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import utils.Base58;
+
+import java.util.Base64;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+@Path("/utils")
+@Produces({MediaType.TEXT_PLAIN})
+@Tag(name = "utils")
+public class UtilsResource {
+
+	@Context
+	HttpServletRequest request;
+
+	@GET
+	@Path("/base58from64/{base64}")
+	@Operation(
+		summary = "Convert base64 data to base58",
+		responses = {
+			@ApiResponse(
+				description = "base58 data",
+				content = @Content(schema = @Schema(implementation = String.class))
+			)
+		}
+	)
+	public String base58from64(@PathParam("base64") String base64) {
+		try {
+			return Base58.encode(Base64.getDecoder().decode(base64));
+		} catch (IllegalArgumentException e) {
+			throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_DATA);
+		}
+	}
+
+	@GET
+	@Path("/base64from58/{base58}")
+	@Operation(
+		summary = "Convert base58 data to base64",
+		responses = {
+			@ApiResponse(
+				description = "base64 data",
+				content = @Content(schema = @Schema(implementation = String.class))
+			)
+		}
+	)
+	public String base64from58(@PathParam("base58") String base58) {
+		try {
+			return Base64.getEncoder().encodeToString(Base58.decode(base58));
+		} catch (NumberFormatException e) {
+			throw ApiErrorFactory.getInstance().createError(ApiError.INVALID_DATA);
+		}
+	}
+
+}
diff --git a/src/api/models/BlockWithTransactions.java b/src/api/models/BlockWithTransactions.java
new file mode 100644
index 00000000..ae323aa9
--- /dev/null
+++ b/src/api/models/BlockWithTransactions.java
@@ -0,0 +1,43 @@
+package api.models;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.xml.bind.annotation.XmlElement;
+
+import api.ApiError;
+import api.ApiErrorFactory;
+import data.block.BlockData;
+import data.transaction.TransactionData;
+import io.swagger.v3.oas.annotations.media.Schema;
+import qora.block.Block;
+import repository.DataException;
+import repository.Repository;
+
+@Schema(description = "Block with (optional) transactions")
+public class BlockWithTransactions {
+
+	@Schema(implementation = BlockData.class, name = "block", title = "block data")
+	@XmlElement(name = "block")
+	public BlockData blockData;
+
+	public List<TransactionData> transactions;
+
+	// For JAX-RS
+	@SuppressWarnings("unused")
+	private BlockWithTransactions() {
+	}
+
+	public BlockWithTransactions(Repository repository, BlockData blockData, boolean includeTransactions) throws DataException {
+		if (blockData == null)
+			throw ApiErrorFactory.getInstance().createError(ApiError.BLOCK_NO_EXISTS);
+
+		this.blockData = blockData;
+
+		if (includeTransactions) {
+			Block block = new Block(repository, blockData);
+			this.transactions = block.getTransactions().stream().map(transaction -> transaction.getTransactionData()).collect(Collectors.toList());
+		}
+	}
+
+}
diff --git a/src/controller/Controller.java b/src/controller/Controller.java
index 37c01250..460e7368 100644
--- a/src/controller/Controller.java
+++ b/src/controller/Controller.java
@@ -14,19 +14,31 @@ public class Controller {
 	private static final Logger LOGGER = LogManager.getLogger(Controller.class);
 
 	private static final String connectionUrl = "jdbc:hsqldb:file:db/test;create=true";
+
+	public static final long startTime = System.currentTimeMillis();
 	private static final Object shutdownLock = new Object();
 	private static boolean isStopping = false;
 
-	public static void main(String args[]) throws DataException {
+	public static void main(String args[]) {
 		LOGGER.info("Starting up...");
 
 		LOGGER.info("Starting repository");
-		RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
-		RepositoryManager.setRepositoryFactory(repositoryFactory);
+		try {
+			RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(connectionUrl);
+			RepositoryManager.setRepositoryFactory(repositoryFactory);
+		} catch (DataException e) {
+			LOGGER.error("Unable to start repository", e);
+			System.exit(1);
+		}
 
 		LOGGER.info("Starting API");
-		ApiService apiService = ApiService.getInstance();
-		apiService.start();
+		try {
+			ApiService apiService = ApiService.getInstance();
+			apiService.start();
+		} catch (Exception e) {
+			LOGGER.error("Unable to start API", e);
+			System.exit(1);
+		}
 
 		Runtime.getRuntime().addShutdownHook(new Thread() {
 			@Override
diff --git a/src/data/block/BlockData.java b/src/data/block/BlockData.java
index 1af4803e..4b794426 100644
--- a/src/data/block/BlockData.java
+++ b/src/data/block/BlockData.java
@@ -29,8 +29,7 @@ public class BlockData implements Serializable {
 	private BigDecimal atFees;
 
 	// necessary for JAX-RS serialization
-	@SuppressWarnings("unused")
-	private BlockData() {
+	protected BlockData() {
 	}
 
 	public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
diff --git a/src/data/transaction/GenesisTransactionData.java b/src/data/transaction/GenesisTransactionData.java
index 06393825..f30561b6 100644
--- a/src/data/transaction/GenesisTransactionData.java
+++ b/src/data/transaction/GenesisTransactionData.java
@@ -2,9 +2,16 @@ package data.transaction;
 
 import java.math.BigDecimal;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+
+import io.swagger.v3.oas.annotations.media.Schema;
 import qora.account.GenesisAccount;
 import qora.transaction.Transaction.TransactionType;
 
+// All properties to be converted to JSON via JAX-RS
+@XmlAccessorType(XmlAccessType.FIELD)
+@Schema( allOf = { TransactionData.class } )
 public class GenesisTransactionData extends TransactionData {
 
 	// Properties
@@ -13,6 +20,10 @@ public class GenesisTransactionData extends TransactionData {
 
 	// Constructors
 
+	// For JAX-RS
+	protected GenesisTransactionData() {
+	}
+
 	public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
 		// Zero fee
 		super(TransactionType.GENESIS, BigDecimal.ZERO, GenesisAccount.PUBLIC_KEY, timestamp, null, signature);
diff --git a/src/data/transaction/PaymentTransactionData.java b/src/data/transaction/PaymentTransactionData.java
index 98afb3ab..e93ada3a 100644
--- a/src/data/transaction/PaymentTransactionData.java
+++ b/src/data/transaction/PaymentTransactionData.java
@@ -2,8 +2,15 @@ package data.transaction;
 
 import java.math.BigDecimal;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+
+import io.swagger.v3.oas.annotations.media.Schema;
 import qora.transaction.Transaction.TransactionType;
 
+// All properties to be converted to JSON via JAX-RS
+@XmlAccessorType(XmlAccessType.FIELD)
+@Schema( allOf = { TransactionData.class } )
 public class PaymentTransactionData extends TransactionData {
 
 	// Properties
@@ -13,6 +20,10 @@ public class PaymentTransactionData extends TransactionData {
 
 	// Constructors
 
+	// For JAX-RS
+	protected PaymentTransactionData() {
+	}
+
 	public PaymentTransactionData(byte[] senderPublicKey, String recipient, BigDecimal amount, BigDecimal fee, long timestamp, byte[] reference,
 			byte[] signature) {
 		super(TransactionType.PAYMENT, fee, senderPublicKey, timestamp, reference, signature);
diff --git a/src/data/transaction/TransactionData.java b/src/data/transaction/TransactionData.java
index 428223c5..820d54c8 100644
--- a/src/data/transaction/TransactionData.java
+++ b/src/data/transaction/TransactionData.java
@@ -23,6 +23,10 @@ public abstract class TransactionData {
 
 	// Constructors
 
+	// For JAX-RS
+	protected TransactionData() {
+	}
+
 	public TransactionData(TransactionType type, BigDecimal fee, byte[] creatorPublicKey, long timestamp, byte[] reference, byte[] signature) {
 		this.fee = fee;
 		this.type = type;
diff --git a/src/qora/account/Account.java b/src/qora/account/Account.java
index 23053a31..78dca2eb 100644
--- a/src/qora/account/Account.java
+++ b/src/qora/account/Account.java
@@ -1,7 +1,6 @@
 package qora.account;
 
 import java.math.BigDecimal;
-import java.util.Arrays;
 import java.util.List;
 
 import org.apache.logging.log4j.LogManager;
diff --git a/src/repository/hsqldb/HSQLDBRepositoryFactory.java b/src/repository/hsqldb/HSQLDBRepositoryFactory.java
index b5e368bc..ed35a067 100644
--- a/src/repository/hsqldb/HSQLDBRepositoryFactory.java
+++ b/src/repository/hsqldb/HSQLDBRepositoryFactory.java
@@ -20,6 +20,12 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
 		// one-time initialization goes in here
 		this.connectionUrl = connectionUrl;
 
+		// Check no-one else is accessing database
+		try (Connection connection = DriverManager.getConnection(this.connectionUrl)) {
+		} catch (SQLException e) {
+			throw new DataException("Unable to open repository: " + e.getMessage());
+		}
+
 		this.connectionPool = new JDBCPool();
 		this.connectionPool.setUrl(this.connectionUrl);