From 535979044777b633b1b3c10651d9671d37fdcaca Mon Sep 17 00:00:00 2001 From: imotai Date: Mon, 3 Jul 2023 21:58:24 +0800 Subject: [PATCH] Feat/java sdk (#538) * feat: add doc store * feat: add java sdk * feat: add java test * fix: update pom group id * feat: add add doc * feat: add add doc * fix: update readme * feat: add doc query * fix: remove the main * feat: add usage * feat: remove the english * feat: add Readme --- .github/workflows/ci.yml | 3 + README.md | 14 +- java/.gitignore | 38 ++++++ java/README.md | 37 ++++++ java/pom.xml | 118 +++++++++++++++++ .../java/network/db3/client/AddDocResult.java | 19 +++ .../main/java/network/db3/client/Client.java | 120 ++++++++++++++++++ .../db3/client/CreateCollectonResult.java | 13 ++ .../network/db3/client/CreateDBResult.java | 19 +++ .../db3/provider/EIP712TypedMessage.java | 46 +++++++ .../network/db3/provider/IndexProvider.java | 20 +++ .../network/db3/provider/StorageProvider.java | 73 +++++++++++ .../network/db3/provider/TypedMessage.java | 23 ++++ .../java/network/db3/store/ResultSet.java | 26 ++++ .../java/network/db3/provider/ClientTest.java | 90 +++++++++++++ .../db3/provider/StorageProviderTest.java | 42 ++++++ java/src/test/resources/typed_data.json | 57 +++++++++ src/node/src/mutation_utils.rs | 16 ++- src/node/src/storage_node_light_impl.rs | 1 - 19 files changed, 764 insertions(+), 11 deletions(-) create mode 100644 java/.gitignore create mode 100644 java/README.md create mode 100644 java/pom.xml create mode 100644 java/src/main/java/network/db3/client/AddDocResult.java create mode 100644 java/src/main/java/network/db3/client/Client.java create mode 100644 java/src/main/java/network/db3/client/CreateCollectonResult.java create mode 100644 java/src/main/java/network/db3/client/CreateDBResult.java create mode 100644 java/src/main/java/network/db3/provider/EIP712TypedMessage.java create mode 100644 java/src/main/java/network/db3/provider/IndexProvider.java create mode 100644 java/src/main/java/network/db3/provider/StorageProvider.java create mode 100644 java/src/main/java/network/db3/provider/TypedMessage.java create mode 100644 java/src/main/java/network/db3/store/ResultSet.java create mode 100644 java/src/test/java/network/db3/provider/ClientTest.java create mode 100644 java/src/test/java/network/db3/provider/StorageProviderTest.java create mode 100644 java/src/test/resources/typed_data.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4421b98..5fd529c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,9 @@ jobs: ROOT_DIR=`pwd` cd $ROOT_DIR/sdk && yarn && make cd $ROOT_DIR/sdk && yarn test --coverage + - name: Rust java sdk test + run: | + cd java && mvn test - uses: codecov/codecov-action@v3 with: token: ${{secrets.COV_TOKEN}} diff --git a/README.md b/README.md index b8104ee1..723f2e34 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ ![GitHub release (latest by date)](https://img.shields.io/github/v/release/dbpunk-labs/db3?color=green&display_name=tag&label=db3&logo=db3&logoColor=https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F102341693%3Fs%3D96%26v%3D4&style=flat-square)[![Twitter Follow](https://img.shields.io/twitter/follow/Db3Network?style=flat-square)](https://twitter.com/Db3Network) [![GitPOAP Badge](https://public-api.gitpoap.io/v1/repo/dbpunk-labs/db3/badge)](https://www.gitpoap.io/gh/dbpunk-labs/db3) [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.gg/sz3bmZx2uh) +![Java SDK](https://img.shields.io/maven-central/v/network.db3/sdk) +![npm](https://img.shields.io/npm/dw/db3.js?style=flat-square) -**English** # DB3 Network @@ -83,7 +84,7 @@ if you have any questions, please feel free to ask us for help and you can find # Try Our Cloud Sandbox -* [Console](https://console.cloud.db3.network/console/home) +* [Console](https://console.cloud.db3.network/console/home):https://console.cloud.db3.network/console/home * Data Rollup Node: https://rollup.cloud.db3.network * Data Index Node: https://index.cloud.db3.network @@ -106,24 +107,19 @@ JSON document must be signed by its owner. Only the owner can update or delete t # What can we build with the db3 network? -1. A fully on-chain game. -2. A fully on-chain social network. - -All user data will be stored permanently on the DB3 network. +Now building a fully on-chain application is an easy thing. You can create a fully on-chain game or a fully on-chain social network, or any other application you desire. # FAQ Q: Is the DB3 Network a blockchain? - A: No, the DB3 Network is not a blockchain. It is simply a developer tool that can be hosted locally or used through our cloud service. Q: What are the differences between MongoDB and DB3 Network? - A: MongoDB uses centralized data storage, whereas DB3 Network uses decentralized data storage. Additionally, DB3 Network ensures that data is permanently available. Q: Will my data be lost if the Data Rollup Node or Data Index Node is not available? - A: No, you can set up your data index node and recover your data from the blockchain. + # License Apache License, Version 2.0 diff --git a/java/.gitignore b/java/.gitignore new file mode 100644 index 00000000..5ff6309b --- /dev/null +++ b/java/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/java/README.md b/java/README.md new file mode 100644 index 00000000..e38483d8 --- /dev/null +++ b/java/README.md @@ -0,0 +1,37 @@ +# Quick Started + +the java sdk is under active development, please feel free to ask for help if you have some problems with it. + +## DB3 SDK + +```xml + + network.db3 + sdk + 1.0-SNAPSHOT + +``` + +## How to use + +```java +// from web3j +ECKeyPair keyPair = Keys.createEcKeyPair(); +Client client = new Client("https://rollup.cloud.db3.network", "https://index.cloud.db3.network", keyPair); +// update the nonce for the first time +client.updateNonce(); +String db = "0x6ef32f0d8fc6bc872ffa977eb80920a0a75d0206"; +String col = "book"; +String doc = """{ +"name":"The Three-Body Problem", +"author":"Cixin-Liu", +"rate":"4.8" +}"""; +AddDocResult addDocResult = client.addDoc(db, col, doc); +ResultSet resultSet = client.runQuery(db, col, """/[author=Cixin-Liu]"""); +``` +you can the the db3 [console](https://console.cloud.db3.network/console/database/) to create a database + + + + diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 00000000..99c7d0e0 --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + network.db3 + sdk + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + + + + + org.slf4j + slf4j-api + 2.0.7 + + + + io.grpc + grpc-all + 1.56.1 + + + + org.mongodb + bson + 4.9.1 + + + + org.web3j + core + 4.9.8 + + + org.web3j + crypto + 4.9.8 + + + commons-codec + commons-codec + 1.15 + + + com.google.code.gson + gson + 2.10 + + + + javax.annotation + javax.annotation-api + 1.2 + compile + + + + + + + + + kr.motd.maven + os-maven-plugin + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + com.google.protobuf:protoc:3.21.10:exe:${os.detected.classifier} + + grpc-java + + io.grpc:protoc-gen-grpc-java:1.51.0:exe:${os.detected.classifier} + + + ${basedir}/../src/proto/proto + + + + + + compile + compile-custom + + + + + + + + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + \ No newline at end of file diff --git a/java/src/main/java/network/db3/client/AddDocResult.java b/java/src/main/java/network/db3/client/AddDocResult.java new file mode 100644 index 00000000..912d57c1 --- /dev/null +++ b/java/src/main/java/network/db3/client/AddDocResult.java @@ -0,0 +1,19 @@ +package network.db3.client; + +public class AddDocResult { + private String mutationId; + private long docId; + + public AddDocResult(String mutationId, long docId) { + this.mutationId = mutationId; + this.docId = docId; + } + + public long getDocId() { + return docId; + } + + public String getMutationId() { + return mutationId; + } +} diff --git a/java/src/main/java/network/db3/client/Client.java b/java/src/main/java/network/db3/client/Client.java new file mode 100644 index 00000000..ddebdc8e --- /dev/null +++ b/java/src/main/java/network/db3/client/Client.java @@ -0,0 +1,120 @@ +package network.db3.client; + +import com.google.protobuf.ByteString; +import db3_database_v2_proto.Db3DatabaseV2; +import db3_indexer_proto.Db3Indexer; +import db3_indexer_proto.IndexerNodeGrpc; +import db3_mutation_v2_proto.Db3MutationV2; +import db3_storage_proto.Db3Storage; +import db3_storage_proto.StorageNodeGrpc; +import io.grpc.Grpc; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.TlsChannelCredentials; +import io.grpc.netty.GrpcSslContexts; +import io.netty.handler.ssl.SslContext; +import network.db3.provider.IndexProvider; +import network.db3.provider.StorageProvider; +import network.db3.store.ResultSet; +import org.bson.ByteBuf; +import org.bson.RawBsonDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Keys; +import org.web3j.utils.Numeric; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +public class Client { + private static final Logger logger = LoggerFactory.getLogger(Client.class); + private final ECKeyPair keyPair; + private final StorageProvider storageProvider; + private final IndexProvider indexProvider; + private final AtomicLong nonce; + + public Client(String rollupUrl, String indexUrl, + ECKeyPair keyPair) throws MalformedURLException { + ManagedChannel rollupChannel = Client.buildFrom(rollupUrl); + ManagedChannel indexChannel = Client.buildFrom(indexUrl); + StorageNodeGrpc.StorageNodeBlockingStub rollupStub = StorageNodeGrpc.newBlockingStub(rollupChannel); + IndexerNodeGrpc.IndexerNodeBlockingStub indexStub = IndexerNodeGrpc.newBlockingStub(indexChannel); + this.storageProvider = new StorageProvider(rollupStub, keyPair); + this.indexProvider = new IndexProvider(indexStub); + this.keyPair = keyPair; + this.nonce = new AtomicLong(0); + } + + private static ManagedChannel buildFrom(String url) throws MalformedURLException { + URL uri = new URL(url); + if (uri.getProtocol().equals("https")){ + TlsChannelCredentials.Builder tlsBuilder = TlsChannelCredentials.newBuilder(); + return Grpc.newChannelBuilderForAddress(uri.getHost(), uri.getPort() == 0 ? 443: uri.getPort(), + tlsBuilder.build()).build(); + } else { + return ManagedChannelBuilder.forTarget(uri.getHost() + ":" + uri.getPort()).usePlaintext().build(); + } + } + + public void updateNonce() { + long nonce = this.storageProvider.getNonce(Keys.getAddress(keyPair)); + this.nonce.set(nonce); + logger.info("the new nonce {} for address {}", nonce, Keys.getAddress(keyPair)); + } + + public CreateDBResult createDocDatabase(String desc) throws IOException { + Db3MutationV2.DocumentDatabaseMutation docMutation = Db3MutationV2.DocumentDatabaseMutation.newBuilder().setDbDesc(desc).build(); + Db3MutationV2.Mutation.BodyWrapper body = Db3MutationV2.Mutation.BodyWrapper.newBuilder().setDocDatabaseMutation(docMutation).setDbAddress(ByteString.copyFromUtf8("")).build(); + Db3MutationV2.Mutation mutation = Db3MutationV2.Mutation.newBuilder().setAction(Db3MutationV2.MutationAction.CreateDocumentDB).addBodies(body).build(); + byte[] data = mutation.toByteArray(); + long nonce = this.nonce.getAndIncrement(); + Db3Storage.SendMutationResponse response = this.storageProvider.sendMutation(data, nonce); + return new CreateDBResult(response.getId(), response.getItems(0).getValue()); + } + + public Db3DatabaseV2.DatabaseMessage getDatabase(String db) { + Db3Storage.GetDatabaseResponse response = this.storageProvider.getDatabase(db); + return response.getDatabase(); + } + + public CreateCollectonResult createCollection(String db, String col) throws IOException { + byte[] address = Numeric.hexStringToByteArray(db); + Db3MutationV2.CollectionMutation collectionMutation = Db3MutationV2.CollectionMutation.newBuilder().setCollectionName(col).build(); + Db3MutationV2.Mutation.BodyWrapper body = Db3MutationV2.Mutation.BodyWrapper.newBuilder().setCollectionMutation(collectionMutation).setDbAddress(ByteString.copyFrom(address)).build(); + Db3MutationV2.Mutation mutation = Db3MutationV2.Mutation.newBuilder().setAction(Db3MutationV2.MutationAction.AddCollection).addBodies(body).build(); + byte[] data = mutation.toByteArray(); + long nonce = this.nonce.getAndIncrement(); + Db3Storage.SendMutationResponse response = this.storageProvider.sendMutation(data, nonce); + return new CreateCollectonResult(response.getId()); + } + + public Optional getCollection(String db, String col) { + return this.storageProvider.getCollection(db, col); + } + + public AddDocResult addDoc(String db, String col, String json) throws IOException { + RawBsonDocument rawBsonDocument = RawBsonDocument.parse(json); + ByteBuf buf = rawBsonDocument.getByteBuffer(); + byte[] data = new byte[buf.remaining()]; + buf.get(data); + Db3MutationV2.DocumentMutation documentMutation = Db3MutationV2.DocumentMutation.newBuilder().addDocuments(ByteString.copyFrom(data)).setCollectionName(col).build(); + Db3MutationV2.Mutation.BodyWrapper body = Db3MutationV2.Mutation.BodyWrapper.newBuilder().setDbAddress(ByteString.copyFrom(Numeric.hexStringToByteArray(db))).setDocumentMutation(documentMutation).build(); + Db3MutationV2.Mutation mutation = Db3MutationV2.Mutation.newBuilder().setAction(Db3MutationV2.MutationAction.AddDocument).addBodies(body).build(); + byte[] buffer = mutation.toByteArray(); + long nonce = this.nonce.getAndIncrement(); + Db3Storage.SendMutationResponse response = this.storageProvider.sendMutation(buffer, nonce); + return new AddDocResult(response.getId(), Long.parseLong(response.getItems(0).getValue())); + } + + public ResultSet runQuery(String db, String col, String query) { + Db3Indexer.RunQueryResponse response = this.indexProvider.runQuery(db, col, query); + ResultSet resultSet = new ResultSet(); + resultSet.setDocs(response.getDocumentsList()); + resultSet.setCount(response.getCount()); + return resultSet; + } +} diff --git a/java/src/main/java/network/db3/client/CreateCollectonResult.java b/java/src/main/java/network/db3/client/CreateCollectonResult.java new file mode 100644 index 00000000..cf541468 --- /dev/null +++ b/java/src/main/java/network/db3/client/CreateCollectonResult.java @@ -0,0 +1,13 @@ +package network.db3.client; + +public class CreateCollectonResult { + private String mutationId; + + public CreateCollectonResult(String mutationId) { + this.mutationId = mutationId; + } + + public String getMutationId() { + return mutationId; + } +} diff --git a/java/src/main/java/network/db3/client/CreateDBResult.java b/java/src/main/java/network/db3/client/CreateDBResult.java new file mode 100644 index 00000000..0e219a48 --- /dev/null +++ b/java/src/main/java/network/db3/client/CreateDBResult.java @@ -0,0 +1,19 @@ +package network.db3.client; + +public class CreateDBResult { + private String mutationId; + private String db; + + public CreateDBResult(String mutationId, String db) { + this.mutationId = mutationId; + this.db = db; + } + + public String getMutationId() { + return mutationId; + } + + public String getDb() { + return db; + } +} diff --git a/java/src/main/java/network/db3/provider/EIP712TypedMessage.java b/java/src/main/java/network/db3/provider/EIP712TypedMessage.java new file mode 100644 index 00000000..7fb9cd00 --- /dev/null +++ b/java/src/main/java/network/db3/provider/EIP712TypedMessage.java @@ -0,0 +1,46 @@ +package network.db3.provider; + +import org.web3j.crypto.StructuredData; + +import java.util.*; + +public class EIP712TypedMessage { + private final HashMap> types; + private final String primaryType; + private final TypedMessage message; + private final Map domain = new HashMap<>(); + + public EIP712TypedMessage(TypedMessage message) { + this.types = new LinkedHashMap<>(); + this.types.put("EIP712Domain", + Arrays.asList( + new StructuredData.Entry("name", "string") + ) + ); + this.types.put("Message", + Arrays.asList( + new StructuredData.Entry("payload", "bytes"), + new StructuredData.Entry("nonce", "string") + ) + ); + this.primaryType = "Message"; + this.message = message; + this.domain.put("name", "db3.network"); + } + + public HashMap> getTypes() { + return types; + } + + public String getPrimaryType() { + return primaryType; + } + + public TypedMessage getMessage() { + return message; + } + + public Map getDomain() { + return domain; + } +} \ No newline at end of file diff --git a/java/src/main/java/network/db3/provider/IndexProvider.java b/java/src/main/java/network/db3/provider/IndexProvider.java new file mode 100644 index 00000000..bae06b75 --- /dev/null +++ b/java/src/main/java/network/db3/provider/IndexProvider.java @@ -0,0 +1,20 @@ +package network.db3.provider; + +import db3_database_v2_proto.Db3DatabaseV2; +import db3_indexer_proto.Db3Indexer; +import db3_indexer_proto.IndexerNodeGrpc; + +public class IndexProvider { + private final IndexerNodeGrpc.IndexerNodeBlockingStub stub; + + public IndexProvider(IndexerNodeGrpc.IndexerNodeBlockingStub stub) { + this.stub = stub; + } + + public Db3Indexer.RunQueryResponse runQuery(String db, String col, String query) { + Db3DatabaseV2.Query db3Query = Db3DatabaseV2.Query.newBuilder().setQueryStr(query).build(); + Db3Indexer.RunQueryRequest request = Db3Indexer.RunQueryRequest.newBuilder().setDb(db).setColName(col).setQuery(db3Query).build(); + return stub.runQuery(request); + } + +} diff --git a/java/src/main/java/network/db3/provider/StorageProvider.java b/java/src/main/java/network/db3/provider/StorageProvider.java new file mode 100644 index 00000000..617cf485 --- /dev/null +++ b/java/src/main/java/network/db3/provider/StorageProvider.java @@ -0,0 +1,73 @@ +package network.db3.provider; + +import com.google.gson.Gson; +import com.google.protobuf.ByteString; +import db3_database_v2_proto.Db3DatabaseV2; +import db3_storage_proto.Db3Storage; +import db3_storage_proto.StorageNodeGrpc; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Sign; +import org.web3j.utils.Numeric; + +import java.io.IOException; +import java.util.Optional; +import java.util.function.Predicate; + +public class StorageProvider { + private final StorageNodeGrpc.StorageNodeBlockingStub stub; + private final ECKeyPair keyPair; + private final Gson gson = new Gson(); + + public StorageProvider(StorageNodeGrpc.StorageNodeBlockingStub stub, + ECKeyPair keyPair) { + this.stub = stub; + this.keyPair = keyPair; + } + + public Db3Storage.SendMutationResponse sendMutation(byte[] mutation, long nonce) throws IOException { + EIP712TypedMessage message = wrapTypedRequest(mutation, nonce); + String strMessage = gson.toJson(message); + Sign.SignatureData sig = Sign.signTypedData(strMessage, keyPair); + byte[] retrieval = new byte[65]; + System.arraycopy(sig.getR(), 0, retrieval, 0, 32); + System.arraycopy(sig.getS(), 0, retrieval, 32, 32); + System.arraycopy(sig.getV(), 0, retrieval, 64, 1); + String signedMessage = Numeric.toHexString(retrieval); + Db3Storage.SendMutationRequest.Builder requestBuilder = Db3Storage.SendMutationRequest.newBuilder(); + requestBuilder.setPayload(ByteString.copyFromUtf8(strMessage)); + requestBuilder.setSignature(signedMessage); + Db3Storage.SendMutationRequest request = requestBuilder.build(); + return stub.sendMutation(request); + } + + public long getNonce(String address) { + Db3Storage.GetNonceRequest request = Db3Storage.GetNonceRequest.newBuilder().setAddress(address).build(); + Db3Storage.GetNonceResponse response = stub.getNonce(request); + return response.getNonce(); + } + + public Db3Storage.GetDatabaseResponse getDatabase(String address) { + Db3Storage.GetDatabaseRequest request = Db3Storage.GetDatabaseRequest.newBuilder().setAddr(address).build(); + return stub.getDatabase(request); + } + + public Optional getCollection(String address, String col) { + Db3Storage.GetCollectionOfDatabaseRequest request = Db3Storage.GetCollectionOfDatabaseRequest.newBuilder().setDbAddr(address).build(); + Db3Storage.GetCollectionOfDatabaseResponse response = stub.getCollectionOfDatabase(request); + return response.getCollectionsList().stream().filter(new Predicate() { + @Override + public boolean test(Db3DatabaseV2.Collection collection) { + if (collection.getName().equals(col)) return true; + return false; + } + }).findFirst(); + } + + private EIP712TypedMessage wrapTypedRequest(byte[] mutation, long nonce) { + String payload = Numeric.toHexString(mutation); + TypedMessage message = new TypedMessage(); + message.setPayload(payload); + message.setNonce(String.valueOf(nonce)); + return new EIP712TypedMessage(message); + } +} diff --git a/java/src/main/java/network/db3/provider/TypedMessage.java b/java/src/main/java/network/db3/provider/TypedMessage.java new file mode 100644 index 00000000..72ab39e5 --- /dev/null +++ b/java/src/main/java/network/db3/provider/TypedMessage.java @@ -0,0 +1,23 @@ +package network.db3.provider; + +public class TypedMessage { + private String payload; + + private String nonce; + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } +} diff --git a/java/src/main/java/network/db3/store/ResultSet.java b/java/src/main/java/network/db3/store/ResultSet.java new file mode 100644 index 00000000..8485dc7a --- /dev/null +++ b/java/src/main/java/network/db3/store/ResultSet.java @@ -0,0 +1,26 @@ +package network.db3.store; + +import db3_database_v2_proto.Db3DatabaseV2; + +import java.util.List; + +public class ResultSet { + private List docs; + private long count; + + public List getDocs() { + return docs; + } + + public void setDocs(List docs) { + this.docs = docs; + } + + public long getCount() { + return count; + } + + public void setCount(long count) { + this.count = count; + } +} diff --git a/java/src/test/java/network/db3/provider/ClientTest.java b/java/src/test/java/network/db3/provider/ClientTest.java new file mode 100644 index 00000000..28c5dfa0 --- /dev/null +++ b/java/src/test/java/network/db3/provider/ClientTest.java @@ -0,0 +1,90 @@ +package network.db3.provider; + +import db3_database_v2_proto.Db3DatabaseV2; +import network.db3.client.AddDocResult; +import network.db3.client.Client; +import network.db3.client.CreateCollectonResult; +import network.db3.client.CreateDBResult; +import network.db3.store.ResultSet; +import org.junit.Assert; +import org.junit.Test; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Keys; +import org.web3j.utils.Numeric; + +import java.util.Optional; + +public class ClientTest { + + private Client buildRandClient() throws Exception { + ECKeyPair keyPair = Keys.createEcKeyPair(); + System.out.println(Keys.getAddress(keyPair)); + return new Client("http://127.0.0.1:26619", "http://127.0.0.1:26639", keyPair); + } + + private Client buildCloudClient() throws Exception{ + ECKeyPair keyPair = ECKeyPair.create(Numeric.hexStringToByteArray("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")); + return new Client("https://rollup.cloud.db3.network", "https://index.cloud.db3.network", keyPair); + } + + private Client buildFromPrivateKey() throws Exception { + ECKeyPair keyPair = ECKeyPair.create(Numeric.hexStringToByteArray("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")); + return new Client("http://127.0.0.1:26619", "http://127.0.0.1:26639", keyPair); + } + + @Test + public void testCreateDB() { + try { + Client client = buildRandClient(); + client.updateNonce(); + CreateDBResult result = client.createDocDatabase("desc"); + Db3DatabaseV2.DatabaseMessage database = client.getDatabase(result.getDb()); + String address = Numeric.toHexString(database.getDocDb().getAddress().toByteArray()); + Assert.assertEquals(result.getDb(), address); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test + public void testDBNotExist() { + try { + String address = "0x0"; + Client client = buildRandClient(); + Db3DatabaseV2.DatabaseMessage database = client.getDatabase(address); + Assert.fail(); + } catch (Exception e) { + } + } + + @Test + public void testAddCollection() { + try { + Client client = buildRandClient(); + client.updateNonce(); + CreateDBResult result = client.createDocDatabase("desc"); + Db3DatabaseV2.DatabaseMessage database = client.getDatabase(result.getDb()); + String address = Numeric.toHexString(database.getDocDb().getAddress().toByteArray()); + Assert.assertEquals(result.getDb(), address); + CreateCollectonResult result1 = client.createCollection(result.getDb(), "col1"); + Assert.assertNotNull(result1.getMutationId()); + Optional collection = client.getCollection(result.getDb(), "col1"); + Assert.assertEquals(collection.isPresent(), true); + Assert.assertEquals(collection.get().getName(), "col1"); + String doc = """{ + "name":"a"} + + """; + AddDocResult addDocResult = client.addDoc(result.getDb(), "col1", doc); + Assert.assertNotNull(addDocResult.getMutationId()); + Thread.sleep(1000 * 3); + ResultSet resultSet = client.runQuery(result.getDb(), "col1", "/[name=\"a\"]"); + Assert.assertEquals(1, resultSet.getDocs().size()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + +} diff --git a/java/src/test/java/network/db3/provider/StorageProviderTest.java b/java/src/test/java/network/db3/provider/StorageProviderTest.java new file mode 100644 index 00000000..c5bcdf9e --- /dev/null +++ b/java/src/test/java/network/db3/provider/StorageProviderTest.java @@ -0,0 +1,42 @@ +package network.db3.provider; + +import com.google.protobuf.ByteString; +import db3_mutation_v2_proto.Db3MutationV2; +import db3_storage_proto.Db3Storage; +import db3_storage_proto.StorageNodeGrpc; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Assert; +import org.junit.Test; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Keys; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + + +public class StorageProviderTest { + + @Test + public void testSendMutation() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { + ManagedChannel mchannel = ManagedChannelBuilder.forTarget("127.0.0.1:26619").usePlaintext().build(); + ECKeyPair keyPair = Keys.createEcKeyPair(); + byte[] privateKey = Hex.decode("ad689d9b7751da07b0fb39c5091672cbfe50f59131db015f8a0e76c9790a6fcc"); + StorageNodeGrpc.StorageNodeBlockingStub stub = StorageNodeGrpc.newBlockingStub(mchannel); + StorageProvider provider = new StorageProvider(stub, keyPair); + Db3MutationV2.DocumentDatabaseMutation docMutation = Db3MutationV2.DocumentDatabaseMutation.newBuilder().setDbDesc("desc").build(); + Db3MutationV2.Mutation.BodyWrapper body = Db3MutationV2.Mutation.BodyWrapper.newBuilder().setDocDatabaseMutation(docMutation).setDbAddress(ByteString.copyFromUtf8("")).build(); + Db3MutationV2.Mutation mutation = Db3MutationV2.Mutation.newBuilder().setAction(Db3MutationV2.MutationAction.CreateDocumentDB).addBodies(body).build(); + byte[] data = mutation.toByteArray(); + try { + long nonce = provider.getNonce(Keys.getAddress(keyPair)) + 1; + Db3Storage.SendMutationResponse response = provider.sendMutation(data, nonce); + Assert.assertNotNull(response.getId()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } +} diff --git a/java/src/test/resources/typed_data.json b/java/src/test/resources/typed_data.json new file mode 100644 index 00000000..5c9c3142 --- /dev/null +++ b/java/src/test/resources/typed_data.json @@ -0,0 +1,57 @@ +{ + "domain": {}, + "types": { + "EIP712Domain": [], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + }, + { + "name": "replyTo", + "type": "Mail" + } + ] + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!", + "replyTo": { + "to": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "from": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello!" + } + } +} \ No newline at end of file diff --git a/src/node/src/mutation_utils.rs b/src/node/src/mutation_utils.rs index e7a7c0dc..8a54c7d6 100644 --- a/src/node/src/mutation_utils.rs +++ b/src/node/src/mutation_utils.rs @@ -15,7 +15,7 @@ use ethers::types::{ }; use prost::Message; use std::str::FromStr; -use tracing::warn; +use tracing::{info, warn}; /// parse mutation macro_rules! parse_mutation { @@ -238,4 +238,18 @@ mod tests { let (_, payload_type, _) = MutationUtil::unwrap_and_verify(request).unwrap(); assert_eq!(PayloadType::DatabasePayload, payload_type); } + #[test] + pub fn test_java_sdk_verfiy_ut() { + //let expected_addr = "f39fd6e51aad88f6f4ce6ab8827279cfffb92266"; + let typed_data = r#" + {"types":{"EIP712Domain":[{"name":"name","type":"string"}],"Message":[{"name":"payload","type":"bytes"},{"name":"nonce","type":"string"}]},"primaryType":"Message","message":{"payload":"0x1a0822060a0464657363","nonce":"1"},"domain":{"name":"db3.network"}} + "#; + let typed_data_obj = serde_json::from_slice::(typed_data.as_bytes()).unwrap(); + let hashed_message = typed_data_obj.encode_eip712().unwrap(); + let hex_str = hex::encode(hashed_message); + assert_eq!( + "2b6ab2777e1ffb472f2f3206566f0cb691228ba5fb02692fd8fe933576b5003e", + hex_str.as_str() + ); + } } diff --git a/src/node/src/storage_node_light_impl.rs b/src/node/src/storage_node_light_impl.rs index c5042573..ca1be763 100644 --- a/src/node/src/storage_node_light_impl.rs +++ b/src/node/src/storage_node_light_impl.rs @@ -626,7 +626,6 @@ impl StorageNode for StorageNodeV2Impl { request: Request, ) -> std::result::Result, Status> { let r = request.into_inner(); - // validate the signature let (dm, address, nonce) = MutationUtil::unwrap_and_light_verify( &r.payload, r.signature.as_str(),