Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token transaction trustchain #136

Merged
merged 13 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@
android:enabled="true"
android:exported="false" />

<service
android:name="nl.tudelft.trustchain.detoks.GossiperService"
android:enabled="true"
android:exported="false" />

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyBAGd8-KwX1epS_0CPl5RF0hD8VeTmM-7Y" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import nl.tudelft.trustchain.common.bitcoin.WalletService
import nl.tudelft.trustchain.common.eurotoken.GatewayStore
import nl.tudelft.trustchain.common.eurotoken.TransactionRepository
import nl.tudelft.trustchain.currencyii.CoinCommunity
import nl.tudelft.trustchain.detoks.DeToksCommunity
import nl.tudelft.trustchain.eurotoken.community.EuroTokenCommunity
import nl.tudelft.trustchain.eurotoken.db.TrustStore
import nl.tudelft.trustchain.gossipML.RecommenderCommunity
Expand Down Expand Up @@ -117,7 +118,8 @@ class TrustChainApplication : Application() {
createLiteratureCommunity(),
createRecommenderCommunity(),
createIdentityCommunity(),
createFOCCommunity()
createFOCCommunity(),
createDeToksCommunity()
),
walkerInterval = 5.0
)
Expand Down Expand Up @@ -447,6 +449,14 @@ class TrustChainApplication : Application() {
)
}

private fun createDeToksCommunity(): OverlayConfiguration<DeToksCommunity> {
val randomWalk = RandomWalk.Factory()
return OverlayConfiguration(
DeToksCommunity.Factory(this),
listOf(randomWalk)
)
}

