Skip to content

Commit 7a7b10f

Browse files
author
Edoardo Ierina
authored
Added flows and Corda interface to EVM project (hyperledger-labs#58)
* fixed compile error Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> * Unified EVM contracts, build, generate wrappers, test, run, deploy under evm project. * new embedded events Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> * added evm swapvault and flows for swap Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> * Fixed missing interface updates Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> * Fixed event encoding and unlock verification Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> * Fixed event encoding and unlock verification Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com> --------- Signed-off-by: Edoardo Ierina <edoardo.ierina@r3.com>
1 parent 712c62e commit 7a7b10f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+12466
-1169
lines changed

.gitmodules

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "src/r3/atomic-swap/evm/lib/forge-std"]
2+
path = src/r3/atomic-swap/evm/lib/forge-std
3+
url = https://github.com/foundry-rs/forge-std
4+
[submodule "src/r3/atomic-swap/evm/lib/openzeppelin-contracts"]
5+
path = src/r3/atomic-swap/evm/lib/openzeppelin-contracts
6+
url = https://github.com/OpenZeppelin/openzeppelin-contracts

src/r3/atomic-swap/corda/evm-interop-contracts/src/main/java/org/web3j/generated/contracts/SwapVault.java

+292
Large diffs are not rendered by default.

src/r3/atomic-swap/corda/evm-interop-contracts/src/main/kotlin/com/r3/corda/evminterop/DefaultEventEncoder.kt

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import com.r3.corda.evminterop.dto.TransactionReceipt
44
import com.r3.corda.evminterop.dto.TransactionReceiptLog
55
import net.corda.core.serialization.CordaSerializable
66
import org.web3j.abi.DefaultFunctionEncoder
7-
import org.web3j.abi.TypeEncoder
87
import org.web3j.abi.datatypes.*
8+
import org.web3j.abi.datatypes.generated.Bytes32
99
import org.web3j.abi.datatypes.generated.Int256
1010
import org.web3j.abi.datatypes.generated.Uint256
1111
import org.web3j.abi.datatypes.generated.Uint8
@@ -50,6 +50,7 @@ object DefaultEventEncoder {
5050
"address" -> Address(unwrappedValue as String)
5151
"bool" -> Bool(unwrappedValue as Boolean)
5252
"bytes" -> DynamicBytes(unwrappedValue as ByteArray)
53+
"bytes32" -> unwrappedValue as Bytes32
5354
else -> throw IllegalArgumentException("Unsupported type: $type")
5455
}, isIndexed)
5556
}
@@ -60,7 +61,7 @@ object DefaultEventEncoder {
6061
val nonIndexedParams = web3jParamsWithIndexedInfo.filterNot { it.second }.map { it.first }
6162

6263
val topic0 = Hash.sha3String(whitespaceRegex.replace(eventSignature, ""))
63-
val topics = listOf(topic0) + indexedParams.map { Numeric.prependHexPrefix(TypeEncoder.encode(it)) }
64+
val topics = listOf(topic0) + indexedParams.map { Hash.sha3String(it.toString()) }
6465
val data = Numeric.prependHexPrefix(DefaultFunctionEncoder().encodeParameters(nonIndexedParams))
6566

6667
return EncodedEvent(contractAddress, topics, data)
@@ -138,3 +139,9 @@ data class EncodedEvent(
138139
}
139140
}
140141

