Skip to content

Commit

Permalink
Merge pull request #21 from MKuijpers/wallet-discovery-crawling
Browse files Browse the repository at this point in the history
Wallet discovery crawling
  • Loading branch information
brian2509 authored Mar 30, 2020
2 parents b1fece1 + e073acc commit 59fa788
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,14 @@ class CoinCommunity : Community() {
}

/**
* Discover shared wallets that you can join, return the latest (known) blocks
* Fetch the latest block associated with a shared wallet.
* swBlockHash - the hash of one of the blocks associated with a shared wallet.
*/
private fun fetchLatestSharedWalletTransactionBlock(swBlockHash: ByteArray): TrustChainBlock? {
val swBlock = getTrustChainCommunity().database.getBlockWithHash(swBlockHash)
?: return null
val transactionBlockTypes = listOf(SHARED_WALLET_BLOCK, TRANSFER_FINAL_BLOCK)
val transactionBlocks = fetchSharedWalletBlocks(transactionBlockTypes)
val transactionBlocks = fetchSharedWalletBlocks(SW_TRANSACTION_BLOCK_KEYS)
return fetchLatestSharedWalletBlock(swBlock, transactionBlocks)
}

Expand Down Expand Up @@ -450,6 +450,8 @@ class CoinCommunity : Community() {
public const val TRANSFER_FUNDS_ASK_BLOCK = "TRANSFER_FUNDS_ASK_BLOCK"
public const val SIGNATURE_AGREEMENT_BLOCK = "SIGNATURE_AGREEMENT_BLOCK"

// Values below are present in SW_TRANSACTION_BLOCK_KEYS block types
public val SW_TRANSACTION_BLOCK_KEYS = listOf(SHARED_WALLET_BLOCK, TRANSFER_FINAL_BLOCK)
public const val SW_UNIQUE_ID = "SW_UNIQUE_ID"
public const val SW_TRANSACTION_SERIALIZED = "SW_PK"
public const val SW_TRUSTCHAIN_PKS = "SW_TRUSTCHAIN_PKS"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.fragment_join_network.*
import kotlinx.coroutines.*
import nl.tudelft.ipv8.android.demo.CoinCommunity
import nl.tudelft.ipv8.android.demo.R
import nl.tudelft.ipv8.android.demo.sharedWallet.SWSignatureAskTransactionData
import nl.tudelft.ipv8.android.demo.sharedWallet.SWUtil
import nl.tudelft.ipv8.android.demo.ui.BaseFragment
import nl.tudelft.ipv8.attestation.trustchain.TrustChainBlock
import nl.tudelft.ipv8.attestation.trustchain.TrustChainCrawler
import nl.tudelft.ipv8.util.toHex
import kotlin.concurrent.thread

Expand All @@ -22,20 +27,88 @@ import kotlin.concurrent.thread
class JoinNetworkFragment(
) : BaseFragment(R.layout.fragment_join_network) {
private val tempBitcoinPk = ByteArray(2)
private var adapter: SharedWalletListAdapter? = null

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")
loadSharedWallets()
}

/**
* Load shared wallet trust chain blocks. Blocks are crawled from trust chain users and loaded
* from the local database.
*/
private fun loadSharedWallets() {
lifecycleScope.launchWhenStarted {
val discoveredWallets = getCoinCommunity().discoverSharedWallets()
val foundWallets = withContext(Dispatchers.IO) {
crawlAvailableSharedWallets()
}

Log.i(
"Coin",
"${foundWallets.size} found with crawling and ${discoveredWallets.size} in database"
)

// Filter the wallets on the correct block type and unique wallet ids
val allWallets = discoveredWallets
.union(foundWallets)
.filter {
CoinCommunity.SW_TRANSACTION_BLOCK_KEYS.contains(it.type)
}.distinctBy {
SWUtil.parseTransaction(it.transaction).get(CoinCommunity.SW_UNIQUE_ID).asString
}

val publicKey = getTrustChainCommunity().myPeer.publicKey.keyToBin().toHex()

// Update the list view with the found shared wallets
adapter = SharedWalletListAdapter(
this@JoinNetworkFragment,
allWallets,
publicKey,
"Click to join"
)
list_view.adapter = adapter
list_view.setOnItemClickListener { _, view, position, id ->
joinSharedWalletClicked(allWallets[position])
Log.i("Coin", "Clicked: $view, $position, $id")
}
}
}

/**
* Crawl all shared wallet blocks of users in the trust chain.
*/
private suspend fun crawlAvailableSharedWallets(): ArrayList<TrustChainBlock> {
val allUsers = trustchain.getUsers()
val discoveredBlocks: ArrayList<TrustChainBlock> = arrayListOf()

for (index in allUsers.indices) {
// Continue with the next user if the peer is not found!
val publicKey = allUsers[index].publicKey
val peer = trustchain.getPeerByPublicKeyBin(publicKey) ?: continue

try {
withTimeout(SW_CRAWLING_TIMEOUT_MILLI) {
trustchain.crawlChain(peer)
var crawlResult = trustchain
.getChainByUser(peer.publicKey.keyToBin())

crawlResult = crawlResult.filter {
CoinCommunity.SW_TRANSACTION_BLOCK_KEYS.contains(it.type)
}
discoveredBlocks.addAll(crawlResult)
}
} catch (t: Throwable) {
val message = t.message ?: "no message"
Log.i("Coin", "Crawling failed for: ${peer.publicKey} message: $message")
}
}

return discoveredBlocks
}

private fun joinSharedWalletClicked(block: TrustChainBlock) {
val transactionPackage = getCoinCommunity().createBitcoinSharedWallet(block.calculateHash())
val proposeBlock =
Expand Down Expand Up @@ -70,7 +143,10 @@ class JoinNetworkFragment(
): Boolean {
val blockData = data.getData()
val signatures =
getCoinCommunity().fetchJoinSignatures(blockData.SW_UNIQUE_ID, blockData.SW_UNIQUE_PROPOSAL_ID)
getCoinCommunity().fetchJoinSignatures(
blockData.SW_UNIQUE_ID,
blockData.SW_UNIQUE_PROPOSAL_ID
)

if (signatures.size >= requiredSignatures) {
getCoinCommunity().safeSendingJoinWalletTransaction(data, signatures)
Expand Down Expand Up @@ -99,5 +175,7 @@ class JoinNetworkFragment(
companion object {
@JvmStatic
fun newInstance() = JoinNetworkFragment()

public const val SW_CRAWLING_TIMEOUT_MILLI: Long = 5_000
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class TrustChainCrawler {
}

companion object {
private const val CHAIN_CRAWL_TIMEOUT = 120_000L
public const val CHAIN_CRAWL_TIMEOUT = 120_000L
private const val MAX_CRAWL_REQUEST_ATTEMPTS = 3
}

Expand Down

0 comments on commit 59fa788

Please sign in to comment.