diff --git a/app/build.gradle b/app/build.gradle index 0712dd50e..f529cef7a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,7 +35,7 @@ android { defaultConfig { applicationId "nl.tudelft.trustchain" - minSdkVersion 24 + minSdkVersion 22 targetSdkVersion 29 versionCode generateVersionCode() versionName "0.1" @@ -117,8 +117,8 @@ dependencies { // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // Logging @@ -135,6 +135,7 @@ dependencies { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { kotlinOptions.freeCompilerArgs += [ - "-Xuse-experimental=kotlin.Experimental,kotlin.ExperimentalUnsignedTypes" + "-Xuse-experimental=kotlin.Experimental,kotlin.ExperimentalUnsignedTypes", + "-Xopt-in=kotlin.RequiresOptIn" ] } diff --git a/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt b/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt index a3876a202..f0fc90fd0 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/TrustChainApplication.kt @@ -2,6 +2,7 @@ package nl.tudelft.trustchain.app import android.app.Application import android.bluetooth.BluetoothManager +import android.os.Build import android.util.Log import androidx.core.content.getSystemService import androidx.preference.PreferenceManager @@ -111,8 +112,8 @@ class TrustChainApplication : Application() { val strategies = mutableListOf( randomWalk, randomChurn, periodicSimilarity, nsd ) - if (bluetoothManager.adapter != null) { - val ble = BluetoothLeDiscovery.Factory(this, bluetoothManager) + if (bluetoothManager.adapter != null && Build.VERSION.SDK_INT >= 24) { + val ble = BluetoothLeDiscovery.Factory() strategies += ble } diff --git a/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardItemRenderer.kt b/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardItemRenderer.kt index 6916a03a3..96caeedb1 100644 --- a/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardItemRenderer.kt +++ b/app/src/main/java/nl/tudelft/trustchain/app/ui/dashboard/DashboardItemRenderer.kt @@ -1,6 +1,7 @@ package nl.tudelft.trustchain.app.ui.dashboard import android.content.res.ColorStateList +import androidx.core.content.res.ResourcesCompat import com.mattskala.itemadapter.BindingItemRenderer import nl.tudelft.trustchain.app.databinding.ItemDashboardBinding @@ -12,7 +13,7 @@ class DashboardItemRenderer( ) { override fun bindView(item: DashboardItem, binding: ItemDashboardBinding) { val context = binding.root.context - val color = context.getColor(item.app.color) + val color = ResourcesCompat.getColor(context.resources, item.app.color, null) binding.imgIcon.setImageResource(item.app.icon) binding.imgIcon.imageTintList = ColorStateList.valueOf(color) binding.txtAppName.text = item.app.appName diff --git a/build.gradle b/build.gradle index 69511f797..53912852e 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ buildscript { ext.kotlin_version = '1.3.72' ext.ktlint_version = '0.36.0' + ext.coroutines_version = '1.3.7' ext.ktlint_gradle_version = '9.1.1' ext.sqldelight_version = '1.2.2' ext.nav_version = "2.2.1" diff --git a/common/build.gradle b/common/build.gradle index 6648075e6..4806919ae 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -15,7 +15,7 @@ android { buildToolsVersion "29.0.3" defaultConfig { - minSdkVersion 24 + minSdkVersion 22 targetSdkVersion 29 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -91,3 +91,10 @@ dependencies { testImplementation "com.squareup.sqldelight:sqlite-driver:$sqldelight_version" testImplementation "com.goterl.lazycode:lazysodium-java:4.2.4" } + + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions.freeCompilerArgs += [ + "-Xopt-in=kotlin.RequiresOptIn" + ] +} diff --git a/common/src/main/java/nl/tudelft/trustchain/common/DemoCommunity.kt b/common/src/main/java/nl/tudelft/trustchain/common/DemoCommunity.kt index f44ee4e33..d8e46784c 100644 --- a/common/src/main/java/nl/tudelft/trustchain/common/DemoCommunity.kt +++ b/common/src/main/java/nl/tudelft/trustchain/common/DemoCommunity.kt @@ -1,6 +1,8 @@ package nl.tudelft.trustchain.common import android.util.Log +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.BroadcastChannel import nl.tudelft.ipv8.IPv4Address import nl.tudelft.ipv8.Community import nl.tudelft.ipv8.Peer @@ -8,6 +10,7 @@ import nl.tudelft.ipv8.android.IPv8Android import nl.tudelft.ipv8.attestation.trustchain.TrustChainCommunity import nl.tudelft.ipv8.messaging.* import nl.tudelft.ipv8.messaging.payload.IntroductionResponsePayload +import nl.tudelft.ipv8.messaging.payload.PuncturePayload import java.util.* class DemoCommunity : Community() { @@ -17,6 +20,9 @@ class DemoCommunity : Community() { val lastTrackerResponses = mutableMapOf() + @ExperimentalCoroutinesApi + val punctureChannel = BroadcastChannel>(10000) + // Retrieve the trustchain community private fun getTrustChainCommunity(): TrustChainCommunity { return IPv8Android.getInstance().getOverlay() @@ -46,6 +52,7 @@ class DemoCommunity : Community() { object MessageId { const val THALIS_MESSAGE = 222 const val TORRENT_MESSAGE = 223 + const val PUNCTURE_TEST = 251 } // SEND MESSAGE @@ -69,10 +76,17 @@ class DemoCommunity : Community() { } } + fun sendPuncture(address: IPv4Address, id: Int) { + val payload = PuncturePayload(myEstimatedLan, myEstimatedWan, id) + val packet = serializePacket(MessageId.PUNCTURE_TEST, payload, sign = false) + endpoint.send(address, packet) + } + // RECEIVE MESSAGE init { messageHandlers[MessageId.THALIS_MESSAGE] = ::onMessage messageHandlers[MessageId.TORRENT_MESSAGE] = ::onTorrentMessage + messageHandlers[MessageId.PUNCTURE_TEST] = ::onPunctureTest } private fun onMessage(packet: Packet) { @@ -85,6 +99,12 @@ class DemoCommunity : Community() { val (peer, payload) = packet.getAuthPayload(MyMessage.Deserializer) Log.i("personal", peer.mid + ": " + payload.message) } + + @OptIn(ExperimentalCoroutinesApi::class) + private fun onPunctureTest(packet: Packet) { + val payload = packet.getPayload(PuncturePayload.Deserializer) + punctureChannel.offer(Pair(packet.source, payload)) + } } // THE MESSAGE (CLASS) diff --git a/common/src/main/java/nl/tudelft/trustchain/common/MarketCommunity.kt b/common/src/main/java/nl/tudelft/trustchain/common/MarketCommunity.kt index feeea7089..dd0a71681 100644 --- a/common/src/main/java/nl/tudelft/trustchain/common/MarketCommunity.kt +++ b/common/src/main/java/nl/tudelft/trustchain/common/MarketCommunity.kt @@ -1,3 +1,4 @@ + package nl.tudelft.trustchain.common import android.util.Log @@ -47,7 +48,7 @@ class MarketCommunity : Community() { private fun onTradePacket(packet: Packet) { val payload = packet.getAuthPayload(TradePayload.Deserializer).second notifyListeners(payload) - Log.d("MarketCommunity::onTradePacket", "Received packet:{${payload.primaryCurrency}, ${payload.secondaryCurrency}, ${payload.amount}, ${payload.price}, ${payload.type}}") + Log.d("MarketCommunity", "Received packet:{${payload.primaryCurrency}, ${payload.secondaryCurrency}, ${payload.amount}, ${payload.price}, ${payload.type}}") } fun addListener(type: TradePayload.Type?, listener: (TradePayload) -> Unit) { diff --git a/currencyii/build.gradle b/currencyii/build.gradle index 0233d6e30..20fe15d91 100644 --- a/currencyii/build.gradle +++ b/currencyii/build.gradle @@ -16,7 +16,7 @@ android { buildToolsVersion "29.0.3" defaultConfig { - minSdkVersion 24 + minSdkVersion 22 targetSdkVersion 29 versionCode 1 versionName "1.0" diff --git a/debug/build.gradle b/debug/build.gradle index 1026170bf..121297c75 100644 --- a/debug/build.gradle +++ b/debug/build.gradle @@ -7,7 +7,7 @@ android { buildToolsVersion "29.0.3" defaultConfig { - minSdkVersion 24 + minSdkVersion 22 targetSdkVersion 29 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -57,8 +57,8 @@ dependencies { // Kotlin implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" // Logging @@ -72,3 +72,9 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions.freeCompilerArgs += [ + "-Xopt-in=kotlin.RequiresOptIn" + ] +} diff --git a/debug/src/main/java/nl/tudelft/trustchain/debug/DebugFragment.kt b/debug/src/main/java/nl/tudelft/trustchain/debug/DebugFragment.kt index 21920397e..47e4c059e 100644 --- a/debug/src/main/java/nl/tudelft/trustchain/debug/DebugFragment.kt +++ b/debug/src/main/java/nl/tudelft/trustchain/debug/DebugFragment.kt @@ -7,25 +7,56 @@ import android.graphics.Color import android.graphics.Typeface import android.os.Bundle import android.text.style.ForegroundColorSpan +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.widget.LinearLayout import android.widget.TextView +import androidx.core.content.res.ResourcesCompat import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController import kotlinx.coroutines.* import nl.tudelft.ipv8.Community +import nl.tudelft.ipv8.peerdiscovery.DiscoveryCommunity import nl.tudelft.ipv8.util.toHex import nl.tudelft.trustchain.common.ui.BaseFragment import nl.tudelft.trustchain.common.util.viewBinding import nl.tudelft.trustchain.debug.databinding.FragmentDebugBinding +import java.text.SimpleDateFormat import java.util.* class DebugFragment : BaseFragment(R.layout.fragment_debug) { private val binding by viewBinding(FragmentDebugBinding::bind) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setHasOptionsMenu(true) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.debug_options, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.wanLog -> { + findNavController().navigate(R.id.wanLogFragment) + true + } + R.id.multiPunch -> { + findNavController().navigate(R.id.punctureFragment) + true + } + else -> super.onOptionsItemSelected(item) + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -56,7 +87,7 @@ class DebugFragment : BaseFragment(R.layout.fragment_debug) { append(overlay.javaClass.simpleName) append(" (") val textColorResId = if (overlay.getPeers().isNotEmpty()) R.color.green else R.color.red - val textColor = resources.getColor(textColorResId, null) + val textColor = ResourcesCompat.getColor(resources, textColorResId, null) inSpans(ForegroundColorSpan(textColor)) { val peers = overlay.getPeers() val peersCountStr = resources.getQuantityString( @@ -74,7 +105,7 @@ class DebugFragment : BaseFragment(R.layout.fragment_debug) { } val totalPeersCount = ipv8.network.verifiedPeers.size val textColorResId = if (totalPeersCount > 0) R.color.green else R.color.red - val textColor = resources.getColor(textColorResId, null) + val textColor = ResourcesCompat.getColor(resources, textColorResId, null) inSpans(ForegroundColorSpan(textColor)) { append(resources.getQuantityString(R.plurals.x_peers, totalPeersCount, totalPeersCount)) } @@ -82,20 +113,27 @@ class DebugFragment : BaseFragment(R.layout.fragment_debug) { updateBootstrapList() - lifecycleScope.launch { + lifecycleScope.launchWhenCreated { val blockCount = withContext(Dispatchers.IO) { getTrustChainCommunity().database.getBlockCount(null) } - binding.txtBlockCount.text = blockCount.toString() + if (view != null) { + binding.txtBlockCount.text = blockCount.toString() + } } - lifecycleScope.launch { + lifecycleScope.launchWhenCreated { val chainLength = withContext(Dispatchers.IO) { getTrustChainCommunity().getChainLength() } - binding.txtChainLength.text = chainLength.toString() + if (view != null) { + binding.txtChainLength.text = chainLength.toString() + } } + binding.txtConnectionType.text = getDemoCommunity().network.wanLog + .estimateConnectionType().value + try { val pInfo: PackageInfo = requireContext().packageManager.getPackageInfo(requireContext().packageName, 0) diff --git a/debug/src/main/java/nl/tudelft/trustchain/debug/PunctureFragment.kt b/debug/src/main/java/nl/tudelft/trustchain/debug/PunctureFragment.kt new file mode 100644 index 000000000..f62df2cfe --- /dev/null +++ b/debug/src/main/java/nl/tudelft/trustchain/debug/PunctureFragment.kt @@ -0,0 +1,128 @@ +package nl.tudelft.trustchain.debug + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.consumeAsFlow +import nl.tudelft.ipv8.IPv4Address +import nl.tudelft.ipv8.peerdiscovery.DiscoveryCommunity +import nl.tudelft.trustchain.common.ui.BaseFragment +import nl.tudelft.trustchain.common.util.viewBinding +import nl.tudelft.trustchain.debug.databinding.FragmentPunctureBinding +import nl.tudelft.trustchain.debug.databinding.FragmentWanLogBinding +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.max +import kotlin.math.min + +@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) +class PunctureFragment : BaseFragment(R.layout.fragment_puncture) { + private val binding by viewBinding(FragmentPunctureBinding::bind) + + private var sent = 0 + private var received = 0 + + private val receivedMap = mutableMapOf() + private val firstMessageTimestamps = mutableMapOf() + private var firstSentMessageTimestamp: Date? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + lifecycleScope.launchWhenCreated { + while (isActive) { + updateView() + delay(100) + } + } + lifecycleScope.launchWhenCreated { + getDemoCommunity().punctureChannel.asFlow().collect { (peer, payload) -> + Log.i("PunctureFragment", "Received puncture from ${peer} on port ${payload.identifier}") + received++ + receivedMap[peer.toString()] = (receivedMap[peer.toString()] ?: 0) + 1 + if (firstMessageTimestamps[peer.toString()] == null) { + firstMessageTimestamps[peer.toString()] = Date() + } + } + } + + binding.btnPuncture.setOnClickListener { + val address = binding.edtAddress.text.toString().split(":") + if (address.size == 2) { + val ip = address[0] + val port = address[1].toIntOrNull() ?: MIN_PORT + + lifecycleScope.launchWhenCreated { + firstSentMessageTimestamp = Date() + if (binding.sweep.isChecked) { + punctureAll(ip, false) + delay(30000) + punctureAll(ip, true) + } else { + punctureSingle(ip, port) + } + } + } + } + } + + /* + private suspend fun punctureMultiple(ip: String, port: Int) { + Log.d("PunctureFragment", "Puncture multiple with initial port: $ip $port") + val minPort = max(port - SEARCH_BREADTH/2, MIN_PORT) + val maxPort = min(port + SEARCH_BREADTH/2, MAX_PORT) + for (i in minPort .. maxPort) { + val ipv4 = IPv4Address(ip, i) + getDemoCommunity().sendPuncture(ipv4, i) + sent++ + updateView() + + if (i % 10 == 0) { + delay(40) + } + } + } + */ + + private suspend fun punctureAll(ip: String, slow: Boolean) = with(Dispatchers.Default) { + for (i in MIN_PORT..MAX_PORT) { + val ipv4 = IPv4Address(ip, i) + getDemoCommunity().sendPuncture(ipv4, i) + sent++ + + if (i % 1000 == 0) { + delay(if (slow) 30000L else 1L) + } + } + } + + + private suspend fun punctureSingle(ip: String, port: Int) { + while (true) { + val ipv4 = IPv4Address(ip, port) + getDemoCommunity().sendPuncture(ipv4, port) + sent++ + + delay(1000) + } + } + + private fun updateView() { + val df = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM) + binding.txtResult.text = "Sent: $sent\nFirst Sent: $firstSentMessageTimestamp\nReceived: $received\n\n" + receivedMap.map { + val date = firstMessageTimestamps[it.key] + val time = if (date != null) df.format(date) else null + it.key + " (" + time + ") -> " + it.value + }.joinToString("\n") + } + + companion object { + const val MIN_PORT = 1024 + const val MAX_PORT = 65535 + const val SEARCH_BREADTH = 1000 + } +} diff --git a/debug/src/main/java/nl/tudelft/trustchain/debug/WanLogFragment.kt b/debug/src/main/java/nl/tudelft/trustchain/debug/WanLogFragment.kt new file mode 100644 index 000000000..211f107c7 --- /dev/null +++ b/debug/src/main/java/nl/tudelft/trustchain/debug/WanLogFragment.kt @@ -0,0 +1,30 @@ +package nl.tudelft.trustchain.debug + +import android.os.Bundle +import android.view.View +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.delay +import nl.tudelft.ipv8.peerdiscovery.DiscoveryCommunity +import nl.tudelft.trustchain.common.ui.BaseFragment +import nl.tudelft.trustchain.common.util.viewBinding +import nl.tudelft.trustchain.debug.databinding.FragmentWanLogBinding +import java.text.SimpleDateFormat + +class WanLogFragment : BaseFragment(R.layout.fragment_wan_log) { + private val binding by viewBinding(FragmentWanLogBinding::bind) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + lifecycleScope.launchWhenStarted { + while (true) { + val discovery = getIpv8().getOverlay()!! + val df = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM) + binding.txtLog.text = "Time Sender My WAN\n" + discovery.network.wanLog.getLog().map { + "" + df.format(it.timestamp) + ": " + it.sender.toString().padEnd(20) + " " + it.wan + }.joinToString("\n") + delay(1000) + } + } + } +} diff --git a/debug/src/main/res/layout/fragment_debug.xml b/debug/src/main/res/layout/fragment_debug.xml index e58546b24..4f2461341 100644 --- a/debug/src/main/res/layout/fragment_debug.xml +++ b/debug/src/main/res/layout/fragment_debug.xml @@ -90,6 +90,21 @@ app:fontFamily="monospace" style="@style/TextAppearance.MaterialComponents.Body2"/> + + + + + + + + + + + + + + + +