Fixes and improvements, SystemTest still broken
This commit is contained in:
@ -58,7 +58,53 @@ import kotlin.properties.Delegates
|
||||
*
|
||||
* The port defaults to 8444 (the default Bitmessage port)
|
||||
*/
|
||||
class BitmessageContext private constructor(builder: BitmessageContext.Builder) {
|
||||
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(),
|
||||
port: Int = 8444,
|
||||
connectionTTL: Long = 30 * MINUTE,
|
||||
connectionLimit: Int = 150,
|
||||
sendPubkeyOnIdentityCreation: Boolean,
|
||||
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.port,
|
||||
builder.connectionTTL,
|
||||
builder.connectionLimit,
|
||||
builder.sendPubkeyOnIdentityCreation,
|
||||
builder.doMissingProofOfWorkDelay
|
||||
)
|
||||
|
||||
private val sendPubkeyOnIdentityCreation: Boolean
|
||||
|
||||
@ -72,38 +118,10 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
val labeler: Labeler
|
||||
@JvmName("labeler") get
|
||||
|
||||
init {
|
||||
labeler = builder.labeler ?: DefaultLabeler()
|
||||
internals = InternalContext(
|
||||
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? {
|
||||
LOG.debug("Received custom request, but no custom command handler configured.")
|
||||
return null
|
||||
}
|
||||
},
|
||||
builder.listener,
|
||||
labeler,
|
||||
builder.port,
|
||||
builder.connectionTTL,
|
||||
builder.connectionLimit
|
||||
)
|
||||
internals.proofOfWorkService.doMissingProofOfWork(30000) // TODO: this should be configurable
|
||||
sendPubkeyOnIdentityCreation = builder.sendPubkeyOnIdentityCreation
|
||||
(builder.listener as? Listener.WithContext)?.setContext(this)
|
||||
}
|
||||
|
||||
val addresses: AddressRepository = internals.addressRepository
|
||||
val addresses: AddressRepository
|
||||
@JvmName("addresses") get
|
||||
|
||||
val messages: MessageRepository = internals.messageRepository
|
||||
val messages: MessageRepository
|
||||
@JvmName("messages") get
|
||||
|
||||
fun createIdentity(shorter: Boolean, vararg features: Feature): BitmessageAddress {
|
||||
@ -285,10 +303,9 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
fun addContact(contact: BitmessageAddress) {
|
||||
internals.addressRepository.save(contact)
|
||||
if (contact.pubkey == null) {
|
||||
internals.addressRepository.getAddress(contact.address)?.let {
|
||||
if (it.pubkey == null) {
|
||||
internals.requestPubkey(contact)
|
||||
}
|
||||
// If it already existed, the saved contact might have the public key
|
||||
if (internals.addressRepository.getAddress(contact.address)!!.pubkey == null) {
|
||||
internals.requestPubkey(contact)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -300,13 +317,13 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
}
|
||||
|
||||
private fun tryToFindBroadcastsForAddress(address: BitmessageAddress) {
|
||||
for (`object` in internals.inventory.getObjects(address.stream, Broadcast.getVersion(address), ObjectType.BROADCAST)) {
|
||||
for (objectMessage in internals.inventory.getObjects(address.stream, Broadcast.getVersion(address), ObjectType.BROADCAST)) {
|
||||
try {
|
||||
val broadcast = `object`.payload as Broadcast
|
||||
val broadcast = objectMessage.payload as Broadcast
|
||||
broadcast.decrypt(address)
|
||||
// This decrypts it twice, but on the other hand it doesn't try to decrypt the objects with
|
||||
// other subscriptions and the interface stays as simple as possible.
|
||||
internals.networkListener.receive(`object`)
|
||||
internals.networkListener.receive(objectMessage)
|
||||
} catch (ignore: DecryptionFailedException) {
|
||||
} catch (e: Exception) {
|
||||
LOG.debug(e.message, e)
|
||||
@ -348,6 +365,7 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
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
|
||||
@ -409,6 +427,7 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
return this
|
||||
}
|
||||
|
||||
@JvmName("kotlinListener")
|
||||
fun listener(listener: (Plaintext) -> Unit): Builder {
|
||||
this.listener = object : Listener {
|
||||
override fun receive(plaintext: Plaintext) {
|
||||
@ -428,6 +447,10 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
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.
|
||||
@ -438,18 +461,34 @@ class BitmessageContext private constructor(builder: BitmessageContext.Builder)
|
||||
}
|
||||
|
||||
fun build(): BitmessageContext {
|
||||
nonNull("inventory", inventory)
|
||||
nonNull("nodeRegistry", nodeRegistry)
|
||||
nonNull("networkHandler", networkHandler)
|
||||
nonNull("addressRepo", addressRepo)
|
||||
nonNull("messageRepo", messageRepo)
|
||||
nonNull("proofOfWorkRepo", proofOfWorkRepository)
|
||||
return BitmessageContext(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun nonNull(name: String, o: Any?) {
|
||||
if (o == null) throw IllegalStateException(name + " must not be null")
|
||||
}
|
||||
|
||||
init {
|
||||
this.labeler = labeler
|
||||
this.internals = InternalContext(
|
||||
cryptography,
|
||||
inventory,
|
||||
nodeRegistry,
|
||||
networkHandler,
|
||||
addressRepository,
|
||||
messageRepository,
|
||||
proofOfWorkRepository,
|
||||
proofOfWorkEngine,
|
||||
customCommandHandler,
|
||||
listener,
|
||||
labeler,
|
||||
port,
|
||||
connectionTTL,
|
||||
connectionLimit
|
||||
)
|
||||
this.addresses = addressRepository
|
||||
this.messages = messageRepository
|
||||
this.sendPubkeyOnIdentityCreation = sendPubkeyOnIdentityCreation
|
||||
(listener as? Listener.WithContext)?.setContext(this)
|
||||
internals.proofOfWorkService.doMissingProofOfWork(doMissingProofOfWorkDelayInSeconds * 1000L)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -25,6 +25,7 @@ import ch.dissem.bitmessage.entity.valueobject.InventoryVector
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException
|
||||
import ch.dissem.bitmessage.ports.Labeler
|
||||
import ch.dissem.bitmessage.ports.NetworkHandler
|
||||
import ch.dissem.bitmessage.utils.Strings.hex
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
|
||||
@ -32,23 +33,23 @@ internal open class DefaultMessageListener(
|
||||
private val labeler: Labeler,
|
||||
private val listener: BitmessageContext.Listener
|
||||
) : NetworkHandler.MessageListener {
|
||||
private var ctx by InternalContext
|
||||
private var ctx by InternalContext.lateinit
|
||||
|
||||
override fun receive(`object`: ObjectMessage) {
|
||||
val payload = `object`.payload
|
||||
override fun receive(objectMessage: ObjectMessage) {
|
||||
val payload = objectMessage.payload
|
||||
|
||||
when (payload.type) {
|
||||
ObjectType.GET_PUBKEY -> {
|
||||
receive(`object`, payload as GetPubkey)
|
||||
receive(objectMessage, payload as GetPubkey)
|
||||
}
|
||||
ObjectType.PUBKEY -> {
|
||||
receive(`object`, payload as Pubkey)
|
||||
receive(objectMessage, payload as Pubkey)
|
||||
}
|
||||
ObjectType.MSG -> {
|
||||
receive(`object`, payload as Msg)
|
||||
receive(objectMessage, payload as Msg)
|
||||
}
|
||||
ObjectType.BROADCAST -> {
|
||||
receive(`object`, payload as Broadcast)
|
||||
receive(objectMessage, payload as Broadcast)
|
||||
}
|
||||
null -> {
|
||||
if (payload is GenericPayload) {
|
||||
@ -61,30 +62,33 @@ internal open class DefaultMessageListener(
|
||||
}
|
||||
}
|
||||
|
||||
protected fun receive(`object`: ObjectMessage, getPubkey: GetPubkey) {
|
||||
protected fun receive(objectMessage: ObjectMessage, getPubkey: GetPubkey) {
|
||||
val identity = ctx.addressRepository.findIdentity(getPubkey.ripeTag)
|
||||
if (identity != null && identity.privateKey != null && !identity.isChan) {
|
||||
LOG.info("Got pubkey request for identity " + identity)
|
||||
// FIXME: only send pubkey if it wasn't sent in the last TTL.pubkey() days
|
||||
ctx.sendPubkey(identity, `object`.stream)
|
||||
ctx.sendPubkey(identity, objectMessage.stream)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun receive(`object`: ObjectMessage, pubkey: Pubkey) {
|
||||
val address: BitmessageAddress?
|
||||
protected fun receive(objectMessage: ObjectMessage, pubkey: Pubkey) {
|
||||
try {
|
||||
if (pubkey is V4Pubkey) {
|
||||
address = ctx.addressRepository.findContact(pubkey.tag)
|
||||
if (address != null) {
|
||||
pubkey.decrypt(address.publicDecryptionKey)
|
||||
ctx.addressRepository.findContact(pubkey.tag)?.let {
|
||||
if (it.pubkey == null) {
|
||||
pubkey.decrypt(it.publicDecryptionKey)
|
||||
updatePubkey(it, pubkey)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
address = ctx.addressRepository.findContact(pubkey.ripe)
|
||||
ctx.addressRepository.findContact(pubkey.ripe)?.let {
|
||||
if (it.pubkey == null) {
|
||||
updatePubkey(it, pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address != null && address.pubkey == null) {
|
||||
updatePubkey(address, pubkey)
|
||||
}
|
||||
} catch (_: DecryptionFailedException) {}
|
||||
} catch (_: DecryptionFailedException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -101,19 +105,20 @@ internal open class DefaultMessageListener(
|
||||
}
|
||||
}
|
||||
|
||||
protected fun receive(`object`: ObjectMessage, msg: Msg) {
|
||||
protected fun receive(objectMessage: ObjectMessage, msg: Msg) {
|
||||
for (identity in ctx.addressRepository.getIdentities()) {
|
||||
try {
|
||||
msg.decrypt(identity.privateKey!!.privateEncryptionKey)
|
||||
val plaintext = msg.plaintext!!
|
||||
plaintext.to = identity
|
||||
if (!`object`.isSignatureValid(plaintext.from.pubkey!!)) {
|
||||
LOG.warn("Msg with IV " + `object`.inventoryVector + " was successfully decrypted, but signature check failed. Ignoring.")
|
||||
if (!objectMessage.isSignatureValid(plaintext.from.pubkey!!)) {
|
||||
LOG.warn("Msg with IV " + objectMessage.inventoryVector + " was successfully decrypted, but signature check failed. Ignoring.")
|
||||
} else {
|
||||
receive(`object`.inventoryVector, plaintext)
|
||||
receive(objectMessage.inventoryVector, plaintext)
|
||||
}
|
||||
break
|
||||
} catch (_: DecryptionFailedException) {}
|
||||
} catch (_: DecryptionFailedException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,11 +127,11 @@ internal open class DefaultMessageListener(
|
||||
ctx.messageRepository.getMessageForAck(ack.data)?.let {
|
||||
ctx.labeler.markAsAcknowledged(it)
|
||||
ctx.messageRepository.save(it)
|
||||
}
|
||||
} ?: LOG.debug("Message not found for ack ${hex(ack.data)}")
|
||||
}
|
||||
}
|
||||
|
||||
protected fun receive(`object`: ObjectMessage, broadcast: Broadcast) {
|
||||
protected fun receive(objectMessage: ObjectMessage, broadcast: Broadcast) {
|
||||
val tag = if (broadcast is V5Broadcast) broadcast.tag else null
|
||||
for (subscription in ctx.addressRepository.getSubscriptions(broadcast.version)) {
|
||||
if (tag != null && !Arrays.equals(tag, subscription.tag)) {
|
||||
@ -134,12 +139,13 @@ internal open class DefaultMessageListener(
|
||||
}
|
||||
try {
|
||||
broadcast.decrypt(subscription.publicDecryptionKey)
|
||||
if (!`object`.isSignatureValid(broadcast.plaintext!!.from.pubkey!!)) {
|
||||
LOG.warn("Broadcast with IV " + `object`.inventoryVector + " was successfully decrypted, but signature check failed. Ignoring.")
|
||||
if (!objectMessage.isSignatureValid(broadcast.plaintext!!.from.pubkey!!)) {
|
||||
LOG.warn("Broadcast with IV " + objectMessage.inventoryVector + " was successfully decrypted, but signature check failed. Ignoring.")
|
||||
} else {
|
||||
receive(`object`.inventoryVector, broadcast.plaintext!!)
|
||||
receive(objectMessage.inventoryVector, broadcast.plaintext!!)
|
||||
}
|
||||
} catch (_: DecryptionFailedException) {}
|
||||
} catch (_: DecryptionFailedException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +164,7 @@ internal open class DefaultMessageListener(
|
||||
msg.ackMessage?.let {
|
||||
ctx.inventory.storeObject(it)
|
||||
ctx.networkHandler.offer(it.inventoryVector)
|
||||
}
|
||||
} ?: LOG.debug("ack message expected")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ import ch.dissem.bitmessage.utils.UnixTime
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
@ -68,7 +67,8 @@ class InternalContext(
|
||||
get() = _streams.toLongArray()
|
||||
|
||||
init {
|
||||
instance = this
|
||||
lateinit.instance = this
|
||||
lateinit = ContextDelegate()
|
||||
Singleton.initialize(cryptography)
|
||||
|
||||
// TODO: streams of new identities and subscriptions should also be added. This works only after a restart.
|
||||
@ -102,24 +102,24 @@ class InternalContext(
|
||||
val recipient = to ?: from
|
||||
val expires = UnixTime.now + timeToLive
|
||||
LOG.info("Expires at " + expires)
|
||||
val `object` = ObjectMessage(
|
||||
val objectMessage = ObjectMessage(
|
||||
stream = recipient.stream,
|
||||
expiresTime = expires,
|
||||
payload = payload
|
||||
)
|
||||
if (`object`.isSigned) {
|
||||
`object`.sign(
|
||||
if (objectMessage.isSigned) {
|
||||
objectMessage.sign(
|
||||
from.privateKey ?: throw IllegalArgumentException("The given sending address is no identity")
|
||||
)
|
||||
}
|
||||
if (payload is Broadcast) {
|
||||
payload.encrypt()
|
||||
} else if (payload is Encrypted) {
|
||||
`object`.encrypt(
|
||||
objectMessage.encrypt(
|
||||
recipient.pubkey ?: throw IllegalArgumentException("The public key for the recipient isn't available")
|
||||
)
|
||||
}
|
||||
proofOfWorkService.doProofOfWork(to, `object`)
|
||||
proofOfWorkService.doProofOfWork(to, objectMessage)
|
||||
}
|
||||
|
||||
fun sendPubkey(identity: BitmessageAddress, targetStream: Long) {
|
||||
@ -163,12 +163,11 @@ class InternalContext(
|
||||
}
|
||||
|
||||
val expires = UnixTime.now + TTL.getpubkey
|
||||
LOG.info("Expires at " + expires)
|
||||
val payload = GetPubkey(contact)
|
||||
LOG.info("Expires at $expires")
|
||||
val request = ObjectMessage(
|
||||
stream = contact.stream,
|
||||
expiresTime = expires,
|
||||
payload = payload
|
||||
payload = GetPubkey(contact)
|
||||
)
|
||||
proofOfWorkService.doProofOfWork(request)
|
||||
}
|
||||
@ -179,14 +178,14 @@ class InternalContext(
|
||||
address.alias = it.alias
|
||||
address.isSubscribed = it.isSubscribed
|
||||
}
|
||||
for (`object` in inventory.getObjects(address.stream, address.version, ObjectType.PUBKEY)) {
|
||||
for (objectMessage in inventory.getObjects(address.stream, address.version, ObjectType.PUBKEY)) {
|
||||
try {
|
||||
val pubkey = `object`.payload as Pubkey
|
||||
val pubkey = objectMessage.payload as Pubkey
|
||||
if (address.version == 4L) {
|
||||
val v4Pubkey = pubkey as V4Pubkey
|
||||
if (Arrays.equals(address.tag, v4Pubkey.tag)) {
|
||||
v4Pubkey.decrypt(address.publicDecryptionKey)
|
||||
if (`object`.isSignatureValid(v4Pubkey)) {
|
||||
if (objectMessage.isSignatureValid(v4Pubkey)) {
|
||||
address.pubkey = v4Pubkey
|
||||
addressRepository.save(address)
|
||||
break
|
||||
@ -219,17 +218,19 @@ class InternalContext(
|
||||
fun setContext(context: InternalContext)
|
||||
}
|
||||
|
||||
class ContextDelegate {
|
||||
internal lateinit var instance: InternalContext
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = instance
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: InternalContext) {}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = LoggerFactory.getLogger(InternalContext::class.java)
|
||||
|
||||
@JvmField val NETWORK_NONCE_TRIALS_PER_BYTE: Long = 1000
|
||||
@JvmField val NETWORK_EXTRA_BYTES: Long = 1000
|
||||
|
||||
private var instance: InternalContext by Delegates.notNull<InternalContext>()
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = instance
|
||||
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: InternalContext) {
|
||||
instance = value
|
||||
}
|
||||
var lateinit = ContextDelegate()
|
||||
private set
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import ch.dissem.bitmessage.entity.*
|
||||
import ch.dissem.bitmessage.entity.payload.Msg
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item
|
||||
import ch.dissem.bitmessage.utils.Strings
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
@ -31,7 +32,7 @@ import java.util.*
|
||||
*/
|
||||
class ProofOfWorkService : ProofOfWorkEngine.Callback {
|
||||
|
||||
private val ctx by InternalContext
|
||||
private val ctx by InternalContext.lateinit
|
||||
private val cryptography by lazy { ctx.cryptography }
|
||||
private val powRepo by lazy { ctx.proofOfWorkRepository }
|
||||
private val messageRepo by lazy { ctx.messageRepository }
|
||||
@ -45,75 +46,69 @@ class ProofOfWorkService : ProofOfWorkEngine.Callback {
|
||||
override fun run() {
|
||||
LOG.info("Doing POW for " + items.size + " tasks.")
|
||||
for (initialHash in items) {
|
||||
val (`object`, nonceTrialsPerByte, extraBytes) = powRepo.getItem(initialHash)
|
||||
cryptography.doProofOfWork(`object`, nonceTrialsPerByte, extraBytes,
|
||||
val (objectMessage, nonceTrialsPerByte, extraBytes) = powRepo.getItem(initialHash)
|
||||
cryptography.doProofOfWork(objectMessage, nonceTrialsPerByte, extraBytes,
|
||||
this@ProofOfWorkService)
|
||||
}
|
||||
}
|
||||
}, delayInMilliseconds)
|
||||
}
|
||||
|
||||
fun doProofOfWork(`object`: ObjectMessage) {
|
||||
doProofOfWork(null, `object`)
|
||||
fun doProofOfWork(objectMessage: ObjectMessage) {
|
||||
doProofOfWork(null, objectMessage)
|
||||
}
|
||||
|
||||
fun doProofOfWork(recipient: BitmessageAddress?, `object`: ObjectMessage) {
|
||||
fun doProofOfWork(recipient: BitmessageAddress?, objectMessage: ObjectMessage) {
|
||||
val pubkey = recipient?.pubkey
|
||||
|
||||
val nonceTrialsPerByte = pubkey?.nonceTrialsPerByte ?: NETWORK_NONCE_TRIALS_PER_BYTE
|
||||
val extraBytes = pubkey?.extraBytes ?: NETWORK_EXTRA_BYTES
|
||||
|
||||
powRepo.putObject(`object`, nonceTrialsPerByte, extraBytes)
|
||||
if (`object`.payload is PlaintextHolder) {
|
||||
`object`.payload.plaintext?.let {
|
||||
it.initialHash = cryptography.getInitialHash(`object`)
|
||||
powRepo.putObject(objectMessage, nonceTrialsPerByte, extraBytes)
|
||||
if (objectMessage.payload is PlaintextHolder) {
|
||||
objectMessage.payload.plaintext?.let {
|
||||
it.initialHash = cryptography.getInitialHash(objectMessage)
|
||||
messageRepo.save(it)
|
||||
}
|
||||
} ?: LOG.error("PlaintextHolder without Plaintext shouldn't make it to the POW")
|
||||
}
|
||||
cryptography.doProofOfWork(`object`, nonceTrialsPerByte, extraBytes, this)
|
||||
cryptography.doProofOfWork(objectMessage, nonceTrialsPerByte, extraBytes, this)
|
||||
}
|
||||
|
||||
fun doProofOfWorkWithAck(plaintext: Plaintext, expirationTime: Long) {
|
||||
val ack = plaintext.ackMessage
|
||||
val ack = plaintext.ackMessage!!
|
||||
messageRepo.save(plaintext)
|
||||
val item = Item(ack!!, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES,
|
||||
val item = Item(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES,
|
||||
expirationTime, plaintext)
|
||||
powRepo.putObject(item)
|
||||
cryptography.doProofOfWork(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, this)
|
||||
}
|
||||
|
||||
override fun onNonceCalculated(initialHash: ByteArray, nonce: ByteArray) {
|
||||
val (`object`, _, _, expirationTime, message) = powRepo.getItem(initialHash)
|
||||
val (objectMessage, _, _, expirationTime, message) = powRepo.getItem(initialHash)
|
||||
if (message == null) {
|
||||
`object`.nonce = nonce
|
||||
objectMessage.nonce = nonce
|
||||
messageRepo.getMessage(initialHash)?.let {
|
||||
it.inventoryVector = `object`.inventoryVector
|
||||
it.inventoryVector = objectMessage.inventoryVector
|
||||
it.updateNextTry()
|
||||
ctx.labeler.markAsSent(it)
|
||||
messageRepo.save(it)
|
||||
}
|
||||
try {
|
||||
ctx.networkListener.receive(`object`)
|
||||
} catch (e: IOException) {
|
||||
LOG.debug(e.message, e)
|
||||
}
|
||||
|
||||
ctx.inventory.storeObject(`object`)
|
||||
ctx.networkHandler.offer(`object`.inventoryVector)
|
||||
ctx.inventory.storeObject(objectMessage)
|
||||
ctx.networkHandler.offer(objectMessage.inventoryVector)
|
||||
} else {
|
||||
message.ackMessage!!.nonce = nonce
|
||||
val `object` = ObjectMessage.Builder()
|
||||
val newObjectMessage = ObjectMessage.Builder()
|
||||
.stream(message.stream)
|
||||
.expiresTime(expirationTime!!)
|
||||
.payload(Msg(message))
|
||||
.build()
|
||||
if (`object`.isSigned) {
|
||||
`object`.sign(message.from.privateKey!!)
|
||||
if (newObjectMessage.isSigned) {
|
||||
newObjectMessage.sign(message.from.privateKey!!)
|
||||
}
|
||||
if (`object`.payload is Encrypted) {
|
||||
`object`.encrypt(message.to!!.pubkey!!)
|
||||
if (newObjectMessage.payload is Encrypted) {
|
||||
newObjectMessage.encrypt(message.to!!.pubkey!!)
|
||||
}
|
||||
doProofOfWork(message.to, `object`)
|
||||
doProofOfWork(message.to, newObjectMessage)
|
||||
}
|
||||
powRepo.removeObject(initialHash)
|
||||
}
|
||||
|
@ -28,14 +28,14 @@ data class Addr constructor(val addresses: List<NetworkAddress>) : MessagePayloa
|
||||
override val command: MessagePayload.Command = MessagePayload.Command.ADDR
|
||||
|
||||
override fun write(out: OutputStream) {
|
||||
Encode.varInt(addresses.size.toLong(), out)
|
||||
Encode.varInt(addresses.size, out)
|
||||
for (address in addresses) {
|
||||
address.write(out)
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
Encode.varInt(addresses.size.toLong(), buffer)
|
||||
Encode.varInt(addresses.size, buffer)
|
||||
for (address in addresses) {
|
||||
address.write(buffer)
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ class GetData constructor(var inventory: List<InventoryVector>) : MessagePayload
|
||||
override val command: MessagePayload.Command = MessagePayload.Command.GETDATA
|
||||
|
||||
override fun write(out: OutputStream) {
|
||||
Encode.varInt(inventory.size.toLong(), out)
|
||||
Encode.varInt(inventory.size, out)
|
||||
for (iv in inventory) {
|
||||
iv.write(out)
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
Encode.varInt(inventory.size.toLong(), buffer)
|
||||
Encode.varInt(inventory.size, buffer)
|
||||
for (iv in inventory) {
|
||||
iv.write(buffer)
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ class Inv constructor(val inventory: List<InventoryVector>) : MessagePayload {
|
||||
override val command: MessagePayload.Command = MessagePayload.Command.INV
|
||||
|
||||
override fun write(out: OutputStream) {
|
||||
Encode.varInt(inventory.size.toLong(), out)
|
||||
Encode.varInt(inventory.size, out)
|
||||
for (iv in inventory) {
|
||||
iv.write(out)
|
||||
}
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
Encode.varInt(inventory.size.toLong(), buffer)
|
||||
Encode.varInt(inventory.size, buffer)
|
||||
for (iv in inventory) {
|
||||
iv.write(buffer)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ data class NetworkMessage(
|
||||
@Throws(IOException::class)
|
||||
override fun write(out: OutputStream) {
|
||||
// magic
|
||||
Encode.int32(MAGIC.toLong(), out)
|
||||
Encode.int32(MAGIC, out)
|
||||
|
||||
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
|
||||
val command = payload.command.name.toLowerCase()
|
||||
@ -57,7 +57,7 @@ data class NetworkMessage(
|
||||
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
|
||||
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
|
||||
// larger than this.
|
||||
Encode.int32(payloadBytes.size.toLong(), out)
|
||||
Encode.int32(payloadBytes.size, out)
|
||||
|
||||
// checksum
|
||||
out.write(getChecksum(payloadBytes))
|
||||
@ -92,7 +92,7 @@ data class NetworkMessage(
|
||||
|
||||
private fun writeHeader(out: ByteBuffer): ByteArray {
|
||||
// magic
|
||||
Encode.int32(MAGIC.toLong(), out)
|
||||
Encode.int32(MAGIC, out)
|
||||
|
||||
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
|
||||
val command = payload.command.name.toLowerCase()
|
||||
@ -107,7 +107,7 @@ data class NetworkMessage(
|
||||
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
|
||||
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
|
||||
// larger than this.
|
||||
Encode.int32(payloadBytes.size.toLong(), out)
|
||||
Encode.int32(payloadBytes.size, out)
|
||||
|
||||
// checksum
|
||||
out.put(getChecksum(payloadBytes))
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.entity
|
||||
|
||||
import ch.dissem.bitmessage.entity.Plaintext.Encoding.*
|
||||
import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
|
||||
import ch.dissem.bitmessage.entity.payload.Msg
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature
|
||||
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding
|
||||
@ -38,10 +39,20 @@ import kotlin.collections.HashSet
|
||||
fun message(encoding: Plaintext.Encoding, subject: String, body: String): ByteArray = when (encoding) {
|
||||
SIMPLE -> "Subject:$subject\nBody:$body".toByteArray()
|
||||
EXTENDED -> Message.Builder().subject(subject).body(body).build().zip()
|
||||
TRIVIAL -> (subject+body).toByteArray()
|
||||
TRIVIAL -> (subject + body).toByteArray()
|
||||
IGNORE -> ByteArray(0)
|
||||
}
|
||||
|
||||
fun ackData(type: Plaintext.Type, ackData: ByteArray?): ByteArray? {
|
||||
if (ackData != null) {
|
||||
return ackData
|
||||
} else if (type == MSG) {
|
||||
return cryptography().randomBytes(Msg.ACK_LENGTH)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The unencrypted message to be sent by 'msg' or 'broadcast'.
|
||||
*/
|
||||
@ -52,7 +63,7 @@ class Plaintext private constructor(
|
||||
val encodingCode: Long,
|
||||
val message: ByteArray,
|
||||
val ackData: ByteArray?,
|
||||
ackMessage: Lazy<ObjectMessage?>,
|
||||
ackMessage: Lazy<ObjectMessage?> = lazy { Factory.createAck(from, ackData, ttl) },
|
||||
val conversationId: UUID = UUID.randomUUID(),
|
||||
var inventoryVector: InventoryVector? = null,
|
||||
var signature: ByteArray? = null,
|
||||
@ -121,7 +132,7 @@ class Plaintext private constructor(
|
||||
to: BitmessageAddress?,
|
||||
encoding: Encoding,
|
||||
message: ByteArray,
|
||||
ackData: ByteArray = cryptography().randomBytes(Msg.ACK_LENGTH),
|
||||
ackData: ByteArray? = null,
|
||||
conversationId: UUID = UUID.randomUUID(),
|
||||
inventoryVector: InventoryVector? = null,
|
||||
signature: ByteArray? = null,
|
||||
@ -131,21 +142,20 @@ class Plaintext private constructor(
|
||||
labels: MutableSet<Label> = HashSet(),
|
||||
status: Status
|
||||
) : this(
|
||||
type,
|
||||
from,
|
||||
to,
|
||||
encoding.code,
|
||||
message,
|
||||
ackData,
|
||||
lazy { Factory.createAck(from, ackData, ttl) },
|
||||
conversationId,
|
||||
inventoryVector,
|
||||
signature,
|
||||
received,
|
||||
initialHash,
|
||||
ttl,
|
||||
labels,
|
||||
status
|
||||
type = type,
|
||||
from = from,
|
||||
to = to,
|
||||
encoding = encoding.code,
|
||||
message = message,
|
||||
ackMessage = ackData(type, ackData),
|
||||
conversationId = conversationId,
|
||||
inventoryVector = inventoryVector,
|
||||
signature = signature,
|
||||
received = received,
|
||||
initialHash = initialHash,
|
||||
ttl = ttl,
|
||||
labels = labels,
|
||||
status = status
|
||||
)
|
||||
|
||||
constructor(
|
||||
@ -164,13 +174,13 @@ class Plaintext private constructor(
|
||||
labels: MutableSet<Label> = HashSet(),
|
||||
status: Status
|
||||
) : this(
|
||||
type,
|
||||
from,
|
||||
to,
|
||||
encoding,
|
||||
message,
|
||||
null,
|
||||
lazy {
|
||||
type = type,
|
||||
from = from,
|
||||
to = to,
|
||||
encodingCode = encoding,
|
||||
message = message,
|
||||
ackData = null,
|
||||
ackMessage = lazy {
|
||||
if (ackMessage != null && ackMessage.isNotEmpty()) {
|
||||
Factory.getObjectMessage(
|
||||
3,
|
||||
@ -178,14 +188,14 @@ class Plaintext private constructor(
|
||||
ackMessage.size)
|
||||
} else null
|
||||
},
|
||||
conversationId,
|
||||
inventoryVector,
|
||||
signature,
|
||||
received,
|
||||
initialHash,
|
||||
ttl,
|
||||
labels,
|
||||
status
|
||||
conversationId = conversationId,
|
||||
inventoryVector = inventoryVector,
|
||||
signature = signature,
|
||||
received = received,
|
||||
initialHash = initialHash,
|
||||
ttl = ttl,
|
||||
labels = labels,
|
||||
status = status
|
||||
)
|
||||
|
||||
constructor(
|
||||
@ -195,37 +205,36 @@ class Plaintext private constructor(
|
||||
encoding: Encoding = SIMPLE,
|
||||
subject: String,
|
||||
body: String,
|
||||
ackData: ByteArray = cryptography().randomBytes(Msg.ACK_LENGTH),
|
||||
ackData: ByteArray? = null,
|
||||
conversationId: UUID = UUID.randomUUID(),
|
||||
ttl: Long = TTL.msg,
|
||||
labels: MutableSet<Label> = HashSet(),
|
||||
status: Status = Status.DRAFT
|
||||
) : this(
|
||||
type,
|
||||
from,
|
||||
to,
|
||||
encoding.code,
|
||||
message(encoding, subject, body),
|
||||
ackData,
|
||||
lazy { Factory.createAck(from, ackData, ttl) },
|
||||
conversationId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
ttl,
|
||||
labels,
|
||||
status
|
||||
type = type,
|
||||
from = from,
|
||||
to = to,
|
||||
encoding = encoding.code,
|
||||
message = message(encoding, subject, body),
|
||||
ackMessage = ackData(type, ackData),
|
||||
conversationId = conversationId,
|
||||
inventoryVector = null,
|
||||
signature = null,
|
||||
received = null,
|
||||
initialHash = null,
|
||||
ttl = ttl,
|
||||
labels = labels,
|
||||
status = status
|
||||
)
|
||||
|
||||
constructor(builder: Builder) : this(
|
||||
builder.type,
|
||||
builder.from ?: throw IllegalStateException("sender identity not set"),
|
||||
builder.to,
|
||||
builder.encoding,
|
||||
builder.message,
|
||||
builder.ackData,
|
||||
lazy {
|
||||
type = builder.type,
|
||||
from = builder.from ?: throw IllegalStateException("sender identity not set"),
|
||||
to = builder.to,
|
||||
encodingCode = builder.encoding,
|
||||
message = builder.message,
|
||||
ackData = builder.ackData,
|
||||
ackMessage = lazy {
|
||||
val ackMsg = builder.ackMessage
|
||||
if (ackMsg != null && ackMsg.isNotEmpty()) {
|
||||
Factory.getObjectMessage(
|
||||
@ -236,15 +245,17 @@ class Plaintext private constructor(
|
||||
Factory.createAck(builder.from!!, builder.ackData, builder.ttl)
|
||||
}
|
||||
},
|
||||
builder.conversation ?: UUID.randomUUID(),
|
||||
builder.inventoryVector,
|
||||
builder.signature,
|
||||
builder.received,
|
||||
null,
|
||||
builder.ttl,
|
||||
builder.labels,
|
||||
builder.status ?: Status.RECEIVED
|
||||
)
|
||||
conversationId = builder.conversation ?: UUID.randomUUID(),
|
||||
inventoryVector = builder.inventoryVector,
|
||||
signature = builder.signature,
|
||||
received = builder.received,
|
||||
initialHash = null,
|
||||
ttl = builder.ttl,
|
||||
labels = builder.labels,
|
||||
status = builder.status ?: Status.RECEIVED
|
||||
) {
|
||||
id = builder.id
|
||||
}
|
||||
|
||||
fun write(out: OutputStream, includeSignature: Boolean) {
|
||||
Encode.varInt(from.version, out)
|
||||
@ -259,7 +270,7 @@ class Plaintext private constructor(
|
||||
Encode.varInt(0, out)
|
||||
}
|
||||
} else {
|
||||
Encode.int32(from.pubkey!!.behaviorBitfield.toLong(), out)
|
||||
Encode.int32(from.pubkey!!.behaviorBitfield, out)
|
||||
out.write(from.pubkey!!.signingKey, 1, 64)
|
||||
out.write(from.pubkey!!.encryptionKey, 1, 64)
|
||||
if (from.version >= 3) {
|
||||
@ -267,13 +278,13 @@ class Plaintext private constructor(
|
||||
Encode.varInt(from.pubkey!!.extraBytes, out)
|
||||
}
|
||||
}
|
||||
if (type == Type.MSG) {
|
||||
if (type == MSG) {
|
||||
out.write(to!!.ripe)
|
||||
}
|
||||
Encode.varInt(encodingCode, out)
|
||||
Encode.varInt(message.size.toLong(), out)
|
||||
Encode.varInt(message.size, out)
|
||||
out.write(message)
|
||||
if (type == Type.MSG) {
|
||||
if (type == MSG) {
|
||||
if (to?.has(Feature.DOES_ACK) ?: false) {
|
||||
val ack = ByteArrayOutputStream()
|
||||
ackMessage?.write(ack)
|
||||
@ -286,8 +297,7 @@ class Plaintext private constructor(
|
||||
if (signature == null) {
|
||||
Encode.varInt(0, out)
|
||||
} else {
|
||||
Encode.varInt(signature!!.size.toLong(), out)
|
||||
out.write(signature!!)
|
||||
Encode.varBytes(signature!!, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,7 +315,7 @@ class Plaintext private constructor(
|
||||
Encode.varInt(0, buffer)
|
||||
}
|
||||
} else {
|
||||
Encode.int32(from.pubkey!!.behaviorBitfield.toLong(), buffer)
|
||||
Encode.int32(from.pubkey!!.behaviorBitfield, buffer)
|
||||
buffer.put(from.pubkey!!.signingKey, 1, 64)
|
||||
buffer.put(from.pubkey!!.encryptionKey, 1, 64)
|
||||
if (from.version >= 3) {
|
||||
@ -313,13 +323,12 @@ class Plaintext private constructor(
|
||||
Encode.varInt(from.pubkey!!.extraBytes, buffer)
|
||||
}
|
||||
}
|
||||
if (type == Type.MSG) {
|
||||
if (type == MSG) {
|
||||
buffer.put(to!!.ripe)
|
||||
}
|
||||
Encode.varInt(encodingCode, buffer)
|
||||
Encode.varInt(message.size.toLong(), buffer)
|
||||
buffer.put(message)
|
||||
if (type == Type.MSG) {
|
||||
Encode.varBytes(message, buffer)
|
||||
if (type == MSG) {
|
||||
if (to!!.has(Feature.DOES_ACK) && ackMessage != null) {
|
||||
Encode.varBytes(Encode.bytes(ackMessage!!), buffer)
|
||||
} else {
|
||||
@ -331,8 +340,7 @@ class Plaintext private constructor(
|
||||
if (sig == null) {
|
||||
Encode.varInt(0, buffer)
|
||||
} else {
|
||||
Encode.varInt(sig.size.toLong(), buffer)
|
||||
buffer.put(sig)
|
||||
Encode.varBytes(sig, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -365,7 +373,7 @@ class Plaintext private constructor(
|
||||
val firstLine = s.nextLine()
|
||||
if (encodingCode == EXTENDED.code) {
|
||||
if (Message.TYPE == extendedData?.type) {
|
||||
return (extendedData!!.content as Message?)?.subject
|
||||
return (extendedData!!.content as? Message)?.subject
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
@ -551,10 +559,12 @@ class Plaintext private constructor(
|
||||
return this
|
||||
}
|
||||
|
||||
fun to(address: BitmessageAddress): Builder {
|
||||
if (type != Type.MSG && to != null)
|
||||
throw IllegalArgumentException("recipient address only allowed for msg")
|
||||
to = address
|
||||
fun to(address: BitmessageAddress?): Builder {
|
||||
if (address != null) {
|
||||
if (type != MSG && to != null)
|
||||
throw IllegalArgumentException("recipient address only allowed for msg")
|
||||
to = address
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
@ -594,7 +604,7 @@ class Plaintext private constructor(
|
||||
}
|
||||
|
||||
fun destinationRipe(ripe: ByteArray?): Builder {
|
||||
if (type != Type.MSG && ripe != null) throw IllegalArgumentException("ripe only allowed for msg")
|
||||
if (type != MSG && ripe != null) throw IllegalArgumentException("ripe only allowed for msg")
|
||||
this.destinationRipe = ripe
|
||||
return this
|
||||
}
|
||||
@ -622,7 +632,6 @@ class Plaintext private constructor(
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw ApplicationException(e)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
@ -632,13 +641,13 @@ class Plaintext private constructor(
|
||||
}
|
||||
|
||||
fun ackMessage(ack: ByteArray?): Builder {
|
||||
if (type != Type.MSG && ack != null) throw IllegalArgumentException("ackMessage only allowed for msg")
|
||||
if (type != MSG && ack != null) throw IllegalArgumentException("ackMessage only allowed for msg")
|
||||
this.ackMessage = ack
|
||||
return this
|
||||
}
|
||||
|
||||
fun ackData(ackData: ByteArray?): Builder {
|
||||
if (type != Type.MSG && ackData != null)
|
||||
if (type != MSG && ackData != null)
|
||||
throw IllegalArgumentException("ackMessage only allowed for msg")
|
||||
this.ackData = ackData
|
||||
return this
|
||||
@ -704,7 +713,7 @@ class Plaintext private constructor(
|
||||
if (to == null && type != Type.BROADCAST && destinationRipe != null) {
|
||||
to = BitmessageAddress(0, 0, destinationRipe!!)
|
||||
}
|
||||
if (type == Type.MSG && ackMessage == null && ackData == null) {
|
||||
if (type == MSG && ackMessage == null && ackData == null) {
|
||||
ackData = cryptography().randomBytes(Msg.ACK_LENGTH)
|
||||
}
|
||||
if (ttl <= 0) {
|
||||
@ -733,10 +742,10 @@ class Plaintext private constructor(
|
||||
.publicEncryptionKey(Decode.bytes(`in`, 64))
|
||||
.nonceTrialsPerByte(if (version >= 3) Decode.varInt(`in`) else 0)
|
||||
.extraBytes(if (version >= 3) Decode.varInt(`in`) else 0)
|
||||
.destinationRipe(if (type == Type.MSG) Decode.bytes(`in`, 20) else null)
|
||||
.destinationRipe(if (type == MSG) Decode.bytes(`in`, 20) else null)
|
||||
.encoding(Decode.varInt(`in`))
|
||||
.message(Decode.varBytes(`in`))
|
||||
.ackMessage(if (type == Type.MSG) Decode.varBytes(`in`) else null)
|
||||
.ackMessage(if (type == MSG) Decode.varBytes(`in`) else null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class Version constructor(
|
||||
override val command: MessagePayload.Command = MessagePayload.Command.VERSION
|
||||
|
||||
override fun write(out: OutputStream) {
|
||||
Encode.int32(version.toLong(), out)
|
||||
Encode.int32(version, out)
|
||||
Encode.int64(services, out)
|
||||
Encode.int64(timestamp, out)
|
||||
addrRecv.write(out, true)
|
||||
@ -89,7 +89,7 @@ class Version constructor(
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
Encode.int32(version.toLong(), buffer)
|
||||
Encode.int32(version, buffer)
|
||||
Encode.int64(services, buffer)
|
||||
Encode.int64(timestamp, buffer)
|
||||
addrRecv.write(buffer, true)
|
||||
|
@ -114,7 +114,7 @@ class CryptoBox : Streamable {
|
||||
|
||||
private fun writeWithoutMAC(out: OutputStream) {
|
||||
out.write(initializationVector)
|
||||
Encode.int16(curveType.toLong(), out)
|
||||
Encode.int16(curveType, out)
|
||||
writeCoordinateComponent(out, Points.getX(R))
|
||||
writeCoordinateComponent(out, Points.getY(R))
|
||||
out.write(encrypted)
|
||||
@ -123,14 +123,14 @@ class CryptoBox : Streamable {
|
||||
private fun writeCoordinateComponent(out: OutputStream, x: ByteArray) {
|
||||
val offset = Bytes.numberOfLeadingZeros(x)
|
||||
val length = x.size - offset
|
||||
Encode.int16(length.toLong(), out)
|
||||
Encode.int16(length, out)
|
||||
out.write(x, offset, length)
|
||||
}
|
||||
|
||||
private fun writeCoordinateComponent(buffer: ByteBuffer, x: ByteArray) {
|
||||
val offset = Bytes.numberOfLeadingZeros(x)
|
||||
val length = x.size - offset
|
||||
Encode.int16(length.toLong(), buffer)
|
||||
Encode.int16(length, buffer)
|
||||
buffer.put(x, offset, length)
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ class CryptoBox : Streamable {
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
buffer.put(initializationVector)
|
||||
Encode.int16(curveType.toLong(), buffer)
|
||||
Encode.int16(curveType, buffer)
|
||||
writeCoordinateComponent(buffer, Points.getX(R))
|
||||
writeCoordinateComponent(buffer, Points.getY(R))
|
||||
buffer.put(encrypted)
|
||||
|
@ -31,13 +31,13 @@ open class V2Pubkey constructor(version: Long, override val stream: Long, overri
|
||||
override val encryptionKey: ByteArray = if (encryptionKey.size == 64) add0x04(encryptionKey) else encryptionKey
|
||||
|
||||
override fun write(out: OutputStream) {
|
||||
Encode.int32(behaviorBitfield.toLong(), out)
|
||||
Encode.int32(behaviorBitfield, out)
|
||||
out.write(signingKey, 1, 64)
|
||||
out.write(encryptionKey, 1, 64)
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
Encode.int32(behaviorBitfield.toLong(), buffer)
|
||||
Encode.int32(behaviorBitfield, buffer)
|
||||
buffer.put(signingKey, 1, 64)
|
||||
buffer.put(encryptionKey, 1, 64)
|
||||
}
|
||||
|
@ -19,11 +19,14 @@ package ch.dissem.bitmessage.entity.valueobject
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
data class Label(private val label: String, val type: Label.Type,
|
||||
/**
|
||||
* RGBA representation for the color.
|
||||
*/
|
||||
var color: Int) : Serializable {
|
||||
data class Label(
|
||||
private val label: String,
|
||||
val type: Label.Type?,
|
||||
/**
|
||||
* RGBA representation for the color.
|
||||
*/
|
||||
var color: Int
|
||||
) : Serializable {
|
||||
|
||||
var id: Any? = null
|
||||
|
||||
|
@ -30,7 +30,7 @@ import java.util.*
|
||||
/**
|
||||
* A node's address. It's written in IPv6 format.
|
||||
*/
|
||||
data class NetworkAddress constructor(
|
||||
data class NetworkAddress(
|
||||
var time: Long,
|
||||
|
||||
/**
|
||||
@ -47,25 +47,25 @@ data class NetworkAddress constructor(
|
||||
* IPv6 address. IPv4 addresses are written into the message as a 16 byte IPv4-mapped IPv6 address
|
||||
* (12 bytes 00 00 00 00 00 00 00 00 00 00 FF FF, followed by the 4 bytes of the IPv4 address).
|
||||
*/
|
||||
val iPv6: ByteArray,
|
||||
val IPv6: ByteArray,
|
||||
val port: Int
|
||||
) : Streamable {
|
||||
|
||||
fun provides(service: Version.Service?): Boolean = service?.isEnabled(services) ?: false
|
||||
|
||||
fun toInetAddress(): InetAddress {
|
||||
return InetAddress.getByAddress(iPv6)
|
||||
return InetAddress.getByAddress(IPv6)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is NetworkAddress) return false
|
||||
|
||||
return port == other.port && Arrays.equals(iPv6, other.iPv6)
|
||||
return port == other.port && Arrays.equals(IPv6, other.IPv6)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = Arrays.hashCode(iPv6)
|
||||
var result = Arrays.hashCode(IPv6)
|
||||
result = 31 * result + port
|
||||
return result
|
||||
}
|
||||
@ -84,8 +84,8 @@ data class NetworkAddress constructor(
|
||||
Encode.int32(stream, out)
|
||||
}
|
||||
Encode.int64(services, out)
|
||||
out.write(iPv6)
|
||||
Encode.int16(port.toLong(), out)
|
||||
out.write(IPv6)
|
||||
Encode.int16(port, out)
|
||||
}
|
||||
|
||||
override fun write(buffer: ByteBuffer) {
|
||||
@ -98,8 +98,8 @@ data class NetworkAddress constructor(
|
||||
Encode.int32(stream, buffer)
|
||||
}
|
||||
Encode.int64(services, buffer)
|
||||
buffer.put(iPv6)
|
||||
Encode.int16(port.toLong(), buffer)
|
||||
buffer.put(IPv6)
|
||||
Encode.int16(port, buffer)
|
||||
}
|
||||
|
||||
class Builder {
|
||||
|
@ -121,12 +121,10 @@ class PrivateKey : Streamable {
|
||||
Encode.varInt(pubkey.stream, out)
|
||||
val baos = ByteArrayOutputStream()
|
||||
pubkey.writeUnencrypted(baos)
|
||||
Encode.varInt(baos.size().toLong(), out)
|
||||
Encode.varInt(baos.size(), out)
|
||||
out.write(baos.toByteArray())
|
||||
Encode.varInt(privateSigningKey.size.toLong(), out)
|
||||
out.write(privateSigningKey)
|
||||
Encode.varInt(privateEncryptionKey.size.toLong(), out)
|
||||
out.write(privateEncryptionKey)
|
||||
Encode.varBytes(privateSigningKey, out)
|
||||
Encode.varBytes(privateEncryptionKey, out)
|
||||
}
|
||||
|
||||
|
||||
|
@ -37,7 +37,7 @@ import javax.crypto.spec.SecretKeySpec
|
||||
* Implements everything that isn't directly dependent on either Spongy- or Bouncycastle.
|
||||
*/
|
||||
abstract class AbstractCryptography protected constructor(@JvmField protected val provider: Provider) : Cryptography {
|
||||
private val context by InternalContext
|
||||
private val context by InternalContext.lateinit
|
||||
|
||||
@JvmField protected val ALGORITHM_ECDSA = "ECDSA"
|
||||
@JvmField protected val ALGORITHM_ECDSA_SHA1 = "SHA1withECDSA"
|
||||
@ -87,21 +87,21 @@ abstract class AbstractCryptography protected constructor(@JvmField protected va
|
||||
return result
|
||||
}
|
||||
|
||||
override fun doProofOfWork(`object`: ObjectMessage, nonceTrialsPerByte: Long,
|
||||
override fun doProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long,
|
||||
extraBytes: Long, callback: ProofOfWorkEngine.Callback) {
|
||||
|
||||
val initialHash = getInitialHash(`object`)
|
||||
val initialHash = getInitialHash(objectMessage)
|
||||
|
||||
val target = getProofOfWorkTarget(`object`,
|
||||
val target = getProofOfWorkTarget(objectMessage,
|
||||
max(nonceTrialsPerByte, NETWORK_NONCE_TRIALS_PER_BYTE), max(extraBytes, NETWORK_EXTRA_BYTES))
|
||||
|
||||
context.proofOfWorkEngine.calculateNonce(initialHash, target, callback)
|
||||
}
|
||||
|
||||
@Throws(InsufficientProofOfWorkException::class)
|
||||
override fun checkProofOfWork(`object`: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) {
|
||||
val target = getProofOfWorkTarget(`object`, nonceTrialsPerByte, extraBytes)
|
||||
val value = doubleSha512(`object`.nonce ?: throw ApplicationException("Object without nonce"), getInitialHash(`object`))
|
||||
override fun checkProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) {
|
||||
val target = getProofOfWorkTarget(objectMessage, nonceTrialsPerByte, extraBytes)
|
||||
val value = doubleSha512(objectMessage.nonce ?: throw ApplicationException("Object without nonce"), getInitialHash(objectMessage))
|
||||
if (Bytes.lt(target, value, 8)) {
|
||||
throw InsufficientProofOfWorkException(target, value)
|
||||
}
|
||||
@ -130,18 +130,18 @@ abstract class AbstractCryptography protected constructor(@JvmField protected va
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getInitialHash(`object`: ObjectMessage): ByteArray {
|
||||
return sha512(`object`.payloadBytesWithoutNonce)
|
||||
override fun getInitialHash(objectMessage: ObjectMessage): ByteArray {
|
||||
return sha512(objectMessage.payloadBytesWithoutNonce)
|
||||
}
|
||||
|
||||
override fun getProofOfWorkTarget(`object`: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long): ByteArray {
|
||||
override fun getProofOfWorkTarget(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long): ByteArray {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val nonceTrialsPerByte = if (nonceTrialsPerByte == 0L) NETWORK_NONCE_TRIALS_PER_BYTE else nonceTrialsPerByte
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val extraBytes = if (extraBytes == 0L) NETWORK_EXTRA_BYTES else extraBytes
|
||||
|
||||
val TTL = BigInteger.valueOf(`object`.expiresTime - UnixTime.now)
|
||||
val powLength = BigInteger.valueOf(`object`.payloadBytesWithoutNonce.size + extraBytes)
|
||||
val TTL = BigInteger.valueOf(objectMessage.expiresTime - UnixTime.now)
|
||||
val powLength = BigInteger.valueOf(objectMessage.payloadBytesWithoutNonce.size + extraBytes)
|
||||
val denominator = BigInteger.valueOf(nonceTrialsPerByte)
|
||||
.multiply(
|
||||
powLength.add(
|
||||
|
@ -28,19 +28,19 @@ import ch.dissem.bitmessage.utils.UnixTime
|
||||
import java.util.*
|
||||
|
||||
abstract class AbstractMessageRepository : MessageRepository {
|
||||
protected var ctx by InternalContext
|
||||
protected var ctx by InternalContext.lateinit
|
||||
|
||||
protected fun saveContactIfNecessary(contact: BitmessageAddress?) {
|
||||
contact?.let {
|
||||
val savedAddress = ctx.addressRepository.getAddress(contact.address)
|
||||
val savedAddress = ctx.addressRepository.getAddress(it.address)
|
||||
if (savedAddress == null) {
|
||||
ctx.addressRepository.save(contact)
|
||||
} else if (savedAddress.pubkey == null && contact.pubkey != null) {
|
||||
savedAddress.pubkey = contact.pubkey
|
||||
ctx.addressRepository.save(savedAddress)
|
||||
}
|
||||
if (savedAddress != null) {
|
||||
contact.alias = savedAddress.alias
|
||||
ctx.addressRepository.save(it)
|
||||
} else {
|
||||
if (savedAddress.pubkey == null && it.pubkey != null) {
|
||||
savedAddress.pubkey = it.pubkey
|
||||
ctx.addressRepository.save(savedAddress)
|
||||
}
|
||||
it.alias = savedAddress.alias
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ interface Cryptography {
|
||||
* Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to
|
||||
* live.
|
||||
|
||||
* @param object to do the proof of work for
|
||||
* @param objectMessage to do the proof of work for
|
||||
* *
|
||||
* @param nonceTrialsPerByte difficulty
|
||||
* *
|
||||
@ -153,11 +153,11 @@ interface Cryptography {
|
||||
* *
|
||||
* @param callback to handle nonce once it's calculated
|
||||
*/
|
||||
fun doProofOfWork(`object`: ObjectMessage, nonceTrialsPerByte: Long,
|
||||
fun doProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long,
|
||||
extraBytes: Long, callback: ProofOfWorkEngine.Callback)
|
||||
|
||||
/**
|
||||
* @param object to be checked
|
||||
* @param objectMessage to be checked
|
||||
* *
|
||||
* @param nonceTrialsPerByte difficulty
|
||||
* *
|
||||
@ -166,11 +166,11 @@ interface Cryptography {
|
||||
* @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages)
|
||||
*/
|
||||
@Throws(InsufficientProofOfWorkException::class)
|
||||
fun checkProofOfWork(`object`: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long)
|
||||
fun checkProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long)
|
||||
|
||||
fun getInitialHash(`object`: ObjectMessage): ByteArray
|
||||
fun getInitialHash(objectMessage: ObjectMessage): ByteArray
|
||||
|
||||
fun getProofOfWorkTarget(`object`: ObjectMessage, nonceTrialsPerByte: Long = NETWORK_NONCE_TRIALS_PER_BYTE, extraBytes: Long = NETWORK_EXTRA_BYTES): ByteArray
|
||||
fun getProofOfWorkTarget(objectMessage: ObjectMessage, nonceTrialsPerByte: Long = NETWORK_NONCE_TRIALS_PER_BYTE, extraBytes: Long = NETWORK_EXTRA_BYTES): ByteArray
|
||||
|
||||
/**
|
||||
* Calculates the MAC for a message (data)
|
||||
|
@ -23,7 +23,7 @@ import ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label
|
||||
|
||||
open class DefaultLabeler : Labeler {
|
||||
private var ctx by InternalContext
|
||||
private var ctx by InternalContext.lateinit
|
||||
|
||||
override fun setLabels(msg: Plaintext) {
|
||||
msg.status = RECEIVED
|
||||
|
@ -43,9 +43,9 @@ interface Inventory {
|
||||
*/
|
||||
fun getObjects(stream: Long, version: Long, vararg types: ObjectType): List<ObjectMessage>
|
||||
|
||||
fun storeObject(`object`: ObjectMessage)
|
||||
fun storeObject(objectMessage: ObjectMessage)
|
||||
|
||||
operator fun contains(`object`: ObjectMessage): Boolean
|
||||
operator fun contains(objectMessage: ObjectMessage): Boolean
|
||||
|
||||
/**
|
||||
* Deletes all objects that expired 5 minutes ago or earlier
|
||||
|
@ -28,7 +28,7 @@ interface MessageRepository {
|
||||
|
||||
fun getLabels(vararg types: Label.Type): List<Label>
|
||||
|
||||
fun countUnread(label: Label): Int
|
||||
fun countUnread(label: Label?): Int
|
||||
|
||||
fun getMessage(id: Any): Plaintext
|
||||
|
||||
@ -43,7 +43,7 @@ interface MessageRepository {
|
||||
* *
|
||||
* @return a distinct list of all conversations that have at least one message with the given label.
|
||||
*/
|
||||
fun findConversations(label: Label): List<UUID>
|
||||
fun findConversations(label: Label?): List<UUID>
|
||||
|
||||
fun findMessages(label: Label?): List<Plaintext>
|
||||
|
||||
|
@ -81,6 +81,6 @@ interface NetworkHandler {
|
||||
|
||||
interface MessageListener {
|
||||
@Throws(IOException::class)
|
||||
fun receive(`object`: ObjectMessage)
|
||||
fun receive(objectMessage: ObjectMessage)
|
||||
}
|
||||
}
|
||||
|
@ -29,14 +29,14 @@ interface ProofOfWorkRepository {
|
||||
|
||||
fun getItems(): List<ByteArray>
|
||||
|
||||
fun putObject(`object`: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long)
|
||||
fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long)
|
||||
|
||||
fun putObject(item: Item)
|
||||
|
||||
fun removeObject(initialHash: ByteArray)
|
||||
|
||||
data class Item @JvmOverloads constructor(
|
||||
val `object`: ObjectMessage,
|
||||
val objectMessage: ObjectMessage,
|
||||
val nonceTrialsPerByte: Long,
|
||||
val extraBytes: Long,
|
||||
// Needed for ACK POW calculation
|
||||
|
@ -19,6 +19,7 @@ package ch.dissem.bitmessage.utils
|
||||
import ch.dissem.bitmessage.entity.Plaintext
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector
|
||||
import ch.dissem.bitmessage.ports.MessageRepository
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
import java.util.regex.Pattern
|
||||
|
@ -27,19 +27,20 @@ import java.nio.ByteBuffer
|
||||
*/
|
||||
object Encode {
|
||||
@JvmStatic fun varIntList(values: LongArray, stream: OutputStream) {
|
||||
varInt(values.size.toLong(), stream)
|
||||
varInt(values.size, stream)
|
||||
for (value in values) {
|
||||
varInt(value, stream)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun varIntList(values: LongArray, buffer: ByteBuffer) {
|
||||
varInt(values.size.toLong(), buffer)
|
||||
varInt(values.size, buffer)
|
||||
for (value in values) {
|
||||
varInt(value, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun varInt(value: Int, buffer: ByteBuffer) = varInt(value.toLong(), buffer)
|
||||
@JvmStatic fun varInt(value: Long, buffer: ByteBuffer) {
|
||||
if (value < 0) {
|
||||
// This is due to the fact that Java doesn't really support unsigned values.
|
||||
@ -62,6 +63,7 @@ object Encode {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun varInt(value: Int) = varInt(value.toLong())
|
||||
@JvmStatic fun varInt(value: Long): ByteArray {
|
||||
val buffer = ByteBuffer.allocate(9)
|
||||
varInt(value, buffer)
|
||||
@ -69,6 +71,7 @@ object Encode {
|
||||
return Bytes.truncate(buffer.array(), buffer.limit())
|
||||
}
|
||||
|
||||
@JvmStatic @JvmOverloads fun varInt(value: Int, stream: OutputStream, counter: AccessCounter? = null) = varInt(value.toLong(), stream, counter)
|
||||
@JvmStatic @JvmOverloads fun varInt(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
val buffer = ByteBuffer.allocate(9)
|
||||
varInt(value, buffer)
|
||||
@ -77,27 +80,34 @@ object Encode {
|
||||
AccessCounter.inc(counter, buffer.limit())
|
||||
}
|
||||
|
||||
@JvmStatic @JvmOverloads fun int8(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(value.toInt())
|
||||
@JvmStatic @JvmOverloads fun int8(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int8(value.toInt(), stream, counter)
|
||||
@JvmStatic @JvmOverloads fun int8(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(value)
|
||||
AccessCounter.inc(counter)
|
||||
}
|
||||
|
||||
@JvmStatic @JvmOverloads fun int16(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(ByteBuffer.allocate(2).putShort(value.toShort()).array())
|
||||
@JvmStatic @JvmOverloads fun int16(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
|
||||
@JvmStatic @JvmOverloads fun int16(value: Int, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
|
||||
@JvmStatic @JvmOverloads fun int16(value: Short, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(ByteBuffer.allocate(2).putShort(value).array())
|
||||
AccessCounter.inc(counter, 2)
|
||||
}
|
||||
|
||||
@JvmStatic fun int16(value: Long, buffer: ByteBuffer) {
|
||||
buffer.putShort(value.toShort())
|
||||
@JvmStatic fun int16(value: Long, buffer: ByteBuffer) = int16(value.toShort(), buffer)
|
||||
@JvmStatic fun int16(value: Int, buffer: ByteBuffer) = int16(value.toShort(), buffer)
|
||||
@JvmStatic fun int16(value: Short, buffer: ByteBuffer) {
|
||||
buffer.putShort(value)
|
||||
}
|
||||
|
||||
@JvmStatic @JvmOverloads fun int32(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(ByteBuffer.allocate(4).putInt(value.toInt()).array())
|
||||
@JvmStatic @JvmOverloads fun int32(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int32(value.toInt(), stream, counter)
|
||||
@JvmStatic @JvmOverloads fun int32(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
stream.write(ByteBuffer.allocate(4).putInt(value).array())
|
||||
AccessCounter.inc(counter, 4)
|
||||
}
|
||||
|
||||
@JvmStatic fun int32(value: Long, buffer: ByteBuffer) {
|
||||
buffer.putInt(value.toInt())
|
||||
@JvmStatic fun int32(value: Long, buffer: ByteBuffer) = int32(value.toInt(), buffer)
|
||||
@JvmStatic fun int32(value: Int, buffer: ByteBuffer) {
|
||||
buffer.putInt(value)
|
||||
}
|
||||
|
||||
@JvmStatic @JvmOverloads fun int64(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
|
||||
|
@ -28,8 +28,8 @@ import ch.dissem.bitmessage.ports.DefaultLabeler
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository
|
||||
import ch.dissem.bitmessage.testutils.TestInventory
|
||||
import ch.dissem.bitmessage.utils.Singleton
|
||||
import ch.dissem.bitmessage.utils.Singleton.cryptography
|
||||
import ch.dissem.bitmessage.utils.Strings.hex
|
||||
import ch.dissem.bitmessage.utils.TTL
|
||||
import ch.dissem.bitmessage.utils.TestUtils
|
||||
import ch.dissem.bitmessage.utils.UnixTime.MINUTE
|
||||
@ -40,12 +40,12 @@ import org.junit.Assert.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
class BitmessageContextTest {
|
||||
private lateinit var ctx: BitmessageContext
|
||||
private var listener: BitmessageContext.Listener = mock()
|
||||
private val inventory = spy(TestInventory())
|
||||
private val testPowRepo = spy(object : ProofOfWorkRepository {
|
||||
@ -54,7 +54,7 @@ class BitmessageContextTest {
|
||||
internal var removed = 0
|
||||
|
||||
override fun getItem(initialHash: ByteArray): ProofOfWorkRepository.Item {
|
||||
return items[InventoryVector(initialHash)]!!
|
||||
return items[InventoryVector(initialHash)] ?: throw IllegalArgumentException("${hex(initialHash)} not found in $items")
|
||||
}
|
||||
|
||||
override fun getItems(): List<ByteArray> {
|
||||
@ -66,12 +66,12 @@ class BitmessageContextTest {
|
||||
}
|
||||
|
||||
override fun putObject(item: ProofOfWorkRepository.Item) {
|
||||
items.put(InventoryVector(cryptography().getInitialHash(item.`object`)), item)
|
||||
items.put(InventoryVector(cryptography().getInitialHash(item.objectMessage)), item)
|
||||
added++
|
||||
}
|
||||
|
||||
override fun putObject(`object`: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) {
|
||||
items.put(InventoryVector(cryptography().getInitialHash(`object`)), ProofOfWorkRepository.Item(`object`, nonceTrialsPerByte, extraBytes))
|
||||
override fun putObject(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, extraBytes: Long) {
|
||||
items.put(InventoryVector(cryptography().getInitialHash(objectMessage)), ProofOfWorkRepository.Item(objectMessage, nonceTrialsPerByte, extraBytes))
|
||||
added++
|
||||
}
|
||||
|
||||
@ -87,39 +87,42 @@ class BitmessageContextTest {
|
||||
removed = 0
|
||||
}
|
||||
})
|
||||
private val testPowEngine = spy(object : ProofOfWorkEngine {
|
||||
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) {
|
||||
thread { callback.onNonceCalculated(initialHash, ByteArray(8)) }
|
||||
}
|
||||
})
|
||||
private var ctx = BitmessageContext.Builder()
|
||||
.addressRepo(mock())
|
||||
.cryptography(BouncyCryptography())
|
||||
.inventory(inventory)
|
||||
.listener(listener)
|
||||
.messageRepo(mock())
|
||||
.networkHandler(mock())
|
||||
.nodeRegistry(mock())
|
||||
.labeler(spy(DefaultLabeler()))
|
||||
.powRepo(testPowRepo)
|
||||
.proofOfWorkEngine(testPowEngine)
|
||||
.build()
|
||||
|
||||
init {
|
||||
TTL.msg = 2 * MINUTE
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
Singleton.initialize(BouncyCryptography())
|
||||
ctx = BitmessageContext.Builder()
|
||||
.addressRepo(mock())
|
||||
.cryptography(cryptography())
|
||||
.inventory(inventory)
|
||||
.listener(listener)
|
||||
.messageRepo(mock())
|
||||
.networkHandler(mock())
|
||||
.nodeRegistry(mock())
|
||||
.labeler(spy(DefaultLabeler()))
|
||||
.powRepo(testPowRepo)
|
||||
.proofOfWorkEngine(spy(object : ProofOfWorkEngine {
|
||||
override fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: ProofOfWorkEngine.Callback) {
|
||||
callback.onNonceCalculated(initialHash, ByteArray(8))
|
||||
}
|
||||
}))
|
||||
.build()
|
||||
TTL.msg = 2 * MINUTE
|
||||
testPowRepo.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure contact is saved and pubkey requested`() {
|
||||
val contact = BitmessageAddress("BM-opWQhvk9xtMFvQA2Kvetedpk8LkbraWHT")
|
||||
whenever(ctx.addresses.getAddress(contact.address)).thenReturn(contact)
|
||||
doReturn(contact).whenever(ctx.addresses).getAddress(eq(contact.address))
|
||||
|
||||
ctx.addContact(contact)
|
||||
|
||||
verify(ctx.addresses, timeout(1000).atLeastOnce()).save(contact)
|
||||
verify(ctx.internals.proofOfWorkEngine, timeout(1000)).calculateNonce(any(), any(), any())
|
||||
verify(ctx.addresses, timeout(1000).atLeastOnce()).save(eq(contact))
|
||||
verify(testPowEngine, timeout(1000)).calculateNonce(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -132,7 +135,7 @@ class BitmessageContextTest {
|
||||
ctx.addContact(contact)
|
||||
|
||||
verify(ctx.addresses, times(1)).save(contact)
|
||||
verify(ctx.internals.proofOfWorkEngine, never()).calculateNonce(any(), any(), any())
|
||||
verify(testPowEngine, never()).calculateNonce(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -155,7 +158,7 @@ class BitmessageContextTest {
|
||||
ctx.addContact(contact)
|
||||
|
||||
verify(ctx.addresses, atLeastOnce()).save(contact)
|
||||
verify(ctx.internals.proofOfWorkEngine, never()).calculateNonce(any(), any(), any())
|
||||
verify(testPowEngine, never()).calculateNonce(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -179,7 +182,7 @@ class BitmessageContextTest {
|
||||
ctx.addContact(contact)
|
||||
|
||||
verify(ctx.addresses, atLeastOnce()).save(any())
|
||||
verify(ctx.internals.proofOfWorkEngine, never()).calculateNonce(any(), any(), any())
|
||||
verify(testPowEngine, never()).calculateNonce(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -209,9 +212,9 @@ class BitmessageContextTest {
|
||||
fun `ensure message is sent`() {
|
||||
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(),
|
||||
"Subject", "Message")
|
||||
verify(ctx.internals.proofOfWorkRepository, timeout(10000)).putObject(
|
||||
argThat { payload.type == ObjectType.MSG }, eq(1000L), eq(1000L))
|
||||
assertEquals(2, testPowRepo.added)
|
||||
verify(ctx.internals.proofOfWorkRepository, timeout(10000).atLeastOnce())
|
||||
.putObject(argThat { payload.type == ObjectType.MSG }, eq(1000L), eq(1000L))
|
||||
verify(ctx.messages, timeout(10000).atLeastOnce()).save(argThat { type == Type.MSG })
|
||||
}
|
||||
|
||||
@ -236,10 +239,9 @@ class BitmessageContextTest {
|
||||
fun `ensure broadcast is sent`() {
|
||||
ctx.broadcast(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"),
|
||||
"Subject", "Message")
|
||||
verify(ctx.internals.proofOfWorkRepository, timeout(10000).atLeastOnce())
|
||||
verify(ctx.internals.proofOfWorkRepository, timeout(1000).atLeastOnce())
|
||||
.putObject(argThat { payload.type == ObjectType.BROADCAST }, eq(1000L), eq(1000L))
|
||||
verify(ctx.internals.proofOfWorkEngine)
|
||||
.calculateNonce(any(), any(), any())
|
||||
verify(testPowEngine).calculateNonce(any(), any(), any())
|
||||
verify(ctx.messages, timeout(10000).atLeastOnce()).save(argThat { type == Type.BROADCAST })
|
||||
}
|
||||
|
||||
@ -265,7 +267,7 @@ class BitmessageContextTest {
|
||||
fun `ensure deterministic addresses are created`() {
|
||||
val expected_size = 8
|
||||
val addresses = ctx.createDeterministicAddresses("test", expected_size, 4, 1, false)
|
||||
assertEquals(expected_size.toLong(), addresses.size.toLong())
|
||||
assertEquals(expected_size, addresses.size)
|
||||
val expected = HashSet<String>(expected_size)
|
||||
expected.add("BM-2cWFkyuXXFw6d393RGnin2RpSXj8wxtt6F")
|
||||
expected.add("BM-2cX8TF9vuQZEWvT7UrEeq1HN9dgiSUPLEN")
|
||||
@ -285,7 +287,7 @@ class BitmessageContextTest {
|
||||
fun `ensure short deterministic addresses are created`() {
|
||||
val expected_size = 1
|
||||
val addresses = ctx.createDeterministicAddresses("test", expected_size, 4, 1, true)
|
||||
assertEquals(expected_size.toLong(), addresses.size.toLong())
|
||||
assertEquals(expected_size, addresses.size)
|
||||
val expected = HashSet<String>(expected_size)
|
||||
expected.add("BM-NBGyBAEp6VnBkFWKpzUSgxuTqVdWPi78")
|
||||
for (a in addresses) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package ch.dissem.bitmessage
|
||||
|
||||
import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage
|
||||
import ch.dissem.bitmessage.entity.Plaintext
|
||||
@ -26,9 +27,12 @@ import ch.dissem.bitmessage.entity.payload.GetPubkey
|
||||
import ch.dissem.bitmessage.entity.payload.Msg
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType
|
||||
import ch.dissem.bitmessage.factory.Factory
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository
|
||||
import ch.dissem.bitmessage.utils.Singleton
|
||||
import ch.dissem.bitmessage.utils.TestBase
|
||||
import ch.dissem.bitmessage.utils.TestUtils
|
||||
import ch.dissem.bitmessage.utils.UnixTime.MINUTE
|
||||
import ch.dissem.bitmessage.utils.UnixTime.now
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
@ -40,7 +44,7 @@ class DefaultMessageListenerTest : TestBase() {
|
||||
private lateinit var listener: DefaultMessageListener
|
||||
|
||||
private val ctx = TestUtils.mockedInternalContext(
|
||||
cryptography = Singleton.cryptography()
|
||||
cryptography = BouncyCryptography()
|
||||
)
|
||||
|
||||
@Before
|
||||
@ -52,10 +56,13 @@ class DefaultMessageListenerTest : TestBase() {
|
||||
fun `ensure pubkey is sent on request`() {
|
||||
val identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")
|
||||
whenever(ctx.addressRepository.findIdentity(any())).thenReturn(identity)
|
||||
listener.receive(ObjectMessage.Builder()
|
||||
.stream(2)
|
||||
.payload(GetPubkey(BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")))
|
||||
.build())
|
||||
val objectMessage = ObjectMessage(
|
||||
stream = 2,
|
||||
payload = GetPubkey(BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")),
|
||||
expiresTime = now + MINUTE
|
||||
)
|
||||
whenever(ctx.proofOfWorkRepository.getItem(any())).thenReturn(ProofOfWorkRepository.Item(objectMessage, 1000L, 1000L))
|
||||
listener.receive(objectMessage)
|
||||
verify(ctx.proofOfWorkRepository).putObject(argThat { type == ObjectType.PUBKEY.number }, any(), any())
|
||||
}
|
||||
|
||||
@ -73,6 +80,7 @@ class DefaultMessageListenerTest : TestBase() {
|
||||
.build()
|
||||
objectMessage.sign(identity.privateKey!!)
|
||||
objectMessage.encrypt(Singleton.cryptography().createPublicKey(identity.publicDecryptionKey))
|
||||
whenever(ctx.proofOfWorkRepository.getItem(any())).thenReturn(ProofOfWorkRepository.Item(objectMessage, 1000L, 1000L))
|
||||
listener.receive(objectMessage)
|
||||
|
||||
verify(ctx.addressRepository).save(eq(contact))
|
||||
|
@ -77,24 +77,24 @@ class ProofOfWorkServiceTest {
|
||||
val identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")
|
||||
val address = TestUtils.loadContact()
|
||||
val plaintext = Plaintext.Builder(MSG).from(identity).to(address).message("", "").build()
|
||||
val `object` = ObjectMessage(
|
||||
val objectMessage = ObjectMessage(
|
||||
expiresTime = 0,
|
||||
stream = 1,
|
||||
payload = Msg(plaintext)
|
||||
)
|
||||
`object`.sign(identity.privateKey!!)
|
||||
`object`.encrypt(address.pubkey!!)
|
||||
objectMessage.sign(identity.privateKey!!)
|
||||
objectMessage.encrypt(address.pubkey!!)
|
||||
val initialHash = ByteArray(64)
|
||||
val nonce = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8)
|
||||
|
||||
whenever(ctx.proofOfWorkRepository.getItem(initialHash)).thenReturn(ProofOfWorkRepository.Item(`object`, 1001, 1002))
|
||||
whenever(ctx.proofOfWorkRepository.getItem(initialHash)).thenReturn(ProofOfWorkRepository.Item(objectMessage, 1001, 1002))
|
||||
whenever(ctx.messageRepository.getMessage(initialHash)).thenReturn(plaintext)
|
||||
|
||||
ctx.proofOfWorkService.onNonceCalculated(initialHash, nonce)
|
||||
|
||||
verify(ctx.proofOfWorkRepository).removeObject(eq(initialHash))
|
||||
verify(ctx.inventory).storeObject(eq(`object`))
|
||||
verify(ctx.networkHandler).offer(eq(`object`.inventoryVector))
|
||||
assertThat(plaintext.inventoryVector, equalTo(`object`.inventoryVector))
|
||||
verify(ctx.inventory).storeObject(eq(objectMessage))
|
||||
verify(ctx.networkHandler).offer(eq(objectMessage.inventoryVector))
|
||||
assertThat(plaintext.inventoryVector, equalTo(objectMessage.inventoryVector))
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,9 @@ import org.junit.Test
|
||||
class SignatureTest : TestBase() {
|
||||
@Test
|
||||
fun `ensure validation works`() {
|
||||
val `object` = TestUtils.loadObjectMessage(3, "V3Pubkey.payload")
|
||||
val pubkey = `object`.payload as Pubkey
|
||||
assertTrue(`object`.isSignatureValid(pubkey))
|
||||
val objectMessage = TestUtils.loadObjectMessage(3, "V3Pubkey.payload")
|
||||
val pubkey = objectMessage.payload as Pubkey
|
||||
assertTrue(objectMessage.isSignatureValid(pubkey))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -52,11 +52,11 @@ class SignatureTest : TestBase() {
|
||||
fun `ensure message is properly signed`() {
|
||||
val identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")
|
||||
|
||||
val `object` = TestUtils.loadObjectMessage(3, "V1Msg.payload")
|
||||
val msg = `object`.payload as Msg
|
||||
val objectMessage = TestUtils.loadObjectMessage(3, "V1Msg.payload")
|
||||
val msg = objectMessage.payload as Msg
|
||||
msg.decrypt(identity.privateKey!!.privateEncryptionKey)
|
||||
assertNotNull(msg.plaintext)
|
||||
assertEquals(TestUtils.loadContact().pubkey, msg.plaintext!!.from.pubkey)
|
||||
assertTrue(`object`.isSignatureValid(msg.plaintext!!.from.pubkey!!))
|
||||
assertTrue(objectMessage.isSignatureValid(msg.plaintext!!.from.pubkey!!))
|
||||
}
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ import java.util.*
|
||||
class BitmessageAddressTest : TestBase() {
|
||||
@Test
|
||||
fun `ensure feature flag is calculated correctly`() {
|
||||
Assert.assertEquals(1, Pubkey.Feature.bitfield(DOES_ACK).toLong())
|
||||
assertEquals(2, Pubkey.Feature.bitfield(INCLUDE_DESTINATION).toLong())
|
||||
assertEquals(3, Pubkey.Feature.bitfield(DOES_ACK, INCLUDE_DESTINATION).toLong())
|
||||
Assert.assertEquals(1, Pubkey.Feature.bitfield(DOES_ACK))
|
||||
assertEquals(2, Pubkey.Feature.bitfield(INCLUDE_DESTINATION))
|
||||
assertEquals(3, Pubkey.Feature.bitfield(DOES_ACK, INCLUDE_DESTINATION))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -84,9 +84,9 @@ class BitmessageAddressTest : TestBase() {
|
||||
val address = BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ")
|
||||
Assert.assertArrayEquals(Bytes.fromHex("007402be6e76c3cb87caa946d0c003a3d4d8e1d5"), address.ripe)
|
||||
|
||||
val `object` = TestUtils.loadObjectMessage(3, "V3Pubkey.payload")
|
||||
val pubkey = `object`.payload as Pubkey
|
||||
assertTrue(`object`.isSignatureValid(pubkey))
|
||||
val objectMessage = TestUtils.loadObjectMessage(3, "V3Pubkey.payload")
|
||||
val pubkey = objectMessage.payload as Pubkey
|
||||
assertTrue(objectMessage.isSignatureValid(pubkey))
|
||||
try {
|
||||
address.pubkey = pubkey
|
||||
} catch (e: Exception) {
|
||||
@ -100,10 +100,10 @@ class BitmessageAddressTest : TestBase() {
|
||||
@Test
|
||||
fun `ensure V4Pubkey can be imported`() {
|
||||
val address = BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h")
|
||||
val `object` = TestUtils.loadObjectMessage(4, "V4Pubkey.payload")
|
||||
`object`.decrypt(address.publicDecryptionKey)
|
||||
val pubkey = `object`.payload as V4Pubkey
|
||||
assertTrue(`object`.isSignatureValid(pubkey))
|
||||
val objectMessage = TestUtils.loadObjectMessage(4, "V4Pubkey.payload")
|
||||
objectMessage.decrypt(address.publicDecryptionKey)
|
||||
val pubkey = objectMessage.payload as V4Pubkey
|
||||
assertTrue(objectMessage.isSignatureValid(pubkey))
|
||||
try {
|
||||
address.pubkey = pubkey
|
||||
} catch (e: Exception) {
|
||||
|
@ -170,12 +170,12 @@ class SerializationTest : TestBase() {
|
||||
private fun doTest(resourceName: String, version: Int, expectedPayloadType: Class<*>) {
|
||||
val data = TestUtils.getBytes(resourceName)
|
||||
val `in` = ByteArrayInputStream(data)
|
||||
val `object` = Factory.getObjectMessage(version, `in`, data.size)
|
||||
val objectMessage = Factory.getObjectMessage(version, `in`, data.size)
|
||||
val out = ByteArrayOutputStream()
|
||||
assertNotNull(`object`)
|
||||
`object`!!.write(out)
|
||||
assertNotNull(objectMessage)
|
||||
objectMessage!!.write(out)
|
||||
assertArrayEquals(data, out.toByteArray())
|
||||
assertEquals(expectedPayloadType.canonicalName, `object`.payload.javaClass.canonicalName)
|
||||
assertEquals(expectedPayloadType.canonicalName, objectMessage.payload.javaClass.canonicalName)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -42,12 +42,12 @@ class TestInventory : Inventory {
|
||||
return ArrayList(inventory.values)
|
||||
}
|
||||
|
||||
override fun storeObject(`object`: ObjectMessage) {
|
||||
inventory.put(`object`.inventoryVector, `object`)
|
||||
override fun storeObject(objectMessage: ObjectMessage) {
|
||||
inventory.put(objectMessage.inventoryVector, objectMessage)
|
||||
}
|
||||
|
||||
override fun contains(`object`: ObjectMessage): Boolean {
|
||||
return inventory.containsKey(`object`.inventoryVector)
|
||||
override fun contains(objectMessage: ObjectMessage): Boolean {
|
||||
return inventory.containsKey(objectMessage.inventoryVector)
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
|
@ -27,6 +27,6 @@ class CollectionsTest {
|
||||
fun `ensure select random returns maximum possible items`() {
|
||||
val list = LinkedList<Int>()
|
||||
list += 0..9
|
||||
assertEquals(9, Collections.selectRandom(9, list).size.toLong())
|
||||
assertEquals(9, Collections.selectRandom(9, list).size)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package ch.dissem.bitmessage.utils
|
||||
|
||||
import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import ch.dissem.bitmessage.entity.Plaintext
|
||||
import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
|
||||
@ -30,19 +29,13 @@ import org.junit.Assert.assertThat
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
|
||||
class ConversationServiceTest {
|
||||
class ConversationServiceTest : TestBase() {
|
||||
private val alice = BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")
|
||||
private val bob = BitmessageAddress("BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj")
|
||||
|
||||
private val messageRepository = mock<MessageRepository>()
|
||||
private val conversationService = spy(ConversationService(messageRepository))
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Singleton.initialize(BouncyCryptography())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ensure conversation is sorted properly`() {
|
||||
MockitoKotlin.registerInstanceCreator { UUID.randomUUID() }
|
||||
|
@ -111,11 +111,11 @@ class EncodeTest {
|
||||
|
||||
|
||||
fun checkBytes(stream: ByteArrayOutputStream, vararg bytes: Int) {
|
||||
assertEquals(bytes.size.toLong(), stream.size().toLong())
|
||||
assertEquals(bytes.size, stream.size())
|
||||
val streamBytes = stream.toByteArray()
|
||||
|
||||
for (i in bytes.indices) {
|
||||
assertEquals(bytes[i].toByte().toLong(), streamBytes[i].toLong())
|
||||
assertEquals(bytes[i].toByte(), streamBytes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ object TestUtils {
|
||||
|
||||
@JvmStatic fun int16(number: Int): ByteArray {
|
||||
val out = ByteArrayOutputStream()
|
||||
Encode.int16(number.toLong(), out)
|
||||
Encode.int16(number, out)
|
||||
return out.toByteArray()
|
||||
}
|
||||
|
||||
@ -85,9 +85,9 @@ object TestUtils {
|
||||
@Throws(DecryptionFailedException::class)
|
||||
@JvmStatic fun loadContact(): BitmessageAddress {
|
||||
val address = BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h")
|
||||
val `object` = TestUtils.loadObjectMessage(3, "V4Pubkey.payload")
|
||||
`object`.decrypt(address.publicDecryptionKey)
|
||||
address.pubkey = `object`.payload as V4Pubkey
|
||||
val objectMessage = TestUtils.loadObjectMessage(3, "V4Pubkey.payload")
|
||||
objectMessage.decrypt(address.publicDecryptionKey)
|
||||
address.pubkey = objectMessage.payload as V4Pubkey
|
||||
return address
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user