Skip to content
Open
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import org.jlleitschuh.gradle.ktlint.reporter.ReporterType

buildscript {
ext.kotlin_version = '1.4.32'
ext.dashpay_version = '0.24-SNAPSHOT'
ext.dashpay_version = '0.24-MOCK-SNAPSHOT'
ext.dpp_version = '0.24-SNAPSHOT'
ext.dapi_client_version = '0.24-SNAPSHOT'
ext.dashj_version = '19.1-CJ-SNAPSHOT'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import java.util.Date
import java.util.Timer
import kotlin.concurrent.timerTask
import kotlinx.coroutines.delay
import org.bitcoinj.coinjoin.CoinJoinCoinSelector
//import org.bitcoinj.coinjoin.CoinJoinCoinSelector
import org.bitcoinj.core.Address
import org.bitcoinj.core.Coin
import org.bitcoinj.core.ECKey
Expand Down Expand Up @@ -356,9 +356,9 @@ class BlockchainIdentity {
val request = SendRequest.creditFundingTransaction(wallet!!.params, privateKey as ECKey, credits)
if (useCoinJoin) {
// these are the settings for coinjoin
request.coinSelector = CoinJoinCoinSelector(wallet!!)
request.returnChange = false
request.emptyWallet = true // spend all coinjoin balance
// request.coinSelector = CoinJoinCoinSelector(wallet!!)
// request.returnChange = false
// request.emptyWallet = true // spend all coinjoin balance
} else {
request.coinSelector = ZeroConfCoinSelector.get()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class DefaultIdentity(val params: NetworkParameters) {
init {
when {
params.id.contains("test") -> {
seed = "horn welcome exact penalty beauty marble current leave arrest chunk emotion upset"
seed = "also negative confirm imitate balance foam edit under mule indoor cream huge"
}
params.id.contains("jack-daniels") -> {
seed = "print shuffle enlist object actress allow quality convince believe gauge tree laundry"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Client(private val clientOptions: ClientOptions) {
EnumSet.of(
AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_FUNDING,
AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_TOPUP,
AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_FUNDING,
AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY,
AuthenticationKeyChain.KeyChainType.INVITATION_FUNDING
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ class Contracts(val platform: Platform) {
return localContract.contract
} else {
try {
val contractResponse = platform.client.getDataContract(identifier.toBuffer(), Features.proveContracts, platform.contractsRetryCallback)

val contract = platform.dpp.dataContract.createFromBuffer(contractResponse.dataContract)
contract.metadata = contractResponse.metadata.getMetadata()
val contract = platform.stateRepository.fetchDataContract(identifier)!!

val app = ClientAppDefinition(contract.id, contract)
// If we do not have even the identifier in this.apps, we add it with timestamp as key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,7 @@ class Documents(val platform: Platform) {
callType: MulticallQuery.Companion.CallType = MulticallQuery.Companion.CallType.FIRST
): List<Document> {
try {
val documentResponse = platform.client.getDocuments(
dataContractId.toBuffer(),
documentType,
opts,
Features.proveDocuments,
platform.documentsRetryCallback
)
return documentResponse.documents.map {
val document = platform.dpp.document.createFromBuffer(it, Factory.Options(true))
document.metadata = documentResponse.metadata.getMetadata()
document
}
return platform.stateRepository.fetchDocuments(dataContractId, documentType, opts)
} catch (e: StatusRuntimeException) {
log.error(
"Document query: unable to get documents of $dataContractId: " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,11 @@ class Identities(val platform: Platform) {
}

fun get(id: Identifier): Identity? {
return try {
val identityResponse =
platform.client.getIdentity(id.toBuffer(), Features.proveIdentities, platform.identitiesRetryCallback)
val identity = platform.dpp.identity.createFromBuffer(identityResponse.identity)
identity.metadata = identityResponse.metadata.getMetadata()
identity
} catch (e: NotFoundException) {
null
}
return platform.stateRepository.fetchIdentity(id)
}

fun getByPublicKeyHash(pubKeyHash: ByteArray): Identity? {
val identityBuffer = platform.client.getIdentityByFirstPublicKey(pubKeyHash, true) ?: return null
return platform.dpp.identity.createFromBuffer(identityBuffer)
return platform.stateRepository.fetchIdentityFromPubKeyHash(pubKeyHash)
}

fun topUp(
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import org.dashj.platform.dapiclient.grpc.DefaultGetDataContractWithContractIdRe
import org.dashj.platform.dapiclient.grpc.DefaultGetDocumentsWithContractIdRetryCallback
import org.dashj.platform.dapiclient.grpc.DefaultGetIdentityWithIdentitiesRetryCallback
import org.dashj.platform.dapiclient.model.DocumentQuery
import org.dashj.platform.dapiclient.model.MerkLibVerifyProof
import org.dashj.platform.dpp.DashPlatformProtocol
import org.dashj.platform.dpp.identifier.Identifier
import org.dashj.platform.dpp.identity.Identity
Expand All @@ -38,9 +37,15 @@ class Platform(val params: NetworkParameters) {

companion object {
private val log: Logger = LoggerFactory.getLogger(Platform::class.java)
private const val MOCK_DAPI = true
fun mockDAPI() = MOCK_DAPI
}

var stateRepository = PlatformStateRepository(this)
var stateRepository = if (mockDAPI()) {
MockPlatformStateRepository(this)
} else {
PlatformStateRepository(this)
}
Comment on lines +44 to +46
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is written in a way to easily turn off mocking the data.


val dpp = DashPlatformProtocol(stateRepository, params)
val apps = HashMap<String, ClientAppDefinition>()
Expand All @@ -54,16 +59,6 @@ class Platform(val params: NetworkParameters) {
override val retryContractIds
get() = getAppList() // always use the latest app list
}
val broadcastRetryCallback = object : BroadcastRetryCallback(stateRepository) {
override val retryContractIds
get() = getAppList() // always use the latest app list
override val retryIdentityIds: List<Identifier>
get() = stateRepository.validIdentityIdList()
override val retryDocumentIds: List<Identifier>
get() = stateRepository.validDocumentIdList()
override val retryPreorderSalts: Map<Sha256Hash, Sha256Hash>
get() = stateRepository.validPreorderSalts()
}
val identitiesRetryCallback = object : DefaultGetIdentityWithIdentitiesRetryCallback() {
override val retryIdentityIds: List<Identifier>
get() = stateRepository.validIdentityIdList()
Expand All @@ -79,9 +74,10 @@ class Platform(val params: NetworkParameters) {
when {
params.id.contains("test") -> {
useWhiteList = true
apps["dashwallet"] = ClientAppDefinition("Fds5DDfXoLwpUZ71AAVYZP1uod8S7Ze2bR28JExBvZKR")
}
params.id.contains("bintang") -> {
apps["dashwallet"] = ClientAppDefinition("2Yf43cVQ6bwxzFMTmmsuD6RM4c5XBzdB21tMbyQWg1gv")
apps["dashwallet"] = ClientAppDefinition("Fds5DDfXoLwpUZ71AAVYZP1uod8S7Ze2bR28JExBvZKR")
}
}
client = DapiClient(params.defaultHPMasternodeList.toList(), dpp)
Expand All @@ -104,7 +100,7 @@ class Platform(val params: NetworkParameters) {

fun broadcastStateTransition(signedStateTransition: StateTransitionIdentitySigned) {
// TODO: validate transition structure here
client.broadcastStateTransitionAndWait(signedStateTransition, retryCallback = broadcastRetryCallback, verifyProof = MerkLibVerifyProof(signedStateTransition))
stateRepository.broadcastStateTransition(signedStateTransition)
}

fun hasApp(appName: String): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,50 @@
*/
package org.dashj.platform.sdk.platform

import org.bitcoinj.core.ECKey
import org.bitcoinj.core.Sha256Hash
import org.bitcoinj.core.Transaction
import org.bitcoinj.quorums.InstantSendLock
import org.dashj.platform.dapiclient.errors.NotFoundException
import org.dashj.platform.dapiclient.grpc.BroadcastRetryCallback
import org.dashj.platform.dapiclient.model.DocumentQuery
import org.dashj.platform.dapiclient.model.MerkLibVerifyProof
import org.dashj.platform.dpp.Factory
import org.dashj.platform.dpp.StateRepository
import org.dashj.platform.dpp.contract.DataContract
import org.dashj.platform.dpp.document.Document
import org.dashj.platform.dpp.identifier.Identifier
import org.dashj.platform.dpp.identity.Identity
import org.dashj.platform.dpp.identity.IdentityPublicKey
import org.dashj.platform.dpp.statetransition.StateTransitionIdentitySigned
import org.dashj.platform.dpp.toHex
import org.slf4j.Logger
import org.slf4j.LoggerFactory

open class PlatformStateRepository(val platform: Platform) : StateRepository {
private val identityMap = hashMapOf<Identifier, Identity>()
private val validIdentities = hashSetOf<Identifier>()
private val documentsMap = hashMapOf<Identifier, Document>()
private val validDocuments = hashSetOf<Identifier>()
private val identityHashesMap = hashMapOf<Identifier, List<ByteArray>>()
private val contractsMap = hashMapOf<Identifier, DataContract>()
private val outPointBufferSet = hashSetOf<ByteArray>()
private val preorderSalts = hashMapOf<Sha256Hash, Sha256Hash>()
companion object {
private val log: Logger = LoggerFactory.getLogger(PlatformStateRepository::class.java)
}

protected val identityMap = hashMapOf<Identifier, Identity>()
protected val validIdentities = hashSetOf<Identifier>()
protected val documentsMap = hashMapOf<Identifier, Document>()
protected val validDocuments = hashSetOf<Identifier>()
protected val identityHashesMap = hashMapOf<Identifier, List<ByteArray>>()
protected val contractsMap = hashMapOf<Identifier, DataContract>()
protected val outPointBufferSet = hashSetOf<ByteArray>()
protected val preorderSalts = hashMapOf<Sha256Hash, Sha256Hash>()

private val broadcastRetryCallback = object : BroadcastRetryCallback(this@PlatformStateRepository) {
override val retryContractIds
get() = platform.getAppList() // always use the latest app list
override val retryIdentityIds: List<Identifier>
get() = validIdentityIdList()
override val retryDocumentIds: List<Identifier>
get() = validDocumentIdList()
override val retryPreorderSalts: Map<Sha256Hash, Sha256Hash>
get() = validPreorderSalts()
}

override fun fetchDataContract(id: Identifier): DataContract? {
if (contractsMap.containsKey(id)) {
Expand All @@ -34,15 +59,34 @@ open class PlatformStateRepository(val platform: Platform) : StateRepository {
if (contractInfo?.contract != null) {
return contractInfo.contract
}
val contract = platform.contracts.get(id)
if (contract != null) {

val contractResponse =
platform.client.getDataContract(id.toBuffer(), Features.proveContracts, platform.contractsRetryCallback)

return if (contractResponse.dataContract.isNotEmpty()) {
val contract = platform.dpp.dataContract.createFromBuffer(contractResponse.dataContract)
contract.metadata = contractResponse.metadata.getMetadata()
storeDataContract(contract)
}
return contract
contract
} else null
}

override fun fetchDocuments(contractId: Identifier, type: String, where: Any): List<Document> {
return platform.documents.get(contractId, type, where as DocumentQuery)

override fun fetchDocuments(contractId: Identifier, documentType: String, where: Any): List<Document> {
where as DocumentQuery

val documentResponse = platform.client.getDocuments(
contractId.toBuffer(),
documentType,
where,
Features.proveDocuments,
platform.documentsRetryCallback
)
return documentResponse.documents.map {
val document = platform.dpp.document.createFromBuffer(it, Factory.Options(true))
document.metadata = documentResponse.metadata.getMetadata()
document
}
}

override fun fetchTransaction(id: String): Transaction? {
Expand All @@ -66,20 +110,30 @@ open class PlatformStateRepository(val platform: Platform) : StateRepository {
if (!contractsMap.containsKey(dataContract.id)) {
contractsMap[dataContract.id] = dataContract
}
log.info("store dataContract: {}", dataContract.toBuffer().toHex())
}

override fun storeDocument(document: Document) {
documentsMap[document.id] = document
log.info("store document[{}]: {}", document.type, document.toBuffer().toHex())
}

override fun storeIdentity(identity: Identity) {
if (!identityMap.containsKey(identity.id)) {
identityMap[identity.id]
identityMap[identity.id] = identity
}
storeIdentityPublicKeyHashes(identity.id, identity.publicKeys.map {
when (it.type) {
IdentityPublicKey.Type.ECDSA_HASH160, IdentityPublicKey.Type.BIP13_SCRIPT_HASH -> it.data
IdentityPublicKey.Type.ECDSA_SECP256K1 -> ECKey.fromPublicOnly(it.data).pubKeyHash
else -> Sha256Hash.twiceOf(it.data).bytes
}
})
log.info("store identity: {}", identity.toBuffer().toHex())
}

override fun storeIdentityPublicKeyHashes(identifier: Identifier, publicKeyHashes: List<ByteArray>) {
identityHashesMap[identifier] = publicKeyHashes
override fun storeIdentityPublicKeyHashes(identity: Identifier, publicKeyHashes: List<ByteArray>) {
identityHashesMap[identity] = publicKeyHashes
}

override fun verifyInstantLock(instantLock: InstantSendLock): Boolean {
Expand All @@ -92,14 +146,35 @@ open class PlatformStateRepository(val platform: Platform) : StateRepository {
return identityMap[id]
}

val identity = platform.identities.get(id)
val identity = try {
val identityResponse =
platform.client.getIdentity(id.toBuffer(), Features.proveIdentities, platform.identitiesRetryCallback)
val identity = platform.dpp.identity.createFromBuffer(identityResponse.identity)
identity.metadata = identityResponse.metadata.getMetadata()
identity
} catch (e: NotFoundException) {
null
}

if (identity != null) {
storeIdentity(identity)
}
return identity
}

override fun fetchIdentityFromPubKeyHash(pubKeyHash: ByteArray): Identity? {
return try {
val identifier = identityHashesMap.filter { entry ->
entry.value.any { it.contentEquals(pubKeyHash) }
}.keys.first()
fetchIdentity(identifier)
} catch (e: NoSuchElementException) {
platform.client.getIdentityByFirstPublicKey(pubKeyHash, false)?.let {
platform.dpp.identity.createFromBuffer(it)
}
}
}

override fun fetchLatestPlatformBlockHeader(): ByteArray {
TODO("Not yet implemented")
}
Expand Down Expand Up @@ -131,4 +206,50 @@ open class PlatformStateRepository(val platform: Platform) : StateRepository {
fun validPreorderSalts(): Map<Sha256Hash, Sha256Hash> {
return preorderSalts
}

fun dump() {
println("dataContracts:")
contractsMap.forEach {
println(it.value.toBuffer().toHex())
}
println("identities:")
identityMap.forEach {
println(it.value.toBuffer().toHex())
}
println("documents:")
documentsMap.forEach {
println("${it.value.type}: ${it.value.toBuffer().toHex()}")
}
}

open fun broadcastStateTransition(signedStateTransition: StateTransitionIdentitySigned) {
// TODO: validate transition structure here

platform.client.broadcastStateTransitionAndWait(
signedStateTransition,
retryCallback = broadcastRetryCallback,
verifyProof = MerkLibVerifyProof(signedStateTransition)
)
}

fun storeDataContract(dataContractBytes: ByteArray) {
val (protocolVersion, rawDataContract) = Factory.decodeProtocolEntity(dataContractBytes)
rawDataContract["protocolVersion"] = protocolVersion
val contract = DataContract(rawDataContract)
storeDataContract(contract)
}

fun storeDocument(documentBytes: ByteArray) {
val (protocolVersion, rawDocument) = Factory.decodeProtocolEntity(documentBytes)
rawDocument["protocolVersion"] = protocolVersion
val document = Document(rawDocument, fetchDataContract(Identifier.from(rawDocument["\$dataContractId"]))!!)
storeDocument(document)
}

fun storeIdentity(identityBytes: ByteArray) {
val (protocolVersion, rawIdentity) = Factory.decodeProtocolEntity(identityBytes)
rawIdentity["protocolVersion"] = protocolVersion
val identity = Identity(rawIdentity)
storeIdentity(identity)
}
}