Skip to content

Commit

Permalink
AVM: Adding bmodexp
Browse files Browse the repository at this point in the history
  • Loading branch information
mangoplane committed Sep 21, 2024
1 parent bf4dc8f commit d737fb1
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 20 deletions.
14 changes: 11 additions & 3 deletions data/transactions/logic/assembler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,13 @@ dup; dup
falcon_verify
`

const bmodexpNonsense = `
pushbytes 0x0123456789abcd
pushbytes 0x0123456789abcd
pushbytes 0x0123456789abcd
bmodexp
`

const v8Nonsense = v7Nonsense + switchNonsense + frameNonsense + matchNonsense + boxNonsense

const v9Nonsense = v8Nonsense
Expand All @@ -450,7 +457,7 @@ const spliceNonsence = `

const v10Nonsense = v9Nonsense + pairingNonsense + spliceNonsence

const v11Nonsense = v10Nonsense + incentiveNonsense + stateProofNonsense
const v11Nonsense = v10Nonsense + incentiveNonsense + stateProofNonsense + bmodexpNonsense

const v6Compiled = "2004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f2310231123122313231418191a1b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b400b53a03b6b7043cb8033a0c2349c42a9631007300810881088120978101c53a8101c6003a"

Expand All @@ -475,8 +482,9 @@ const v10Compiled = v9Compiled + pairingCompiled + spliceCompiled
const incentiveCompiled = "757401"

const stateProofCompiled = "80070123456789abcd86494985"
const bmodexpCompiled = "80070123456789abcd80070123456789abcd80070123456789abcde6"

const V11Compiled = v10Compiled + incentiveCompiled + stateProofCompiled
const V11Compiled = v10Compiled + incentiveCompiled + stateProofCompiled + bmodexpCompiled

var nonsense = map[uint64]string{
1: v1Nonsense,
Expand Down Expand Up @@ -557,7 +565,7 @@ func TestAssemble(t *testing.T) {
}
}

var experiments = []uint64{spOpcodesVersion}
var experiments = []uint64{spOpcodesVersion, bmodexpVersion}

