Added StorageBerkeleyDB and rewritten all in Kotlin

This commit is contained in:
ChronosX88 2019-04-07 18:42:04 +04:00
parent 8d1f531160
commit eb1f703d4f
19 changed files with 632 additions and 76 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
/out/
/build/
/.idea/
/.gradle/

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
influence-bootstrap-node

View File

@ -0,0 +1,10 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

8
.idea/compiler.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel>
<module name="io.github.chronosx88.influence-bootstrap-node.test" target="1.8" />
</bytecodeTargetLevel>
</component>
</project>

View File

@ -0,0 +1,7 @@
<component name="libraryTable">
<library name="je-5.0.104">
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: io.github.chronosx88.dhtBootstrap.Main

View File

@ -1,8 +1,20 @@
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.3.21'
}
group 'io.github.chronosx88'
version '0.1'
version '1.0-SNAPSHOT'
task fatJar(type: Jar) {
manifest {
attributes(
'Main-Class': 'io.github.chronosx88.dhtBootstrap.MainKt'
)
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
sourceCompatibility = 1.8
@ -14,14 +26,22 @@ repositories {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8'
implementation 'org.slf4j:slf4j-log4j12:+'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jar {
manifest {
attributes(
'Main-Class': 'io.github.chronosx88.dhtBootstrap.Main'
'Main-Class': 'io.github.chronosx88.dhtBootstrap.MainKt'
)
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}

1
gradle.properties Normal file
View File

@ -0,0 +1 @@
kotlin.code.style=official

View File

@ -1,4 +1,4 @@
#Thu Feb 28 14:04:57 MSK 2019
#Sun Apr 07 16:50:17 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -1,2 +1,3 @@
rootProject.name = 'Influence-Bootstrap-Node'
rootProject.name = 'influence-bootstrap-node'

BIN
src/libs/je-5.0.104.jar Normal file

Binary file not shown.

View File

@ -1,65 +0,0 @@
package io.github.chronosx88.dhtBootstrap;
import net.tomp2p.connection.RSASignatureFactory;
import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.nat.PeerBuilderNAT;
import net.tomp2p.p2p.PeerBuilder;
import net.tomp2p.peers.Number160;
import net.tomp2p.relay.RelayType;
import net.tomp2p.relay.tcp.TCPRelayServerConfig;
import net.tomp2p.storage.StorageDisk;
import java.io.*;
import java.util.Properties;
import java.util.UUID;
public class Main {
private static PeerDHT peerDHT;
private static Number160 peerID;
private static Properties props;
private static final String DATA_DIR_PATH = System.getProperty("user.home") + "/.local/share/Influence-Bootstrap/";
public static void main(String[] args) {
props = new Properties();
org.apache.log4j.BasicConfigurator.configure();
File dataDir = new File(DATA_DIR_PATH);
File config = new File(DATA_DIR_PATH + "config.properties");
try {
if(!dataDir.exists() && !config.exists()) {
dataDir.mkdir();
config.createNewFile();
props.setProperty("isFirstRun", "false");
props.setProperty("peerID", UUID.randomUUID().toString());
props.store(new FileWriter(config), "");
}
} catch (IOException e) {
e.printStackTrace();
}
try{
props.load(new FileInputStream(config));
} catch (IOException e) {
e.printStackTrace();
}
peerID = Number160.createHash(props.getProperty("peerID"));
try {
peerDHT =
new PeerBuilderDHT(new PeerBuilder(peerID).ports(7243).start())
.storage(
new StorageDisk(
peerID,
new File(DATA_DIR_PATH),
new RSASignatureFactory()
)
).start();
new PeerBuilderNAT(peerDHT.peer())
.addRelayServerConfiguration(RelayType.OPENTCP, new TCPRelayServerConfig())
.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,103 @@
package io.github.chronosx88.dhtBootstrap
import com.sleepycat.bind.EntryBinding
import com.sleepycat.je.DatabaseEntry
import net.tomp2p.connection.SignatureFactory
import net.tomp2p.storage.AlternativeCompositeByteBuf
import net.tomp2p.storage.Data
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.OutputStream
import java.io.Serializable
import java.nio.ByteBuffer
import java.security.InvalidKeyException
import java.security.SignatureException
import io.netty.buffer.Unpooled
import org.slf4j.LoggerFactory
class DataSerializer(private val signatureFactory: SignatureFactory) : EntryBinding<Data>, Serializable {
private val LOG_TAG = "DataSerializer"
private val LOG = LoggerFactory.getLogger(DataSerializer::class.java)
override fun entryToObject(databaseEntry: DatabaseEntry): Data? {
if (databaseEntry.data == null) {
return null
}
val inputStream = ByteArrayInputStream(databaseEntry.data)
var buf = Unpooled.buffer()
var data: Data? = null
while (data == null) {
buf.writeByte(inputStream.read())
data = Data.decodeHeader(buf, signatureFactory)
}
val len = data.length()
val me = ByteArray(len)
try {
inputStream.read(me)
} catch (e: IOException) {
e.printStackTrace()
}
buf = Unpooled.wrappedBuffer(me)
var retVal = data.decodeBuffer(buf)
if (!retVal) {
LOG.error("# ERROR: Data could not be deserialized!")
}
retVal = data.decodeDone(buf, signatureFactory)
if (!retVal) {
LOG.error("# ERROR: Signature could not be read!")
}
return data
}
override fun objectToEntry(data: Data, databaseEntry: DatabaseEntry) {
val forSigningKP = keyPairManager.getKeyPair("mainSigningKeyPair")
data.sign(forSigningKP)
val out = ByteArrayOutputStream()
val acb = AlternativeCompositeByteBuf.compBuffer(AlternativeCompositeByteBuf.UNPOOLED_HEAP)
try {
// header first
data.encodeHeader(acb, signatureFactory)
writeData(out, acb.nioBuffers())
acb.skipBytes(acb.writerIndex())
// next data - no need to copy to another buffer, just take the data
// from memory
writeData(out, data.toByteBuffers())
// rest
data.encodeDone(acb, signatureFactory)
writeData(out, acb.nioBuffers())
} catch (e: SignatureException) {
e.printStackTrace()
} catch (e: InvalidKeyException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
databaseEntry.data = out.toByteArray()
}
@Throws(IOException::class)
private fun writeData(out: OutputStream, nioBuffers: Array<ByteBuffer>) {
val length = nioBuffers.size
for (i in 0 until length) {
val remaining = nioBuffers[i].remaining()
if (nioBuffers[i].hasArray()) {
out.write(nioBuffers[i].array(), nioBuffers[i].arrayOffset(), remaining)
} else {
val me = ByteArray(remaining)
nioBuffers[i].get(me)
out.write(me)
}
}
}
companion object {
private const val serialVersionUID = 1428836065493792295L
private val keyPairManager = KeyPairManager()
}
}

View File

@ -0,0 +1,85 @@
package io.github.chronosx88.dhtBootstrap
import java.io.IOException
import java.io.FileOutputStream
import java.io.File
import java.security.KeyPair
import java.security.NoSuchAlgorithmException
import java.security.KeyPairGenerator
import java.io.FileInputStream
class KeyPairManager {
private val keyPairDir: File
private val serializer: Serializer<KeyPair>
init {
this.keyPairDir = File(DATA_DIR_PATH, "keyPairs")
if (!this.keyPairDir.exists()) {
this.keyPairDir.mkdir()
}
this.serializer = Serializer()
}
fun openMainKeyPair(): KeyPair? {
return getKeyPair("mainKeyPair")
}
fun getKeyPair(keyPairName: String): KeyPair? {
var keyPairName = keyPairName
keyPairName = "$keyPairName.kp"
val keyPairFile = File(keyPairDir, keyPairName)
return if (!keyPairFile.exists()) {
createKeyPairFile(keyPairFile)
} else openKeyPairFile(keyPairFile)
}
@Synchronized
private fun openKeyPairFile(keyPairFile: File): KeyPair? {
var keyPair: KeyPair? = null
try {
val inputStream = FileInputStream(keyPairFile)
val serializedKeyPair = ByteArray(keyPairFile.length().toInt())
inputStream.read(serializedKeyPair)
inputStream.close()
keyPair = serializer.deserialize(serializedKeyPair)
} catch (e: IOException) {
e.printStackTrace()
}
return keyPair
}
@Synchronized
private fun createKeyPairFile(keyPairFile: File): KeyPair? {
var keyPair: KeyPair? = null
try {
keyPairFile.createNewFile()
keyPair = KeyPairGenerator.getInstance("DSA").generateKeyPair()
val outputStream = FileOutputStream(keyPairFile)
outputStream.write(serializer.serialize(keyPair))
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
return keyPair
}
@Synchronized
fun saveKeyPair(keyPairID: String, keyPair: KeyPair) {
val keyPairFile = File(keyPairDir, "$keyPairID.kp")
if (!keyPairFile.exists()) {
try {
val outputStream = FileOutputStream(keyPairFile)
outputStream.write(serializer.serialize(keyPair))
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}

View File

@ -0,0 +1,69 @@
package io.github.chronosx88.dhtBootstrap
import net.tomp2p.connection.DSASignatureFactory
import net.tomp2p.dht.PeerBuilderDHT
import net.tomp2p.dht.PeerDHT
import net.tomp2p.nat.PeerBuilderNAT
import net.tomp2p.p2p.PeerBuilder
import net.tomp2p.peers.Number160
import net.tomp2p.relay.RelayType
import net.tomp2p.relay.tcp.TCPRelayServerConfig
import net.tomp2p.replication.AutoReplication
import java.io.File
import java.io.FileInputStream
import java.io.FileWriter
import java.io.IOException
import java.util.Properties
import java.util.UUID
var peerDHT: PeerDHT? = null
var peerID: Number160? = null
var props: Properties? = null
val DATA_DIR_PATH = System.getProperty("user.home") + "/.local/share/Influence-Bootstrap/"
fun main() {
props = Properties()
org.apache.log4j.BasicConfigurator.configure()
val dataDir = File(DATA_DIR_PATH)
val config = File(DATA_DIR_PATH + "config.properties")
try {
if (!dataDir.exists() && !config.exists()) {
dataDir.mkdir()
config.createNewFile()
props!!.setProperty("isFirstRun", "false")
props!!.setProperty("peerID", UUID.randomUUID().toString())
props!!.store(FileWriter(config), "")
}
} catch (e: IOException) {
e.printStackTrace()
}
try {
props!!.load(FileInputStream(config))
} catch (e: IOException) {
e.printStackTrace()
}
peerID = Number160.createHash(props!!.getProperty("peerID"))
try {
peerDHT = PeerBuilderDHT(PeerBuilder(peerID).ports(7243).start())
.storage(
StorageBerkeleyDB(
peerID!!,
File(DATA_DIR_PATH),
DSASignatureFactory()
)
).start()
PeerBuilderNAT(peerDHT!!.peer())
.addRelayServerConfiguration(RelayType.OPENTCP, TCPRelayServerConfig())
.start()
} catch (e: IOException) {
e.printStackTrace()
}
//val replication = AutoReplication(peerDHT!!.peer())
//replication.start()
}

View File

@ -0,0 +1,50 @@
package io.github.chronosx88.dhtBootstrap
import com.sleepycat.bind.EntryBinding
import com.sleepycat.je.DatabaseEntry
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
class Serializer<T> : EntryBinding<T> {
fun serialize(obj: T): ByteArray {
val byteArray = ByteArrayOutputStream()
try {
val objectOutputStream = ObjectOutputStream(byteArray)
objectOutputStream.writeObject(obj)
objectOutputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
return byteArray.toByteArray()
}
fun deserialize(serializedObject: ByteArray?): T? {
if (serializedObject == null)
return null
val inputStream = ByteArrayInputStream(serializedObject)
var obj: Any? = null
try {
val objectInputStream = ObjectInputStream(inputStream)
obj = objectInputStream.readObject()
} catch (e: ClassNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
return obj as T?
}
override fun entryToObject(databaseEntry: DatabaseEntry): T? {
return deserialize(databaseEntry.data)
}
override fun objectToEntry(obj: T, databaseEntry: DatabaseEntry) {
databaseEntry.data = serialize(obj)
}
}

View File

@ -0,0 +1,262 @@
package io.github.chronosx88.dhtBootstrap
import com.sleepycat.collections.StoredSortedMap
import com.sleepycat.je.Database
import com.sleepycat.je.DatabaseConfig
import com.sleepycat.je.Environment
import com.sleepycat.je.EnvironmentConfig
import net.tomp2p.connection.SignatureFactory
import net.tomp2p.dht.Storage
import net.tomp2p.peers.Number160
import net.tomp2p.peers.Number320
import net.tomp2p.peers.Number480
import net.tomp2p.peers.Number640
import net.tomp2p.storage.Data
import java.io.File
import java.security.PublicKey
import java.util.*
import java.util.concurrent.ConcurrentHashMap
class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: SignatureFactory) : Storage {
// Core
private val dataMap: StoredSortedMap<Number640, Data>
// Maintenance
private val timeoutMap: StoredSortedMap<Number640, Long>
private val timeoutMapRev: StoredSortedMap<Long, Set<Number640>>
// Protection
private val protectedDomainMap: StoredSortedMap<Number320, PublicKey>
private val protectedEntryMap: StoredSortedMap<Number480, PublicKey>
// Responsibility
private val responsibilityMap: StoredSortedMap<Number160, Number160>
private val responsibilityMapRev: StoredSortedMap<Number160, Set<Number160>>
private val dataMapDB: Database
private val timeoutMapDB: Database
private val timeoutMapRevDB: Database
private val protectedDomainMapDB: Database
private val protectedEntryMapDB: Database
private val responsibilityMapDB: Database
private val responsibilityMapRevDB: Database
private val storageCheckIntervalMillis: Int
private val dbEnvironment: Environment
init {
val envConfig = EnvironmentConfig()
envConfig.allowCreate = true
dbEnvironment = Environment(path, envConfig)
val dbConfig = DatabaseConfig()
dbConfig.allowCreate = true
dataMapDB = dbEnvironment.openDatabase(null, "dataMap_$peerId", dbConfig)
timeoutMapDB = dbEnvironment.openDatabase(null, "timeoutMap_$peerId", dbConfig)
timeoutMapRevDB = dbEnvironment.openDatabase(null, "timeoutMapRev_$peerId", dbConfig)
protectedDomainMapDB = dbEnvironment.openDatabase(null, "protectedDomainMap_$peerId", dbConfig)
protectedEntryMapDB = dbEnvironment.openDatabase(null, "protectedEntryMap_$peerId", dbConfig)
responsibilityMapDB = dbEnvironment.openDatabase(null, "responsibilityMap_$peerId", dbConfig)
responsibilityMapRevDB = dbEnvironment.openDatabase(null, "responsibilityMapRev_$peerId", dbConfig)
storageCheckIntervalMillis = 60 * 1000
dataMap = StoredSortedMap(dataMapDB, Serializer<Number640>(), DataSerializer(signatureFactory), true)
timeoutMap = StoredSortedMap(timeoutMapDB, Serializer<Number640>(), Serializer<Long>(), true)
timeoutMapRev = StoredSortedMap(timeoutMapRevDB, Serializer<Long>(), Serializer<Set<Number640>>(), true)
protectedDomainMap = StoredSortedMap(protectedDomainMapDB, Serializer<Number320>(), Serializer<PublicKey>(), true)
protectedEntryMap = StoredSortedMap(protectedEntryMapDB, Serializer<Number480>(), Serializer<PublicKey>(), true)
responsibilityMap = StoredSortedMap(responsibilityMapDB, Serializer<Number160>(), Serializer<Number160>(), true)
responsibilityMapRev = StoredSortedMap(responsibilityMapRevDB, Serializer<Number160>(), Serializer<Set<Number160>>(), true)
}
override fun contains(key: Number640?): Boolean {
return dataMap.containsKey(key)
}
override fun contains(from: Number640?, to: Number640?): Int {
return dataMap.subMap(from, true, to, true).size
}
override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet<Number160> {
return responsibilityMapRev[peerID] as MutableSet<Number160>
}
override fun findPeerIDsForResponsibleContent(locationKey: Number160?): Number160? {
return responsibilityMap[locationKey]
}
override fun put(key: Number640?, value: Data?): Data? {
return dataMap.put(key, value)
}
override fun get(key: Number640?): Data? {
return dataMap[key]
}
override fun remove(key: Number640?, returnData: Boolean): Data? {
return dataMap.remove(key)
}
override fun remove(from: Number640?, to: Number640?): NavigableMap<Number640, Data> {
val tmp = dataMap.subMap(from, true, to, true)
val retVal = TreeMap<Number640, Data>()
for(entry : Map.Entry<Number640, Data> in tmp.entries) {
retVal[entry.key] = entry.value
}
tmp.clear()
return retVal
}
override fun addTimeout(key: Number640, expiration: Long) {
val oldExpiration = timeoutMap.put(key, expiration)
putIfAbsent2(expiration, key)
if (oldExpiration == null) {
return
}
removeRevTimeout(key, oldExpiration)
}
private fun putIfAbsent2(expiration: Long, key: Number640) {
var timeouts = timeoutMapRev[expiration]
//var timeouts : MutableSet<Number640> = timeoutMapRev[expiration] as MutableSet<Number640>
if (timeouts == null) {
timeouts = Collections.newSetFromMap(ConcurrentHashMap())
}
(timeouts as MutableSet).add(key)
timeoutMapRev[expiration] = timeouts
}
private fun removeRevTimeout(key: Number640, expiration: Long?) {
val tmp = timeoutMapRev[expiration] as MutableSet<Number640>
if (tmp != null) {
tmp.remove(key)
if (tmp.isEmpty()) {
timeoutMapRev.remove(expiration)
} else {
timeoutMapRev[expiration!!] = tmp
}
}
}
override fun updateResponsibilities(locationKey: Number160, peerId: Number160?): Boolean {
val oldPeerID = responsibilityMap.put(locationKey, peerId)
val hasChanged: Boolean
if (oldPeerID != null) {
if (oldPeerID == peerId) {
hasChanged = false
} else {
removeRevResponsibility(oldPeerID, locationKey)
hasChanged = true
}
} else {
hasChanged = true
}
var contentIDs: MutableSet<Number160>? = responsibilityMapRev[peerId] as MutableSet
if (contentIDs == null) {
contentIDs = HashSet()
}
contentIDs.add(locationKey)
responsibilityMapRev[peerId] = contentIDs
return hasChanged
}
private fun removeRevResponsibility(peerId: Number160, locationKey: Number160) {
val contentIDs = responsibilityMapRev[peerId] as MutableSet
if (contentIDs != null) {
contentIDs.remove(locationKey)
if (contentIDs.isEmpty()) {
responsibilityMapRev.remove(peerId)
} else {
responsibilityMapRev[peerId] = contentIDs
}
}
}
override fun protectDomain(key: Number320?, publicKey: PublicKey?): Boolean {
protectedDomainMap[key] = publicKey
return true
}
override fun storageCheckIntervalMillis(): Int {
return storageCheckIntervalMillis
}
override fun isDomainProtectedByOthers(key: Number320?, publicKey: PublicKey?): Boolean {
val other = protectedDomainMap[key] ?: return false
return other != publicKey
}
override fun removeTimeout(key: Number640) {
val expiration = timeoutMap.remove(key) ?: return
removeRevTimeout(key, expiration)
}
override fun removeResponsibility(locationKey: Number160) {
val peerId = responsibilityMap.remove(locationKey)
if (peerId != null) {
removeRevResponsibility(peerId, locationKey)
}
}
override fun protectEntry(key: Number480?, publicKey: PublicKey?): Boolean {
protectedEntryMap[key] = publicKey
return true
}
override fun map(): NavigableMap<Number640, Data> {
val retVal = TreeMap<Number640, Data>()
for ((key, value) in dataMap) {
retVal[key] = value
}
return retVal
}
override fun isEntryProtectedByOthers(key: Number480?, publicKey: PublicKey?): Boolean {
val other = protectedEntryMap[key] ?: return false
return other != publicKey
}
override fun subMap(from: Number640?, to: Number640?, limit: Int, ascending: Boolean): NavigableMap<Number640, Data> {
val tmp = dataMap.subMap(from, true, to, true)
val descendingMap = TreeMap<Number640, Data>(tmp).descendingMap()
val retVal = TreeMap<Number640, Data>()
if (limit < 0) {
for ((key, value) in if (ascending) tmp else descendingMap) {
retVal[key] = value
}
} else {
val limit1 = Math.min(limit, tmp.size)
val iterator = if (ascending)
tmp.entries.iterator()
else
descendingMap.entries.iterator()
var i = 0
while (iterator.hasNext() && i < limit1) {
val entry = iterator.next()
retVal[entry.key] = entry.value
i++
}
}
return retVal
}
override fun subMapTimeout(to: Long): MutableCollection<Number640> {
val tmp = timeoutMapRev.subMap(0L, to)
val toRemove = ArrayList<Number640>()
for (set in tmp.values) {
toRemove.addAll(set)
}
return toRemove
}
override fun close() {
dataMapDB.close()
timeoutMapDB.close()
timeoutMapRevDB.close()
protectedDomainMapDB.close()
protectedEntryMapDB.close()
responsibilityMapDB.close()
responsibilityMapRevDB.close()
}
}