142+
data class UnencodedEvent(
143+
val data: List<Type<*>>
144+
) {
145+
constructor(vararg args: Type<*>): this(args.toList()) {
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package com.r3.corda.evminterop
2+
3+
import net.corda.core.crypto.SecureHash
4+
import net.corda.core.serialization.CordaSerializable
5+
import org.web3j.abi.DefaultFunctionEncoder
6+
import org.web3j.abi.datatypes.Address
7+
import org.web3j.abi.datatypes.Type
8+
import org.web3j.abi.datatypes.generated.Bytes32
9+
import org.web3j.abi.datatypes.generated.Uint256
10+
import org.web3j.crypto.Hash
11+
import org.web3j.utils.Numeric
12+
import java.math.BigInteger
13+
14+
@CordaSerializable
15+
interface IUnlockEventEncoder {
16+
fun transferEvent(transactionId: SecureHash): EncodedEvent
17+
fun revertEvent(transactionId: SecureHash): EncodedEvent
18+
}
19+
20+
@CordaSerializable
21+
data class SwapVaultEventEncoder(
22+
private val protocolAddress: String,
23+
private val commitmentHash: ByteArray
24+
) : IUnlockEventEncoder {
25+
companion object {
26+
fun create(
27+
chainId: BigInteger,
28+
protocolAddress: String,
29+
owner: String,
30+
recipient: String,
31+
amount: BigInteger,
32+
tokenId: BigInteger,
33+
tokenAddress: String,
34+
signaturesThreshold: BigInteger
35+
) : SwapVaultEventEncoder {
36+
return SwapVaultEventEncoder(
37+
protocolAddress,
38+
commitmentHash(
39+
chainId,
40+
owner,
41+
recipient,
42+
amount,
43+
tokenId,
44+
tokenAddress,
45+
signaturesThreshold
46+
)
47+
)
48+
}
49+
50+
private fun commitmentHash(
51+
chainId: BigInteger,
52+
owner: String,
53+
recipient: String,
54+
amount: BigInteger,
55+
tokenId: BigInteger,
56+
tokenAddress: String,
57+
signaturesThreshold: BigInteger
58+
): ByteArray {
59+
val parameters = listOf<Type<*>>(
60+
Uint256(chainId),
61+
Address(owner),
62+
Address(recipient),
63+
Uint256(amount),
64+
Uint256(tokenId),
65+
Address(tokenAddress),
66+
Uint256(signaturesThreshold)
67+
)
68+
69+
// Encode parameters using the DefaultFunctionEncoder
70+
val encodedParams = DefaultFunctionEncoder().encodeParameters(parameters)
71+
72+
val bytes = Numeric.hexStringToByteArray(encodedParams)
73+
74+
val hash = Hash.sha3(bytes)
75+
76+
return hash
77+
}
78+
}
79+
80+
public fun commitEvent(transactionId: SecureHash) = commitEvent(transactionId, Bytes32(commitmentHash))
81+
override fun transferEvent(transactionId: SecureHash) = transferEvent(transactionId, Bytes32(commitmentHash))
82+
override fun revertEvent(transactionId: SecureHash) = revertEvent(transactionId, Bytes32(commitmentHash))
83+
84+
private fun commitEvent(transactionId: SecureHash, commitmentHash: Bytes32): EncodedEvent {
85+
return DefaultEventEncoder.encodeEvent(
86+
protocolAddress,
87+
"Commit(string,bytes32)",
88+
Indexed(transactionId.toHexString()),
89+
commitmentHash
90+
)
91+
}
92+
93+
private fun transferEvent(transactionId: SecureHash, commitmentHash: Bytes32): EncodedEvent {
94+
return DefaultEventEncoder.encodeEvent(
95+
protocolAddress,
96+
"Transfer(string,bytes32)",
97+
Indexed(transactionId.toHexString()),
98+
commitmentHash
99+
)
100+
}
101+
102+
private fun revertEvent(transactionId: SecureHash, commitmentHash: Bytes32): EncodedEvent {
103+
return DefaultEventEncoder.encodeEvent(
104+
protocolAddress,
105+
"Revert(string,bytes32)",
106+
Indexed(transactionId.toHexString()),
107+
commitmentHash
108+
)
109+
}
110+
}
111+
112+
@CordaSerializable
113+
data class Erc20TransferEventEncoder(
114+
private val tokenAddress: String,
115+
private val aliceAddress: String,
116+
private val bobAddress: String,
117+
private val amount: BigInteger
118+
) : IUnlockEventEncoder {
119+
private val forwardTransferEvent = DefaultEventEncoder.encodeEvent(
120+
tokenAddress,
121+
"Transfer(address,address,uint256)",
122+
Indexed(aliceAddress),
123+
Indexed(bobAddress),
124+
amount
125+
)
126+
127+
// Defines the encoding of an event that transfer an amount of 1 wei from Bob to Bob himself (signals revert)
128+
private val backwardTransferEvent = DefaultEventEncoder.encodeEvent(
129+
tokenAddress,
130+
"Transfer(address,address,uint256)",
131+
Indexed(aliceAddress),
132+
Indexed(aliceAddress),
133+
amount
134+
)
135+
136+
override fun transferEvent(transactionId: SecureHash): EncodedEvent = forwardTransferEvent
137+
138+
override fun revertEvent(transactionId: SecureHash): EncodedEvent = backwardTransferEvent
139+
}

src/r3/atomic-swap/corda/evm-interop-contracts/src/main/kotlin/com/r3/corda/evminterop/contracts/swap/LockStateContract.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class LockStateContract : Contract {
3737
tx: LedgerTransaction,
3838
cmd: LockCommand.Unlock
3939
) {
40+
val inputsTxHash = tx.inputs.map { it.ref.txhash }.distinct().singleOrNull()
41+
?: throw IllegalArgumentException("Inputs from multiple transactions is not supported")
42+
4043
val unlockedAssetState = tx.outputStates.filterIsInstance<OwnableState>().single()
4144
val lockState = tx.inputStates.filterIsInstance<LockState>().single()
4245
val txIndexKey = RlpEncoder.encode(
@@ -51,7 +54,7 @@ class LockStateContract : Contract {
5154
"Only two input states can exist" using (tx.inputStates.size == 2)
5255
"Invalid recipient for this command" using (unlockedAssetState.owner.owningKey == lockState.assetRecipient)
5356
"EVM Transfer event has not been validated by the minimum number of validators" using (cmd.proof.validatorSignatures.size >= lockState.signaturesThreshold)
54-
"The transaction receipt does not contain the expected unlock event" using (lockState.forwardEvent.isFoundIn(
57+
"The transaction receipt does not contain the expected unlock event" using (lockState.unlockEvent.transferEvent(inputsTxHash).isFoundIn(
5558
cmd.proof.transactionReceipt
5659
))
5760
"The transaction receipts merkle proof failed to validate" using (PatriciaTrie.verifyMerkleProof(
@@ -72,6 +75,9 @@ class LockStateContract : Contract {
7275
tx: LedgerTransaction,
7376
cmd: LockCommand.Revert
7477
) {
78+
val inputsTxHash = tx.inputs.map { it.ref.txhash }.distinct().singleOrNull()
79+
?: throw IllegalArgumentException("Inputs from multiple transactions is not supported")
80+
7581
val unlockedAssetState = tx.outputStates.filterIsInstance<OwnableState>().single()
7682
val lockState = tx.inputStates.filterIsInstance<LockState>().single()
7783
val txIndexKey = RlpEncoder.encode(
@@ -85,7 +91,7 @@ class LockStateContract : Contract {
8591
requireThat {
8692
"Only two input states can exist" using (tx.inputStates.size == 2)
8793
"Invalid recipient for this command" using (unlockedAssetState.owner.owningKey == lockState.assetSender)
88-
"The transaction receipt does not contain the expected unlock event" using (lockState.backwardEvent.isFoundIn(
94+
"The transaction receipt does not contain the expected unlock event" using (lockState.unlockEvent.revertEvent(inputsTxHash).isFoundIn(
8995
cmd.proof.transactionReceipt
9096
))
9197
"The transaction receipts merkle proof failed to validate" using (PatriciaTrie.verifyMerkleProof(

src/r3/atomic-swap/corda/evm-interop-contracts/src/main/kotlin/com/r3/corda/evminterop/states/swap/LockState.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.r3.corda.evminterop.states.swap
22

3-
import com.r3.corda.evminterop.EncodedEvent
3+
import com.r3.corda.evminterop.IUnlockEventEncoder
44
import com.r3.corda.evminterop.contracts.swap.LockStateContract
55
import net.corda.core.contracts.BelongsToContract
66
import net.corda.core.contracts.ContractState
@@ -16,6 +16,5 @@ class LockState(val assetSender: PublicKey,
1616
val notary: PublicKey,
1717
val approvedValidators: List<PublicKey>,
1818
val signaturesThreshold: Int,
19-
val forwardEvent: EncodedEvent,
20-
val backwardEvent: EncodedEvent,
19+
val unlockEvent: IUnlockEventEncoder,
2120
override val participants: List<AbstractParty> = emptyList()) : ContractState

src/r3/atomic-swap/corda/evm-interop-contracts/src/main/kotlin/com/r3/corda/evminterop/states/swap/SwapTransactionDetails.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.r3.corda.evminterop.states.swap
22

33
import com.r3.corda.evminterop.EncodedEvent
4+
import com.r3.corda.evminterop.IUnlockEventEncoder
5+
import com.r3.corda.evminterop.SwapVaultEventEncoder
46
import net.corda.core.contracts.OwnableState
57
import net.corda.core.contracts.StateAndRef
68
import net.corda.core.identity.Party
@@ -18,6 +20,5 @@ data class SwapTransactionDetails(val senderCordaName: Party,
1820
val cordaAssetState: StateAndRef<OwnableState>,
1921
val approvedCordaValidators: List<Party>,
2022
val minimumNumberOfEventValidations: Int,
21-
val unlockEvent: EncodedEvent,
22-
val revertEvent: EncodedEvent
23+
val unlockEvent: IUnlockEventEncoder
2324
)
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,15 @@
11
package com.r3.corda.evminterop.services
22

33
import com.r3.corda.evminterop.dto.ContractInfo
4-
import com.r3.corda.evminterop.dto.DepositStatus
54
import com.r3.corda.evminterop.dto.TokenSetup
65
import com.r3.corda.evminterop.dto.TransactionReceipt
7-
import com.r3.corda.evminterop.services.ResponseOperation
86
import net.corda.core.flows.FlowExternalOperation
97
import java.math.BigInteger
108

11-
interface IBridge {
12-
13-
// non-transactional
14-
fun getHash(erc20Address: String, to: String, amount: BigInteger, hash: ByteArray): FlowExternalOperation<ByteArray>
15-
fun hashfn(secret: String): FlowExternalOperation<ByteArray>
16-
17-
// transactional
18-
fun lock(tokenAddress: String, receiverAddress: String, amount: BigInteger, hash: ByteArray, timeout: BigInteger): FlowExternalOperation<TransactionReceipt>
19-
20-
fun revertTokens(tokenAddress: String, to: String, amount: BigInteger, hash: ByteArray): FlowExternalOperation<TransactionReceipt>
21-
22-
fun unlockTokens(tokenAddress: String, amount: BigInteger, secret: String): FlowExternalOperation<TransactionReceipt>
23-
24-
fun depositStatus(tokenAddress: String, receiver: String, amount: BigInteger, hash: ByteArray, timeout: BigInteger) : FlowExternalOperation<DepositStatus>
25-
}
26-
279
interface IContracts {
2810
fun deployReverseERC20(chainId: BigInteger, salt: ByteArray, initCode: ByteArray) : ResponseOperation<TransactionReceipt>
2911

3012
fun deployReverseERC20(chainId: BigInteger, salt: ByteArray, setup: TokenSetup) : ResponseOperation<TransactionReceipt>
3113

3214
fun getReverseERC20Address(chainId: BigInteger, salt: ByteArray): FlowExternalOperation<ContractInfo>
33-
3415
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.web3j.generated.contracts
2+
3+
import net.corda.core.flows.FlowExternalOperation
4+
import java.math.BigInteger
5+
6+
interface ISwapVault {
7+
8+
val contractAddress: String
9+
10+
fun claimCommitment(swapId: String): FlowExternalOperation<com.r3.corda.evminterop.dto.TransactionReceipt>
11+
12+
fun revertCommitment(swapId: String): FlowExternalOperation<com.r3.corda.evminterop.dto.TransactionReceipt>
13+
14+
fun commit(
15+
swapId: String,
16+
recipient: String,
17+
signaturesThreshold: BigInteger
18+
): FlowExternalOperation<com.r3.corda.evminterop.dto.TransactionReceipt>
19+
20+
fun commitWithToken(
21+
swapId: String,
22+
tokenAddress: String,
23+
tokenId: BigInteger,
24+
amount: BigInteger,
25+
recipient: String,
26+
signaturesThreshold: BigInteger
27+
): FlowExternalOperation<com.r3.corda.evminterop.dto.TransactionReceipt>
28+
29+
fun commitWithToken(
30+
swapId: String,
31+
tokenAddress: String,
32+
amount: BigInteger,
33+
recipient: String,
34+
signaturesThreshold: BigInteger
35+
): FlowExternalOperation<com.r3.corda.evminterop.dto.TransactionReceipt>
36+
37+
fun commitmentHash(swapId: String): FlowExternalOperation<ByteArray>
38+
}

0 commit comments

Comments
 (0)