Skip to content

Commit

Permalink
Fix scriptDataHash for plutusv3 and other stories
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewWestberg committed Sep 24, 2024
1 parent bf7573e commit 731c553
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.newm.chain.database.migration

import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.jetbrains.exposed.sql.transactions.transaction

@Suppress("unused")
class V16__AlterLedgerUtxos : BaseJavaMigration() {
override fun migrate(context: Context?) {
transaction {
execInBatch(
listOf(
"""
ALTER TABLE "ledger_utxos" ADD COLUMN "script_ref_version" INTEGER
""".trimIndent(),
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class LedgerRepositoryImpl : LedgerRepository {
LedgerUtxosTable.datumHash,
LedgerUtxosTable.datum,
LedgerUtxosTable.scriptRef,
LedgerUtxosTable.scriptRefVersion,
LedgerUtxosTable.blockSpent,
).mapNotNull { row ->
if (row[LedgerUtxosTable.blockSpent] == null) {
Expand Down Expand Up @@ -136,6 +137,7 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = row[LedgerUtxosTable.datumHash],
datum = row[LedgerUtxosTable.datum],
scriptRef = row[LedgerUtxosTable.scriptRef],
scriptRefVersion = row[LedgerUtxosTable.scriptRefVersion],
)
} else {
null
Expand All @@ -162,6 +164,7 @@ class LedgerRepositoryImpl : LedgerRepository {
LedgerUtxosTable.datumHash,
LedgerUtxosTable.datum,
LedgerUtxosTable.scriptRef,
LedgerUtxosTable.scriptRefVersion,
LedgerUtxosTable.blockSpent,
).mapNotNull { row ->
if (row[LedgerUtxosTable.blockSpent] == null) {
Expand Down Expand Up @@ -195,6 +198,7 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = row[LedgerUtxosTable.datumHash],
datum = row[LedgerUtxosTable.datum],
scriptRef = row[LedgerUtxosTable.scriptRef],
scriptRefVersion = row[LedgerUtxosTable.scriptRefVersion],
)
} else {
null
Expand Down Expand Up @@ -252,6 +256,7 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = row[LedgerUtxosTable.datumHash],
datum = row[LedgerUtxosTable.datum],
scriptRef = row[LedgerUtxosTable.scriptRef],
scriptRefVersion = row[LedgerUtxosTable.scriptRefVersion],
)
}
}
Expand Down Expand Up @@ -324,6 +329,7 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = row[LedgerUtxosTable.datumHash],
datum = row[LedgerUtxosTable.datum],
scriptRef = row[LedgerUtxosTable.scriptRef],
scriptRefVersion = row[LedgerUtxosTable.scriptRefVersion],
)
}.toHashSet()
}
Expand Down Expand Up @@ -507,6 +513,14 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = datumHash,
datum = datum,
scriptRef = scriptRef,
scriptRefVersion = scriptRef?.let {
if (it.startsWith("010100")) {
3
} else {
// 010000 is the plutusV2 script version
2
}
},
).also {
if (log.isDebugEnabled) {
log.debug("LiveUtxo: address: $address, $it")
Expand Down Expand Up @@ -826,6 +840,7 @@ class LedgerRepositoryImpl : LedgerRepository {
?.let { Blake2b.hash256(it).toHexString() }
row[datum] = createdUtxo.datum
row[scriptRef] = createdUtxo.scriptRef
row[scriptRefVersion] = createdUtxo.scriptRefVersion
row[lovelace] = createdUtxo.lovelace.toString()
row[blockCreated] = blockNumber
row[blockSpent] = null
Expand Down Expand Up @@ -1055,6 +1070,7 @@ class LedgerRepositoryImpl : LedgerRepository {
datumHash = row[LedgerUtxosTable.datumHash],
datum = row[LedgerUtxosTable.datum],
scriptRef = row[LedgerUtxosTable.scriptRef],
scriptRefVersion = row[LedgerUtxosTable.scriptRefVersion],
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ object LedgerUtxosTable : LongIdTable(name = "ledger_utxos") {
// script reference value
val scriptRef: Column<String?> = text("script_ref").nullable()

// script reference version of plutus
val scriptRefVersion: Column<Int?> = integer("script_ref_version").nullable()

// lovelaces in this utxo
val lovelace: Column<String> = text("lovelace")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.newm.chain.model

import java.math.BigInteger
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import java.math.BigInteger

@Serializable
data class CreatedUtxo(
Expand All @@ -15,6 +15,7 @@ data class CreatedUtxo(
val datumHash: String?,
val datum: String?,
val scriptRef: String?,
val scriptRefVersion: Int?,
val nativeAssets: List<NativeAsset>,
val cbor: ByteArray?,
val paymentCred: String?,
Expand All @@ -33,6 +34,7 @@ data class CreatedUtxo(
if (datumHash != other.datumHash) return false
if (datum != other.datum) return false
if (scriptRef != other.scriptRef) return false
if (scriptRefVersion != other.scriptRefVersion) return false
if (nativeAssets != other.nativeAssets) return false
if (cbor != null) {
if (other.cbor == null) return false
Expand All @@ -54,6 +56,7 @@ data class CreatedUtxo(
result = 31 * result + (datumHash?.hashCode() ?: 0)
result = 31 * result + (datum?.hashCode() ?: 0)
result = 31 * result + (scriptRef?.hashCode() ?: 0)
result = 31 * result + (scriptRefVersion ?: 0)
result = 31 * result + nativeAssets.hashCode()
result = 31 * result + (cbor?.contentHashCode() ?: 0)
result = 31 * result + (paymentCred?.hashCode() ?: 0)
Expand Down
1 change: 1 addition & 0 deletions newm-chain-db/src/main/kotlin/io/newm/chain/model/Utxo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ data class Utxo(
val datumHash: String?,
val datum: String?,
val scriptRef: String?,
val scriptRefVersion: Int?,
)

// MsgResult for utxo query
Expand Down
9 changes: 9 additions & 0 deletions newm-chain-db/src/main/kotlin/io/newm/chain/util/DbKtx.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.newm.kogmios.protocols.model.MetadataList
import io.newm.kogmios.protocols.model.MetadataMap
import io.newm.kogmios.protocols.model.MetadataString
import io.newm.kogmios.protocols.model.MetadataValue
import io.newm.kogmios.protocols.model.ScriptPlutusV1
import io.newm.kogmios.protocols.model.ScriptPlutusV2
import io.newm.kogmios.protocols.model.ScriptPlutusV3
import io.newm.kogmios.protocols.model.StakeCredentialRegistrationCertificate
Expand Down Expand Up @@ -184,6 +185,14 @@ fun List<UtxoOutput>.toCreatedUtxoList(
else -> null
}
},
scriptRefVersion = utxoOutput.script?.let { script ->
when (script) {
is ScriptPlutusV1 -> 1
is ScriptPlutusV2 -> 2
is ScriptPlutusV3 -> 3
else -> null
}
},
nativeAssets =
utxoOutput.value.assets
?.map { asset ->
Expand Down
15 changes: 15 additions & 0 deletions newm-chain/src/main/kotlin/io/newm/chain/grpc/NewmChainService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,20 @@ class NewmChainService : NewmChainGrpcKt.NewmChainCoroutineImplBase() {
}
}

val calculateReferenceScriptsVersions: suspend (Set<Utxo>) -> Set<Int> = { utxos ->
utxos
.mapNotNull { utxo ->
val firstUtxo = ledgerRepository.queryUtxosByOutputRef(utxo.hash, utxo.ix.toInt()).firstOrNull()
firstUtxo?.scriptRefVersion ?: firstUtxo?.scriptRef?.let { scriptRef ->
if (scriptRef.startsWith("010100")) {
3
} else {
2
}
}
}.toSet()
}

val updatedRequest =
if (request.signaturesCount == 0 && request.signingKeysCount == 0 && request.requiredSignersCount == 0) {
// Calculate the number of different payment keys associated with all input utxos
Expand Down Expand Up @@ -446,6 +460,7 @@ class NewmChainService : NewmChainGrpcKt.NewmChainCoroutineImplBase() {
cardanoEra,
calculateTxExecutionUnits,
calculateReferenceScriptBytes,
calculateReferenceScriptsVersions,
) {
loadFrom(updatedRequest)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Cip68DatumTest {
datumHash = null,
datum = "d8799fa3446e616d6548537061636542756445696d6167654b697066733a2f2f7465737445696d616765583061723a2f2f66355738525a6d4151696d757a5f7679744659396f66497a6439517047614449763255587272616854753401ff",
scriptRef = null,
scriptRefVersion = null,
nativeAssets =
listOf(
NativeAsset(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ class TransactionBuilder(
private val cardanoEra: CardanoEra = CardanoEra.CONWAY,
private val calculateTxExecutionUnits: (suspend (ByteArray) -> EvaluateTxResult)? = null,
private val calculateReferenceScriptBytes: (suspend (Set<Utxo>) -> Long)? = null,
private val calculateReferenceScriptsVersions: (suspend (Set<Utxo>) -> Set<Int>)? = null,
) {
// private val log by lazy { LoggerFactory.getLogger("TransactionBuilder") }
// private val log by lazy { LoggerFactory.getLogger("TransactionBuilder") }
private val secureRandom by lazy { SecureRandom() }

private val txFeeFixed by lazy {
Expand Down Expand Up @@ -232,15 +233,18 @@ class TransactionBuilder(
suspend fun build(): ByteArray {
validateInputs()
val auxData: CborObject? = createAuxData()

val scriptVersions = referenceInputs?.let { calculateReferenceScriptsVersions?.invoke(it) }

val startingScriptDataHash = scriptDataHash
createScriptDataHash()
createScriptDataHash(scriptVersions)
calculateTemporaryCollateral()
calculateTxFees(auxData)

if (startingScriptDataHash == null) {
// recalculate scriptDataHash now that we've calculated proper exUnits
scriptDataHash = null
createScriptDataHash()
createScriptDataHash(scriptVersions)
}

// assemble the final transaction now that fees and collateral are correct
Expand Down Expand Up @@ -361,14 +365,17 @@ class TransactionBuilder(
}
}

private fun createScriptDataHash() {
private fun createScriptDataHash(scriptVersions: Set<Int>?) {
if (scriptDataHash == null && (!redeemers.isNullOrEmpty() || !datums.isNullOrEmpty())) {
// calculate the scriptDataHash - // redeemerBytes + datumBytes + languageViewMap
val redeemerBytes = createRedeemerWitnesses()?.toCborByteArray() ?: ByteArray(1) { 0xa0.toByte() }
val datumBytes = createDatumWitnesses()?.toCborByteArray() ?: ByteArray(0)
val languageViewMap =
if (!redeemers.isNullOrEmpty()) {
if (!plutusV3Scripts.isNullOrEmpty() || !auxPlutusV3Scripts.isNullOrEmpty()) {
if (!plutusV3Scripts.isNullOrEmpty() ||
!auxPlutusV3Scripts.isNullOrEmpty() ||
scriptVersions?.contains(3) == true
) {
// Plutus V3
protocolParameters.plutusCostModels.plutusV3!!
.toCborObject(PlutusLanguageKey.PLUTUSV3)
Expand All @@ -384,10 +391,11 @@ class TransactionBuilder(
ByteArray(1) { 0xa0.toByte() }
}
scriptDataHash = Blake2b.hash256(redeemerBytes + datumBytes + languageViewMap)
// log.warn("redeeemerBytes: ${redeemerBytes.toHexString()}")
// log.warn("datumBytes: ${datumBytes.toHexString()}")
// log.warn("languageViewMap: ${languageViewMap.toHexString()}")
// log.warn("scriptDataHash: ${scriptDataHash!!.toHexString()}")
// log.warn("scriptVersions: $scriptVersions")
// log.warn("redeeemerBytes: ${redeemerBytes.toHexString()}")
// log.warn("datumBytes: ${datumBytes.toHexString()}")
// log.warn("languageViewMap: ${languageViewMap.toHexString()}")
// log.warn("scriptDataHash: ${scriptDataHash!!.toHexString()}")
}
}

Expand Down Expand Up @@ -881,13 +889,15 @@ class TransactionBuilder(
cardanoEra: CardanoEra = CardanoEra.CONWAY,
calculateTxExecutionUnits: (suspend (ByteArray) -> EvaluateTxResult)? = null,
calculateReferenceScriptBytes: (suspend (Set<Utxo>) -> Long)? = null,
calculateReferenceScriptsVersions: (suspend (Set<Utxo>) -> Set<Int>)? = null,
block: TransactionBuilder.() -> Unit
): Pair<String, ByteArray> {
val transactionBuilder = TransactionBuilder(
protocolParameters,
cardanoEra,
calculateTxExecutionUnits,
calculateReferenceScriptBytes,
calculateReferenceScriptsVersions,
)
block.invoke(transactionBuilder)
val cborBytes = transactionBuilder.build()
Expand Down

0 comments on commit 731c553

Please sign in to comment.