private fun getIdAlgorithmKey(idFormat: String): BonehPrivateKey {
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val privateKey = prefs.getString(idFormat, null)
Expand Down
109 changes: 109 additions & 0 deletions detoks/src/main/java/nl/tudelft/trustchain/detoks/DeToksCommunity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package nl.tudelft.trustchain.detoks

import android.content.Context
import android.util.Log
import nl.tudelft.ipv8.Community
import nl.tudelft.ipv8.Overlay
import nl.tudelft.ipv8.Peer
import nl.tudelft.ipv8.messaging.Packet


class DeToksCommunity(private val context: Context) : Community() {

private val walletManager = WalletManager(context)
private val visitedPeers = mutableListOf<Peer>()

init {
messageHandlers[MESSAGE_TORRENT_ID] = ::onGossip
messageHandlers[MESSAGE_TRANSACTION_ID] = ::onTransactionMessage

}

companion object {
const val MESSAGE_TORRENT_ID = 1
const val MESSAGE_TRANSACTION_ID = 2

}

override val serviceId = "c86a7db45eb3563ae047639817baec4db2bc7c25"


fun sendTokens(amount: Int, recipientMid: String) {
val senderWallet = walletManager.getOrCreateWallet(myPeer.mid)

Log.d("DetoksCommunity", "my wallet ${senderWallet.balance}")

if (senderWallet.balance >= amount) {
Log.d("DetoksCommunity", "Sending $amount money to $recipientMid")
senderWallet.balance -= amount
walletManager.setWalletBalance(myPeer.mid, senderWallet.balance)

val recipientWallet = walletManager.getOrCreateWallet(recipientMid)
recipientWallet.balance += amount
walletManager.setWalletBalance(recipientMid, recipientWallet.balance)

for (peer in getPeers()) {
val packet = serializePacket(
MESSAGE_TRANSACTION_ID,
TransactionMessage(amount, myPeer.mid, recipientMid)
)
send(peer.address, packet)
}
} else {
Log.d("DeToksCommunity", "Insufficient funds!")
}

}

fun gossipWith(peer: Peer) {
Log.d("DeToksCommunity", "Gossiping with ${peer.mid}, address: ${peer.address}")
Log.d("DetoksCommunity", "My wallet size: ${walletManager.getOrCreateWallet(myPeer.mid)}")
Log.d("DetoksCommunity", "My peer wallet size: ${walletManager.getOrCreateWallet(peer.mid)}")
val listOfTorrents = TorrentManager.getInstance(context).getListOfTorrents()
if(listOfTorrents.isEmpty()) return
val magnet = listOfTorrents.random().makeMagnetUri()

val packet = serializePacket(MESSAGE_TORRENT_ID, TorrentMessage(magnet))

// Send a token only to a new peer
if (!visitedPeers.contains(peer)) {
visitedPeers.add(peer)
sendTokens(1, peer.mid)
}

send(peer.address, packet)
}

private fun onGossip(packet: Packet) {
val (peer, payload) = packet.getAuthPayload(TorrentMessage.Deserializer)
val torrentManager = TorrentManager.getInstance(context)
Log.d("DeToksCommunity", "received torrent from ${peer.mid}, address: ${peer.address}, magnet: ${payload.magnet}")
torrentManager.addTorrent(payload.magnet)
}
private fun onTransactionMessage(packet: Packet) {
val (_, payload) = packet.getAuthPayload(TransactionMessage.Deserializer)

val senderWallet = walletManager.getOrCreateWallet(payload.senderMID)

if (senderWallet.balance >= payload.amount) {
senderWallet.balance -= payload.amount
walletManager.setWalletBalance(payload.senderMID, senderWallet.balance)

val recipientWallet = walletManager.getOrCreateWallet(payload.recipientMID)
recipientWallet.balance += payload.amount
walletManager.setWalletBalance(payload.recipientMID, recipientWallet.balance)

Log.d("DeToksCommunity", "Received ${payload.amount} tokens from ${payload.senderMID}")
} else {
Log.d("DeToksCommunity", "Insufficient funds from ${payload.senderMID}!")
}
}

class Factory(
private val context: Context
) : Overlay.Factory<DeToksCommunity>(DeToksCommunity::class.java) {
override fun create(): DeToksCommunity {
return DeToksCommunity(context)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,8 @@ class DeToksFragment : BaseFragment(R.layout.fragment_detoks) {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
cacheDefaultTorrent()
torrentManager = TorrentManager(
File("${requireActivity().cacheDir.absolutePath}/media"),
File("${requireActivity().cacheDir.absolutePath}/torrent"),
DEFAULT_CACHING_AMOUNT
)
cacheDefaultTorrent() //TODO: change to caching from peers
torrentManager = TorrentManager.getInstance(requireActivity())
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
package nl.tudelft.trustchain.detoks

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import nl.tudelft.trustchain.common.BaseActivity

class DeToksActivity : BaseActivity() {
override val navigationGraph = R.navigation.nav_graph_detoks
var gossipService: GossiperService? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val actionBar = supportActionBar
actionBar!!.hide()

Intent(this, GossiperService::class.java).also { intent ->
startService(intent)
bindService(intent, gossipConnection, Context.BIND_AUTO_CREATE)
}
}

private val gossipConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as GossiperService.LocalBinder
gossipService = binder.getService()
}

override fun onServiceDisconnected(p0: ComponentName?) {
gossipService = null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package nl.tudelft.trustchain.detoks

import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import kotlinx.coroutines.*
import nl.tudelft.ipv8.Peer
import nl.tudelft.ipv8.android.IPv8Android
import kotlin.system.exitProcess

class GossiperService : Service() {
private val binder = LocalBinder()
private val scope = CoroutineScope(Dispatchers.IO)

inner class LocalBinder : Binder() {
fun getService(): GossiperService = this@GossiperService
}

override fun onBind(intent: Intent): IBinder {
return binder
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return START_NOT_STICKY
}

override fun onCreate() {
super.onCreate()
scope.launch {
gossipTorrents()
}
}

override fun onDestroy() {
scope.cancel()
super.onDestroy()

exitProcess(0)
}

private suspend fun gossipTorrents() {
while (scope.isActive) {
val deToksCommunity = IPv8Android.getInstance().getOverlay<DeToksCommunity>()!!

val randomPeer = pickRandomPeer(deToksCommunity)
if(randomPeer != null)
deToksCommunity.gossipWith(randomPeer)

delay(4000)
}
}

private fun pickRandomPeer(deToksCommunity: DeToksCommunity): Peer? {
val peers = deToksCommunity.getPeers()
if (peers.isEmpty()) return null
return peers.random()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nl.tudelft.trustchain.detoks

import android.content.Context
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
Expand All @@ -19,7 +20,7 @@ import java.io.File
* It is responsible for downloading the torrent files and caching the videos.
* It also provides the videos to the video adapter.
*/
class TorrentManager(
class TorrentManager private constructor (
private val cacheDir: File,
private val torrentDir: File,
private val cachingAmount: Int = 1,
Expand All @@ -37,6 +38,20 @@ class TorrentManager(
initializeVideoPool()
}

companion object {
private lateinit var instance: TorrentManager
fun getInstance(context: Context): TorrentManager {
if (!::instance.isInitialized) {
instance = TorrentManager(
File("${context.cacheDir.absolutePath}/media"),
File("${context.cacheDir.absolutePath}/torrent"),
DeToksFragment.DEFAULT_CACHING_AMOUNT
)
}
return instance
}
}

fun notifyIncrease() {
Log.i("DeToks", "Increasing index ... ${(currentIndex + 1) % getNumberOfTorrents()}")
notifyChange((currentIndex + 1) % getNumberOfTorrents(), loopedToFront = true)
Expand Down Expand Up @@ -131,7 +146,7 @@ class TorrentManager(
val priorities = Array(torrentInfo.numFiles()) { Priority.IGNORE }
handle.prioritizeFiles(priorities)
handle.pause()
for (it in 0..torrentInfo.numFiles()) {
for (it in 0 until torrentInfo.numFiles()) {
val fileName = torrentInfo.files().fileName(it)
if (fileName.endsWith(".mp4")) {
torrentFiles.add(
Expand Down Expand Up @@ -191,6 +206,44 @@ class TorrentManager(
fileOrDirectory.delete()
}

fun addTorrent(magnet: String) {
val torrentInfo = getInfoFromMagnet(magnet)?:return
val hash = torrentInfo.infoHash()

if(sessionManager.find(hash) != null) return
Log.d("DeToksCommunity","Is a new torrent: ${torrentInfo.name()}")

sessionManager.download(torrentInfo, cacheDir)
val handle = sessionManager.find(hash)
handle.setFlags(TorrentFlags.SEQUENTIAL_DOWNLOAD)
handle.prioritizeFiles(arrayOf(Priority.IGNORE))
handle.pause()

for (it in 0 until torrentInfo.numFiles()) {
val fileName = torrentInfo.files().fileName(it)
if (fileName.endsWith(".mp4")) {
torrentFiles.add(
TorrentHandler(
cacheDir,
handle,
torrentInfo.name(),
fileName,
it
)
)
}
}
}

private fun getInfoFromMagnet(magnet: String): TorrentInfo? {
val bytes = sessionManager.fetchMagnet(magnet, 10)?:return null
return TorrentInfo.bdecode(bytes)
}

fun getListOfTorrents(): List<TorrentHandle> {
return torrentFiles.map {it.handle}.distinct()
}

class TorrentHandler(
private val cacheDir: File,
val handle: TorrentHandle,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nl.tudelft.trustchain.detoks

import android.util.Log
import nl.tudelft.ipv8.messaging.Deserializable
import nl.tudelft.ipv8.messaging.Serializable
import org.json.JSONArray


class TorrentMessage(val magnet: String) : Serializable {

override fun serialize(): ByteArray {
return magnet.toByteArray()
}

companion object Deserializer : Deserializable<TorrentMessage> {
override fun deserialize(buffer: ByteArray, offset: Int): Pair<TorrentMessage, Int> {
val tempStr = String(buffer, offset,buffer.size - offset)

return Pair(TorrentMessage(tempStr), offset)
}
}


}
Loading