summaryrefslogtreecommitdiff
path: root/data/transactions/logic/eval_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'data/transactions/logic/eval_test.go')
-rw-r--r--data/transactions/logic/eval_test.go634
1 files changed, 308 insertions, 326 deletions
diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go
index 9a16c0016..3e1fe56af 100644
--- a/data/transactions/logic/eval_test.go
+++ b/data/transactions/logic/eval_test.go
@@ -17,7 +17,6 @@
package logic
import (
- "crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
@@ -33,6 +32,7 @@ import (
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/data/transactions/logictest"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
@@ -44,10 +44,6 @@ func defaultEvalProto() config.ConsensusParams {
return defaultEvalProtoWithVersion(LogicVersion)
}
-func defaultEvalProtoV1() config.ConsensusParams {
- return defaultEvalProtoWithVersion(1)
-}
-
func defaultEvalProtoWithVersion(version uint64) config.ConsensusParams {
return config.ConsensusParams{
LogicSigVersion: version,
@@ -59,11 +55,24 @@ func defaultEvalProtoWithVersion(version uint64) config.ConsensusParams {
// These must be identical to keep an old backward compat test working
MinTxnFee: 1001,
MinBalance: 1001,
+ // Our sample txn is 42-1066 (and that's used as default in itxn_begin)
+ MaxTxnLife: 1500,
// Strange choices below so that we test against conflating them
AppFlatParamsMinBalance: 1002,
SchemaMinBalancePerEntry: 1003,
SchemaUintMinBalance: 1004,
SchemaBytesMinBalance: 1005,
+
+ MaxInnerTransactions: 4,
+
+ // With the addition of itxn_field, itxn_submit, which rely on
+ // machinery outside logic package for validity checking, we
+ // need a more realistic set of consensus paramaters.
+ Asset: true,
+ MaxAssetNameBytes: 12,
+ MaxAssetUnitNameBytes: 6,
+ MaxAssetURLBytes: 32,
+ MaxAssetDecimals: 4,
}
}
@@ -88,14 +97,14 @@ func defaultEvalParamsWithVersion(sb *strings.Builder, txn *transactions.SignedT
if txn != nil {
pt = txn
} else {
- var at transactions.SignedTxn
- pt = &at
+ pt = &transactions.SignedTxn{}
}
ep := EvalParams{}
ep.Proto = &proto
ep.Txn = pt
ep.PastSideEffects = MakePastSideEffects(5)
+ ep.Specials = &transactions.SpecialAddresses{}
if sb != nil { // have to do this since go's nil semantics: https://golang.org/doc/faq#nil_error
ep.Trace = sb
}
@@ -499,35 +508,14 @@ func TestMulOverflow(t *testing.T) {
testPanics(t, "int 0x111111111; int 0x222222222; *; pop; int 1", 1)
}
-func TestMulwImpl(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- t.Parallel()
- high, low, err := opMulwImpl(1, 2)
- require.NoError(t, err)
- require.Equal(t, uint64(0), high)
- require.Equal(t, uint64(2), low)
-
- high, low, err = opMulwImpl(0x111111111, 0x222222222)
- require.NoError(t, err)
- require.Equal(t, uint64(2), high)
- require.Equal(t, uint64(0x468acf130eca8642), low)
-
- high, low, err = opMulwImpl(1, 0)
- require.NoError(t, err)
- require.Equal(t, uint64(0), high)
- require.Equal(t, uint64(0), low)
-
- high, low, err = opMulwImpl((1<<64)-1, (1<<64)-1)
- require.NoError(t, err)
- require.Equal(t, uint64(0xfffffffffffffffe), high)
- require.Equal(t, uint64(1), low)
-}
-
func TestMulw(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
+ testAccepts(t, "int 1; int 2; mulw; int 2; ==; assert; int 0; ==", 3)
+ testAccepts(t, "int 0x111111111; int 0x222222222; mulw; int 0x468acf130eca8642; ==; assert; int 2; ==", 3)
+ testAccepts(t, "int 1; int 0; mulw; int 0; ==; assert; int 0; ==", 3)
+ testAccepts(t, "int 0xFFFFFFFFFFFFFFFF; int 0xFFFFFFFFFFFFFFFF; mulw; int 1; ==; assert; int 0xFFFFFFFFFFFFFFFe; ==", 3)
testAccepts(t, `
int 0x111111111
int 0x222222222
@@ -546,31 +534,15 @@ int 1 // ret 1
`, 1)
}
-func TestAddwImpl(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- t.Parallel()
- carry, sum := opAddwImpl(1, 2)
- require.Equal(t, uint64(0), carry)
- require.Equal(t, uint64(3), sum)
-
- carry, sum = opAddwImpl(0xFFFFFFFFFFFFFFFD, 0x45)
- require.Equal(t, uint64(1), carry)
- require.Equal(t, uint64(0x42), sum)
-
- carry, sum = opAddwImpl(0, 0)
- require.Equal(t, uint64(0), carry)
- require.Equal(t, uint64(0), sum)
-
- carry, sum = opAddwImpl((1<<64)-1, (1<<64)-1)
- require.Equal(t, uint64(1), carry)
- require.Equal(t, uint64((1<<64)-2), sum)
-}
-
func TestAddw(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
+ testAccepts(t, "int 1; int 2; addw; int 3; ==; assert; int 0; ==", 3)
+ testAccepts(t, "int 0xFFFFFFFFFFFFFFFD; int 0x45; addw; int 0x42; ==; assert; int 1; ==", 3)
+ testAccepts(t, "int 0; int 0; addw; int 0; ==; assert; int 0; ==", 3)
+ testAccepts(t, "int 0xFFFFFFFFFFFFFFFF; dup; addw; int 0xFFFFFFFFFFFFFFFe; ==; assert; int 1; ==", 3)
+
testAccepts(t, `
int 0xFFFFFFFFFFFFFFFF
int 0x43
@@ -695,6 +667,35 @@ int 1
}
}
+func TestMulDiv(t *testing.T) {
+ // Demonstrate a "function" that expects three u64s on stack,
+ // and calculates B*C/A. (Following opcode documentation
+ // convention, C is top-of-stack, B is below it, and A is
+ // below B.
+
+ muldiv := `
+muldiv:
+mulw // multiply B*C. puts TWO u64s on stack
+int 0 // high word of C as a double-word
+dig 3 // pull C to TOS
+divmodw
+pop // pop unneeded remainder low word
+pop // pop unneeded remainder high word
+swap
+int 0
+==
+assert // ensure high word of quotient was 0
+swap // bring C to surface
+pop // in order to get rid of it
+retsub
+`
+ testAccepts(t, "int 5; int 8; int 10; callsub muldiv; int 16; ==; return;"+muldiv, 4)
+
+ testRejects(t, "int 5; int 8; int 10; callsub muldiv; int 15; ==; return;"+muldiv, 4)
+
+ testAccepts(t, "int 500000000000; int 80000000000; int 100000000000; callsub muldiv; int 16000000000; ==; return;"+muldiv, 4)
+}
+
func TestDivZero(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -905,18 +906,11 @@ func TestArg(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops := testProg(t, `arg 0
-arg 1
-==
-arg 2
-arg 3
-!=
-&&
-arg 4
-len
-int 9
-<
-&&`, v)
+ source := "arg 0; arg 1; ==; arg 2; arg 3; !=; &&; arg 4; len; int 9; <; &&;"
+ if v >= 5 {
+ source += "int 0; args; int 1; args; ==; assert; int 2; args; int 3; args; !=; assert"
+ }
+ ops := testProg(t, source, v)
err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err)
var txn transactions.SignedTxn
@@ -994,7 +988,15 @@ const globalV4TestProgram = globalV3TestProgram + `
`
const globalV5TestProgram = globalV4TestProgram + `
-// No new globals in v5
+global CurrentApplicationAddress
+len
+int 32
+==
+&&
+global GroupID
+byte 0x0706000000000000000000000000000000000000000000000000000000000000
+==
+&&
`
func TestGlobal(t *testing.T) {
@@ -1023,18 +1025,18 @@ func TestGlobal(t *testing.T) {
EvalStateful, CheckStateful,
},
5: {
- CreatorAddress, globalV5TestProgram,
+ GroupID, globalV5TestProgram,
EvalStateful, CheckStateful,
},
}
// tests keys are versions so they must be in a range 1..AssemblerMaxVersion plus zero version
require.LessOrEqual(t, len(tests), AssemblerMaxVersion+1)
- ledger := makeTestLedger(nil)
- ledger.appID = 42
+
+ ledger := logictest.MakeLedger(nil)
addr, err := basics.UnmarshalChecksumAddress(testAddr)
require.NoError(t, err)
- ledger.creatorAddr = addr
- for v := uint64(0); v <= AssemblerMaxVersion; v++ {
+ ledger.NewApp(addr, basics.AppIndex(42), basics.AppParams{})
+ for v := uint64(1); v <= AssemblerMaxVersion; v++ {
_, ok := tests[v]
require.True(t, ok)
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
@@ -1052,12 +1054,10 @@ func TestGlobal(t *testing.T) {
require.NoError(t, err)
var txn transactions.SignedTxn
txn.Lsig.Logic = ops.Program
+ txn.Txn.Group = crypto.Digest{0x07, 0x06}
txgroup := make([]transactions.SignedTxn, 1)
txgroup[0] = txn
sb := strings.Builder{}
- block := bookkeeping.Block{}
- block.BlockHeader.Round = 999999
- block.BlockHeader.TimeStamp = 2069
proto := config.ConsensusParams{
MinTxnFee: 123,
MinBalance: 1000000,
@@ -1116,9 +1116,8 @@ txn TypeEnum
int %s
==
&&`, symbol, string(tt))
- ops, err := AssembleStringWithVersion(text, v)
- require.NoError(t, err)
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ ops := testProg(t, text, v)
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err)
var txn transactions.SignedTxn
txn.Txn.Type = tt
@@ -1420,6 +1419,26 @@ assert
int 1
`
+const testTxnProgramTextV5 = testTxnProgramTextV4 + `
+txn Nonparticipation
+pop
+int 1
+==
+assert
+txn ConfigAssetMetadataHash
+int 2
+txnas ApplicationArgs
+==
+assert
+txn Sender
+int 0
+args
+==
+assert
+
+int 1
+`
+
func makeSampleTxn() transactions.SignedTxn {
var txn transactions.SignedTxn
copy(txn.Txn.Sender[:], []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"))
@@ -1438,6 +1457,7 @@ func makeSampleTxn() transactions.SignedTxn {
txn.Txn.VoteFirst = 1317
txn.Txn.VoteLast = 17776
txn.Txn.VoteKeyDilution = 1
+ txn.Txn.Nonparticipation = false
txn.Txn.Type = protocol.PaymentTx
txn.Txn.AssetAmount = 1234
txn.Txn.AssetSender = txn.Txn.Receiver
@@ -1504,8 +1524,9 @@ func TestTxn(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- for _, txnField := range TxnFieldNames {
- if !strings.Contains(testTxnProgramTextV4, txnField) {
+ for i, txnField := range TxnFieldNames {
+ fs := txnFieldSpecByField[TxnField(i)]
+ if !fs.effects && !strings.Contains(testTxnProgramTextV5, txnField) {
if txnField != FirstValidTime.String() {
t.Errorf("TestTxn missing field %v", txnField)
}
@@ -1517,6 +1538,7 @@ func TestTxn(t *testing.T) {
2: testTxnProgramTextV2,
3: testTxnProgramTextV3,
4: testTxnProgramTextV4,
+ 5: testTxnProgramTextV5,
}
clearOps := testProg(t, "int 1", 1)
@@ -1553,7 +1575,7 @@ func TestTxn(t *testing.T) {
}
sb := strings.Builder{}
ep := defaultEvalParams(&sb, &txn)
- ep.Ledger = makeTestLedger(nil)
+ ep.Ledger = logictest.MakeLedger(nil)
ep.GroupIndex = 3
pass, err := Eval(ops.Program, ep)
if !pass {
@@ -1609,10 +1631,9 @@ fail:
int 0
return
`
- ops, err := AssembleStringWithVersion(cachedTxnProg, 2)
- require.NoError(t, err)
+ ops := testProg(t, cachedTxnProg, 2)
sb := strings.Builder{}
- err = Check(ops.Program, defaultEvalParams(&sb, nil))
+ err := Check(ops.Program, defaultEvalParams(&sb, nil))
if err != nil {
t.Log(hex.EncodeToString(ops.Program))
t.Log(sb.String())
@@ -1657,10 +1678,8 @@ int 100
targetTxn.Txn.Type = protocol.AssetConfigTx
txgroup[0] = targetTxn
sb := strings.Builder{}
- ledger := makeTestLedger(nil)
- ledger.setTrackedCreatable(0, basics.CreatableLocator{
- Index: 100,
- })
+ ledger := logictest.MakeLedger(nil)
+ ledger.SetTrackedCreatable(0, basics.CreatableLocator{Index: 100})
ep := defaultEvalParams(&sb, &txn)
ep.Ledger = ledger
ep.TxnGroup = txgroup
@@ -1701,8 +1720,7 @@ int 0
ep.TxnGroup[0].Txn.Type = protocol.AssetConfigTx
// should fail when no creatable was created
- var nilIndex basics.CreatableIndex
- ledger.trackedCreatables[0] = nilIndex
+ ledger.SetTrackedCreatable(0, basics.CreatableLocator{})
_, err = EvalStateful(ops.Program, ep)
require.Error(t, err)
require.Contains(t, err.Error(), "the txn did not create anything")
@@ -1782,7 +1800,7 @@ int 1
==
&&
`
- gtxnText := gtxnTextV2 + ` gtxn 0 ExtraProgramPages
+ gtxnTextV4 := gtxnTextV2 + ` gtxn 0 ExtraProgramPages
int 0
==
&&
@@ -1792,10 +1810,24 @@ int 2
&&
`
+ gtxnTextV5 := gtxnTextV4 + `int 0
+gtxnas 0 Accounts
+gtxn 0 Sender
+==
+&&
+int 0
+int 0
+gtxnsas Accounts
+gtxn 0 Sender
+==
+&&
+`
+
tests := map[uint64]string{
1: gtxnTextV1,
2: gtxnTextV2,
- 4: gtxnText,
+ 4: gtxnTextV4,
+ 5: gtxnTextV5,
}
for v, source := range tests {
@@ -2028,6 +2060,85 @@ global ZeroAddress
require.True(t, pass)
}
+func TestTxnas(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ t.Parallel()
+
+ source := `int 1
+txnas Accounts
+int 0
+txnas ApplicationArgs
+==
+`
+ ops := testProg(t, source, AssemblerMaxVersion)
+ var txn transactions.SignedTxn
+ txn.Txn.Accounts = make([]basics.Address, 1)
+ txn.Txn.Accounts[0] = txn.Txn.Sender
+ txn.Txn.ApplicationArgs = make([][]byte, 1)
+ txn.Txn.ApplicationArgs[0] = []byte(protocol.PaymentTx)
+ txgroup := make([]transactions.SignedTxn, 1)
+ txgroup[0] = txn
+ ep := defaultEvalParams(nil, &txn)
+ ep.TxnGroup = txgroup
+ _, err := Eval(ops.Program, ep)
+ require.NoError(t, err)
+
+ // check special case: Account 0 == Sender
+ // even without any additional context
+ source = `int 0
+txnas Accounts
+txn Sender
+==
+`
+ ops2 := testProg(t, source, AssemblerMaxVersion)
+ var txn2 transactions.SignedTxn
+ copy(txn2.Txn.Sender[:], []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"))
+ ep2 := defaultEvalParams(nil, &txn2)
+ pass, err := Eval(ops2.Program, ep2)
+ require.NoError(t, err)
+ require.True(t, pass)
+
+ // check gtxnas
+ source = `int 1
+gtxnas 0 Accounts
+txna ApplicationArgs 0
+==`
+ ops = testProg(t, source, AssemblerMaxVersion)
+ require.NoError(t, err)
+ _, err = Eval(ops.Program, ep)
+ require.NoError(t, err)
+
+ // check special case: Account 0 == Sender
+ // even without any additional context
+ source = `int 0
+gtxnas 0 Accounts
+txn Sender
+==
+ `
+ ops3 := testProg(t, source, AssemblerMaxVersion)
+ var txn3 transactions.SignedTxn
+ copy(txn2.Txn.Sender[:], []byte("aoeuiaoeuiaoeuiaoeuiaoeuiaoeui00"))
+ txgroup3 := make([]transactions.SignedTxn, 1)
+ txgroup3[0] = txn3
+ ep3 := defaultEvalParams(nil, &txn3)
+ ep3.TxnGroup = txgroup3
+ pass, err = Eval(ops3.Program, ep3)
+ require.NoError(t, err)
+ require.True(t, pass)
+
+ // check gtxnsas
+ source = `int 0
+int 1
+gtxnsas Accounts
+txna ApplicationArgs 0
+==`
+ ops = testProg(t, source, AssemblerMaxVersion)
+ require.NoError(t, err)
+ _, err = Eval(ops.Program, ep)
+ require.NoError(t, err)
+}
+
func TestBitOps(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -2143,6 +2254,7 @@ len`, 2)
}
func TestExtractOp(t *testing.T) {
+ partitiontest.PartitionTest(t)
t.Parallel()
testAccepts(t, "byte 0x123456789abc; extract 1 2; byte 0x3456; ==", 5)
testAccepts(t, "byte 0x123456789abc; extract 0 6; byte 0x123456789abc; ==", 5)
@@ -2152,13 +2264,17 @@ func TestExtractOp(t *testing.T) {
testAccepts(t, "byte 0x123456789abc; int 5; int 1; extract3; byte 0xbc; ==", 5)
- testAccepts(t, "byte 0x123456789abcdef0; int 1; extract16bits; int 0x3456; ==", 5)
- testAccepts(t, "byte 0x123456789abcdef0; int 1; extract32bits; int 0x3456789a; ==", 5)
- testAccepts(t, "byte 0x123456789abcdef0; int 0; extract64bits; int 0x123456789abcdef0; ==", 5)
- testAccepts(t, "byte 0x123456789abcdef0; int 0; extract64bits; int 0x123456789abcdef; !=", 5)
+ testAccepts(t, "byte 0x123456789abcdef0; int 1; extract_uint16; int 0x3456; ==", 5)
+ testAccepts(t, "byte 0x123456789abcdef0; int 1; extract_uint32; int 0x3456789a; ==", 5)
+ testAccepts(t, "byte 0x123456789abcdef0; int 0; extract_uint64; int 0x123456789abcdef0; ==", 5)
+ testAccepts(t, "byte 0x123456789abcdef0; int 0; extract_uint64; int 0x123456789abcdef; !=", 5)
+
+ testAccepts(t, `byte "hello"; extract 5 0; byte ""; ==`, 5)
+ testAccepts(t, `byte "hello"; int 5; int 0; extract3; byte ""; ==`, 5)
}
func TestExtractFlop(t *testing.T) {
+ partitiontest.PartitionTest(t)
t.Parallel()
// fails in compiler
testProg(t, `byte 0xf000000000000000
@@ -2196,17 +2312,17 @@ func TestExtractFlop(t *testing.T) {
err = testPanics(t, `byte 0xf000000000000000
int 55
- extract16bits`, 5)
+ extract_uint16`, 5)
require.Contains(t, err.Error(), "extract range beyond length of string")
err = testPanics(t, `byte 0xf000000000000000
int 9
- extract32bits`, 5)
+ extract_uint32`, 5)
require.Contains(t, err.Error(), "extract range beyond length of string")
err = testPanics(t, `byte 0xf000000000000000
int 1
- extract64bits`, 5)
+ extract_uint64`, 5)
require.Contains(t, err.Error(), "extract range beyond length of string")
}
@@ -2214,6 +2330,8 @@ func TestLoadStore(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
+ testAccepts(t, "load 3; int 0; ==;", 1)
+
testAccepts(t, `int 37
int 37
store 1
@@ -2231,6 +2349,35 @@ load 1
&&`, 1)
}
+func TestLoadStoreStack(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ t.Parallel()
+ testAccepts(t, "int 3; loads; int 0; ==;", 5)
+ testAccepts(t, `int 37
+int 1
+int 37
+stores
+int 42
+byte 0xabbacafe
+stores
+int 37
+==
+int 0
+swap
+stores
+int 42
+loads
+byte 0xabbacafe
+==
+int 0
+loads
+int 1
+loads
++
+&&`, 5)
+}
+
func TestLoadStore2(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -2355,7 +2502,7 @@ int 1`,
Proto: &proto,
Txn: &txgroup[j],
TxnGroup: txgroup,
- GroupIndex: j,
+ GroupIndex: uint64(j),
PastSideEffects: pastSideEffects,
}
}
@@ -2429,7 +2576,7 @@ int 1`,
Proto: &proto,
Txn: &txgroup[j],
TxnGroup: txgroup,
- GroupIndex: j,
+ GroupIndex: uint64(j),
PastSideEffects: pastSideEffects,
}
}
@@ -2502,7 +2649,7 @@ byte "txn 2"
Proto: &proto,
Txn: &txgroup[j],
TxnGroup: txgroup,
- GroupIndex: j,
+ GroupIndex: uint64(j),
PastSideEffects: pastSideEffects,
}
}
@@ -2582,44 +2729,6 @@ func TestCompares(t *testing.T) {
testAccepts(t, testCompareProgramText, 1)
}
-func TestKeccak256(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- t.Parallel()
- /*
- pip install sha3
- import sha3
- blob=b'fnord'
- sha3.keccak_256(blob).hexdigest()
- */
- progText := `byte 0x666E6F7264
-keccak256
-byte 0xc195eca25a6f4c82bfba0287082ddb0d602ae9230f9cf1f1a40b68f8e2c41567
-==`
- testAccepts(t, progText, 1)
-}
-
-func TestSHA512_256(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- t.Parallel()
- /*
- pip cryptography
- from cryptography.hazmat.backends import default_backend
- from cryptography.hazmat.primitives import hashes
- import base64
- digest = hashes.Hash(hashes.SHA512_256(), backend=default_backend())
- digest.update(b'fnord')
- base64.b16encode(digest.finalize())
- */
- progText := `byte 0x666E6F7264
-sha512_256
-
-byte 0x98D2C31612EA500279B6753E5F6E780CA63EBA8274049664DAD66A2565ED1D2A
-==`
- testAccepts(t, progText, 1)
-}
-
func TestSlowLogic(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -2684,10 +2793,9 @@ func TestStackUnderflow(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`int 1`, v)
+ ops := testProg(t, `int 1`, v)
ops.Program = append(ops.Program, 0x08) // +
- require.NoError(t, err)
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err)
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2707,10 +2815,9 @@ func TestWrongStackTypeRuntime(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`int 1`, v)
- require.NoError(t, err)
+ ops := testProg(t, `int 1`, v)
ops.Program = append(ops.Program, 0x01, 0x15) // sha256, len
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err)
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2730,11 +2837,9 @@ func TestEqMismatch(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`byte 0x1234
-int 1`, v)
- require.NoError(t, err)
+ ops := testProg(t, `byte 0x1234; int 1`, v)
ops.Program = append(ops.Program, 0x12) // ==
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err) // TODO: Check should know the type stack was wrong
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2754,11 +2859,9 @@ func TestNeqMismatch(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`byte 0x1234
-int 1`, v)
- require.NoError(t, err)
+ ops := testProg(t, `byte 0x1234; int 1`, v)
ops.Program = append(ops.Program, 0x13) // !=
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err) // TODO: Check should know the type stack was wrong
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2778,11 +2881,9 @@ func TestWrongStackTypeRuntime2(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`byte 0x1234
-int 1`, v)
- require.NoError(t, err)
+ ops := testProg(t, `byte 0x1234; int 1`, v)
ops.Program = append(ops.Program, 0x08) // +
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(t, err)
sb := strings.Builder{}
pass, _ := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2802,15 +2903,14 @@ func TestIllegalOp(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`int 1`, v)
- require.NoError(t, err)
+ ops := testProg(t, `int 1`, v)
for opcode, spec := range opsByOpcode[v] {
if spec.op == nil {
ops.Program = append(ops.Program, byte(opcode))
break
}
}
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.Error(t, err)
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2830,15 +2930,14 @@ func TestShortProgram(t *testing.T) {
t.Parallel()
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(`int 1
+ ops := testProg(t, `int 1
bnz done
done:
int 1
`, v)
- require.NoError(t, err)
// cut two last bytes - intc_1 and last byte of bnz
ops.Program = ops.Program[:len(ops.Program)-2]
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.Error(t, err)
sb := strings.Builder{}
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2928,10 +3027,10 @@ func TestShortBytecblock2(t *testing.T) {
const panicString = "out of memory, buffer overrun, stack overflow, divide by zero, halt and catch fire"
-func opPanic(cx *evalContext) {
+func opPanic(cx *EvalContext) {
panic(panicString)
}
-func checkPanic(cx *evalContext) error {
+func checkPanic(cx *EvalContext) error {
panic(panicString)
}
@@ -3470,20 +3569,9 @@ func evalLoop(b *testing.B, runs int, program []byte) {
}
func benchmarkBasicProgram(b *testing.B, source string) {
- ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(b, err)
- err = Check(ops.Program, defaultEvalParams(nil, nil))
- require.NoError(b, err)
- evalLoop(b, b.N, ops.Program)
-}
-
-func benchmarkExpensiveProgram(b *testing.B, source string) {
- ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(b, err)
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ ops := testProg(b, source, AssemblerMaxVersion)
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(b, err)
- _, err = Eval(ops.Program, defaultEvalParams(nil, nil))
- require.Error(b, err) // excessive cost
evalLoop(b, b.N, ops.Program)
}
@@ -3498,9 +3586,8 @@ func benchmarkOperation(b *testing.B, prefix string, operation string, suffix st
inst := strings.Count(operation, ";") + strings.Count(operation, "\n")
source := prefix + ";" + strings.Repeat(operation+";", 2000) + ";" + suffix
source = strings.ReplaceAll(source, ";", "\n")
- ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
- require.NoError(b, err)
- err = Check(ops.Program, defaultEvalParams(nil, nil))
+ ops := testProg(b, source, AssemblerMaxVersion)
+ err := Check(ops.Program, defaultEvalParams(nil, nil))
require.NoError(b, err)
evalLoop(b, runs, ops.Program)
b.ReportMetric(float64(inst)*15.0, "waste/op")
@@ -3511,8 +3598,10 @@ func BenchmarkUintMath(b *testing.B) {
{"pop1", "", "int 1234576; pop", "int 1"},
{"pop", "", "int 1234576; int 6712; pop; pop", "int 1"},
{"add", "", "int 1234576; int 6712; +; pop", "int 1"},
+ {"addw", "", "int 21276237623; int 32387238723; addw; pop; pop", "int 1"},
{"sub", "", "int 1234576; int 2; -; pop", "int 1"},
{"mul", "", "int 212; int 323; *; pop", "int 1"},
+ {"mulw", "", "int 21276237623; int 32387238723; mulw; pop; pop", "int 1"},
{"div", "", "int 736247364; int 892; /; pop", "int 1"},
{"divmodw", "", "int 736247364; int 892; int 126712; int 71672; divmodw; pop; pop; pop; pop", "int 1"},
{"sqrt", "", "int 736247364; sqrt; pop", "int 1"},
@@ -3602,22 +3691,6 @@ func BenchmarkBigMath(b *testing.B) {
}
}
-func BenchmarkHash(b *testing.B) {
- for _, hash := range []string{"sha256", "keccak256", "sha512_256"} {
- b.Run(hash+"-small", func(b *testing.B) { // hash 32 bytes
- benchmarkOperation(b, "int 32; bzero", hash, "pop; int 1")
- })
- b.Run(hash+"-med", func(b *testing.B) { // hash 128 bytes
- benchmarkOperation(b, "int 32; bzero",
- "dup; concat; dup; concat;"+hash, "pop; int 1")
- })
- b.Run(hash+"-big", func(b *testing.B) { // hash 512 bytes
- benchmarkOperation(b, "int 32; bzero",
- "dup; concat; dup; concat; dup; concat; dup; concat;"+hash, "pop; int 1")
- })
- }
-}
-
func BenchmarkAddx64(b *testing.B) {
progs := [][]string{
{"add long stack", addBenchmarkSource},
@@ -3634,123 +3707,6 @@ func BenchmarkNopPassx1(b *testing.B) {
benchmarkBasicProgram(b, "int 1")
}
-func BenchmarkSha256Raw(b *testing.B) {
- addr, _ := basics.UnmarshalChecksumAddress("OC6IROKUJ7YCU5NV76AZJEDKYQG33V2CJ7HAPVQ4ENTAGMLIOINSQ6EKGE")
- a := addr[:]
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- t := sha256.Sum256(a)
- a = t[:]
- }
-}
-
-func TestEd25519verify(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- t.Parallel()
- var s crypto.Seed
- crypto.RandBytes(s[:])
- c := crypto.GenerateSignatureSecrets(s)
- msg := "62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd"
- data, err := hex.DecodeString(msg)
- require.NoError(t, err)
- pk := basics.Address(c.SignatureVerifier)
- pkStr := pk.String()
-
- for v := uint64(1); v <= AssemblerMaxVersion; v++ {
- t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
- ops, err := AssembleStringWithVersion(fmt.Sprintf(`arg 0
-arg 1
-addr %s
-ed25519verify`, pkStr), v)
- require.NoError(t, err)
- sig := c.Sign(Msg{
- ProgramHash: crypto.HashObj(Program(ops.Program)),
- Data: data[:],
- })
- var txn transactions.SignedTxn
- txn.Lsig.Logic = ops.Program
- txn.Lsig.Args = [][]byte{data[:], sig[:]}
- sb := strings.Builder{}
- pass, err := Eval(ops.Program, defaultEvalParams(&sb, &txn))
- if !pass {
- t.Log(hex.EncodeToString(ops.Program))
- t.Log(sb.String())
- }
- require.True(t, pass)
- require.NoError(t, err)
-
- // short sig will fail
- txn.Lsig.Args[1] = sig[1:]
- pass, err = Eval(ops.Program, defaultEvalParams(nil, &txn))
- require.False(t, pass)
- require.Error(t, err)
- isNotPanic(t, err)
-
- // flip a bit and it should not pass
- msg1 := "52fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd"
- data1, err := hex.DecodeString(msg1)
- require.NoError(t, err)
- txn.Lsig.Args = [][]byte{data1, sig[:]}
- sb1 := strings.Builder{}
- pass1, err := Eval(ops.Program, defaultEvalParams(&sb1, &txn))
- require.False(t, pass1)
- require.NoError(t, err)
- isNotPanic(t, err)
- })
- }
-}
-
-func BenchmarkEd25519Verifyx1(b *testing.B) {
- //benchmark setup
- var data [][32]byte
- var programs [][]byte
- var signatures []crypto.Signature
-
- for i := 0; i < b.N; i++ {
- var buffer [32]byte //generate data to be signed
- crypto.RandBytes(buffer[:])
- data = append(data, buffer)
-
- var s crypto.Seed //generate programs and signatures
- crypto.RandBytes(s[:])
- secret := crypto.GenerateSignatureSecrets(s)
- pk := basics.Address(secret.SignatureVerifier)
- pkStr := pk.String()
- ops, err := AssembleStringWithVersion(fmt.Sprintf(`arg 0
-arg 1
-addr %s
-ed25519verify`, pkStr), AssemblerMaxVersion)
- require.NoError(b, err)
- programs = append(programs, ops.Program)
- sig := secret.Sign(Msg{
- ProgramHash: crypto.HashObj(Program(ops.Program)),
- Data: buffer[:],
- })
- signatures = append(signatures, sig)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- var txn transactions.SignedTxn
- txn.Lsig.Logic = programs[i]
- txn.Lsig.Args = [][]byte{data[i][:], signatures[i][:]}
- sb := strings.Builder{}
- ep := defaultEvalParams(&sb, &txn)
- pass, err := Eval(programs[i], ep)
- if !pass {
- b.Log(hex.EncodeToString(programs[i]))
- b.Log(sb.String())
- }
- if err != nil {
- require.NoError(b, err)
- }
- if !pass {
- require.True(b, pass)
- }
- }
-}
-
func BenchmarkCheckx5(b *testing.B) {
sourcePrograms := []string{
tlhcProgramText,
@@ -4234,6 +4190,7 @@ func testEvaluation(t *testing.T, program string, introduced uint64, tester eval
var outer error
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Helper()
if v < introduced {
testProg(t, obfuscate(program), v, expect{0, "...was introduced..."})
return
@@ -4275,17 +4232,20 @@ func testEvaluation(t *testing.T, program string, introduced uint64, tester eval
}
func testAccepts(t *testing.T, program string, introduced uint64) {
+ t.Helper()
testEvaluation(t, program, introduced, func(pass bool, err error) bool {
return pass && err == nil
})
}
func testRejects(t *testing.T, program string, introduced uint64) {
+ t.Helper()
testEvaluation(t, program, introduced, func(pass bool, err error) bool {
// Returned False, but didn't panic
return !pass && err == nil
})
}
func testPanics(t *testing.T, program string, introduced uint64) error {
+ t.Helper()
return testEvaluation(t, program, introduced, func(pass bool, err error) bool {
// TEAL panic! not just reject at exit
return !pass && err != nil
@@ -4314,6 +4274,7 @@ func TestBits(t *testing.T) {
testPanics(t, "int 1; int 64; getbit; int 0; ==", 3)
testAccepts(t, "int 0; int 3; int 1; setbit; int 8; ==", 3)
+ testPanics(t, "int 0; int 3; int 2; setbit; pop; int 1", 3)
testAccepts(t, "int 8; int 3; getbit; int 1; ==", 3)
testAccepts(t, "int 15; int 3; int 0; setbit; int 7; ==", 3)
@@ -4355,6 +4316,7 @@ func TestBytes(t *testing.T) {
testPanics(t, `byte "john"; int 4; getbyte; int 1; ==`, 3) // past end
testAccepts(t, `byte "john"; int 2; int 105; setbyte; byte "join"; ==`, 3)
+ testPanics(t, `byte "john"; int 2; int 256; setbyte; pop; int 1;`, 3)
testPanics(t, `global ZeroAddress; dup; concat; int 64; int 7; setbyte; int 1; return`, 3)
testAccepts(t, `global ZeroAddress; dup; concat; int 63; int 7; setbyte; int 1; return`, 3)
@@ -4368,6 +4330,7 @@ func TestBytes(t *testing.T) {
}
func TestMethod(t *testing.T) {
+ partitiontest.PartitionTest(t)
t.Parallel()
// Although 'method' is new around the time of v5, it is a
// pseudo-op, so it's ok to use it earlier, as it compiles to
@@ -4404,6 +4367,7 @@ func TestDig(t *testing.T) {
}
func TestCover(t *testing.T) {
+ partitiontest.PartitionTest(t)
t.Parallel()
testAccepts(t, "int 4; int 3; int 2; int 1; cover 0; int 1; ==; return", 5)
testAccepts(t, "int 4; int 3; int 2; int 1; cover 1; int 2; ==; return", 5)
@@ -4414,6 +4378,7 @@ func TestCover(t *testing.T) {
}
func TestUncover(t *testing.T) {
+ partitiontest.PartitionTest(t)
t.Parallel()
testAccepts(t, "int 4; int 3; int 2; int 1; uncover 0; int 1; ==; return", 5)
testAccepts(t, "int 4; int 3; int 2; int 1; uncover 2; int 3; ==; return", 5)
@@ -4555,17 +4520,21 @@ func TestShifts(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
+ testAccepts(t, "int 1; int 0; shl; int 1; ==", 4)
testAccepts(t, "int 1; int 1; shl; int 2; ==", 4)
testAccepts(t, "int 1; int 2; shl; int 4; ==", 4)
testAccepts(t, "int 3; int 2; shl; int 12; ==", 4)
testAccepts(t, "int 2; int 63; shl; int 0; ==", 4)
+ testAccepts(t, "int 3; int 0; shr; int 3; ==", 4)
testAccepts(t, "int 1; int 1; shr; int 0; ==", 4)
testAccepts(t, "int 1; int 2; shr; int 0; ==", 4)
testAccepts(t, "int 3; int 1; shr; int 1; ==", 4)
testAccepts(t, "int 96; int 3; shr; int 12; ==", 4)
testAccepts(t, "int 8756675; int 63; shr; int 0; ==", 4)
+ testPanics(t, "int 8756675; int 64; shr; int 0; ==", 4)
+ testPanics(t, "int 8756675; int 64; shl; int 0; ==", 4)
}
func TestSqrt(t *testing.T) {
@@ -4605,6 +4574,10 @@ func TestExp(t *testing.T) {
testAccepts(t, "int 3; int 1; exp; int 3; ==", 4)
testAccepts(t, "int 96; int 3; exp; int 884736; ==", 4)
testPanics(t, "int 96; int 15; exp; int 884736; >", 4)
+
+ // These seem the same but check different code paths
+ testPanics(t, "int 2; int 64; exp; pop; int 1", 4)
+ testPanics(t, "int 4; int 32; exp; pop; int 1", 4)
}
func TestExpw(t *testing.T) {
@@ -4621,6 +4594,10 @@ func TestExpw(t *testing.T) {
testPanics(t, "int 64; int 22; expw; pop; pop; int 1", 4) // (2^6)^22 = 2^132
testAccepts(t, "int 97; int 15; expw; int 10271255586529954209; ==; assert; int 34328615749; ==;", 4)
+
+ testPanics(t, "int 2; int 128; expw; pop; pop; int 1", 4) // 2^128 is too big
+ // looks the same, but different code path
+ testPanics(t, "int 4; int 64; expw; pop; pop; int 1", 4) // 2^128 is too big
}
func TestBitLen(t *testing.T) {
@@ -4685,6 +4662,8 @@ func TestBytesCompare(t *testing.T) {
testAccepts(t, "byte 0x10; byte 0x10; b<; !", 4)
testAccepts(t, "byte 0x10; byte 0x10; b<=", 4)
+ testAccepts(t, "byte 0x10; int 64; bzero; b>", 4)
+ testPanics(t, "byte 0x10; int 65; bzero; b>", 4)
testAccepts(t, "byte 0x11; byte 0x10; b>", 4)
testAccepts(t, "byte 0x11; byte 0x0010; b>", 4)
@@ -4695,6 +4674,8 @@ func TestBytesCompare(t *testing.T) {
testAccepts(t, "byte 0x11; byte 0x11; b==", 4)
testAccepts(t, "byte 0x0011; byte 0x11; b==", 4)
testAccepts(t, "byte 0x11; byte 0x00000000000011; b==", 4)
+ testAccepts(t, "byte 0x00; int 64; bzero; b==", 4)
+ testPanics(t, "byte 0x00; int 65; bzero; b==", 4)
testAccepts(t, "byte 0x11; byte 0x00; b!=", 4)
testAccepts(t, "byte 0x0011; byte 0x1100; b!=", 4)
@@ -4726,6 +4707,9 @@ func TestBytesBits(t *testing.T) {
testAccepts(t, "int 3; bzero; byte 0x000000; ==", 4)
testAccepts(t, "int 33; bzero; byte 0x000000000000000000000000000000000000000000000000000000000000000000; ==", 4)
+
+ testAccepts(t, "int 4096; bzero; len; int 4096; ==", 4)
+ testPanics(t, "int 4097; bzero; len; int 4097; ==", 4)
}
func TestBytesConversions(t *testing.T) {
@@ -4745,8 +4729,8 @@ func TestLog(t *testing.T) {
Type: protocol.ApplicationCallTx,
},
}
- ledger := makeTestLedger(nil)
- ledger.newApp(txn.Txn.Receiver, 0, basics.AppParams{})
+ ledger := logictest.MakeLedger(nil)
+ ledger.NewApp(txn.Txn.Receiver, 0, basics.AppParams{})
sb := strings.Builder{}
ep := defaultEvalParams(&sb, &txn)
ep.Proto = &proto
@@ -4764,7 +4748,7 @@ func TestLog(t *testing.T) {
loglen: 2,
},
{
- source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, config.MaxLogCalls)),
+ source: fmt.Sprintf(`%s int 1`, strings.Repeat(`byte "a logging message"; log;`, MaxLogCalls)),
loglen: MaxLogCalls,
},
{
@@ -4777,24 +4761,22 @@ func TestLog(t *testing.T) {
},
}
- //track expected number of logs in ep.Ledger
- count := 0
+ //track expected number of logs in cx.Logs
for i, s := range testCases {
ops := testProg(t, s.source, AssemblerMaxVersion)
err := CheckStateful(ops.Program, ep)
require.NoError(t, err, s)
- pass, err := EvalStateful(ops.Program, ep)
+ pass, cx, err := EvalStatefulCx(ops.Program, ep)
require.NoError(t, err)
require.True(t, pass)
- count += s.loglen
- require.Equal(t, len(ledger.logs), count)
+ require.Len(t, cx.Logs, s.loglen)
if i == len(testCases)-1 {
- require.Equal(t, strings.Repeat("a", MaxLogSize), ledger.logs[count-1].Message)
+ require.Equal(t, strings.Repeat("a", MaxLogSize), cx.Logs[0])
} else {
- for _, l := range ledger.logs[count-s.loglen:] {
- require.Equal(t, "a logging message", l.Message)
+ for _, l := range cx.Logs {
+ require.Equal(t, "a logging message", l)
}
}
}
@@ -4816,7 +4798,7 @@ func TestLog(t *testing.T) {
runMode: runModeApplication,
},
{
- source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, config.MaxLogCalls+1)),
+ source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log;`, MaxLogCalls+1)),
errContains: "too many log calls",
runMode: runModeApplication,
},