mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
2022 02 03 issue 4032 (#4042)
* get all dlcWalletTest/test passing * Get dlcTest/test working with ignored test cases * WIP * Rework match oracleSignatures * Refactor checkSingleContractInfoOracleSigs to use matchOracleSignatures * Clean up checkOracleSignaturesAgainstContract for disjoint, still doesn't pass test cases * Some DRY in numeric test cases * Add test case for enum contracts * Fix disjoint union contract bug * Refactor matching of oracle announcements and oracle signatures into DLCUtil * Fix compile on on 2.12.x * Address parts of code review
This commit is contained in:
parent
142612f034
commit
7a6f0430d6
9 changed files with 253 additions and 112 deletions
|
@ -749,7 +749,7 @@ lazy val dlcWalletTest = project
|
|||
name := "bitcoin-s-dlc-wallet-test",
|
||||
libraryDependencies ++= Deps.dlcWalletTest
|
||||
)
|
||||
.dependsOn(coreJVM % testAndCompile, dlcWallet, testkit, dlcTest)
|
||||
.dependsOn(coreJVM % testAndCompile, dlcWallet, testkit, testkitCoreJVM, dlcTest)
|
||||
|
||||
lazy val dlcNode = project
|
||||
.in(file("dlc-node"))
|
||||
|
|
|
@ -8,9 +8,14 @@ import org.bitcoins.core.protocol.dlc.models.DLCMessage.{
|
|||
DLCAcceptWithoutSigs,
|
||||
DLCOffer
|
||||
}
|
||||
import org.bitcoins.core.protocol.dlc.models.{ContractInfo, OracleOutcome}
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
import org.bitcoins.core.protocol.script.P2WSHWitnessV0
|
||||
import org.bitcoins.core.protocol.tlv.{
|
||||
OracleAnnouncementTLV,
|
||||
OracleAttestmentTLV
|
||||
}
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||
import org.bitcoins.core.util.sorted.OrderedAnnouncements
|
||||
import org.bitcoins.crypto._
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
|
@ -187,4 +192,98 @@ object DLCUtil {
|
|||
outputIdx = fundingOutputIdx,
|
||||
tempContractId = offer.tempContractId)
|
||||
}
|
||||
|
||||
/** Checks that the oracles signatures given to us are correct
|
||||
* Things we need to check
|
||||
* 1. We have all the oracle signatures
|
||||
* 2. The oracle signatures are for one of the contracts in the [[ContractInfo]]
|
||||
* @see https://github.com/bitcoin-s/bitcoin-s/issues/4032
|
||||
*/
|
||||
def checkOracleSignaturesAgainstContract(
|
||||
contractInfo: ContractInfo,
|
||||
oracleSigs: Vector[OracleSignatures]): Boolean = {
|
||||
contractInfo match {
|
||||
case single: SingleContractInfo =>
|
||||
checkSingleContractInfoOracleSigs(single, oracleSigs)
|
||||
case disjoint: DisjointUnionContractInfo =>
|
||||
//at least one disjoint union contract
|
||||
//has to have matching signatures
|
||||
disjoint.contracts.exists { single: SingleContractInfo =>
|
||||
checkSingleContractInfoOracleSigs(single, oracleSigs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if the given [[SingleContractInfo]] has one [[OracleSignatures]]
|
||||
* matches it inside of oracleSignatures.
|
||||
*/
|
||||
private def checkSingleContractInfoOracleSigs(
|
||||
contractInfo: SingleContractInfo,
|
||||
oracleSignatures: Vector[OracleSignatures]): Boolean = {
|
||||
require(oracleSignatures.nonEmpty, s"Signatures cannot be empty")
|
||||
matchOracleSignatures(contractInfo, oracleSignatures).isDefined
|
||||
}
|
||||
|
||||
/** Matches a [[SingleContractInfo]] to its oracle's signatures */
|
||||
def matchOracleSignatures(
|
||||
contractInfo: SingleContractInfo,
|
||||
oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = {
|
||||
matchOracleSignatures(contractInfo.announcements, oracleSignatures)
|
||||
}
|
||||
|
||||
def matchOracleSignatures(
|
||||
announcements: Vector[OracleAnnouncementTLV],
|
||||
oracleSignatures: Vector[OracleSignatures]): Option[OracleSignatures] = {
|
||||
val announcementNonces: Vector[Vector[SchnorrNonce]] = {
|
||||
announcements
|
||||
.map(_.eventTLV.nonces)
|
||||
.map(_.vec)
|
||||
}
|
||||
val resultOpt = oracleSignatures.find { case oracleSignature =>
|
||||
val oracleSigNonces: Vector[SchnorrNonce] = oracleSignature.sigs.map(_.rx)
|
||||
announcementNonces.contains(oracleSigNonces)
|
||||
}
|
||||
resultOpt
|
||||
}
|
||||
|
||||
/** Checks to see if the given oracle signatures and announcement have the same nonces */
|
||||
private def matchOracleSignaturesForAnnouncements(
|
||||
announcement: OracleAnnouncementTLV,
|
||||
signature: OracleSignatures): Option[OracleSignatures] = {
|
||||
matchOracleSignatures(
|
||||
Vector(announcement),
|
||||
Vector(signature)
|
||||
)
|
||||
}
|
||||
|
||||
/** Builds a set of oracle signatures from given announcements
|
||||
* and attestations. This method discards attestments
|
||||
* that do not have a matching announcement. Those attestments
|
||||
* are not included in the returned set of [[OracleSignatures]]
|
||||
*/
|
||||
def buildOracleSignatures(
|
||||
announcements: OrderedAnnouncements,
|
||||
attestments: Vector[OracleAttestmentTLV]): Vector[OracleSignatures] = {
|
||||
val result: Vector[OracleSignatures] = {
|
||||
val init = Vector.empty[OracleSignatures]
|
||||
attestments
|
||||
.foldLeft(init) { (acc, attestment) =>
|
||||
val r: Vector[OracleSignatures] = announcements.flatMap { ann =>
|
||||
val oracleSig =
|
||||
OracleSignatures(SingleOracleInfo(ann), attestment.sigs)
|
||||
val isMatch = matchOracleSignaturesForAnnouncements(ann, oracleSig)
|
||||
isMatch match {
|
||||
case Some(matchedSig) =>
|
||||
acc.:+(matchedSig)
|
||||
case None =>
|
||||
//don't add it, skip it
|
||||
acc
|
||||
}
|
||||
}.toVector
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.bitcoins.core.protocol.dlc.execution
|
|||
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.protocol.dlc.build.DLCTxBuilder
|
||||
import org.bitcoins.core.protocol.dlc.compute.CETCalculator
|
||||
import org.bitcoins.core.protocol.dlc.compute.{CETCalculator, DLCUtil}
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
import org.bitcoins.core.protocol.dlc.sign.DLCTxSigner
|
||||
import org.bitcoins.core.protocol.transaction.{Transaction, WitnessTransaction}
|
||||
|
@ -136,6 +136,9 @@ object DLCExecutor {
|
|||
fundingTx: Transaction,
|
||||
fundOutputIndex: Int
|
||||
): ExecutedDLCOutcome = {
|
||||
require(
|
||||
DLCUtil.checkOracleSignaturesAgainstContract(contractInfo, oracleSigs),
|
||||
s"Incorrect oracle signatures and contract combination")
|
||||
val sigOracles = oracleSigs.map(_.oracle)
|
||||
|
||||
val oracleInfoOpt = contractInfo.oracleInfos.find { oracleInfo =>
|
||||
|
|
|
@ -3,16 +3,16 @@ package org.bitcoins.dlc.wallet
|
|||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.DLCOffer
|
||||
import org.bitcoins.core.protocol.dlc.models.{
|
||||
DLCState,
|
||||
DisjointUnionContractInfo,
|
||||
SingleContractInfo
|
||||
}
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCStatus.{
|
||||
Claimed,
|
||||
Refunded,
|
||||
RemoteClaimed
|
||||
}
|
||||
import org.bitcoins.core.protocol.dlc.models.{
|
||||
DLCState,
|
||||
DisjointUnionContractInfo,
|
||||
SingleContractInfo
|
||||
}
|
||||
import org.bitcoins.core.protocol.tlv._
|
||||
import org.bitcoins.core.script.interpreter.ScriptInterpreter
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||
|
@ -416,4 +416,41 @@ class DLCExecutionTest extends BitcoinSDualWalletTest {
|
|||
_ <- walletA.listDLCs()
|
||||
} yield succeed
|
||||
}
|
||||
|
||||
it must "throw an exception for a enum contract when do not have all the oracle signatures/outcomes" in {
|
||||
wallets =>
|
||||
val walletA = wallets._1.wallet
|
||||
val resultF = for {
|
||||
contractId <- getContractId(walletA)
|
||||
status <- getDLCStatus(walletA)
|
||||
(goodAttestment, _) = {
|
||||
status.contractInfo match {
|
||||
case single: SingleContractInfo =>
|
||||
DLCWalletUtil.getSigs(single)
|
||||
case disjoint: DisjointUnionContractInfo =>
|
||||
sys.error(
|
||||
s"Cannot retrieve sigs for disjoint union contract, got=$disjoint")
|
||||
}
|
||||
}
|
||||
//purposefully drop these
|
||||
//we cannot drop just a sig, or just an outcome because
|
||||
//of invariants in OracleAttestmentV0TLV
|
||||
badSigs = goodAttestment.sigs.dropRight(1)
|
||||
badOutcomes = goodAttestment.outcomes.dropRight(1)
|
||||
badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
|
||||
publicKey =
|
||||
goodAttestment.publicKey,
|
||||
sigs = badSigs,
|
||||
outcomes = badOutcomes)
|
||||
func = (wallet: DLCWallet) =>
|
||||
wallet.executeDLC(contractId, badAttestment)
|
||||
|
||||
result <- dlcExecutionTest(wallets = wallets,
|
||||
asInitiator = true,
|
||||
func = func,
|
||||
expectedOutputs = 1)
|
||||
} yield assert(result)
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException](resultF)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,27 +74,7 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
|||
initiatorWinVec,
|
||||
None)
|
||||
|
||||
val initiatorWinSigs =
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt = initWinOutcomes.oraclesAndOutcomes.find(
|
||||
_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
outcome.digits.map(_.toString))
|
||||
}
|
||||
}
|
||||
|
||||
val initiatorWinSigs = buildAttestments(initWinOutcomes)
|
||||
val recipientChosenOracles =
|
||||
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
||||
|
||||
|
@ -114,26 +94,7 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
|||
recipientWinVec,
|
||||
None)
|
||||
|
||||
val recipientWinSigs =
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt = recipientWinOutcomes.oraclesAndOutcomes.find(
|
||||
_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
outcome.digits.map(_.toString))
|
||||
}
|
||||
}
|
||||
val recipientWinSigs = buildAttestments(recipientWinOutcomes)
|
||||
|
||||
// Shuffle to make sure ordering doesn't matter
|
||||
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
||||
|
@ -242,4 +203,32 @@ class DLCMultiOracleExactNumericExecutionTest extends BitcoinSDualWalletTest {
|
|||
aggregateSignature == statusB.oracleSig
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds an attestment for the given numeric oracle outcome */
|
||||
private def buildAttestments(
|
||||
outcome: NumericOracleOutcome): Vector[OracleAttestmentTLV] = {
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt =
|
||||
outcome.oraclesAndOutcomes.find(_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val neededPadding = numDigits - outcome.digits.length
|
||||
val digitsPadded = outcome.digits ++ Vector.fill(neededPadding)(0)
|
||||
val sigs = digitsPadded.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
require(kValues.length == sigs.length,
|
||||
s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
digitsPadded.map(_.toString))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,26 +81,7 @@ class DLCMultiOracleNumericExecutionTest
|
|||
initiatorWinVec,
|
||||
Some(params))
|
||||
|
||||
val initiatorWinSigs =
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt = initWinOutcomes.oraclesAndOutcomes.find(
|
||||
_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
outcome.digits.map(_.toString))
|
||||
}
|
||||
}
|
||||
val initiatorWinSigs = buildAttestments(initWinOutcomes)
|
||||
|
||||
val recipientChosenOracles =
|
||||
Random.shuffle(oracleIndices).take(oracleInfo.threshold).sorted
|
||||
|
@ -121,26 +102,8 @@ class DLCMultiOracleNumericExecutionTest
|
|||
recipientWinVec,
|
||||
Some(params))
|
||||
|
||||
val recipientWinSigs =
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt = recipientWinOutcomes.oraclesAndOutcomes.find(
|
||||
_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val sigs = outcome.digits.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
outcome.digits.map(_.toString))
|
||||
}
|
||||
}
|
||||
val recipientWinSigs: Vector[OracleAttestmentTLV] = buildAttestments(
|
||||
recipientWinOutcomes)
|
||||
|
||||
// Shuffle to make sure ordering doesn't matter
|
||||
(Random.shuffle(initiatorWinSigs), Random.shuffle(recipientWinSigs))
|
||||
|
@ -249,4 +212,32 @@ class DLCMultiOracleNumericExecutionTest
|
|||
aggregateSignature == statusB.oracleSig
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds an attestment for the given numeric oracle outcome */
|
||||
private def buildAttestments(
|
||||
outcome: NumericOracleOutcome): Vector[OracleAttestmentTLV] = {
|
||||
privateKeys.zip(kValues).flatMap { case (priv, kValues) =>
|
||||
val outcomeOpt =
|
||||
outcome.oraclesAndOutcomes.find(_._1.publicKey == priv.schnorrPublicKey)
|
||||
|
||||
outcomeOpt.map { case (oracleInfo, outcome) =>
|
||||
val neededPadding = numDigits - outcome.digits.length
|
||||
val digitsPadded = outcome.digits ++ Vector.fill(neededPadding)(0)
|
||||
val sigs = digitsPadded.zip(kValues).map { case (num, kValue) =>
|
||||
val hash = CryptoUtil.sha256DLCAttestation(num.toString).bytes
|
||||
priv.schnorrSignWithNonce(hash, kValue)
|
||||
}
|
||||
val eventId = oracleInfo.announcement.eventTLV match {
|
||||
case v0: OracleEventV0TLV => v0.eventId
|
||||
}
|
||||
|
||||
require(kValues.length == sigs.length,
|
||||
s"kValues.length=${kValues.length} sigs.length=${sigs.length}")
|
||||
OracleAttestmentV0TLV(eventId,
|
||||
priv.schnorrPublicKey,
|
||||
sigs,
|
||||
digitsPadded.map(_.toString))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,6 +164,34 @@ class DLCNumericExecutionTest extends BitcoinSDualWalletTest {
|
|||
}
|
||||
}
|
||||
|
||||
it must "throw an exception for a numeric contract when do not have all the oracle signatures/outcomes" in {
|
||||
wallets =>
|
||||
val resultF = for {
|
||||
contractId <- getContractId(wallets._1.wallet)
|
||||
status <- getDLCStatus(wallets._2.wallet)
|
||||
(_, goodAttestment) = getSigs(status.contractInfo)
|
||||
//purposefully drop these
|
||||
//we cannot drop just a sig, or just an outcome because
|
||||
//of invariants in OracleAttestmentV0TLV
|
||||
badSigs = goodAttestment.sigs.dropRight(1)
|
||||
badOutcomes = goodAttestment.outcomes.dropRight(1)
|
||||
badAttestment = OracleAttestmentV0TLV(eventId = goodAttestment.eventId,
|
||||
publicKey =
|
||||
goodAttestment.publicKey,
|
||||
sigs = badSigs,
|
||||
outcomes = badOutcomes)
|
||||
func = (wallet: DLCWallet) =>
|
||||
wallet.executeDLC(contractId, badAttestment)
|
||||
|
||||
result <- dlcExecutionTest(wallets = wallets,
|
||||
asInitiator = false,
|
||||
func = func,
|
||||
expectedOutputs = 1)
|
||||
} yield assert(result)
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException](resultF)
|
||||
}
|
||||
|
||||
private def verifyingMatchingOracleSigs(
|
||||
statusA: Claimed,
|
||||
statusB: RemoteClaimed): Boolean = {
|
||||
|
|
|
@ -1355,21 +1355,9 @@ abstract class DLCWallet
|
|||
announcementData,
|
||||
nonceDbs)
|
||||
|
||||
oracleSigs =
|
||||
sigs.foldLeft(Vector.empty[OracleSignatures]) { (acc, sig) =>
|
||||
// Nonces should be unique so searching for the first nonce should be safe
|
||||
val firstNonce = sig.sigs.head.rx
|
||||
announcementTLVs
|
||||
.find(
|
||||
_.eventTLV.nonces.headOption
|
||||
.contains(firstNonce)) match {
|
||||
case Some(announcement) =>
|
||||
acc :+ OracleSignatures(SingleOracleInfo(announcement), sig.sigs)
|
||||
case None =>
|
||||
throw new RuntimeException(
|
||||
s"Cannot find announcement for associated public key, ${sig.publicKey.hex}")
|
||||
}
|
||||
}
|
||||
oracleSigs = DLCUtil.buildOracleSignatures(announcements =
|
||||
announcementTLVs,
|
||||
attestments = sigs.toVector)
|
||||
|
||||
tx <- executeDLC(contractId, oracleSigs)
|
||||
} yield tx
|
||||
|
|
|
@ -880,8 +880,11 @@ trait DLCTest {
|
|||
.get
|
||||
._2
|
||||
|
||||
val neededPadding =
|
||||
singleOracleInfo.announcement.eventTLV.eventDescriptor.noncesNeeded - digitsToSign.digits.length
|
||||
val paddedDigits = digitsToSign.digits ++ Vector.fill(neededPadding)(0)
|
||||
val sigs =
|
||||
computeNumericOracleSignatures(digitsToSign.digits,
|
||||
computeNumericOracleSignatures(paddedDigits,
|
||||
oraclePrivKeys(index),
|
||||
preCommittedKsPerOracle(index))
|
||||
|
||||
|
@ -1044,7 +1047,8 @@ trait DLCTest {
|
|||
outcomeIndices: Vector[Long],
|
||||
contractParams: ContractParams)(implicit
|
||||
ec: ExecutionContext): Future[Assertion] = {
|
||||
executeForCasesInUnion(outcomeIndices.map((0, _)), contractParams)
|
||||
executeForCasesInUnion(outcomeIndices = outcomeIndices.map((0, _)),
|
||||
contractParams = contractParams)
|
||||
}
|
||||
|
||||
def executeForCasesInUnion(
|
||||
|
@ -1110,12 +1114,14 @@ trait DLCTest {
|
|||
(numDigits, true, paramsOpt)
|
||||
}
|
||||
|
||||
val oracleSigs = genOracleSignatures(numOutcomes,
|
||||
isMultiDigit,
|
||||
singleContractInfo,
|
||||
possibleOutcomesForContract,
|
||||
outcomeIndex,
|
||||
paramsOpt)
|
||||
val oracleSigs = genOracleSignatures(
|
||||
numOutcomesOrDigits = numOutcomes,
|
||||
isNumeric = isMultiDigit,
|
||||
contractInfo = singleContractInfo,
|
||||
outcomes = possibleOutcomesForContract,
|
||||
outcomeIndex = outcomeIndex,
|
||||
paramsOpt = paramsOpt
|
||||
)
|
||||
|
||||
for {
|
||||
offerOutcome <-
|
||||
|
|
Loading…
Add table
Reference in a new issue