Skip to content

Commit

Permalink
Merge pull request Tribler#12 from MKuijpers/magical_idea
Browse files Browse the repository at this point in the history
Magical idea
  • Loading branch information
MKuijpers authored Mar 18, 2020
2 parents 5c95749 + 6222b3a commit 9b412f5
Show file tree
Hide file tree
Showing 20 changed files with 778 additions and 134 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package nl.tudelft.ipv8.android.demo
import android.util.JsonWriter
import android.util.Log
import nl.tudelft.ipv8.Address
import nl.tudelft.ipv8.Community
import nl.tudelft.ipv8.IPv8
import nl.tudelft.ipv8.android.IPv8Android
import nl.tudelft.ipv8.android.demo.coin.CoinUtil
import nl.tudelft.ipv8.attestation.trustchain.*
import nl.tudelft.ipv8.peerdiscovery.DiscoveryCommunity
import nl.tudelft.ipv8.util.hexToBytes
import nl.tudelft.ipv8.util.toHex
import org.json.JSONObject
import java.util.*
import kotlin.collections.ArrayList
Expand Down Expand Up @@ -32,9 +36,18 @@ class CoinCommunity: Community() {
return IPv8Android.getInstance()
}

public fun sendCurrency(amount: Double, toPublicKey: ByteArray = myPeer.publicKey.keyToBin()) {
val message = "Transaction amount: $amount bitcoins"
trustchain.createProposalBlock(message, toPublicKey, SW_JOIN_BLOCK)
private fun createTransactionData(entranceFee: Double, sharedWalletPublicKey: String,
votingThreshold: Int, trustchainPks: List<String>,
bitcoinPks: List<String>, uniqueId: String? = null): String {
val transactionValues = mapOf(
SW_UNIQUE_ID to (uniqueId ?: CoinUtil.randomUUID()),
SW_ENTRANCE_FEE to entranceFee,
SW_PK to sharedWalletPublicKey,
SW_VOTING_THRESHOLD to votingThreshold,
SW_TRUSTCHAIN_PKS to trustchainPks,
SW_BITCOIN_PKS to bitcoinPks
)
return JSONObject(transactionValues).toString()
}

/**
Expand All @@ -45,7 +58,7 @@ class CoinCommunity: Community() {
*/
public fun createSharedWallet(entranceFee: Double, votingThreshold: Int, bitcoinPk: ByteArray) {
if (votingThreshold <= 0 || votingThreshold > 100) {
throw IllegalStateException("The voting threshold (%) for a shared wallet should be [0,100>")
throw IllegalStateException("The voting threshold (%) for a shared wallet should be <0,100]")
}

val trustchainPk = myPeer.publicKey.keyToBin()
Expand All @@ -54,50 +67,43 @@ class CoinCommunity: Community() {
// TODO: Create bitcoin wallet
// TODO: Fill wallet with entrance fee

val transactionValues = mapOf(
SW_ENTRANCE_FEE to entranceFee,
SW_PK to sharedWalletPK,
SW_VOTING_THRESHOLD to votingThreshold,
SW_TRUSTCHAIN_PKS to arrayListOf(trustchainPk),
SW_BITCOIN_PKS to arrayListOf(bitcoinPk)
val values = createTransactionData(
entranceFee,
sharedWalletPK.toHex(),
votingThreshold,
arrayListOf(trustchainPk.toHex()),
arrayListOf(bitcoinPk.toHex())
)
val transaction = mapOf("message" to JSONObject(transactionValues).toString())
// Create a self signed proposal block
val proposalBlock = trustchain.createProposalBlock(transaction, trustchainPk, SW_JOIN_BLOCK)
trustchain.createAgreementBlock(proposalBlock, mapOf("message" to "ack"))
trustchain.createProposalBlock(values, trustchainPk, SHARED_WALLET_BLOCK)
}

public fun joinSharedWallet(swBlockHash: ByteArray, bitcoinPk: ByteArray) {
val swJoinBlock: TrustChainBlock = getTrustChainCommunity().database.getBlockWithHash(swBlockHash)
?: throw IllegalStateException("Shared Wallet not found given the hash: $swBlockHash")
if (!swJoinBlock.isAgreement) {
throw IllegalStateException("Shared Wallet block is not signed!")
}

val entranceFee = swJoinBlock.transaction[SW_ENTRANCE_FEE]
val oldTrustchainPks = swJoinBlock.transaction[SW_BITCOIN_PKS] as ArrayList<ByteArray>
val oldBitcoinPks = swJoinBlock.transaction[SW_JOIN_BLOCK] as ArrayList<ByteArray>
val trustchainPk = myPeer.publicKey.keyToBin()

val parsedTransaction = CoinUtil.parseTransaction(swJoinBlock.transaction)
val oldTrustchainPks = CoinUtil.parseJSONArray(parsedTransaction.getJSONArray(SW_TRUSTCHAIN_PKS))
// TODO: Pay the entrance fee with bitcoinPk
// TODO: Create new shared wallet using bitcoinPks

val newTrustchainPks: ArrayList<ByteArray> = swJoinBlock.transaction[SW_TRUSTCHAIN_PKS] as ArrayList<ByteArray>
newTrustchainPks.add(trustchainPk)
val newTrustchainPks: ArrayList<String> = arrayListOf()
newTrustchainPks.addAll(oldTrustchainPks)
newTrustchainPks.add(myPeer.publicKey.keyToBin().toHex())

val newBitcoinPks: ArrayList<ByteArray> = swJoinBlock.transaction[SW_BITCOIN_PKS] as ArrayList<ByteArray>
newBitcoinPks.add(bitcoinPk)
val newBitcoinPks: ArrayList<String> = CoinUtil.parseJSONArray(parsedTransaction.getJSONArray(SW_BITCOIN_PKS))
newBitcoinPks.add(bitcoinPk.toHex())

val transaction = mapOf(
SW_ENTRANCE_FEE to swJoinBlock.transaction[SW_ENTRANCE_FEE],
SW_PK to swJoinBlock.transaction[SW_PK],
SW_VOTING_THRESHOLD to swJoinBlock.transaction[SW_VOTING_THRESHOLD],
SW_TRUSTCHAIN_PKS to newTrustchainPks,
SW_BITCOIN_PKS to newBitcoinPks
val values = createTransactionData(
parsedTransaction.getDouble(SW_ENTRANCE_FEE),
parsedTransaction.getString(SW_PK),
parsedTransaction.getInt(SW_VOTING_THRESHOLD),
newTrustchainPks,
newBitcoinPks,
parsedTransaction.getString(SW_UNIQUE_ID)
)

for (swParticipantPk in oldTrustchainPks) {
trustchain.createProposalBlock(transaction, swParticipantPk, SW_JOIN_AGREEMENT_BLOCK)
trustchain.createProposalBlock(values, swParticipantPk.hexToBytes(), SHARED_WALLET_BLOCK)
}

// TODO: TIMEOUT, wait for votes, collect key parts
Expand All @@ -106,14 +112,53 @@ class CoinCommunity: Community() {
public fun transferFunds(oldSwPk: ByteArray, newSwPk: ByteArray) {
// TODO: send funds to new wallet
}

private fun fetchSharedWalletBlocks(): List<TrustChainBlock> {
return getTrustChainCommunity().database.getBlocksWithType(SHARED_WALLET_BLOCK)
}

/**
* Fetch the latest shared wallet block, based on a given block 'block'.
* The unique shared wallet id is used to find the most recent block in
* the 'sharedWalletBlocks' list.
*/
private fun fetchLatestSharedWalletBlock(block: TrustChainBlock, sharedWalletBlocks: List<TrustChainBlock>)
: TrustChainBlock? {
val walletId = CoinUtil.parseTransaction(block.transaction).getString(SW_UNIQUE_ID)
return sharedWalletBlocks
.filter{ CoinUtil.parseTransaction(it.transaction).getString(SW_UNIQUE_ID) == walletId }
.maxBy { it.timestamp.time }
}

/**
* Discover shared wallets that you can join, return the latest (known) blocks
*/
public fun discoverSharedWallets(): List<TrustChainBlock> {
val sharedWalletBlocks = fetchSharedWalletBlocks()
// For every distinct unique shared wallet, find the latest block
return sharedWalletBlocks
.distinctBy { CoinUtil.parseTransaction(it.transaction).getString(SW_UNIQUE_ID) }
.map { fetchLatestSharedWalletBlock(it, sharedWalletBlocks) ?: it }
}

/**
* Fetch the shared wallet blocks that you are part of, based on your trustchain PK.
*/
public fun fetchLatestJoinedSharedWalletBlocks(): List<TrustChainBlock> {
return discoverSharedWallets().filter {
val blockData = CoinUtil.parseTransaction(it.transaction)
val userTrustchainPks = CoinUtil.parseJSONArray(blockData.getJSONArray(SW_TRUSTCHAIN_PKS))
userTrustchainPks.contains(myPeer.publicKey.keyToBin().toHex())
}
}

companion object {
private const val SW_JOIN_BLOCK = "SW_JOIN"
private const val SW_JOIN_AGREEMENT_BLOCK = "SW_JOIN"
private const val SW_ENTRANCE_FEE = "SW_ENTRANCE_FEE"
private const val SW_PK = "SW_PK"
private const val SW_VOTING_THRESHOLD = "SW_VOTING_THRESHOLD"
private const val SW_TRUSTCHAIN_PKS = "SW_TRUSTCHAIN_PKS"
private const val SW_BITCOIN_PKS = "SW_BLOCKCHAIN_PKS"
public const val SHARED_WALLET_BLOCK = "SHARED_WALLET_BLOCK"
public const val SW_UNIQUE_ID = "SW_UNIQUE_ID"
public const val SW_ENTRANCE_FEE = "SW_ENTRANCE_FEE"
public const val SW_PK = "SW_PK"
public const val SW_VOTING_THRESHOLD = "SW_VOTING_THRESHOLD"
public const val SW_TRUSTCHAIN_PKS = "SW_TRUSTCHAIN_PKS"
public const val SW_BITCOIN_PKS = "SW_BLOCKCHAIN_PKS"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class DemoApplication : Application() {
Log.d("TrustChainDemo", "onBlockReceived: ${block.blockId} ${block.transaction}")
}
})

trustchain.addListener(CoinCommunity.SHARED_WALLET_BLOCK, object : BlockListener {
override fun onBlockReceived(block: TrustChainBlock) {
Log.d("Coin", "onBlockReceived: ${block.blockId} ${block.transaction}")
}
})
}

private fun createDiscoveryCommunity(): OverlayConfiguration<DiscoveryCommunity> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package nl.tudelft.ipv8.android.demo.coin

import nl.tudelft.ipv8.attestation.trustchain.TrustChainTransaction
import org.json.JSONArray
import org.json.JSONObject
import java.nio.ByteBuffer
import java.util.*
import kotlin.collections.ArrayList


object CoinUtil {
/**
* Generate a random 128 bit string
* From: https://sakthipriyan.com/2017/04/02/creating-base64-uuid-in-java.html
*/
@JvmStatic
fun randomUUID(): String {
val uuid: UUID = UUID.randomUUID()
val src: ByteArray = ByteBuffer.wrap(ByteArray(16))
.putLong(uuid.mostSignificantBits)
.putLong(uuid.leastSignificantBits)
.array()
return Base64.getUrlEncoder().encodeToString(src).substring(0, 22)
}

@JvmStatic
fun parseTransaction(transaction: TrustChainTransaction): JSONObject {
return JSONObject(transaction["message"].toString())
}

@JvmStatic
fun parseJSONArray(jsonArray: JSONArray): ArrayList<String> {
return Array(jsonArray.length()) {
jsonArray.getString(it)
}.toCollection(ArrayList())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class WalletManager(walletManagerConfiguration: WalletManagerConfiguration, wall

kit = object : WalletAppKit(params, walletDir, filePrefix) {
override fun onSetupCompleted() {
// Make a fresh new key if no keys in stored wallet.
// Make a fresh new key if no keys in stored wallet.
if (wallet().keyChainGroupSize < 1) wallet().importKey(ECKey())
wallet().allowSpendingUnconfirmedTransactions()
Log.i("Coin", "Coin: WalletManager started successfully.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import nl.tudelft.ipv8.android.demo.DemoCommunity

import nl.tudelft.ipv8.android.demo.TrustChainHelper
import nl.tudelft.ipv8.attestation.trustchain.TrustChainCommunity
import nl.tudelft.ipv8.peerdiscovery.DiscoveryCommunity

abstract class BaseFragment(@LayoutRes contentLayoutId: Int = 0) : Fragment(contentLayoutId) {
protected val trustchain: TrustChainHelper by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import kotlinx.android.synthetic.main.fragment_bitcoin.*
import nl.tudelft.ipv8.android.demo.R
import nl.tudelft.ipv8.android.demo.coin.WalletManagerAndroid
import nl.tudelft.ipv8.android.demo.ui.BaseFragment
import nl.tudelft.ipv8.util.hexToBytes

/**
* A simple [Fragment] subclass.
Expand All @@ -21,31 +20,19 @@ class BitcoinFragment(
override val controller: BitcoinViewController
) : BitcoinView, BaseFragment(R.layout.fragment_bitcoin) {

private var publicKeyReceiver: String = ""
private var bitcoinPrivateKey: String = ""
private var transactionAmount: Double = 0.0

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
button3.setOnClickListener {
publicKeyReceiver = pk_receiver.text.toString()
transactionAmount = tx_amount.text.toString().toDouble()
outputTextView.text = "PK Receiver: $publicKeyReceiver, Amount: $transactionAmount"

getCoinCommunity().sendCurrency(transactionAmount, publicKeyReceiver.hexToBytes())
}
initClickListeners()
}

private fun initClickListeners() {
refreshButton.setOnClickListener {
refresh()
}

refresh()
}

private fun refresh() {
val walletManager = WalletManagerAndroid.getInstance()

Log.i("Coin", "Coin: ${walletManager.toSeed()}")
show_wallet_button.setOnClickListener {
controller.showView("MySharedWalletsFragment")
}

create_wallet_button.setOnClickListener {
controller.showView("CreateSWFragment")
Expand All @@ -54,6 +41,14 @@ class BitcoinFragment(
search_wallet_button.setOnClickListener {
controller.showView("JoinNetworkFragment")
}


}

private fun refresh() {
val walletManager = WalletManagerAndroid.getInstance()

Log.i("Coin", "Coin: ${walletManager.toSeed()}")
}

override fun onCreateView(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package nl.tudelft.ipv8.android.demo.ui.bitcoin

import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock

interface BitcoinViewController {
fun showView(bitcoinViewName: String)
fun showSharedWalletTransactionView(sharedWalletBlock: TrustChainBlock)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nl.tudelft.ipv8.android.demo.ui.bitcoin

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
Expand Down Expand Up @@ -37,6 +38,7 @@ class CreateSWFragment(
val threshold = voting_threshold_tf.text.toString().toInt()

getCoinCommunity().createSharedWallet(entranceFee, threshold, ByteArray(5))
controller.showView("BitcoinFragment")
} else {
alert_label.text = "Entrance fee should be a double, threshold an integer, both >0"
}
Expand All @@ -46,7 +48,7 @@ class CreateSWFragment(
val entranceFee = entrance_fee_tf.text.toString().toDoubleOrNull()
val votingThreshold = voting_threshold_tf.text.toString().toIntOrNull()
return entranceFee != null && entranceFee > 0
&& votingThreshold != null && votingThreshold > 0 && votingThreshold < 100
&& votingThreshold != null && votingThreshold > 0 && votingThreshold <= 100
}

override fun onCreateView(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package nl.tudelft.ipv8.android.demo.ui.bitcoin

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_join_network.*
import nl.tudelft.ipv8.android.demo.CoinCommunity
import nl.tudelft.ipv8.android.demo.R
import nl.tudelft.ipv8.android.demo.ui.BaseFragment
import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
import nl.tudelft.ipv8.util.toHex

/**
* A simple [Fragment] subclass.
Expand All @@ -16,8 +21,23 @@ import nl.tudelft.ipv8.android.demo.ui.BaseFragment
class JoinNetworkFragment (
override val controller: BitcoinViewController
) : BitcoinView, BaseFragment(R.layout.fragment_join_network) {
private val tempBitcoinPk = ByteArray(2)

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)

val sharedWalletBlocks = getCoinCommunity().discoverSharedWallets()
val publicKey = getTrustChainCommunity().myPeer.publicKey.keyToBin().toHex()
val adaptor = SharedWalletListAdapter(this, sharedWalletBlocks, publicKey, "Click to join")
list_view.adapter = adaptor
list_view.setOnItemClickListener { _, view, position, id ->
joinSharedWalletClicked(sharedWalletBlocks[position])
Log.i("Coin", "Clicked: $view, $position, $id")
}
}

private fun joinSharedWalletClicked(block: TrustChainBlock) {
getCoinCommunity().joinSharedWallet(block.calculateHash(), tempBitcoinPk)
}

override fun onCreateView(
Expand All @@ -29,12 +49,6 @@ class JoinNetworkFragment (
}

companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment bitcoinFragment.
*/
@JvmStatic
fun newInstance(controller: BitcoinViewController) = JoinNetworkFragment(controller)
}
Expand Down
Loading

0 comments on commit 9b412f5

Please sign in to comment.