diff --git a/build.gradle b/build.gradle index 5ad1c24..008b61e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,8 @@ buildscript { ext.kotlin_version = '1.4.32' ext.dashj_version = '19.1-CJ-SNAPSHOT' - ext.dpp_version = '0.24-SNAPSHOT' - ext.dapi_client_version = '0.24-SNAPSHOT' + ext.dpp_version = '0.25-SNAPSHOT' + ext.dapi_client_version = '0.25-SNAPSHOT' repositories { mavenLocal() mavenCentral() @@ -75,6 +75,7 @@ dependencies { implementation "org.dashj:dashj-core:$dashj_version" implementation "org.dashj:dashj-bls:1.0.0" implementation "org.dashj:dashj-merk:0.22-SNAPSHOT" + implementation 'org.dashj:manual:0.25-SNAPSHOT' implementation 'com.squareup.retrofit2:retrofit:2.6.4' implementation 'com.squareup.retrofit2:converter-gson:2.6.4' implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0' @@ -95,6 +96,9 @@ dependencies { exclude module: "protobuf-lite" } implementation 'io.grpc:grpc-stub:1.54.0' // CURRENT_GRPC_VERSION + implementation 'io.grpc:grpc-netty-shaded:1.54.0' // replace with the latest version + implementation 'io.netty:netty-tcnative-boringssl-static:2.0.46.Final' // replace with the latest version + compileOnly 'javax.annotation:javax.annotation-api:1.3.2' implementation 'org.slf4j:slf4j-api:1.7.30' implementation 'org.json:json:20190722' diff --git a/src/main/jniLibs/libdashj_dpp_bindings.dylib b/src/main/jniLibs/libdashj_dpp_bindings.dylib new file mode 100755 index 0000000..b49decf Binary files /dev/null and b/src/main/jniLibs/libdashj_dpp_bindings.dylib differ diff --git a/src/main/kotlin/org/dashj/platform/dapiclient/DapiClient.kt b/src/main/kotlin/org/dashj/platform/dapiclient/DapiClient.kt index f87c26a..4818256 100644 --- a/src/main/kotlin/org/dashj/platform/dapiclient/DapiClient.kt +++ b/src/main/kotlin/org/dashj/platform/dapiclient/DapiClient.kt @@ -19,6 +19,7 @@ import org.bitcoinj.core.BloomFilter import org.bitcoinj.core.Sha256Hash import org.bitcoinj.core.Utils import org.bitcoinj.evolution.SimplifiedMasternodeListManager +import org.bitcoinj.params.DevNetParams import org.bitcoinj.quorums.LLMQParameters import org.dash.platform.dapi.v0.CoreOuterClass import org.dash.platform.dapi.v0.PlatformOuterClass @@ -375,8 +376,7 @@ class DapiClient( when { waitForResult == null -> { logger.info("broadcastStateTransitionAndWait: failure: Timeout or no proof returned") - // TODO: uncomment the next line when proofs are enabled - // throw StateTransitionBroadcastException(2, "Timeout", ByteArray(0)) + throw StateTransitionBroadcastException(2, "Timeout", ByteArray(0)) // TODO: remove this line when proofs are enabled logger.info("broadcastStateTransitionAndWait: success ($successRate)") @@ -479,6 +479,9 @@ class DapiClient( throw NotFoundException("Identity $identityId does not exist in the proof") } } + response.identity.isEmpty -> { + return throw NotFoundException("Identity $identityId does not exist") + } else -> { return GetIdentityResponse(response) } @@ -595,7 +598,7 @@ class DapiClient( val method = GetContractMethod(contractIdByteArray, prove) val response = grpcRequest(method, retryCallback = retryCallback) as PlatformOuterClass.GetDataContractResponse? return when { - response == null -> { + response == null || response.dataContract.isEmpty -> { throw NotFoundException("DataContract ${Identifier.from(contractId)} does not exist") } prove && response.hasProof() -> { @@ -935,7 +938,8 @@ class DapiClient( retriesLeft // if called recursively } val address = dapiAddress ?: dapiAddressListProvider.getLiveAddress() - val grpcMasternode = DAPIGrpcMasternode(address, timeOut) + val allowSelfSignedCertificate = dpp.params is DevNetParams + val grpcMasternode = DAPIGrpcMasternode(address, timeOut, allowSelfSignedCertificate) lastUsedAddress = address logger.info( @@ -1046,6 +1050,7 @@ class DapiClient( e.status.code != Status.INTERNAL.code && e.status.code != Status.CANCELLED.code && e.status.code != Status.UNKNOWN.code && + e.status.code != Status.ALREADY_EXISTS.code && // ST was already submitted e.status.code != Status.UNIMPLEMENTED.code // perhaps we contacted an old node ) { throw e diff --git a/src/main/kotlin/org/dashj/platform/dapiclient/grpc/DefaultBroadcastRetryCallback.kt b/src/main/kotlin/org/dashj/platform/dapiclient/grpc/DefaultBroadcastRetryCallback.kt index f66d4ac..b87735a 100644 --- a/src/main/kotlin/org/dashj/platform/dapiclient/grpc/DefaultBroadcastRetryCallback.kt +++ b/src/main/kotlin/org/dashj/platform/dapiclient/grpc/DefaultBroadcastRetryCallback.kt @@ -102,6 +102,7 @@ open class BroadcastRetryCallback( when (e.status.code) { Status.UNAUTHENTICATED.code -> return false Status.FAILED_PRECONDITION.code -> return false + Status.ALREADY_EXISTS.code -> return false } if (grpcMethod is BroadcastStateTransitionMethod) { if (e.status.code == Status.INVALID_ARGUMENT.code) { diff --git a/src/main/kotlin/org/dashj/platform/dapiclient/model/DocumentQuery.kt b/src/main/kotlin/org/dashj/platform/dapiclient/model/DocumentQuery.kt index 788aadf..37b9f2c 100644 --- a/src/main/kotlin/org/dashj/platform/dapiclient/model/DocumentQuery.kt +++ b/src/main/kotlin/org/dashj/platform/dapiclient/model/DocumentQuery.kt @@ -39,7 +39,7 @@ class DocumentQuery private constructor( } data class Builder( - private var where: MutableList>? = null, + private var where: MutableList>? = mutableListOf(), private var orderBy: MutableList>? = null, private var limit: Int = -1, private var startAt: Identifier? = null, diff --git a/src/main/kotlin/org/dashj/platform/dapiclient/provider/DAPIGrpcMasternode.kt b/src/main/kotlin/org/dashj/platform/dapiclient/provider/DAPIGrpcMasternode.kt index f895bc8..408093e 100644 --- a/src/main/kotlin/org/dashj/platform/dapiclient/provider/DAPIGrpcMasternode.kt +++ b/src/main/kotlin/org/dashj/platform/dapiclient/provider/DAPIGrpcMasternode.kt @@ -9,12 +9,15 @@ package org.dashj.platform.dapiclient.provider import com.google.common.base.Stopwatch import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder +import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts +import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder +import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory import org.dash.platform.dapi.v0.CoreGrpc import org.dash.platform.dapi.v0.PlatformGrpc import org.slf4j.LoggerFactory import java.util.concurrent.TimeUnit -class DAPIGrpcMasternode(address: DAPIAddress, val timeout: Long) : DAPIMasternode(address) { +class DAPIGrpcMasternode(address: DAPIAddress, val timeout: Long, val allowSelfSignedCertificate: Boolean) : DAPIMasternode(address) { // gRPC properties private lateinit var channel: ManagedChannel val platform: PlatformGrpc.PlatformBlockingStub by lazy { @@ -41,9 +44,19 @@ class DAPIGrpcMasternode(address: DAPIAddress, val timeout: Long) : DAPIMasterno init { val watch = Stopwatch.createStarted() - channel = ManagedChannelBuilder.forAddress(address.host, address.grpcPort) - .useTransportSecurity() - .build() + if (!allowSelfSignedCertificate) { + channel = ManagedChannelBuilder.forAddress(address.host, address.grpcPort) + .useTransportSecurity() + .build() + } else { + channel = NettyChannelBuilder.forAddress(address.host, address.grpcPort) + .sslContext( + GrpcSslContexts.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) // a trust manager that trusts all certificates + .build() + ) + .build() + } logger.debug("Connecting to GRPC host: ${address.host}:${address.grpcPort} (time: $watch)") } diff --git a/src/test/kotlin/org/dashj/platform/dapiclient/DapiClientTest.kt b/src/test/kotlin/org/dashj/platform/dapiclient/DapiClientTest.kt index 52eba3c..a214b92 100644 --- a/src/test/kotlin/org/dashj/platform/dapiclient/DapiClientTest.kt +++ b/src/test/kotlin/org/dashj/platform/dapiclient/DapiClientTest.kt @@ -6,13 +6,14 @@ */ package org.dashj.platform.dapiclient +import org.bitcoinj.params.AbsintheDevNetParams import org.bitcoinj.params.TestNet3Params import org.dashj.platform.dpp.DashPlatformProtocol import org.junit.jupiter.api.Test class DapiClientTest : BaseTest() { - val PARAMS = TestNet3Params.get() + val PARAMS = AbsintheDevNetParams.get() val stateRepository = StateRepositoryMock() val dpp = DashPlatformProtocol(stateRepository, PARAMS) val client = DapiClient(PARAMS.defaultHPMasternodeList.toList(), dpp) diff --git a/src/test/kotlin/org/dashj/platform/dapiclient/DapiGrpcClientTest.kt b/src/test/kotlin/org/dashj/platform/dapiclient/DapiGrpcClientTest.kt index 8a8deab..26b05f6 100644 --- a/src/test/kotlin/org/dashj/platform/dapiclient/DapiGrpcClientTest.kt +++ b/src/test/kotlin/org/dashj/platform/dapiclient/DapiGrpcClientTest.kt @@ -1,5 +1,6 @@ package org.dashj.platform.dapiclient +import com.google.common.base.Converter import com.google.common.base.Stopwatch import com.hashengineering.crypto.X11 import io.grpc.Status @@ -10,8 +11,10 @@ import org.bitcoinj.core.Context import org.bitcoinj.core.ECKey import org.bitcoinj.core.Sha256Hash import org.bitcoinj.core.Utils +import org.bitcoinj.params.AbsintheDevNetParams import org.bitcoinj.params.DevNetParams import org.bitcoinj.params.TestNet3Params +import org.dashj.dpp.DPP import org.dashj.platform.dapiclient.errors.NotFoundException import org.dashj.platform.dapiclient.model.DocumentQuery import org.dashj.platform.dapiclient.provider.DAPIAddress @@ -19,19 +22,20 @@ import org.dashj.platform.dapiclient.provider.ListDAPIAddressProvider import org.dashj.platform.dpp.DashPlatformProtocol import org.dashj.platform.dpp.document.Document import org.dashj.platform.dpp.identifier.Identifier +import org.dashj.platform.dpp.identity.IdentityCreateTransition +import org.dashj.platform.dpp.identity.IdentityFactory import org.dashj.platform.dpp.toHex +import org.dashj.platform.dpp.util.Cbor +import org.dashj.platform.dpp.util.Converters import org.json.JSONObject -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Assertions.assertThrows -import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import java.io.File class DapiGrpcClientTest : BaseTest() { - val PARAMS = TestNet3Params.get() + val PARAMS = AbsintheDevNetParams.get() val CONTEXT = Context.getOrCreate(PARAMS) val masternodeList = PARAMS.defaultHPMasternodeList.toList() val dpnsContractId = SystemIds.dpnsDataContractId // DPNS contract @@ -155,7 +159,7 @@ class DapiGrpcClientTest : BaseTest() { .orderBy("normalizedLabel", true) .build() val documentsResponse = client.getDocuments(dpnsContractId.toBuffer(), "domain", query) - + assertTrue(documentsResponse.documents.isNotEmpty()) val jsonDpnsFile = File("src/test/resources/dpns-contract.json").readText() val jsonDpns = JSONObject(jsonDpnsFile) val rawContract = jsonDpns.toMap() @@ -209,6 +213,15 @@ class DapiGrpcClientTest : BaseTest() { } } + @Test + fun getIdentityTest() { + val dppA = DPP + val id = "7633TgdebkBWnBQ7peF56mxLaGSTBxuzCavHYbN6ZW8V"; + val result = client.getIdentity(Identifier.from(id).toBuffer()) + assertEquals(125, result.identity.size) + print(IdentityFactory(dpp, stateRepository).createFromBuffer(result.identity).toJSON()) + } + @Test fun getIdentityFromBadPubKeyBytes() { val key = ECKey() @@ -259,8 +272,8 @@ class DapiGrpcClientTest : BaseTest() { "profile", DocumentQuery.builder() .where("\$ownerId", "==", Identifier.from("3HSUPuMgR5qpZt1y5NbE2BBheM11yLRXKZoqdsKgxVNt")) - .where("\$updatedAt", ">", 0) - .orderBy("\$updatedAt", true) + //.where("\$updatedAt", ">", 0) + //.orderBy("\$updatedAt", true) .build() ) @@ -319,8 +332,8 @@ class DapiGrpcClientTest : BaseTest() { "profile", DocumentQuery.builder() .where("\$ownerId", "in", Identifier.from("3HSUPuMgR5qpZt1y5NbE2BBheM11yLRXKZoqdsKgxVNt")) - .where("\$updatedAt", ">", 0) - .orderBy("\$updatedAt") + //.where("\$updatedAt", ">", 0) + //.orderBy("\$updatedAt") .orderBy("\$ownerId") .build() ) @@ -334,4 +347,14 @@ class DapiGrpcClientTest : BaseTest() { .build() ) } + + @Test + fun broadcastStateTransitionTest() { + val st = "a5647479706502697369676e617475726558411ff1764ead95ff03f90b177fa138bd0b510309da5ac75b6b0498d18f4a1f55036c77d6626b9dc660b54ca9f7085fef1a9070d2694e064175c6b68cb9d947b2017e6a7075626c69634b65797382a76269640064646174615821038035e6856dd646654eb1a76dd9bd93af0e21889feb68a652fb8360974be3b6a964747970650067707572706f73650068726561644f6e6c79f4697369676e617475726558412085d4139c1f81b223beee448f92b117fd489a995424102c40b2ca4ec6cd31ce7e7b9410e2aa8a88f47f6fcf757bdd801b7f8f091c3bf803f850c64ee24e7c62e96d73656375726974794c6576656c00a762696401646461746158210396605ff4ca17f88a6294d8ce5b65d7ae797ab7ef61f7ff38acdf036cf9c61c0d64747970650067707572706f73650068726561644f6e6c79f4697369676e6174757265584120ca609cbb2f4a2fe563ffb4d093e59e3f2bb3f3bdb56cf7fe2ddf7b4e16f43d685d1a56caac7e8476adb7e2cc131db1b9a3a3fa78fa10f36724d20fb69de69d5a6d73656375726974794c6576656c026e61737365744c6f636b50726f6f66a3647479706501686f7574506f696e7458249683e679611f3c2f0bfaf7f8ee55f9312fbf059a421e36ab5d3c4a854946ca220000000075636f7265436861696e4c6f636b65644865696768741a0001edce6f70726f746f636f6c56657273696f6e01" + val map = Cbor.decode(Converters.fromHex(st)) + map["signature"] = ByteArray(65) + val icst = IdentityCreateTransition(AbsintheDevNetParams.get(), map) + assertTrue(DPP.validateIdentityCreateTransition(icst.toObject())) + client.broadcastStateTransitionInternal(icst, false) + } } diff --git a/src/test/kotlin/org/dashj/platform/dapiclient/ProofTest.kt b/src/test/kotlin/org/dashj/platform/dapiclient/ProofTest.kt index 554693b..037f4c0 100644 --- a/src/test/kotlin/org/dashj/platform/dapiclient/ProofTest.kt +++ b/src/test/kotlin/org/dashj/platform/dapiclient/ProofTest.kt @@ -12,10 +12,12 @@ import org.bitcoinj.core.Context import org.bitcoinj.core.Sha256Hash import org.bitcoinj.core.Utils import org.bitcoinj.crypto.BLSPublicKey +import org.bitcoinj.params.AbsintheDevNetParams import org.bitcoinj.params.TestNet3Params import org.bitcoinj.quorums.LLMQParameters import org.bitcoinj.quorums.Quorum import org.dash.platform.dapi.v0.PlatformOuterClass +import org.dashj.dpp.DPP import org.dashj.merk.ByteArrayKey import org.dashj.merk.MerkVerifyProof import org.dashj.merk.blake3 @@ -29,9 +31,11 @@ import org.dashj.platform.dapiclient.proofs.MerkleTree import org.dashj.platform.dapiclient.proofs.ProofVerifier import org.dashj.platform.dpp.DashPlatformProtocol import org.dashj.platform.dpp.identifier.Identifier +import org.dashj.platform.dpp.identity.Identity import org.dashj.platform.dpp.identity.IdentityFactory import org.dashj.platform.dpp.toByteArray import org.dashj.platform.dpp.toHex +import org.dashj.platform.dpp.util.Cbor import org.dashj.platform.dpp.util.Converters import org.junit.jupiter.api.Assertions.assertArrayEquals import org.junit.jupiter.api.Assertions.assertEquals @@ -47,9 +51,9 @@ class ProofTest : BaseTest() { // BLS.Init() } - val PARAMS = TestNet3Params.get() + val PARAMS = AbsintheDevNetParams.get() val CONTEXT = Context.getOrCreate(PARAMS) - val masternodeList = PARAMS.defaultMasternodeList.toList() + val masternodeList = PARAMS.defaultHPMasternodeList.toList() val dpnsContractId = SystemIds.dpnsDataContractId // DPNS contract val dashPayContractId = SystemIds.dashpayDataContractId val identityId = SystemIds.dashpayOwnerId @@ -58,8 +62,8 @@ class ProofTest : BaseTest() { val badIdentityId = Identifier.from("GrdbRMnZ5pPiFWuzPR62goRVj6sxpqvLKMT87ZmuZPyr") // these values are taken from https://github.com/dashevo/dash-network-configs/blob/master/testnet.yml - val publicKeyHashDpns = Utils.sha256hash160(Converters.fromHex("03922abfd8765ba334e8c16c63ea0d6f2f09ba19bb07684c12f75f46a1255a136f")) - val publicKeyHashDashpay = Utils.sha256hash160(Converters.fromHex("03a5d8392f3793699a53ed00e87600536679bd1c44f2bfda28c51e30c8daa10f9a")) + val publicKeyHashDpns = Utils.sha256hash160(Converters.fromHex("037cb691d72a2ea23ea76a5e311bd6dffa4abbb6a359fc86108aeefa6d4268e3d9")) + val publicKeyHashDashpay = Utils.sha256hash160(Converters.fromHex("03292da1684067c3b78ecef3d3e2c132434e7aa9930c0a520bfc4e2ad73a3edd6b")) val publicKeyHash = publicKeyHashDpns val publicKeyHashes = listOf(publicKeyHashDpns, publicKeyHashDashpay) @@ -77,7 +81,8 @@ class ProofTest : BaseTest() { fun getIdentityWithProof() { try { val identityBytes = client.getIdentity(identityId.toBuffer(), false).identity - val identity = dpp.identity.createFromBuffer(identityBytes) + val identityBytesCbor = DPP.getIdentityCborFromBincode(identityBytes) + val identity = dpp.identity.createFromBuffer(identityBytesCbor) assertEquals(identityId, identity.id) println("identity: ${identityBytes.toHex()}")