Refactored BitmessageContext creation

This commit is contained in:
Christian Basler 2017-11-25 20:34:11 +01:00
parent a5c78fd8cf
commit ddb2073c2f
12 changed files with 187 additions and 221 deletions

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.1.60'
ext.kotlin_version = '1.1.61'
repositories {
mavenCentral()
}

View File

@ -16,6 +16,7 @@
package ch.dissem.bitmessage
import ch.dissem.bitmessage.BitmessageContext.Companion.version
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_EXTRA_BYTES
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_NONCE_TRIALS_PER_BYTE
import ch.dissem.bitmessage.entity.BitmessageAddress
@ -33,7 +34,6 @@ import ch.dissem.bitmessage.exception.DecryptionFailedException
import ch.dissem.bitmessage.factory.Factory
import ch.dissem.bitmessage.ports.*
import ch.dissem.bitmessage.utils.Property
import ch.dissem.bitmessage.utils.UnixTime.HOUR
import ch.dissem.bitmessage.utils.UnixTime.MINUTE
import org.slf4j.LoggerFactory
import java.net.InetAddress
@ -58,57 +58,7 @@ import kotlin.properties.Delegates
*
* The port defaults to 8444 (the default Bitmessage port)
*/
class BitmessageContext(
cryptography: Cryptography,
inventory: Inventory,
nodeRegistry: NodeRegistry,
networkHandler: NetworkHandler,
addressRepository: AddressRepository,
messageRepository: MessageRepository,
proofOfWorkRepository: ProofOfWorkRepository,
proofOfWorkEngine: ProofOfWorkEngine = MultiThreadedPOWEngine(),
customCommandHandler: CustomCommandHandler = object : CustomCommandHandler {
override fun handle(request: CustomMessage): MessagePayload? {
BitmessageContext.LOG.debug("Received custom request, but no custom command handler configured.")
return null
}
},
listener: Listener,
labeler: Labeler = DefaultLabeler(),
userAgent: String? = null,
port: Int = 8444,
connectionTTL: Long = 30 * MINUTE,
connectionLimit: Int = 150,
sendPubkeyOnIdentityCreation: Boolean = true,
doMissingProofOfWorkDelayInSeconds: Int = 30
) {
private constructor(builder: BitmessageContext.Builder) : this(
builder.cryptography,
builder.inventory,
builder.nodeRegistry,
builder.networkHandler,
builder.addressRepo,
builder.messageRepo,
builder.proofOfWorkRepository,
builder.proofOfWorkEngine ?: MultiThreadedPOWEngine(),
builder.customCommandHandler ?: object : CustomCommandHandler {
override fun handle(request: CustomMessage): MessagePayload? {
BitmessageContext.LOG.debug("Received custom request, but no custom command handler configured.")
return null
}
},
builder.listener,
builder.labeler ?: DefaultLabeler(),
builder.userAgent,
builder.port,
builder.connectionTTL,
builder.connectionLimit,
builder.sendPubkeyOnIdentityCreation,
builder.doMissingProofOfWorkDelay
)
private val sendPubkeyOnIdentityCreation: Boolean
class BitmessageContext private constructor(builder: BitmessageContext.Builder) {
/**
* The [InternalContext] - normally you wouldn't need it,
@ -135,7 +85,7 @@ class BitmessageContext(
*features
))
internals.addressRepository.save(identity)
if (sendPubkeyOnIdentityCreation) {
if (internals.preferences.sendPubkeyOnIdentityCreation) {
internals.sendPubkey(identity, identity.stream)
}
return identity
@ -262,9 +212,8 @@ class BitmessageContext(
* @param request the request
* @return the response
*/
fun send(server: InetAddress, port: Int, request: CustomMessage): CustomMessage {
return internals.networkHandler.send(server, port, request)
}
fun send(server: InetAddress, port: Int, request: CustomMessage): CustomMessage =
internals.networkHandler.send(server, port, request)
/**
* Removes expired objects from the inventory. You should call this method regularly,
@ -327,7 +276,7 @@ class BitmessageContext(
fun status(): Property {
return Property("status",
Property("user agent", internals.userAgent),
Property("user agent", internals.preferences.userAgent),
internals.networkHandler.getNetworkStatus(),
Property("unacknowledged", internals.messageRepository.findMessagesToResend().size)
)
@ -344,29 +293,22 @@ class BitmessageContext(
}
}
/**
* Kotlin users: you might want to use [BitmessageContext.build] instead.
*/
class Builder {
internal var port = 8444
internal var inventory by Delegates.notNull<Inventory>()
internal var nodeRegistry by Delegates.notNull<NodeRegistry>()
internal var networkHandler by Delegates.notNull<NetworkHandler>()
internal var addressRepo by Delegates.notNull<AddressRepository>()
internal var messageRepo by Delegates.notNull<MessageRepository>()
internal var proofOfWorkRepository by Delegates.notNull<ProofOfWorkRepository>()
internal var proofOfWorkEngine: ProofOfWorkEngine? = null
internal var cryptography by Delegates.notNull<Cryptography>()
internal var customCommandHandler: CustomCommandHandler? = null
internal var labeler: Labeler? = null
internal var userAgent: String? = null
internal var listener by Delegates.notNull<Listener>()
internal var connectionLimit = 150
internal var connectionTTL = 30 * MINUTE
internal var sendPubkeyOnIdentityCreation = true
internal var doMissingProofOfWorkDelay = 30
fun port(port: Int): Builder {
this.port = port
return this
}
var inventory by Delegates.notNull<Inventory>()
var nodeRegistry by Delegates.notNull<NodeRegistry>()
var networkHandler by Delegates.notNull<NetworkHandler>()
var addressRepo by Delegates.notNull<AddressRepository>()
var messageRepo by Delegates.notNull<MessageRepository>()
var proofOfWorkRepo by Delegates.notNull<ProofOfWorkRepository>()
var proofOfWorkEngine: ProofOfWorkEngine? = null
var cryptography by Delegates.notNull<Cryptography>()
var customCommandHandler: CustomCommandHandler? = null
var labeler: Labeler? = null
var listener by Delegates.notNull<Listener>()
val preferences = Preferences()
fun inventory(inventory: Inventory): Builder {
this.inventory = inventory
@ -394,7 +336,7 @@ class BitmessageContext(
}
fun powRepo(proofOfWorkRepository: ProofOfWorkRepository): Builder {
this.proofOfWorkRepository = proofOfWorkRepository
this.proofOfWorkRepo = proofOfWorkRepository
return this
}
@ -423,7 +365,7 @@ class BitmessageContext(
return this
}
@JvmName("kotlinListener")
@JvmSynthetic
fun listener(listener: (Plaintext) -> Unit): Builder {
this.listener = object : Listener {
override fun receive(plaintext: Plaintext) {
@ -433,63 +375,39 @@ class BitmessageContext(
return this
}
fun connectionLimit(connectionLimit: Int): Builder {
this.connectionLimit = connectionLimit
return this
fun build() = BitmessageContext(this)
}
fun connectionTTL(hours: Int): Builder {
this.connectionTTL = hours * HOUR
return this
}
fun doMissingProofOfWorkDelay(seconds: Int) {
this.doMissingProofOfWorkDelay = seconds
}
/**
* By default a client will send the public key when an identity is being created. On weaker devices
* this behaviour might not be desirable.
*/
fun doNotSendPubkeyOnIdentityCreation(): Builder {
this.sendPubkeyOnIdentityCreation = false
return this
}
fun build(): BitmessageContext {
return BitmessageContext(this)
}
}
init {
this.labeler = labeler
this.labeler = builder.labeler ?: DefaultLabeler()
this.internals = InternalContext(
cryptography,
inventory,
nodeRegistry,
networkHandler,
addressRepository,
messageRepository,
proofOfWorkRepository,
proofOfWorkEngine,
customCommandHandler,
listener,
builder.cryptography,
builder.inventory,
builder.nodeRegistry,
builder.networkHandler,
builder.addressRepo,
builder.messageRepo,
builder.proofOfWorkRepo,
builder.proofOfWorkEngine ?: MultiThreadedPOWEngine(),
builder.customCommandHandler ?: object : CustomCommandHandler {
override fun handle(request: CustomMessage): MessagePayload? {
BitmessageContext.LOG.debug("Received custom request, but no custom command handler configured.")
return null
}
},
builder.listener,
labeler,
userAgent?.let { "/$it/Jabit:$version/" } ?: "/Jabit:$version/",
port,
connectionTTL,
connectionLimit
builder.preferences
)
this.addresses = addressRepository
this.messages = messageRepository
this.sendPubkeyOnIdentityCreation = sendPubkeyOnIdentityCreation
(listener as? Listener.WithContext)?.setContext(this)
internals.proofOfWorkService.doMissingProofOfWork(doMissingProofOfWorkDelayInSeconds * 1000L)
this.addresses = builder.addressRepo
this.messages = builder.messageRepo
(builder.listener as? Listener.WithContext)?.setContext(this)
internals.proofOfWorkService.doMissingProofOfWork(builder.preferences.doMissingProofOfWorkDelayInSeconds * 1000L)
}
companion object {
@JvmField val CURRENT_VERSION = 3
@JvmField
val CURRENT_VERSION = 3
private val LOG = LoggerFactory.getLogger(BitmessageContext::class.java)
val version: String by lazy {
@ -497,5 +415,40 @@ class BitmessageContext(
}
@JvmStatic get
@JvmSynthetic
inline fun build(block: Builder.() -> Unit): BitmessageContext {
val builder = Builder()
block(builder)
return builder.build()
}
}
}
class Preferences {
var port = 8444
/**
* Defaults to "/Jabit:<version>/", and whatever you set will be inserted into "/<your user agent>/Jabit:<version>/"
*/
var userAgent = "/Jabit:$version/"
set(value) {
field = "/$value/Jabit:$version/"
}
/**
* Time to live for any connection
*/
var connectionTTL = 30 * MINUTE
/**
* Maximum number of connections. Values below 8 would probably result in erratic behaviour, so you shouldn't do that.
*/
var connectionLimit = 150
/**
* By default a client will send the public key when an identity is being created. On weaker devices
* this behaviour might not be desirable.
*/
var sendPubkeyOnIdentityCreation = true
/**
* Delay in seconds before outstandinng proof of work is calculated.
*/
var doMissingProofOfWorkDelayInSeconds = 30
}

View File

@ -51,11 +51,7 @@ class InternalContext(
listener: BitmessageContext.Listener,
val labeler: Labeler,
val userAgent: String,
val port: Int,
val connectionTTL: Long,
val connectionLimit: Int
val preferences: Preferences
) {
private val threadPool = Executors.newCachedThreadPool()

View File

@ -47,8 +47,8 @@ import kotlin.concurrent.thread
* @author Christian Basler
*/
class BitmessageContextTest {
private var listener: BitmessageContext.Listener = mock()
private val inventory = spy(TestInventory())
private var testListener: BitmessageContext.Listener = mock()
private val testInventory = spy(TestInventory())
private val testPowRepo = spy(object : ProofOfWorkRepository {
internal var items: MutableMap<InventoryVector, ProofOfWorkRepository.Item> = HashMap()
internal var added = 0
@ -93,20 +93,20 @@ class BitmessageContextTest {
thread { callback.onNonceCalculated(initialHash, ByteArray(8)) }
}
})
private var ctx = BitmessageContext.Builder()
.addressRepo(mock())
.cryptography(BouncyCryptography())
.inventory(inventory)
.listener(listener)
.messageRepo(mock())
.networkHandler(mock {
private var ctx = BitmessageContext.build {
addressRepo = mock()
cryptography = BouncyCryptography()
inventory = testInventory
listener = testListener
messageRepo = mock()
networkHandler = mock {
on { getNetworkStatus() } doReturn Property("test", "mocked")
})
.nodeRegistry(mock())
.labeler(spy(DefaultLabeler()))
.powRepo(testPowRepo)
.proofOfWorkEngine(testPowEngine)
.build()
}
nodeRegistry = mock()
labeler = spy(DefaultLabeler())
proofOfWorkRepo = testPowRepo
proofOfWorkEngine = testPowEngine
}
init {
TTL.msg = 2 * MINUTE
@ -143,7 +143,7 @@ class BitmessageContextTest {
@Test
fun `ensure V2Pubkey is not requested if it exists in inventory`() {
inventory.init(
testInventory.init(
"V1Msg.payload",
"V2GetPubkey.payload",
"V2Pubkey.payload",
@ -166,7 +166,7 @@ class BitmessageContextTest {
@Test
fun `ensure V4Pubkey is not requested if it exists in inventory`() {
inventory.init(
testInventory.init(
"V1Msg.payload",
"V2GetPubkey.payload",
"V2Pubkey.payload",
@ -192,7 +192,7 @@ class BitmessageContextTest {
fun `ensure subscription is added and existing broadcasts retrieved`() {
val address = BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ")
inventory.init(
testInventory.init(
"V4Broadcast.payload",
"V5Broadcast.payload"
)
@ -203,7 +203,7 @@ class BitmessageContextTest {
verify(ctx.addresses, atLeastOnce()).save(address)
assertThat(address.isSubscribed, `is`(true))
verify(ctx.internals.inventory).getObjects(eq(address.stream), any(), any())
verify(listener).receive(any())
verify(testListener).receive(any())
}
@Test

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.utils
import ch.dissem.bitmessage.BitmessageContext
import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.Preferences
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.ObjectMessage
import ch.dissem.bitmessage.entity.payload.V4Pubkey
@ -39,21 +40,25 @@ import kotlin.NoSuchElementException
* If there's ever a need for this in production code, it should be rewritten to be more efficient.
*/
object TestUtils {
@JvmField val RANDOM = Random()
@JvmField
val RANDOM = Random()
@JvmStatic fun int16(number: Int): ByteArray {
@JvmStatic
fun int16(number: Int): ByteArray {
val out = ByteArrayOutputStream()
Encode.int16(number, out)
return out.toByteArray()
}
@JvmStatic fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage {
@JvmStatic
fun loadObjectMessage(version: Int, resourceName: String): ObjectMessage {
val data = getBytes(resourceName)
val input = ByteArrayInputStream(data)
return Factory.getObjectMessage(version, input, data.size) ?: throw NoSuchElementException("error loading object message")
}
@JvmStatic fun getBytes(resourceName: String): ByteArray {
@JvmStatic
fun getBytes(resourceName: String): ByteArray {
val input = javaClass.classLoader.getResourceAsStream(resourceName)
val out = ByteArrayOutputStream()
val buffer = ByteArray(1024)
@ -65,16 +70,19 @@ object TestUtils {
return out.toByteArray()
}
@JvmStatic fun randomInventoryVector(): InventoryVector {
@JvmStatic
fun randomInventoryVector(): InventoryVector {
val bytes = ByteArray(32)
RANDOM.nextBytes(bytes)
return InventoryVector(bytes)
}
@JvmStatic fun getResource(resourceName: String): InputStream =
@JvmStatic
fun getResource(resourceName: String): InputStream =
javaClass.classLoader.getResourceAsStream(resourceName)
@JvmStatic fun loadIdentity(address: String): BitmessageAddress {
@JvmStatic
fun loadIdentity(address: String): BitmessageAddress {
val privateKey = PrivateKey.read(TestUtils.getResource(address + ".privkey"))
val identity = BitmessageAddress(privateKey)
assertEquals(address, identity.address)
@ -82,7 +90,8 @@ object TestUtils {
}
@Throws(DecryptionFailedException::class)
@JvmStatic fun loadContact(): BitmessageAddress {
@JvmStatic
fun loadContact(): BitmessageAddress {
val address = BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h")
val objectMessage = TestUtils.loadObjectMessage(3, "V4Pubkey.payload")
objectMessage.decrypt(address.publicDecryptionKey)
@ -90,13 +99,15 @@ object TestUtils {
return address
}
@JvmStatic fun loadPubkey(address: BitmessageAddress) {
@JvmStatic
fun loadPubkey(address: BitmessageAddress) {
val bytes = getBytes(address.address + ".pubkey")
val pubkey = Factory.readPubkey(address.version, address.stream, ByteArrayInputStream(bytes), bytes.size, false)
address.pubkey = pubkey
}
@JvmStatic fun mockedInternalContext(
@JvmStatic
fun mockedInternalContext(
cryptography: Cryptography = mock {},
inventory: Inventory = mock {},
nodeRegistry: NodeRegistry = mock {},
@ -124,10 +135,12 @@ object TestUtils {
customCommandHandler,
listener,
labeler,
"/Jabit:TEST/",
port,
connectionTTL,
connectionLimit
Preferences().apply {
this.userAgent = "/Jabit:TEST/"
this.port = port
this.connectionTTL = connectionTTL
this.connectionLimit = connectionLimit
}
))
}
}

View File

@ -64,8 +64,8 @@ public class Main {
.messageRepo(new JdbcMessageRepository(jdbcConfig))
.powRepo(new JdbcProofOfWorkRepository(jdbcConfig))
.networkHandler(new NioNetworkHandler())
.cryptography(new BouncyCryptography())
.port(48444);
.cryptography(new BouncyCryptography());
ctxBuilder.getPreferences().setPort(48444);
if (options.localPort != null) {
ctxBuilder.nodeRegistry(new NodeRegistry() {
@Override

View File

@ -73,13 +73,13 @@ public class SystemTest {
.inventory(new JdbcInventory(aliceDB))
.messageRepo(new JdbcMessageRepository(aliceDB))
.powRepo(new JdbcProofOfWorkRepository(aliceDB))
.port(alicePort)
.nodeRegistry(new TestNodeRegistry(bobPort))
.networkHandler(new NioNetworkHandler())
.cryptography(new BouncyCryptography())
.listener(aliceListener)
.labeler(aliceLabeler)
.build();
alice.internals().getPreferences().setPort(alicePort);
alice.startup();
aliceIdentity = alice.createIdentity(false, DOES_ACK);
}
@ -91,13 +91,13 @@ public class SystemTest {
.inventory(new JdbcInventory(bobDB))
.messageRepo(new JdbcMessageRepository(bobDB))
.powRepo(new JdbcProofOfWorkRepository(bobDB))
.port(bobPort)
.nodeRegistry(new TestNodeRegistry(alicePort))
.networkHandler(new NioNetworkHandler())
.cryptography(new BouncyCryptography())
.listener(bobListener)
.labeler(new DebugLabeler("Bob"))
.build();
bob.internals().getPreferences().setPort(bobPort);
bob.startup();
bobIdentity = bob.createIdentity(false, DOES_ACK);
}

View File

@ -41,7 +41,7 @@ class NetworkConnectionInitializer(
fun start() {
if (mode == Connection.Mode.CLIENT || mode == Connection.Mode.SYNC) {
send(Version(nonce = ctx.clientNonce, addrFrom = NetworkAddress.ANY, addrRecv = node, userAgent = ctx.userAgent))
send(Version(nonce = ctx.clientNonce, addrFrom = NetworkAddress.ANY, addrRecv = node, userAgent = ctx.preferences.userAgent))
}
}

View File

@ -199,7 +199,7 @@ class NioNetworkHandler : NetworkHandler, InternalContext.ContextHolder {
val serverChannel = ServerSocketChannel.open()
this.serverChannel = serverChannel
serverChannel.configureBlocking(false)
serverChannel.socket().bind(InetSocketAddress(ctx.port))
serverChannel.socket().bind(InetSocketAddress(ctx.preferences.port))
serverChannel.register(selector, OP_ACCEPT, null)
while (selector.isOpen) {

View File

@ -23,7 +23,8 @@ import ch.dissem.bitmessage.entity.MessagePayload
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
import ch.dissem.bitmessage.exception.NodeException
import ch.dissem.bitmessage.networking.nio.NioNetworkHandler
import ch.dissem.bitmessage.ports.*
import ch.dissem.bitmessage.ports.CustomCommandHandler
import ch.dissem.bitmessage.ports.NetworkHandler
import ch.dissem.bitmessage.testutils.TestInventory
import ch.dissem.bitmessage.utils.Property
import ch.dissem.bitmessage.utils.Singleton.cryptography
@ -31,7 +32,8 @@ import com.nhaarman.mockito_kotlin.mock
import org.hamcrest.Matchers.`is`
import org.hamcrest.Matchers.notNullValue
import org.junit.After
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -56,20 +58,22 @@ class NetworkHandlerTest {
private lateinit var peerNetworkHandler: NetworkHandler
private lateinit var nodeNetworkHandler: NetworkHandler
@JvmField @Rule val timeout: TestRule = DisableOnDebug(Timeout.seconds(60))
@JvmField
@Rule
val timeout: TestRule = DisableOnDebug(Timeout.seconds(60))
@Before
fun setUp() {
peerInventory = TestInventory()
peerNetworkHandler = NioNetworkHandler()
peer = BitmessageContext(
cryptography = BouncyCryptography(),
inventory = peerInventory,
nodeRegistry = TestNodeRegistry(),
networkHandler = peerNetworkHandler,
addressRepository = mock<AddressRepository>(),
messageRepository = mock<MessageRepository>(),
proofOfWorkRepository = mock<ProofOfWorkRepository>(),
peer = BitmessageContext.build {
cryptography = BouncyCryptography()
inventory = peerInventory
nodeRegistry = TestNodeRegistry()
networkHandler = peerNetworkHandler
addressRepo = mock()
messageRepo = mock()
proofOfWorkRepo = mock()
customCommandHandler = object : CustomCommandHandler {
override fun handle(request: CustomMessage): MessagePayload? {
val data = request.getData()
@ -83,23 +87,23 @@ class NetworkHandlerTest {
}
return CustomMessage("test response", request.getData())
}
},
listener = mock<BitmessageContext.Listener>(),
port = peerAddress.port
)
}
listener = mock()
preferences.port = peerAddress.port
}
peer.startup()
Thread.sleep(100)
nodeInventory = TestInventory()
nodeNetworkHandler = NioNetworkHandler()
node = BitmessageContext(
cryptography = BouncyCryptography(),
inventory = nodeInventory,
nodeRegistry = TestNodeRegistry(peerAddress),
networkHandler = nodeNetworkHandler,
addressRepository = mock<AddressRepository>(),
messageRepository = mock<MessageRepository>(),
proofOfWorkRepository = mock<ProofOfWorkRepository>(),
node = BitmessageContext.build {
cryptography = BouncyCryptography()
inventory = nodeInventory
nodeRegistry = TestNodeRegistry(peerAddress)
networkHandler = nodeNetworkHandler
addressRepo = mock()
messageRepo = mock()
proofOfWorkRepo = mock()
customCommandHandler = object : CustomCommandHandler {
override fun handle(request: CustomMessage): MessagePayload? {
val data = request.getData()
@ -113,10 +117,10 @@ class NetworkHandlerTest {
}
return CustomMessage("test response", request.getData())
}
},
listener = mock<BitmessageContext.Listener>(),
port = 6002
)
}
listener = mock()
preferences.port = 6002
}
}
@After

View File

@ -49,16 +49,16 @@ class WifExporterTest {
@Before
fun setUp() {
ctx = BitmessageContext.Builder()
.cryptography(BouncyCryptography())
.networkHandler(mock())
.inventory(mock())
.messageRepo(mock())
.powRepo(mock())
.nodeRegistry(mock())
.addressRepo(repo)
.listener { }
.build()
ctx = BitmessageContext.build {
cryptography = BouncyCryptography()
networkHandler = mock()
inventory = mock()
messageRepo = mock()
proofOfWorkRepo = mock()
nodeRegistry = mock()
addressRepo = repo
listener {}
}
importer = WifImporter(ctx, javaClass.classLoader.getResourceAsStream("nuked.dat"))
assertEquals(81, importer.getIdentities().size)
exporter = WifExporter(ctx)

View File

@ -50,16 +50,16 @@ class WifImporterTest {
@Before
fun setUp() {
ctx = BitmessageContext.Builder()
.cryptography(BouncyCryptography())
.networkHandler(mock())
.inventory(mock())
.messageRepo(mock())
.powRepo(mock())
.nodeRegistry(mock())
.addressRepo(repo)
.listener { }
.build()
ctx = BitmessageContext.build {
cryptography = BouncyCryptography()
networkHandler = mock()
inventory = mock()
messageRepo = mock()
proofOfWorkRepo = mock()
nodeRegistry = mock()
addressRepo = repo
listener { }
}
importer = WifImporter(ctx, javaClass.classLoader.getResourceAsStream("nuked.dat"))
}