Skip to content

Commit

Permalink
feat: IIBC-26-javascore-xcall-contract-code-update (#166)
Browse files Browse the repository at this point in the history
* update build.gradle to implement IBC project

* update settings.gradle to implement use xCall

* move and update xCall service for IBC

* add xCall readme

* move and update xCall service for IBC

* update xCall to support IBC messages on HandleBTPMessage

* remove tests for now

* feat(javascore): implement IBCModule on xCall app (#228)

* feat(javascore): implement IBCModule on xCall app

* feat: IIBC-26-javascore-xcall-contract-code-update-review (#286)

* remove commented test files

* remove commented lines for ```checkService```

* set destinationChannel from counterPartyChannelId

* use sequenceNumber from IBCHandler for IBC specific sequence number for IBC Message.

* remove order for ```setTimeoutHeight_unauthorized()```
as they are tested on separate deployments

* remove order as they are tested on separate deployments

---------

Co-authored-by: redlarva <[email protected]>
Co-authored-by: Night Owl <[email protected]>
  • Loading branch information
3 people authored Apr 24, 2023
1 parent db12f58 commit c0c063d
Show file tree
Hide file tree
Showing 21 changed files with 2,158 additions and 9 deletions.
21 changes: 21 additions & 0 deletions contracts/javascore/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,27 @@ subprojects {
}
}

dependencies {
compileOnly("foundation.icon:javaee-api:$javaeeVersion")
implementation("foundation.icon:javaee-scorex:$scorexVersion")

annotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion")
compileOnly("foundation.icon:javaee-score-client:$scoreClientVersion")

testImplementation 'com.google.protobuf:protobuf-javalite:3.13.0'
testImplementation 'foundation.icon:javaee-rt:0.9.3'
testImplementation("org.mockito:mockito-core:$mockitoCoreVersion")
testImplementation("org.mockito:mockito-inline:$mockitoCoreVersion")
testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion")
testAnnotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion")
testImplementation("foundation.icon:javaee-score-client:$scoreClientVersion")
testImplementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
testImplementation("foundation.icon:icon-sdk:$iconsdkVersion")
testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterApiVersion")
testImplementation("org.junit.jupiter:junit-jupiter-params:$jupiterParamsVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterEngineVersion")
}

configurations {
intTestImplementation.extendsFrom testImplementation
intTestAnnotationProcessor.extendsFrom testAnnotationProcessor
Expand Down
14 changes: 7 additions & 7 deletions contracts/javascore/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ dependencies {
compileOnly("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion")
compileOnly("foundation.icon:icon-sdk:$iconsdkVersion")

testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterApiVersion")
testImplementation("org.junit.jupiter:junit-jupiter-params:$jupiterParamsVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterEngineVersion")
testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion")
// testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterApiVersion")
// testImplementation("org.junit.jupiter:junit-jupiter-params:$jupiterParamsVersion")
// testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterEngineVersion")
// testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion")
}

test {
useJUnitPlatform()
}
//test {
// useJUnitPlatform()
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2022 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ibc.icon.interfaces;

import foundation.icon.score.client.ScoreInterface;
import score.annotation.External;

@ScoreInterface
public interface ICallServiceReceiver {

/**
* Handles the call message received from the source chain.
* Only called from the Call Message Service.
*
* @param _from The BTP address of the caller on the source chain
* @param _data The calldata delivered from the caller
*/
@External
void handleCallMessage(String _from, byte[] _data);
}
44 changes: 44 additions & 0 deletions contracts/javascore/modules/mock-dapp/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version = '0.1.0'

dependencies {
implementation project(':lib')
implementation project(':score-util')
}

tasks.named('compileJava') {
dependsOn(':score-util:jar')
dependsOn(':lib:jar')
}

optimizedJar {
dependsOn(project(':lib').jar)
dependsOn(project(':score-util').jar)

mainClassName = 'ibc.mockapp.MockDApp'
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}

deployJar {
endpoints {
berlin {
uri = 'https://berlin.net.solidwallet.io/api/v3'
nid = 0x7
}
lisbon {
uri = 'https://lisbon.net.solidwallet.io/api/v3'
nid = 0x2
}
local {
uri = 'http://localhost:9082/api/v3'
nid = 0x3
}
}
keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : ''
password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : ''
parameters {
arg('_callService', 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2022 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ibc.mockapp;

import ibc.icon.interfaces.ICallServiceReceiver;
import java.math.BigInteger;
import score.Address;
import score.Context;
import score.DictDB;
import score.UserRevertedException;
import score.VarDB;
import score.annotation.EventLog;
import score.annotation.External;
import score.annotation.Optional;
import score.annotation.Payable;


public class MockDApp implements ICallServiceReceiver {
private final Address callSvc;
private final VarDB<BigInteger> id = Context.newVarDB("id", BigInteger.class);
private final DictDB<BigInteger, RollbackData> rollbacks = Context.newDictDB("rollbacks", RollbackData.class);

public MockDApp(Address _callService) {
this.callSvc = _callService;
}

private void onlyCallService() {
Context.require(Context.getCaller().equals(this.callSvc), "onlyCallService");
}

private BigInteger getNextId() {
BigInteger _id = this.id.getOrDefault(BigInteger.ZERO);
_id = _id.add(BigInteger.ONE);
this.id.set(_id);
return _id;
}

@Payable
@External
public void sendMessage(String _to, byte[] _data, @Optional byte[] _rollback) {
if (_rollback != null) {
// The code below is not actually necessary because the _rollback data is stored on the xCall side,
// but in this example, it is needed for testing to compare the _rollback data later.
var id = getNextId();
Context.println("DAppProxy: store rollback data with id=" + id);
RollbackData rbData = new RollbackData(id, _rollback);
var ssn = _sendCallMessage(Context.getValue(), _to, _data, rbData.toBytes());
rbData.setSvcSn(ssn);
rollbacks.set(id, rbData);
} else {
// This is for one-way message
_sendCallMessage(Context.getValue(), _to, _data, null);
}
}

private BigInteger _sendCallMessage(BigInteger value, String to, byte[] data, byte[] rollback) {
try {
return Context.call(BigInteger.class, value, this.callSvc, "sendCallMessage", to, data, rollback);
} catch (UserRevertedException e) {
// propagate the error code to the caller
Context.revert(e.getCode(), "UserReverted");
return BigInteger.ZERO; // call flow does not reach here, but make compiler happy
}
}

@External
public void handleCallMessage(String _from, byte[] _data) {
onlyCallService();
Context.println("handleCallMessage: from=" + _from + ", data=" + new String(_data));
if (Context.getAddress().equals(Address.fromString(_from))) {
// handle rollback data here
// In this example, just compare it with the stored one.
RollbackData received = RollbackData.fromBytes(_data);
var id = received.getId();
RollbackData stored = rollbacks.get(id);
Context.require(stored != null, "invalid received id");
Context.require(received.equals(stored), "rollbackData mismatch");
rollbacks.set(id, null); // cleanup
RollbackDataReceived(_from, stored.getSvcSn(), received.getRollback());
} else {
// normal message delivery
MessageReceived(_from, _data);
}
}

@EventLog
public void MessageReceived(String _from, byte[] _data) {}

@EventLog
public void RollbackDataReceived(String _from, BigInteger _ssn, byte[] _rollback) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2022 ICON Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package ibc.mockapp;

import java.math.BigInteger;
import score.ByteArrayObjectWriter;
import score.Context;
import score.ObjectReader;
import score.ObjectWriter;

public class RollbackData {
private final BigInteger id;
private final byte[] rollback;
private BigInteger ssn;

public RollbackData(BigInteger id, byte[] rollback) {
this.id = id;
this.rollback = rollback;
}

public BigInteger getId() {
return id;
}

public byte[] getRollback() {
return rollback;
}

public BigInteger getSvcSn() {
return ssn;
}

public void setSvcSn(BigInteger ssn) {
this.ssn = ssn;
}

public static void writeObject(ObjectWriter w, RollbackData data) {
w.beginList(3);
w.write(data.id);
w.write(data.rollback);
w.writeNullable(data.ssn);
w.end();
}

public static RollbackData readObject(ObjectReader r) {
r.beginList();
RollbackData rbData = new RollbackData(
r.readBigInteger(),
r.readByteArray()
);
rbData.setSvcSn(r.readNullable(BigInteger.class));
r.end();
return rbData;
}

public byte[] toBytes() {
ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn");
writeObject(writer, this);
return writer.toByteArray();
}

public static RollbackData fromBytes(byte[] bytes) {
ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes);
return readObject(reader);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof RollbackData)) {
return false;
} else {
RollbackData other = (RollbackData) obj;
if (this.rollback == null || other.rollback == null) {
return false;
}
if (this.rollback.length != other.rollback.length) {
return false;
}
for (int i = 0; i < this.rollback.length; i++) {
if (this.rollback[i] != other.rollback[i]) {
return false;
}
}
return true;
}
}
}
9 changes: 7 additions & 2 deletions contracts/javascore/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ include(
'lib',
'test-lib',
'ibc',
// 'xcall',
'xcall',
// 'lightclients:archway',
)

include(':tendermint')
Expand All @@ -18,4 +19,8 @@ project(':mockclient').name = "mockclient"

include(':mockapp')
project(':mockapp').projectDir = file("modules/mockapp")
project(':mockapp').name = "mockapp"
project(':mockapp').name = "mockapp"

include(':mock-dapp')
project(':mock-dapp').projectDir = file("modules/mock-dapp")
project(':mock-dapp').name = "mock-dapp"
13 changes: 13 additions & 0 deletions contracts/javascore/xcall/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Arbitrary Call Service

The reference implementation of the arbitrary call service (a.k.a. `xcall`) specification between ICON to ICON.

## How to test

Assuming the [gochain-local](https://github.com/icon-project/gochain-local) docker container is running,
you can start integration testing with the following command.

```
$ ./gradlew clean xcall:test -PintegrationTest=true \
-Pscore-test.default.keyStore=<path_to_god_wallet_json> -Pscore-test.default.keyPassword=gochain
```
Loading

0 comments on commit c0c063d

Please sign in to comment.