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

Thomas nfc transfer p2c #78

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 12 additions & 15 deletions crates/zk-commit-core/src/prover.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::fs::File;

use anyhow::Result;
use log::Level;
use plonky2::{
Expand All @@ -17,6 +15,7 @@ use plonky2::{
};
use plonky2_field::{goldilocks_field::GoldilocksField, types::PrimeField64};
use serde::{Deserialize, Serialize};
use std::{fs::File, io::Write};

use crate::{
circuits::{
Expand All @@ -25,8 +24,10 @@ use crate::{
},
claim_execution::{get_claim_proving_inputs, Claim},
commitment_tree::CommitmentTree,
groth16::Cbn128,
types::{C, F},
utils::AmountSecretPairing,
verifier::{generate_proof_base64, generate_verifier_config},
};
use thiserror::Error;

Expand All @@ -43,6 +44,9 @@ pub enum ProofError {

#[error("Unknown error")]
Unknown,

#[error("Unable to generate json: {0}")]
GenerateJsonError(#[from] anyhow::Error),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -68,23 +72,16 @@ pub fn generate_proof_of_claim(
eth_addr: [u8; 20],
) -> Result<(), ProofError> {
// Create claim from inputs
let proof_res = get_claim_proof(amount, secret, index, &siblings, root, eth_addr);

// If proof failed then return error
if proof_res.is_err() {
return Err(ProofError::InvalidProof);
}
let (proof, _, _) =
get_claim_proof::<Cbn128>(amount, secret, index, &siblings, root, eth_addr)?;

let (proof, _, _) = proof_res.expect("Proof failed");
let conf = generate_verifier_config(&proof)?;

let write_res = write_to_file(
path,
MobileProofData { proof_with_pis: proof.clone(), merkle_depth: siblings.len() },
);
let proof_json = generate_proof_base64(&proof, &conf)?;

if write_res.is_err() {
return Err(ProofError::FileReadError);
}
let mut file = File::create(path)?;
file.write_all(proof_json.as_bytes())?;

return Ok(());
}
Expand Down
1 change: 1 addition & 0 deletions crates/zk-commit-mobile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ uniffi = { version = "0.28.0", features = ["cli"] }
thiserror = { workspace = true }
zk-commit-core = { path = "../zk-commit-core" }
plonky2 = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

[lib]
Expand Down
2 changes: 1 addition & 1 deletion crates/zk-commit-mobile/android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ android {
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/META-INF/{AL2.0,LGPL2.1,DISCLAIMER}"
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/zk-commit-mobile/android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@
-keepclassmembers class * extends org.web3j.abi.datatypes.NumericType {
<init>(...);
}
-keep class org.web3j.** { *; }
-dontwarn groovy.lang.GroovyShell
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package com.okx.zkcommitmobile

import androidx.compose.ui.util.fastMapIndexed
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import com.okx.zkcommitmobile.data.Chain
Expand All @@ -16,10 +17,12 @@ import java.math.BigInteger
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
Expand All @@ -32,11 +35,17 @@ import kotlinx.coroutines.reactive.collect
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import org.java_websocket.exceptions.WebsocketNotConnectedException
import org.web3j.abi.EventEncoder
import org.web3j.abi.EventValues
import org.web3j.abi.FunctionReturnDecoder
import org.web3j.abi.datatypes.Event
import org.web3j.protocol.Web3j
import org.web3j.protocol.Web3jService
import org.web3j.protocol.core.DefaultBlockParameter
import org.web3j.protocol.core.DefaultBlockParameterName
import org.web3j.protocol.core.Request
import org.web3j.protocol.core.Response
import org.web3j.protocol.core.methods.request.EthFilter
import org.web3j.protocol.core.methods.response.TransactionReceipt
import org.web3j.protocol.http.HttpService
import org.web3j.protocol.websocket.WebSocketService
Expand Down Expand Up @@ -172,6 +181,30 @@ class Web3jManager(
get() = web3jContext.flatMapLatest { it.flow { transactionFlowable() } }
.onEach { Log.i("Transaction: ${it.hash}") }

fun getEventFlow(address: String, event: Event): Flow<EventValues?> {
val topic = EventEncoder.encode(event)
return web3jContext.flatMapLatest { web3jContext ->
if (web3jContext.service is WebSocketService) {
flow { logsNotifications(listOf(address), listOf(topic)) }
.map { it.params.result }
.map { extractEventParameters(event, it.topics, it.data) }
} else {
val blockNumber =
send {
ethBlockNumber()
}.onErrorReturn { return@flatMapLatest emptyFlow() }.blockNumber
val ethFilter = EthFilter(
DefaultBlockParameter.valueOf(blockNumber),
DefaultBlockParameterName.LATEST,
address
)
ethFilter.addSingleTopic(topic)
flow { ethLogFlowable(ethFilter) }
.map { extractEventParameters(event, it.topics, it.data) }
}
}
}

suspend fun waitForTransactionReceipt(transactionHash: String) = web3jContext.flatMapLatest {
it.processor.waitForTransactionReceipt(transactionHash)
transactionReceipts
Expand All @@ -186,3 +219,19 @@ class Web3jManager(
return Result.success(balance)
}
}

private fun extractEventParameters(
event: Event,
topics: List<String>?,
data: String
): EventValues? {
val encodedEventSignature = EventEncoder.encode(event)
if (topics.isNullOrEmpty() || topics[0] != encodedEventSignature) {
return null
}
val nonIndexedValues = FunctionReturnDecoder.decode(data, event.nonIndexedParameters)
val indexedValues = event.indexedParameters.fastMapIndexed { index, indexedParameter ->
FunctionReturnDecoder.decodeIndexedValue(topics[index + 1], indexedParameter)
}
return EventValues(indexedValues, nonIndexedValues)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import com.okx.zkcommitmobile.data.send
import com.okx.zkcommitmobile.data.totalAmount
import com.okx.zkcommitmobile.data.trySend
import com.okx.zkcommitmobile.data.withClaimStatus
import com.okx.zkcommitmobile.network.RapidsnarkService
import com.okx.zkcommitmobile.network.StatusResponse
import com.okx.zkcommitmobile.network.ZkCommitService
import com.okx.zkcommitmobile.network.wrapGroth16
import com.okx.zkcommitmobile.network.inputPlonky2
import com.okx.zkcommitmobile.nfc.firstTextOrNull
import com.okx.zkcommitmobile.nfc.onTagDiscovered
import com.okx.zkcommitmobile.uniffi.AmountSecretPairing
Expand All @@ -53,11 +55,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
Expand All @@ -82,6 +84,7 @@ class ZkCommitMobileViewModel(
private val json: Json,
private val preferences: DataStore<Preferences>,
private val zkCommitService: ZkCommitService,
private val rapidsnarkService: RapidsnarkService,
private val defaultDispatcher: CoroutineDispatcher
) : ViewModel(),
NfcAdapter.ReaderCallback {
Expand Down Expand Up @@ -316,8 +319,7 @@ class ZkCommitMobileViewModel(
viewModelScope.launch {
val from = getFrom(action = "test transfer") ?: return@launch
launch {
val transfer = testERC20.transfer.onEach { Log.d("testTransfer: transfer=$it") }
.first { it.to == from }
val transfer = testERC20.transfer.first { it.to == from }
Log.d("testTransfer: first=$transfer")
}
testERC20.transfer(Address(from), Uint256(BigInteger.ONE)).onSuccess {
Expand Down Expand Up @@ -553,34 +555,61 @@ class ZkCommitMobileViewModel(
}

private suspend fun claimInternal(proofFile: File, from: String): Result<Unit> = runCatching {
zkCommitService.wrapGroth16(proofFile)
// zkCommitService.wrapGroth16(proofFile)
rapidsnarkService.inputPlonky2(proofFile)
}.onFailure {
Log.e(it, "Failed to wrap Groth16")
_messages.send("Failed to wrap Groth16: ${it.localizedMessage}")
return Result.failure(it)
}.suspendMapCatching { pwi ->
withContext(defaultDispatcher) { getClaimTokenCallData(pwi).toHexString() }
}.onFailure {
Log.e(it, "Failed to get claim token call data")
_messages.send("Failed to get claim token call data: ${it.localizedMessage}")
return Result.failure(it)
}.fold(
onSuccess = { data ->
if (useWalletConnect()) {
sendTransactionWithWalletConnect(
from = from,
to = payCommitment.getAddress(),
data = data
).map { Unit }
} else {
payCommitment.withdraw(data).onFailure {
Log.e(it, "Failed to withdraw")
_messages.send("Failed to withdraw: ${it.localizedMessage}")
onSuccess = {
suspendRunCatching {
val success: StatusResponse.Success
while (true) {
when (val statusResponse = rapidsnarkService.status()) {
is StatusResponse.Success -> {
success = statusResponse
break
}

is StatusResponse.Busy -> delay(1000)
}
}
success
}
},
onFailure = { Result.failure(it) }
)
// .suspendMapCatching { pwi ->
// withContext(defaultDispatcher) { getClaimTokenCallData(pwi).toHexString() }
// }
.suspendMapCatching { success ->
withContext(defaultDispatcher) {
val pwi = json.encodeToString<StatusResponse>(success)
getClaimTokenCallData(pwi).toHexString()
}
}
.onFailure {
Log.e(it, "Failed to get claim token call data")
_messages.send("Failed to get claim token call data: ${it.localizedMessage}")
return Result.failure(it)
}.fold(
onSuccess = { data ->
if (useWalletConnect()) {
sendTransactionWithWalletConnect(
from = from,
to = payCommitment.getAddress(),
data = data
).map { Unit }
} else {
payCommitment.withdraw(data).onFailure {
Log.e(it, "Failed to withdraw")
_messages.send("Failed to withdraw: ${it.localizedMessage}")
}
}
},
onFailure = { Result.failure(it) }
)

private suspend fun generateProofOfClaim(
amount: ULong,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.datastore.preferences.core.Preferences
import com.okx.zkcommitmobile.ContractManager
import com.okx.zkcommitmobile.Web3jManager
import com.okx.zkcommitmobile.data.chain
import com.okx.zkcommitmobile.onErrorReturn
import java.math.BigInteger
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
Expand All @@ -16,6 +15,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import org.web3j.abi.EventEncoder
import org.web3j.abi.EventValues
import org.web3j.abi.FunctionEncoder
import org.web3j.abi.FunctionReturnDecoder
import org.web3j.abi.Utils
Expand All @@ -27,11 +27,6 @@ import org.web3j.abi.datatypes.Type
import org.web3j.abi.datatypes.Utf8String
import org.web3j.abi.datatypes.generated.Uint256
import org.web3j.abi.datatypes.generated.Uint8
import org.web3j.protocol.core.DefaultBlockParameter
import org.web3j.protocol.core.DefaultBlockParameterName
import org.web3j.protocol.core.methods.request.EthFilter
import org.web3j.protocol.core.methods.response.Log
import org.web3j.tx.Contract
import timber.log.Timber

class TestERC20(
Expand Down Expand Up @@ -114,31 +109,22 @@ class TestERC20(
}

val transfer
get() = addresses.mapNotNull { address ->
val blockNumber = web3jManager.send { ethBlockNumber() }
.onErrorReturn { return@mapNotNull null }.blockNumber
EthFilter(
DefaultBlockParameter.valueOf(blockNumber),
DefaultBlockParameterName.LATEST,
address.value
).apply { addSingleTopic(encodedTransfer) }
}.flatMapLatest { ethFilter -> web3jManager.flow { ethLogFlowable(ethFilter) } }
.mapNotNull { log ->
val eventValues = Contract.staticExtractEventParameters(Transfer, log)
eventValues?.let {
TransferEventResponse(
log = log,
from = it.indexedValues[0].value as String,
to = it.indexedValues[1].value as String,
value = it.nonIndexedValues[0].value as BigInteger
)
}
get() = addresses.flatMapLatest { address ->
web3jManager.getEventFlow(address.value, Transfer)
}.mapNotNull { eventValues ->
eventValues?.let {
TransferEventResponse(
eventValues = it,
from = it.indexedValues[0].value as String,
to = it.indexedValues[1].value as String,
value = it.nonIndexedValues[0].value as BigInteger
)
}
.onEach { Log.i("Transfer: $it") }
}.onEach { Log.i("Transfer: $it") }
}

data class TransferEventResponse(
val log: Log,
val eventValues: EventValues,
val from: String,
val to: String,
val value: BigInteger
Expand Down
Loading