// TestExperimental forces a conscious choice to promote "experimental" opcode
// groups. This will fail when we increment vFuture's LogicSigVersion. If we had
Expand Down
33 changes: 17 additions & 16 deletions data/transactions/logic/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,21 +257,22 @@ var opDescByName = map[string]OpDesc{
"proto": {"Prepare top call frame for a retsub that will assume A args and R return values.", "Fails unless the last instruction executed was a `callsub`.", []string{"number of arguments", "number of return values"}},
"retsub": {"pop the top instruction from the call stack and branch to it", "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", nil},

"b+": {"A plus B. A and B are interpreted as big-endian unsigned integers", "", nil},
"b-": {"A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", "", nil},
"b/": {"A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil},
"b*": {"A times B. A and B are interpreted as big-endian unsigned integers.", "", nil},
"b<": {"1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b>": {"1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b<=": {"1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b>=": {"1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b==": {"1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b!=": {"0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", "", nil},
"b%": {"A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil},
"b|": {"A bitwise-or B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b&": {"A bitwise-and B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b^": {"A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b~": {"A with all bits inverted", "", nil},
"b+": {"A plus B. A and B are interpreted as big-endian unsigned integers", "", nil},
"b-": {"A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", "", nil},
"b/": {"A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil},
"b*": {"A times B. A and B are interpreted as big-endian unsigned integers.", "", nil},
"b<": {"1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b>": {"1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b<=": {"1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b>=": {"1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b==": {"1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil},
"b!=": {"0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", "", nil},
"b%": {"A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil},
"bmodexp": {"A raised to the Bth power modulo C. A, B and C are interpreted as big-endian unsigned integers limited to 4096 bytes. Fail if C is zero.", "", nil},
"b|": {"A bitwise-or B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b&": {"A bitwise-and B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b^": {"A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", "", nil},
"b~": {"A with all bits inverted", "", nil},

"bsqrt": {"The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers", "", nil},

Expand Down Expand Up @@ -352,7 +353,7 @@ func OpDocExtra(opName string) string {
var OpGroups = map[string][]string{
"Arithmetic": {"+", "-", "/", "*", "<", ">", "<=", ">=", "&&", "||", "shl", "shr", "sqrt", "bitlen", "exp", "==", "!=", "!", "itob", "btoi", "%", "|", "&", "^", "~", "mulw", "addw", "divw", "divmodw", "expw"},
"Byte Array Manipulation": {"getbit", "setbit", "getbyte", "setbyte", "concat", "len", "substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "replace2", "replace3", "base64_decode", "json_ref"},
"Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%", "bsqrt"},
"Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%", "bsqrt", "bmodexp"},
"Byte Array Logic": {"b|", "b&", "b^", "b~"},
"Cryptography": {"sha256", "keccak256", "sha512_256", "sha3_256", "sumhash512", "falcon_verify", "ed25519verify", "ed25519verify_bare", "ecdsa_verify", "ecdsa_pk_recover", "ecdsa_pk_decompress", "vrf_verify", "ec_add", "ec_scalar_mul", "ec_pairing_check", "ec_multi_scalar_mul", "ec_subgroup_check", "ec_map_to"},
"Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "pushints", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "pushbytess", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "args", "txn", "gtxn", "txna", "txnas", "gtxna", "gtxnas", "gtxns", "gtxnsa", "gtxnsas", "global", "load", "loads", "store", "stores", "gload", "gloads", "gloadss", "gaid", "gaids"},
Expand Down
27 changes: 27 additions & 0 deletions data/transactions/logic/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -2325,6 +2325,33 @@ func opBytesNeq(cx *EvalContext) error {
return opNot(cx)
}

func opBytesModExp(cx *EvalContext) error {
result := new(big.Int)
last := len(cx.Stack) - 1 // z
prev := last - 1 // y
pprev := last - 2 // x

if len(cx.Stack[last].Bytes) > maxStringSize || len(cx.Stack[prev].Bytes) > maxStringSize || len(cx.Stack[pprev].Bytes) > maxStringSize {
return errors.New("bmodexp attempted on large byte-array")

Check warning on line 2335 in data/transactions/logic/eval.go

View check run for this annotation

Codecov / codecov/patch

data/transactions/logic/eval.go#L2335

Added line #L2335 was not covered by tests
}

z := new(big.Int).SetBytes(cx.Stack[last].Bytes)
y := new(big.Int).SetBytes(cx.Stack[prev].Bytes)
x := new(big.Int).SetBytes(cx.Stack[pprev].Bytes)
if z.BitLen() == 0 {
return errors.New("modulo by zero")
}

result.Exp(x, y, z)
if result.Sign() < 0 {
return errors.New("bmodexp would have negative result")

Check warning on line 2347 in data/transactions/logic/eval.go

View check run for this annotation

Codecov / codecov/patch

data/transactions/logic/eval.go#L2347

Added line #L2347 was not covered by tests
}

cx.Stack[pprev].Bytes = result.Bytes()
cx.Stack = cx.Stack[:prev]
return nil
}

func opBytesModulo(cx *EvalContext) error {
result := new(big.Int)
var inner error
Expand Down
1 change: 1 addition & 0 deletions data/transactions/logic/evalStateful_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3254,6 +3254,7 @@ func TestReturnTypes(t *testing.T) {

"box_create": "int 9; +; box_create", // make the size match the 10 in CreateBox
"box_put": "byte 0x010203040506; concat; box_put", // make the 4 byte arg into a 10
"bmodexp": ": byte 0x0102030405; byte 0x010203040506; byte 0x01020304050607; bmodexp",
}

/* Make sure the specialCmd tests the opcode in question */
Expand Down
Loading

0 comments on commit d737fb1

Please sign in to comment.