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

v5.x-fromBase-type-param: Add type parameter to fromBase16 #819

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ env:
jobs:
build:
name: Test and publish a snapshot
env:
HAS_SECRETS: ${{ secrets.SONATYPE_PASSWORD != '' }}
strategy:
matrix:
os: [ubuntu-latest]
Expand Down
11 changes: 8 additions & 3 deletions docs/LangSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ The following syntax is supported to access registers on box objects:
```
box.R3[Int].get // access R3 register, check that its value of type Int and return it
box.R3[Int].isDefined // check that value of R3 is defined and has type Int
box.R3[Int].isEmpty // check that value of R3 is undefined
box.R3[Int].getOrElse(d) // access R3 value if defined, otherwise return `d`
```

Expand Down Expand Up @@ -933,13 +932,19 @@ def proveDHTuple(g: GroupElement, h: GroupElement,
*/
def proveDlog(value: GroupElement): SigmaProp

/** Transforms Base58 encoded string litereal into constant of type Coll[Byte].
/** Transforms Base16 encoded string literal into constant of type Coll[Byte].
* It is a compile-time operation and only string literal (constant) can be its
* argument.
*/
def fromBase16(input: String): Coll[Byte]

/** Transforms Base58 encoded string literal into constant of type Coll[Byte].
* It is a compile-time operation and only string literal (constant) can be its
* argument.
*/
def fromBase58(input: String): Coll[Byte]

/** Transforms Base64 encoded string litereal into constant of type Coll[Byte].
/** Transforms Base64 encoded string literal into constant of type Coll[Byte].
* It is a compile-time operation and only string literal (constant) can be its
* argument.
*/
Expand Down
20 changes: 18 additions & 2 deletions sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package sigmastate.lang
import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress}
import scalan.Nullable
import scorex.util.encode.{Base64, Base58}
import scorex.util.encode.{Base64, Base58, Base16}
import sigmastate.SCollection.{SIntArray, SByteArray}
import sigmastate.SOption._
import sigmastate.Values.{StringConstant, Constant, EvaluatedValue, SValue, IntValue, SigmaPropConstant, ConstantPlaceholder, BoolValue, Value, ByteArrayConstant, SigmaPropValue, ValueCompanion}
import sigmastate._
import sigmastate.lang.Terms._
import sigmastate.lang.exceptions.InvalidArguments
import sigmastate.serialization.ValueSerializer
import sigmastate.serialization.{ConstantSerializer, ValueSerializer, SigmaSerializer}
import sigmastate.utxo.{GetVar, DeserializeContext, DeserializeRegister, SelectField}

object SigmaPredef {
Expand Down Expand Up @@ -180,6 +180,21 @@ object SigmaPredef {
Seq(ArgInfo("", "")))
)

val FromBase16Func = PredefinedFunc("fromBase16",
Lambda(Array(paramT), Array("input" -> SString), tT, None),
PredefFuncInfo(
{ case (Ident(_, SFunc(_, tpe, _)), Seq(arg: EvaluatedValue[SString.type]@unchecked)) =>
val bytes = Base16.decode(arg.value).get
val r = SigmaSerializer.startReader(bytes)
val ser = ConstantSerializer(DeserializationSigmaBuilder)
val res = ser.parse(r)
if (res.tpe != tpe) throw new InvalidArguments(s"Types doesn't match: declared $tpe, actual: ${res.tpe}")
res.asInstanceOf[Values.ConstantNode[SType]]
}),
OperationInfo(Constant, "",
Seq(ArgInfo("", "")))
)

val FromBase58Func = PredefinedFunc("fromBase58",
Lambda(Array("input" -> SString), SByteArray, None),
PredefFuncInfo(
Expand Down Expand Up @@ -381,6 +396,7 @@ object SigmaPredef {
SigmaPropFunc,
GetVarFunc,
DeserializeFunc,
FromBase16Func,
FromBase64Func,
FromBase58Func,
Blake2b256Func,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe
}

property("fromBaseX") {
comp(""" fromBase16[Coll[Byte]]("0e0131") """) shouldBe ByteArrayConstant(Array[Byte](49))
// no type specified
an[InvalidArguments] should be thrownBy comp(""" fromBase16("0e0131") """)
// declared type != parsed type
an[InvalidArguments] should be thrownBy comp(""" fromBase16[Int]("0e0131") """)
Copy link
Member

Choose a reason for hiding this comment

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

  1. use assertExceptionThrown to test exceptions more precisely. See how it is used all over the code.
  2. Since the method should be generic and work for all types.
    This mean all types should be tested. See ConstantSerializerSpecification or DataSerializerSpecification for how it may look like. Those specifications are a bit less generic that they could be.
    Ideally you need:
  3. a generator of all valid types
  4. a generator of valid values for any valid type
  5. use forAll to test comp(...) for any (type, value) pair

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

see updated version


comp(""" fromBase58("r") """) shouldBe ByteArrayConstant(Array[Byte](49))
comp(""" fromBase64("MQ") """) shouldBe ByteArrayConstant(Array[Byte](49))
comp(""" fromBase64("M" + "Q") """) shouldBe ByteArrayConstant(Array[Byte](49))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,7 @@ class SigmaParserTest extends PropSpec with PropertyChecks with Matchers with La
}

property("fromBaseX string decoding") {
parse("""fromBase16("1111")""") shouldBe Apply(FromBase16Func.symNoType, IndexedSeq(StringConstant("1111")))
parse("""fromBase58("111")""") shouldBe Apply(FromBase58Func.symNoType, IndexedSeq(StringConstant("111")))
parse("""fromBase64("111")""") shouldBe Apply(FromBase64Func.symNoType, IndexedSeq(StringConstant("111")))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class SigmaTyperTest extends PropSpec with PropertyChecks with Matchers with Lan
typecheck(env, "min(HEIGHT, INPUTS.size)") shouldBe SInt
typecheck(env, "max(1, 2)") shouldBe SInt
typecheck(env, "max(1L, 2)") shouldBe SLong
typecheck(env, """fromBase16("1111")""") shouldBe SByteArray
typecheck(env, """fromBase58("111")""") shouldBe SByteArray
typecheck(env, """fromBase64("111")""") shouldBe SByteArray

Expand Down