Refactor to utilize Kotlin features

This commit is contained in:
Christian Basler 2018-02-16 17:04:08 +01:00
parent b93f382ccd
commit fab1c06135
7 changed files with 92 additions and 53 deletions

View File

@ -37,33 +37,33 @@ import java.util.*
data class Message constructor( data class Message constructor(
val subject: String, val subject: String,
val body: String, val body: String,
val parents: List<InventoryVector>, val parents: List<InventoryVector> = emptyList(),
val files: List<Attachment> val files: List<Attachment> = emptyList()
) : ExtendedEncoding.ExtendedType { ) : ExtendedEncoding.ExtendedType {
override val type: String = TYPE override val type: String = TYPE
override fun pack(): MPMap<MPString, MPType<*>> { override fun pack(): MPMap<MPString, MPType<*>> {
val result = MPMap<MPString, MPType<*>>() val result = MPMap<MPString, MPType<*>>()
result.put("".mp, TYPE.mp) result["".mp] = TYPE.mp
result.put("subject".mp, subject.mp) result["subject".mp] = subject.mp
result.put("body".mp, body.mp) result["body".mp] = body.mp
if (!files.isEmpty()) { if (!files.isEmpty()) {
val items = MPArray<MPMap<MPString, MPType<*>>>() val items = MPArray<MPMap<MPString, MPType<*>>>()
result.put("files".mp, items) result["files".mp] = items
for (file in files) { for (file in files) {
val item = MPMap<MPString, MPType<*>>() val item = MPMap<MPString, MPType<*>>()
item.put("name".mp, file.name.mp) item["name".mp] = file.name.mp
item.put("data".mp, file.data.mp) item["data".mp] = file.data.mp
item.put("type".mp, file.type.mp) item["type".mp] = file.type.mp
item.put("disposition".mp, file.disposition.name.mp) item["disposition".mp] = file.disposition.name.mp
items.add(item) items.add(item)
} }
} }
if (!parents.isEmpty()) { if (!parents.isEmpty()) {
val items = MPArray<MPBinary>() val items = MPArray<MPBinary>()
result.put("parents".mp, items) result["parents".mp] = items
for ((hash) in parents) { for ((hash) in parents) {
items.add(mp(*hash)) items.add(mp(*hash))
} }
@ -179,6 +179,6 @@ data class Message constructor(
companion object { companion object {
private val LOG = LoggerFactory.getLogger(Message::class.java) private val LOG = LoggerFactory.getLogger(Message::class.java)
val TYPE = "message" const val TYPE = "message"
} }
} }

View File

@ -156,6 +156,16 @@ interface Cryptography {
fun doProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long, fun doProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long,
extraBytes: Long, callback: ProofOfWorkEngine.Callback) extraBytes: Long, callback: ProofOfWorkEngine.Callback)
@JvmSynthetic
fun doProofOfWork(objectMessage: ObjectMessage, nonceTrialsPerByte: Long,
extraBytes: Long, callback: (ByteArray, ByteArray) -> Unit) {
doProofOfWork(objectMessage, nonceTrialsPerByte, extraBytes, object : ProofOfWorkEngine.Callback {
override fun onNonceCalculated(initialHash: ByteArray, nonce: ByteArray) {
callback.invoke(initialHash, nonce)
}
})
}
/** /**
* @param objectMessage to be checked * @param objectMessage to be checked
* * * *

View File

@ -23,16 +23,32 @@ interface ProofOfWorkEngine {
/** /**
* Returns a nonce, such that the first 8 bytes from sha512(sha512(nonce||initialHash)) represent a unsigned long * Returns a nonce, such that the first 8 bytes from sha512(sha512(nonce||initialHash)) represent a unsigned long
* smaller than target. * smaller than target.
*
* @param initialHash the SHA-512 hash of the object to send, sans nonce * @param initialHash the SHA-512 hash of the object to send, sans nonce
* *
* @param target the target, representing an unsigned long * @param target the target, representing an unsigned long
* * * @param callback called with the initial hash and the calculated nonce as argument. The ProofOfWorkEngine
* @param callback called with the calculated nonce as argument. The ProofOfWorkEngine implementation must make * implementation must make sure this is only called once.
* * sure this is only called once.
*/ */
fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: Callback) fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: Callback)
/**
* Returns a nonce, such that the first 8 bytes from sha512(sha512(nonce||initialHash)) represent a unsigned long
* smaller than target.
*
* @param initialHash the SHA-512 hash of the object to send, sans nonce
* @param target the target, representing an unsigned long
* @param callback called with the initial hash and the calculated nonce as argument. The ProofOfWorkEngine
* implementation must make sure this is only called once.
*/
@JvmSynthetic
fun calculateNonce(initialHash: ByteArray, target: ByteArray, callback: (ByteArray, ByteArray) -> Unit) {
calculateNonce(initialHash, target, object : Callback {
override fun onNonceCalculated(initialHash: ByteArray, nonce: ByteArray) {
callback.invoke(initialHash, nonce)
}
})
}
interface Callback { interface Callback {
/** /**
* @param nonce 8 bytes nonce * @param nonce 8 bytes nonce

View File

@ -126,7 +126,7 @@ class BitmessageContextTest {
ctx.addContact(contact) ctx.addContact(contact)
verify(ctx.addresses, timeout(1000).atLeastOnce()).save(eq(contact)) verify(ctx.addresses, timeout(1000).atLeastOnce()).save(eq(contact))
verify(testPowEngine, timeout(1000)).calculateNonce(any(), any(), any()) verify(testPowEngine, timeout(1000)).calculateNonce(any(), any(), any<ProofOfWorkEngine.Callback>())
} }
@Test @Test
@ -139,7 +139,7 @@ class BitmessageContextTest {
ctx.addContact(contact) ctx.addContact(contact)
verify(ctx.addresses, times(1)).save(contact) verify(ctx.addresses, times(1)).save(contact)
verify(testPowEngine, never()).calculateNonce(any(), any(), any()) verify(testPowEngine, never()).calculateNonce(any(), any(), any<ProofOfWorkEngine.Callback>())
} }
@Test @Test
@ -162,7 +162,7 @@ class BitmessageContextTest {
ctx.addContact(contact) ctx.addContact(contact)
verify(ctx.addresses, atLeastOnce()).save(contact) verify(ctx.addresses, atLeastOnce()).save(contact)
verify(testPowEngine, never()).calculateNonce(any(), any(), any()) verify(testPowEngine, never()).calculateNonce(any(), any(), any<ProofOfWorkEngine.Callback>())
} }
@Test @Test
@ -186,7 +186,7 @@ class BitmessageContextTest {
ctx.addContact(contact) ctx.addContact(contact)
verify(ctx.addresses, atLeastOnce()).save(any()) verify(ctx.addresses, atLeastOnce()).save(any())
verify(testPowEngine, never()).calculateNonce(any(), any(), any()) verify(testPowEngine, never()).calculateNonce(any(), any(), any<ProofOfWorkEngine.Callback>())
} }
@Test @Test
@ -245,7 +245,7 @@ class BitmessageContextTest {
"Subject", "Message") "Subject", "Message")
verify(ctx.internals.proofOfWorkRepository, timeout(1000).atLeastOnce()) verify(ctx.internals.proofOfWorkRepository, timeout(1000).atLeastOnce())
.putObject(argThat { payload.type == ObjectType.BROADCAST }, eq(1000L), eq(1000L)) .putObject(argThat { payload.type == ObjectType.BROADCAST }, eq(1000L), eq(1000L))
verify(testPowEngine).calculateNonce(any(), any(), any()) verify(testPowEngine).calculateNonce(any(), any(), any<ProofOfWorkEngine.Callback>())
verify(ctx.messages, timeout(10000).atLeastOnce()).save(argThat<Plaintext> { type == Type.BROADCAST }) verify(ctx.messages, timeout(10000).atLeastOnce()).save(argThat<Plaintext> { type == Type.BROADCAST })
} }

View File

@ -23,6 +23,7 @@ import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
import ch.dissem.bitmessage.entity.payload.GenericPayload import ch.dissem.bitmessage.entity.payload.GenericPayload
import ch.dissem.bitmessage.entity.payload.Msg import ch.dissem.bitmessage.entity.payload.Msg
import ch.dissem.bitmessage.ports.Cryptography import ch.dissem.bitmessage.ports.Cryptography
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
import ch.dissem.bitmessage.ports.ProofOfWorkRepository import ch.dissem.bitmessage.ports.ProofOfWorkRepository
import ch.dissem.bitmessage.utils.Singleton import ch.dissem.bitmessage.utils.Singleton
import ch.dissem.bitmessage.utils.TestUtils import ch.dissem.bitmessage.utils.TestUtils
@ -65,11 +66,11 @@ class ProofOfWorkServiceTest {
fun `ensure missing proof of work is done`() { fun `ensure missing proof of work is done`() {
whenever(ctx.proofOfWorkRepository.getItems()).thenReturn(Arrays.asList<ByteArray>(ByteArray(64))) whenever(ctx.proofOfWorkRepository.getItems()).thenReturn(Arrays.asList<ByteArray>(ByteArray(64)))
whenever(ctx.proofOfWorkRepository.getItem(any())).thenReturn(ProofOfWorkRepository.Item(obj, 1001, 1002)) whenever(ctx.proofOfWorkRepository.getItem(any())).thenReturn(ProofOfWorkRepository.Item(obj, 1001, 1002))
doNothing().whenever(cryptography).doProofOfWork(any(), any(), any(), any()) doNothing().whenever(cryptography).doProofOfWork(any(), any(), any(), any<ProofOfWorkEngine.Callback>())
ctx.proofOfWorkService.doMissingProofOfWork(10) ctx.proofOfWorkService.doMissingProofOfWork(10)
verify(cryptography, timeout(1000)).doProofOfWork(eq(obj), eq(1001L), eq(1002L), any()) verify(cryptography, timeout(1000)).doProofOfWork(eq(obj), eq(1001L), eq(1002L), any<ProofOfWorkEngine.Callback>())
} }
@Test @Test

View File

@ -89,12 +89,7 @@ class CryptographyTest {
stream = 1 stream = 1
) )
val waiter = CallbackWaiter<ByteArray>() val waiter = CallbackWaiter<ByteArray>()
crypto.doProofOfWork(objectMessage, 1000, 1000, crypto.doProofOfWork(objectMessage, 1000, 1000) { _, nonce -> waiter.setValue(nonce) }
object : ProofOfWorkEngine.Callback {
override fun onNonceCalculated(initialHash: ByteArray, nonce: ByteArray) {
waiter.setValue(nonce)
}
})
objectMessage.nonce = waiter.waitForValue() objectMessage.nonce = waiter.waitForValue()
try { try {
crypto.checkProofOfWork(objectMessage, 1000, 1000) crypto.checkProofOfWork(objectMessage, 1000, 1000)

View File

@ -42,19 +42,21 @@ import java.io.OutputStream
*/ */
class CryptoCustomMessage<T : Streamable> : CustomMessage { class CryptoCustomMessage<T : Streamable> : CustomMessage {
private val dataReader: Reader<T>? private val dataReader: (BitmessageAddress, InputStream) -> T
private var container: CryptoBox? = null private var container: CryptoBox? = null
var sender: BitmessageAddress? = null var sender: BitmessageAddress? = null
private set private set
private var data: T? = null private var data: T? = null
private set
constructor(data: T) : super(COMMAND, null) { constructor(data: T) : super(COMMAND, null) {
this.data = data this.data = data
this.dataReader = null this.dataReader = { _, _ -> data }
} }
private constructor(container: CryptoBox, dataReader: Reader<T>) : super(COMMAND, null) { private constructor(container: CryptoBox, dataReader: (BitmessageAddress, InputStream) -> T) : super(
COMMAND,
null
) {
this.container = container this.container = container
this.dataReader = dataReader this.dataReader = dataReader
} }
@ -81,8 +83,9 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
@Throws(DecryptionFailedException::class) @Throws(DecryptionFailedException::class)
fun decrypt(privateKey: ByteArray): T { fun decrypt(privateKey: ByteArray): T {
val input = SignatureCheckingInputStream(container?.decrypt(privateKey) ?: throw IllegalStateException("no encrypted data available")) val input = SignatureCheckingInputStream(
if (dataReader == null) throw IllegalStateException("no data reader available") container?.decrypt(privateKey) ?: throw IllegalStateException("no encrypted data available")
)
val addressVersion = varInt(input) val addressVersion = varInt(input)
val stream = varInt(input) val stream = varInt(input)
@ -92,18 +95,20 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
val nonceTrialsPerByte = if (addressVersion >= 3) varInt(input) else 0 val nonceTrialsPerByte = if (addressVersion >= 3) varInt(input) else 0
val extraBytes = if (addressVersion >= 3) varInt(input) else 0 val extraBytes = if (addressVersion >= 3) varInt(input) else 0
val sender = BitmessageAddress(Factory.createPubkey( val sender = BitmessageAddress(
addressVersion, Factory.createPubkey(
stream, addressVersion,
publicSigningKey, stream,
publicEncryptionKey, publicSigningKey,
nonceTrialsPerByte, publicEncryptionKey,
extraBytes, nonceTrialsPerByte,
behaviorBitfield extraBytes,
)) behaviorBitfield
)
)
this.sender = sender this.sender = sender
data = dataReader.read(sender, input) data = dataReader.invoke(sender, input)
input.checkSignature(sender.pubkey!!) input.checkSignature(sender.pubkey!!)
@ -127,7 +132,8 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
fun read(sender: BitmessageAddress, input: InputStream): T fun read(sender: BitmessageAddress, input: InputStream): T
} }
private inner class SignatureCheckingInputStream internal constructor(private val wrapped: InputStream) : InputStream() { private inner class SignatureCheckingInputStream internal constructor(private val wrapped: InputStream) :
InputStream() {
private val out = ByteArrayOutputStream() private val out = ByteArrayOutputStream()
override fun read(): Int { override fun read(): Int {
@ -145,13 +151,24 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
} }
companion object { companion object {
@JvmField const val COMMAND = "ENCRYPTED"
val COMMAND = "ENCRYPTED"
@JvmStatic @JvmStatic
fun <T : Streamable> read(data: CustomMessage, dataReader: Reader<T>): CryptoCustomMessage<T> { fun <T : Streamable> read(data: CustomMessage, dataReader: Reader<T>): CryptoCustomMessage<T> =
val cryptoBox = CryptoBox.read(ByteArrayInputStream(data.getData()), data.getData().size) CryptoCustomMessage(
return CryptoCustomMessage(cryptoBox, dataReader) CryptoBox.read(ByteArrayInputStream(data.getData()), data.getData().size)
} ) { address, input ->
dataReader.read(address, input)
}
@JvmSynthetic
fun <T : Streamable> read(
data: CustomMessage,
dataReader: (BitmessageAddress, InputStream) -> T
): CryptoCustomMessage<T> =
CryptoCustomMessage(
CryptoBox.read(ByteArrayInputStream(data.getData()), data.getData().size),
dataReader
)
} }
} }