Skip to content

Commit

Permalink
final
Browse files Browse the repository at this point in the history
  • Loading branch information
JoepMulder committed Apr 26, 2024
1 parent 630570d commit 1fcf7de
Show file tree
Hide file tree
Showing 21 changed files with 1,341 additions and 10 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ dependencies {
implementation('net.java.dev.jna:jna:5.12.1@aar')

implementation "com.github.MattSkala:recyclerview-itemadapter:$recyclerview_adapter_version"
implementation project(':currencyii')

// Testing
testImplementation "junit:junit:$junit_version"
testImplementation 'io.mockk:mockk:1.11.0'
androidTestImplementation "androidx.test.ext:junit:$android_junit_version"

// Hilt
Expand Down
7 changes: 6 additions & 1 deletion app/src/main/res/xml/network_security_config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
<!-- This is for RegTest faucet, to claim some starter money -->
<domain includeSubdomains="true">131.180.27.224</domain>
<!-- IP address of the host machine when ran from the Android emulator -->
<!-- <domain includeSubdomains="true">10.0.2.2</domain>-->
<domain includeSubdomains="true">10.0.2.2</domain>


<domain includeSubdomains="true">145.94.149.176</domain>
<!--<trust-anchors>
<certificates src="@raw/fullchain"/>
</trust-anchors>-->
<!-- This is for RegTest faucet, to claim some starter money -->
<domain includeSubdomains="true">95.179.182.243</domain>
</domain-config>
Expand Down
13 changes: 11 additions & 2 deletions currencyii/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ android {

testOptions {
unitTests.returnDefaultValues = true
unitTests.all {
useJUnitPlatform()
}
}

namespace 'nl.tudelft.trustchain.currencyii'
Expand All @@ -61,6 +64,9 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"


testImplementation "com.goterl:lazysodium-java:5.1.4"

// Material
api "com.google.android.material:material:$material_version"

Expand All @@ -71,8 +77,11 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

// Testing
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
testImplementation 'junit:junit:4.12'
testImplementation "io.mockk:mockk:$mockk_version"
testImplementation project(':musicdao')
testImplementation project(':ipv8')
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
androidTestImplementation "androidx.test.ext:junit:$android_junit_version"

// BitcoinJ
Expand Down Expand Up @@ -113,4 +122,4 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions.freeCompilerArgs += [
"-opt-in=kotlin.ExperimentalUnsignedTypes"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,34 @@ package nl.tudelft.trustchain.currencyii

import android.app.Activity
import android.content.Context
import android.util.Log
import androidx.core.content.ContentProviderCompat.requireContext
import nl.tudelft.ipv8.Community
import nl.tudelft.ipv8.Peer
import nl.tudelft.ipv8.android.IPv8Android
import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
import nl.tudelft.ipv8.attestation.trustchain.TrustChainCommunity
import nl.tudelft.ipv8.attestation.trustchain.TrustChainTransaction
import nl.tudelft.ipv8.messaging.Packet
import nl.tudelft.ipv8.util.hexToBytes
import nl.tudelft.ipv8.util.toHex
import nl.tudelft.trustchain.currencyii.payload.*
import nl.tudelft.trustchain.currencyii.sharedWallet.*
import nl.tudelft.trustchain.currencyii.util.DAOCreateHelper
import nl.tudelft.trustchain.currencyii.util.DAOJoinHelper
import nl.tudelft.trustchain.currencyii.util.DAOTransferFundsHelper

@Suppress("UNCHECKED_CAST")
class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc8db5899c5df5b") : Community() {
open class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc8db5899c5df5b") : Community() {
override val serviceId = serviceId
private var currentLeader: HashMap<String, Peer?> = HashMap()
private var candidates: HashMap<String, ArrayList<Peer>> = HashMap()
init {
messageHandlers[MessageId.ELECTION_REQUEST] = ::onElectionRequestPacket
messageHandlers[MessageId.ELECTED_RESPONSE] = ::onElectedResponsePacket
messageHandlers[MessageId.ALIVE_RESPONSE] = ::onAliveResponsePacket
messageHandlers[MessageId.SIGNATURE_ASK] = ::onSignPayloadResponsePacket
}

private fun getTrustChainCommunity(): TrustChainCommunity {
return IPv8Android.getInstance().getOverlay()
Expand All @@ -26,6 +39,7 @@ class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc
private val daoCreateHelper = DAOCreateHelper()
private val daoJoinHelper = DAOJoinHelper()
private val daoTransferFundsHelper = DAOTransferFundsHelper()
// private val leaderElectionHelper = LeaderElectionHelper()

/**
* Create a bitcoin genesis wallet and broadcast the result on trust chain.
Expand Down Expand Up @@ -205,6 +219,200 @@ class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc
return "invalid-pk"
}

fun sendPayload(
peer: Peer,
payload: ByteArray
) {
Log.i("CoinCommunitySending", "Sending payload to ${peer.address}")
send(
peer,
payload
)
}

internal fun createElectionRequest(dAOid: ByteArray,
): ByteArray {
val payload = ElectionPayload(dAOid)
return serializePacket(MessageId.ELECTION_REQUEST, payload)
}

internal fun createElectedResponse(dAOid: ByteArray,
): ByteArray {
val payload = ElectedPayload(dAOid)
return serializePacket(MessageId.ELECTED_RESPONSE, payload)
}

internal fun createAliveResponse(dAOid: ByteArray,
): ByteArray {
val payload = AlivePayload(dAOid)
return serializePacket(MessageId.ALIVE_RESPONSE, payload)
}

internal fun createSignPayloadResponse(
dAOid: ByteArray,
recentSWBlock: TrustChainBlock,
proposeBlockData: SWSignatureAskBlockTD,
signatures: List<SWResponseSignatureBlockTD>
): ByteArray {
val payload = SignPayload(dAOid, recentSWBlock, proposeBlockData, signatures)
return serializePacket(MessageId.SIGNATURE_ASK, payload)
}

fun onAliveResponsePacket(packet: Packet){
val (peer, payload) = packet.getAuthPayload(
AlivePayload.Deserializer
)
this.onAliveResponse(peer, payload)
}

fun onSignPayloadResponsePacket(packet: Packet){
val (peer, payload) = packet.getAuthPayload(
SignPayload.Deserializer
)
this.onSignPayloadResponse(peer, payload)
}

fun onSignPayloadResponse(peer: Peer, payload: SignPayload){
//TODO: Implement adding to the wallet without a Context
// try {
// joinBitcoinWallet(
// payload.mostRecentSWBlock.transaction,
// payload.proposeBlockData,
// payload.signatures
// )
// // Add new nonceKey after joining a DAO
// WalletManagerAndroid.getInstance()
// .addNewNonceKey(payload.proposeBlockData.SW_UNIQUE_ID)
// } catch (t: Throwable) {
// Log.e("Coin", "Joining failed. ${t.message ?: "No further information"}.")
// }

}

fun onAliveResponse(peer: Peer, payload: AlivePayload) {
this.getCandidates()[payload.DAOid.decodeToString()]?.add(peer)
}
fun onElectedResponsePacket(packet: Packet){
val (peer, payload) = packet.getAuthPayload(
ElectedPayload.Deserializer
)
this.onElectedResponse(peer, payload)
}
fun onElectedResponse(peer: Peer, payload: ElectedPayload) {
Log.d("LEADER", "Elected: " + peer.publicKey)
getCurrentLeader()[payload.DAOid.decodeToString()] = peer
}

fun onElectionRequestPacket(packet: Packet){
val (peer, payload) = packet.getAuthPayload(
ElectionPayload.Deserializer
)
Log.d("Leader", "Election packet received.")
getCandidates()[payload.DAOid.decodeToString()] = ArrayList()
onElectionRequest(peer, payload)
}

fun getPeersPKInDao(DAOid: ByteArray): ArrayList<String>{
val mostRecentWalletBlock = fetchLatestSharedWalletBlock(DAOid)
?: throw IllegalStateException("Most recent DAO block not found")
val peerPK: ArrayList<String> = ArrayList<String>()
val blockData = SWJoinBlockTransactionData(mostRecentWalletBlock.transaction).getData()
for (swParticipantPk in blockData.SW_TRUSTCHAIN_PKS) {
peerPK.add(swParticipantPk)
}
return peerPK
}

fun onElectionRequest(peer: Peer, payload:ElectionPayload) {

val peerPK = getPeersPKInDao(payload.DAOid)

Log.d("Leader", "Election started.")
val aliveResponse = this.createAliveResponse(payload.DAOid)
this.sendPayload(peer, aliveResponse)

Log.d("Leader", "Election started.")

getCurrentLeader()[payload.DAOid.decodeToString()] = null

val higherPeers = ArrayList<Peer>()
for (p in this.getPeers()) {
if (peerPK.contains(p.publicKey.keyToBin().decodeToString()) && p.address.hashCode() > this.myPeer.address.hashCode()) {
higherPeers.add(p)
}
}
Log.d("Leader", "peers with higher ips:$higherPeers")

if(higherPeers.isEmpty()) {
Log.d("Leader", "Elected: " + this.myPeer.publicKey)

val electedPayload = this.createElectedResponse(payload.DAOid)
this.sendPayload(peer, electedPayload)
getCurrentLeader()[payload.DAOid.decodeToString()] = this.myPeer
return
}
var lastTime = System.currentTimeMillis()
var i = 0
for (p in higherPeers) {
// Send election request to the peer with the highest hash
val generatedPayload = this.createElectionRequest(payload.DAOid)
i++
this.sendPayload(p, generatedPayload)
if(i == higherPeers.size) {
lastTime = System.currentTimeMillis()
}
}
while (System.currentTimeMillis() - lastTime < 1000) {
// Wait for responses
}
if(this.candidates[payload.DAOid.decodeToString()]?.isEmpty() == true){
getCurrentLeader()[payload.DAOid.decodeToString()] = this.myPeer
val electedPayload = this.createElectedResponse(payload.DAOid)
this.sendPayload(peer, electedPayload)
}
}
fun getCandidates(): HashMap<String, ArrayList<Peer>> {
return this.candidates
}

fun getCurrentLeader(): HashMap<String, Peer?> {
return this.currentLeader
}
fun getServiceIdNew(): String{
return serviceId
}

fun leaderSignProposal(
mostRecentSWBlock: TrustChainBlock,
proposeBlockData: SWSignatureAskBlockTD,
signatures: List<SWResponseSignatureBlockTD>,
publicKeyBlock: ByteArray
) {
Log.d("LEADER", "Leader doesn't exists.")
Log.d("LEADER", "Requesting election...")
val peers = this.getPeers()
for (peer in peers) {
sendPayload(peer, this.createElectionRequest(publicKeyBlock))
Log.d("LEADER", "Sending to peer at " + peer.address + " in " + serviceId + "...")
}
Log.d("LEADER", "Waiting for leader...")
while (!this.checkLeaderExists(publicKeyBlock)) {
Thread.sleep(1000)
}
Log.d("LEADER", "Leader found.")
Log.d("LEADER", "sending proposal to leader...")
sendPayload(getCurrentLeader()[publicKeyBlock.decodeToString()]!!,
SignPayload(
getServiceIdNew().toByteArray(),
mostRecentSWBlock,
proposeBlockData,
signatures
).serialize()
)
}
private fun checkLeaderExists(dAOid: ByteArray): Boolean {
return getCurrentLeader()[dAOid.decodeToString()] != null
}
fun fetchSignatureRequestProposalId(block: TrustChainBlock): String {
if (block.type == SIGNATURE_ASK_BLOCK) {
return SWSignatureAskTransactionData(block.transaction).getData().SW_UNIQUE_PROPOSAL_ID
Expand Down Expand Up @@ -275,6 +483,7 @@ class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc
}
}


/**
* Given a shared wallet proposal block, calculate the signature and respond with a trust chain block.
*/
Expand Down Expand Up @@ -390,6 +599,13 @@ class CoinCommunity constructor(serviceId: String = "02313685c1912a141279f8248fc

return requiredVotes <= totalVoters.size - againstSignatures.size
}
object MessageId {
const val ELECTION_REQUEST = 1
const val ELECTED_RESPONSE = 2
const val ALIVE_RESPONSE = 3
const val SIGNATURE_ASK = 4
}


companion object {
// Default maximum wait timeout for bitcoin transaction broadcasts in seconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const val MAIN_NET_WALLET_NAME = "forwarding-service"
const val MIN_BLOCKCHAIN_PEERS_TEST_NET = 5
const val MIN_BLOCKCHAIN_PEERS_REG_TEST = 1
const val MIN_BLOCKCHAIN_PEERS_PRODUCTION = 5
//const val REG_TEST_FAUCET_IP = "10.0.2.2"
//const val REG_TEST_FAUCET_DOMAIN = "10.0.2.2:443"
const val REG_TEST_FAUCET_IP = "131.180.27.224"
const val REG_TEST_FAUCET_DOMAIN = "taproot.tribler.org"

Expand Down Expand Up @@ -469,7 +471,7 @@ class WalletManager(
* @param context used to retrieve the nonce key of the user to sign
* @return BigInteger
*/
fun safeSigningTransactionFromMultiSig(
fun safeSigningTransactionFromMultiSig(
oldTransactionSerialized: String,
publicKeys: List<ECKey>,
nonces: List<ECKey>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package nl.tudelft.trustchain.currencyii.payload

import nl.tudelft.ipv8.messaging.*

class AlivePayload(
val DAOid: ByteArray,
) : Serializable {
override fun serialize(): ByteArray {
return DAOid
}

companion object Deserializer : Deserializable<AlivePayload> {
override fun deserialize(
buffer: ByteArray,
offset: Int,
): Pair<AlivePayload, Int> {
// var localOffset = 0
// val payloadSize = deserializeUShort(buffer, offset)
// localOffset += SERIALIZED_USHORT_SIZE
// val publicKey = buffer.copyOfRange(offset + localOffset, offset + localOffset + payloadSize)
// localOffset += payloadSize
// return Pair(AlivePayload(publicKey), localOffset)
var localOffset = 0
val (DAOid, DAOidLen) = deserializeRaw(buffer, offset + localOffset)
localOffset += DAOidLen
return Pair(AlivePayload(DAOid), localOffset)
}
}
}
Loading

0 comments on commit 1fcf7de

Please sign in to comment.