Jabit/core/src/main/kotlin/ch/dissem/bitmessage/ProofOfWorkService.kt

122 lines
4.8 KiB
Kotlin

/*
* Copyright 2017 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.bitmessage
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_EXTRA_BYTES
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_NONCE_TRIALS_PER_BYTE
import ch.dissem.bitmessage.entity.*
import ch.dissem.bitmessage.entity.payload.Msg
import ch.dissem.bitmessage.ports.ProofOfWorkEngine
import ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item
import org.slf4j.LoggerFactory
import java.util.*
/**
* @author Christian Basler
*/
class ProofOfWorkService : ProofOfWorkEngine.Callback, InternalContext.ContextHolder {
private lateinit var ctx: InternalContext
private val cryptography by lazy { ctx.cryptography }
private val powRepo by lazy { ctx.proofOfWorkRepository }
private val messageRepo by lazy { ctx.messageRepository }
override fun setContext(context: InternalContext) {
ctx = context
}
fun doMissingProofOfWork(delayInMilliseconds: Long) {
val items = powRepo.getItems()
if (items.isEmpty()) return
// Wait for 30 seconds, to let the application start up before putting heavy load on the CPU
Timer().schedule(object : TimerTask() {
override fun run() {
LOG.info("Doing POW for " + items.size + " tasks.")
for (initialHash in items) {
val (objectMessage, nonceTrialsPerByte, extraBytes) = powRepo.getItem(initialHash)
cryptography.doProofOfWork(objectMessage, nonceTrialsPerByte, extraBytes,
this@ProofOfWorkService)
}
}
}, delayInMilliseconds)
}
fun doProofOfWork(objectMessage: ObjectMessage) {
doProofOfWork(null, 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(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(objectMessage, nonceTrialsPerByte, extraBytes, this)
}
fun doProofOfWorkWithAck(plaintext: Plaintext, expirationTime: Long) {
val ack = plaintext.ackMessage!!
messageRepo.save(plaintext)
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 (objectMessage, _, _, expirationTime, message) = powRepo.getItem(initialHash)
if (message == null) {
objectMessage.nonce = nonce
messageRepo.getMessage(initialHash)?.let {
it.inventoryVector = objectMessage.inventoryVector
it.updateNextTry()
ctx.labeler.markAsSent(it)
messageRepo.save(it)
}
ctx.inventory.storeObject(objectMessage)
ctx.networkHandler.offer(objectMessage.inventoryVector)
} else {
message.ackMessage!!.nonce = nonce
val newObjectMessage = ObjectMessage.Builder()
.stream(message.stream)
.expiresTime(expirationTime!!)
.payload(Msg(message))
.build()
if (newObjectMessage.isSigned) {
newObjectMessage.sign(message.from.privateKey!!)
}
if (newObjectMessage.payload is Encrypted) {
newObjectMessage.encrypt(message.to!!.pubkey!!)
}
doProofOfWork(message.to, newObjectMessage)
}
powRepo.removeObject(initialHash)
}
companion object {
private val LOG = LoggerFactory.getLogger(ProofOfWorkService::class.java)
}
}