Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mock staterepo #73

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
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)
}
Copy link
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)
}
}
Loading