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

Start work on Teal 6 (AVM 1.1) #2974

Merged
merged 4 commits into from
Oct 2, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions config/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,9 @@ func initConsensusProtocols() {
vFuture.CompactCertWeightThreshold = (1 << 32) * 30 / 100
vFuture.CompactCertSecKQ = 128

// Enable TEAL 6 / AVM 1.1
vFuture.LogicSigVersion = 6

Consensus[protocol.ConsensusFuture] = vFuture
}

Expand Down
31 changes: 16 additions & 15 deletions data/transactions/logic/assembler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ intc 1
mulw
`

const v2Nonsense = `
const v2Nonsense = v1Nonsense + `
dup2
pop
pop
Expand Down Expand Up @@ -219,7 +219,7 @@ txn FreezeAssetAccount
txn FreezeAssetFrozen
`

const v3Nonsense = `
const v3Nonsense = v2Nonsense + `
assert
min_balance
int 0x031337 // get bit 1, negate it, put it back
Expand Down Expand Up @@ -248,7 +248,7 @@ pushbytes "john"

// Keep in mind, only use existing int and byte constants, or else use
// push* instead. The idea is to not cause the *cblocks to change.
const v4Nonsense = `
const v4Nonsense = v3Nonsense + `
int 1
pushint 2000
int 0
Expand Down Expand Up @@ -294,7 +294,7 @@ gaids
int 100
`

const v5Nonsense = `
const v5Nonsense = v4Nonsense + `
app_params_get AppExtraProgramPages
cover 1
uncover 1
Expand Down Expand Up @@ -342,12 +342,16 @@ ecdsa_pk_recover Secp256k1
itxna Logs 3
`

const v6Nonsense = v5Nonsense + `
`

var nonsense = map[uint64]string{
1: v1Nonsense,
2: v1Nonsense + v2Nonsense,
3: v1Nonsense + v2Nonsense + v3Nonsense,
4: v1Nonsense + v2Nonsense + v3Nonsense + v4Nonsense,
5: v1Nonsense + v2Nonsense + v3Nonsense + v4Nonsense + v5Nonsense,
2: v2Nonsense,
3: v3Nonsense,
4: v4Nonsense,
5: v5Nonsense,
6: v6Nonsense,
}

var compiled = map[uint64]string{
Expand All @@ -356,6 +360,7 @@ var compiled = map[uint64]string{
3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e",
4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164",
5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03",
6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03",
}

func pseudoOp(opcode string) bool {
Expand All @@ -381,6 +386,7 @@ func TestAssemble(t *testing.T) {
// This doesn't have to be a sensible program to run, it just has to compile.

t.Parallel()
require.Equal(t, LogicVersion, len(nonsense))
for v := uint64(2); v <= AssemblerMaxVersion; v++ {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
for _, spec := range OpSpecs {
Expand Down Expand Up @@ -1374,12 +1380,6 @@ func TestAssembleDisassembleCycle(t *testing.T) {
// Disassembly won't necessarily perfectly recreate the source text, but assembling the result of Disassemble() should be the same program bytes.
t.Parallel()

tests := map[uint64]string{
1: v1Nonsense,
2: v1Nonsense + v2Nonsense,
3: v1Nonsense + v2Nonsense + v3Nonsense,
}

// This confirms that each program compiles to the same bytes
// (except the leading version indicator), when compiled under
// original version, unspecified version (so it should pick up
Expand All @@ -1388,7 +1388,8 @@ func TestAssembleDisassembleCycle(t *testing.T) {
// optimizations in later versions that change the bytecode
// emitted. But currently it is, so we test it for now to
// catch any suprises.
for v, source := range tests {
require.Equal(t, LogicVersion, len(nonsense))
for v, source := range nonsense {
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
ops := testProg(t, source, v)
t2, err := Disassemble(ops.Program)
Expand Down
71 changes: 49 additions & 22 deletions data/transactions/logic/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -3741,11 +3741,12 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs txnFieldSpec, txn *tr
err = fmt.Errorf("Type arg not a byte array")
return
}
txType, ok := innerTxnTypes[string(sv.Bytes)]
if ok {
txn.Type = txType
txType := string(sv.Bytes)
ver, ok := innerTxnTypes[txType]
if ok && ver <= cx.version {
txn.Type = protocol.TxType(txType)
} else {
err = fmt.Errorf("%s is not a valid Type for itxn_field", sv.Bytes)
err = fmt.Errorf("%s is not a valid Type for itxn_field", txType)
}
case TypeEnum:
var i uint64
Expand All @@ -3755,9 +3756,9 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs txnFieldSpec, txn *tr
}
// i != 0 is so that the error reports 0 instead of Unknown
if i != 0 && i < uint64(len(TxnTypeNames)) {
txType, ok := innerTxnTypes[TxnTypeNames[i]]
if ok {
txn.Type = txType
ver, ok := innerTxnTypes[TxnTypeNames[i]]
if ok && ver <= cx.version {
txn.Type = protocol.TxType(TxnTypeNames[i])
} else {
err = fmt.Errorf("%s is not a valid Type for itxn_field", TxnTypeNames[i])
}
Expand All @@ -3768,14 +3769,48 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs txnFieldSpec, txn *tr
txn.Sender, err = cx.availableAccount(sv)
case Fee:
txn.Fee.Raw, err = sv.uint()
// FirstValid, LastValid unsettable: no motivation
// Note unsettable: would be strange, as this "Note" would not end up "chain-visible"
// FirstValid, LastValid unsettable: little motivation (maybe a app call
// wants to inspect?) If we set, make sure they are legal, both for current
// round, and separation by MaxLifetime (check lifetime in submit, not here)
case Note:
if len(sv.Bytes) > cx.Proto.MaxTxnNoteBytes {
err = fmt.Errorf("%s may not exceed %d bytes", fs.field, cx.Proto.MaxTxnNoteBytes)
} else {
txn.Note = make([]byte, len(sv.Bytes))
copy(txn.Note[:], sv.Bytes)
}
// GenesisID, GenesisHash unsettable: surely makes no sense
// Group unsettable: Can't make groups from AVM (yet?)
// Lease unsettable: This seems potentially useful.
// RekeyTo unsettable: Feels dangerous for first release.

// KeyReg not allowed yet, so no fields settable
case RekeyTo:
txn.RekeyTo, err = sv.address()

// KeyReg
case VotePK:
if len(sv.Bytes) != 32 {
err = fmt.Errorf("%s must be 32 bytes", fs.field)
} else {
copy(txn.VotePK[:], sv.Bytes)
}
case SelectionPK:
if len(sv.Bytes) != 32 {
err = fmt.Errorf("%s must be 32 bytes", fs.field)
} else {
copy(txn.SelectionPK[:], sv.Bytes)
}
case VoteFirst:
var round uint64
round, err = sv.uint()
txn.VoteFirst = basics.Round(round)
case VoteLast:
var round uint64
round, err = sv.uint()
txn.VoteLast = basics.Round(round)
case VoteKeyDilution:
txn.VoteKeyDilution, err = sv.uint()
case Nonparticipation:
txn.Nonparticipation, err = sv.bool()

// Payment
case Receiver:
Expand Down Expand Up @@ -3820,7 +3855,7 @@ func (cx *EvalContext) stackIntoTxnField(sv stackValue, fs txnFieldSpec, txn *tr
txn.AssetParams.URL, err = sv.string(cx.Proto.MaxAssetURLBytes)
case ConfigAssetMetadataHash:
if len(sv.Bytes) != 32 {
err = fmt.Errorf("ConfigAssetMetadataHash must be 32 bytes")
err = fmt.Errorf("%s must be 32 bytes", fs.field)
} else {
copy(txn.AssetParams.MetadataHash[:], sv.Bytes)
}
Expand Down Expand Up @@ -3857,7 +3892,8 @@ func opTxField(cx *EvalContext) {
field := TxnField(cx.program[cx.pc+1])
fs, ok := txnFieldSpecByField[field]
if !ok || fs.itxVersion == 0 || fs.itxVersion > cx.version {
cx.err = fmt.Errorf("invalid itxn_field field %d", field)
cx.err = fmt.Errorf("invalid itxn_field %s", field)
return
}
sv := cx.stack[last]
cx.err = cx.stackIntoTxnField(sv, fs, &cx.subtxn.Txn)
Expand All @@ -3880,15 +3916,6 @@ func opTxSubmit(cx *EvalContext) {
return
}

// Error out on anything unusual. Allow pay, axfer.
switch cx.subtxn.Txn.Type {
case protocol.PaymentTx, protocol.AssetTransferTx, protocol.AssetConfigTx, protocol.AssetFreezeTx:
// only pay, axfer, acfg, afrz for now
default:
cx.err = fmt.Errorf("Invalid inner transaction type %#v", cx.subtxn.Txn.Type)
return
}

// The goal is to follow the same invariants used by the
// transaction pool. Namely that any transaction that makes it
// to Perform (which is equivalent to eval.applyTransaction)
Expand Down
98 changes: 91 additions & 7 deletions data/transactions/logic/evalAppTxn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,28 @@ import (
"github.com/stretchr/testify/require"
)

func TestActionTypes(t *testing.T) {
func TestInnerTypesV5(t *testing.T) {
v5, _ := makeSampleEnvWithVersion(5)
// not alllowed in v5
Copy link
Contributor

Choose a reason for hiding this comment

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

sp allowed

testApp(t, "itxn_begin; byte \"keyreg\"; itxn_field Type; itxn_submit; int 1;", v5, "keyreg is not a valid Type for itxn_field")
testApp(t, "itxn_begin; int keyreg; itxn_field TypeEnum; itxn_submit; int 1;", v5, "keyreg is not a valid Type for itxn_field")
}

func TestCurrentInnerTypes(t *testing.T) {
ep, ledger := makeSampleEnv()
testApp(t, "itxn_submit; int 1;", ep, "itxn_submit without itxn_begin")
testApp(t, "int pay; itxn_field TypeEnum; itxn_submit; int 1;", ep, "itxn_field without itxn_begin")
testApp(t, "itxn_begin; itxn_submit; int 1;", ep, "Invalid inner transaction type")
testApp(t, "itxn_begin; itxn_submit; int 1;", ep, "unknown tx type")
// bad type
testApp(t, "itxn_begin; byte \"pya\"; itxn_field Type; itxn_submit; int 1;", ep, "pya is not a valid Type")
// mixed up the int form for the byte form
testApp(t, obfuscate("itxn_begin; int pay; itxn_field Type; itxn_submit; int 1;"), ep, "Type arg not a byte array")
// or vice versa
testApp(t, obfuscate("itxn_begin; byte \"pay\"; itxn_field TypeEnum; itxn_submit; int 1;"), ep, "not a uint64")

// good types, not alllowed yet
testApp(t, "itxn_begin; byte \"keyreg\"; itxn_field Type; itxn_submit; int 1;", ep, "keyreg is not a valid Type for itxn_field")
// good types, not allowed yet
testApp(t, "itxn_begin; byte \"appl\"; itxn_field Type; itxn_submit; int 1;", ep, "appl is not a valid Type for itxn_field")
// same, as enums
testApp(t, "itxn_begin; int keyreg; itxn_field TypeEnum; itxn_submit; int 1;", ep, "keyreg is not a valid Type for itxn_field")
testApp(t, "itxn_begin; int appl; itxn_field TypeEnum; itxn_submit; int 1;", ep, "appl is not a valid Type for itxn_field")
testApp(t, "itxn_begin; int 42; itxn_field TypeEnum; itxn_submit; int 1;", ep, "42 is not a valid TypeEnum")
testApp(t, "itxn_begin; int 0; itxn_field TypeEnum; itxn_submit; int 1;", ep, "0 is not a valid TypeEnum")
Expand All @@ -58,6 +63,10 @@ func TestActionTypes(t *testing.T) {
testApp(t, "itxn_begin; int acfg; itxn_field TypeEnum; itxn_submit; int 1;", ep, "insufficient balance")
testApp(t, "itxn_begin; int afrz; itxn_field TypeEnum; itxn_submit; int 1;", ep, "insufficient balance")

// alllowed since v6
testApp(t, "itxn_begin; byte \"keyreg\"; itxn_field Type; itxn_submit; int 1;", ep, "insufficient balance")
testApp(t, "itxn_begin; int keyreg; itxn_field TypeEnum; itxn_submit; int 1;", ep, "insufficient balance")

// Establish 888 as the app id, and fund it.
ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{})
ledger.NewAccount(basics.AppIndex(888).Address(), 200000)
Expand Down Expand Up @@ -220,6 +229,30 @@ func TestRekeyPay(t *testing.T) {
// See explanation in logicLedger's Perform()
}

func TestRekeyBack(t *testing.T) {
payAndUnkey := `
itxn_begin
itxn_field Amount
itxn_field Receiver
itxn_field Sender
int pay
itxn_field TypeEnum
txn Sender
itxn_field RekeyTo
itxn_submit
`

ep, ledger := makeSampleEnv()
ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{})
testApp(t, "txn Sender; balance; int 0; ==;", ep)
testApp(t, "txn Sender; txn Accounts 1; int 100"+payAndUnkey, ep, "unauthorized")
ledger.NewAccount(ep.Txn.Txn.Sender, 120+3*ep.Proto.MinTxnFee)
ledger.Rekey(ep.Txn.Txn.Sender, basics.AppIndex(888).Address())
testApp(t, "txn Sender; txn Accounts 1; int 100"+payAndUnkey+"; int 1", ep)
// now rekeyed back to original
testApp(t, "txn Sender; txn Accounts 1; int 100"+payAndUnkey, ep, "unauthorized")
}

func TestDefaultSender(t *testing.T) {
pay := `
itxn_begin
Expand Down Expand Up @@ -320,7 +353,7 @@ func TestExtraFields(t *testing.T) {
"non-zero fields for type axfer")
}

func TestBadField(t *testing.T) {
func TestBadFieldV5(t *testing.T) {
pay := `
itxn_begin
int 7; itxn_field AssetAmount;
Expand All @@ -334,12 +367,34 @@ func TestBadField(t *testing.T) {
itxn_submit
`

ep, ledger := makeSampleEnv()
ep, ledger := makeSampleEnvWithVersion(5)
ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{})
testApp(t, "global CurrentApplicationAddress; txn Accounts 1; int 100"+pay, ep,
"invalid itxn_field RekeyTo")
}

func TestBadField(t *testing.T) {
pay := `
itxn_begin
int 7; itxn_field AssetAmount;
itxn_field Amount
itxn_field Receiver
itxn_field Sender
int pay
itxn_field TypeEnum
txn Receiver
itxn_field RekeyTo // ALLOWED, since v6
int 10
itxn_field FirstValid // NOT ALLOWED
itxn_submit
`

ep, ledger := makeSampleEnv()
ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{})
testApp(t, "global CurrentApplicationAddress; txn Accounts 1; int 100"+pay, ep,
"invalid itxn_field FirstValid")
}

func TestNumInner(t *testing.T) {
pay := `
itxn_begin
Expand Down Expand Up @@ -434,3 +489,32 @@ func TestAssetFreeze(t *testing.T) {
require.NoError(t, err)
require.Equal(t, false, holding.Frozen)
}

func TestFieldSetting(t *testing.T) {
ep, ledger := makeSampleEnv()
ledger.NewApp(ep.Txn.Txn.Receiver, 888, basics.AppParams{})
ledger.NewAccount(ledger.ApplicationID().Address(), 10*defaultEvalProto().MinTxnFee)
testApp(t, "itxn_begin; int 500; bzero; itxn_field Note; int 1", ep)
testApp(t, "itxn_begin; int 501; bzero; itxn_field Note; int 1", ep,
"Note may not exceed")

testApp(t, "itxn_begin; int 32; bzero; itxn_field VotePK; int 1", ep)
testApp(t, "itxn_begin; int 31; bzero; itxn_field VotePK; int 1", ep,
"VotePK must be 32")

testApp(t, "itxn_begin; int 32; bzero; itxn_field SelectionPK; int 1", ep)
testApp(t, "itxn_begin; int 33; bzero; itxn_field SelectionPK; int 1", ep,
"SelectionPK must be 32")

testApp(t, "itxn_begin; int 32; bzero; itxn_field RekeyTo; int 1", ep)
testApp(t, "itxn_begin; int 31; bzero; itxn_field RekeyTo; int 1", ep,
"not an address")

testApp(t, "itxn_begin; int 6; bzero; itxn_field ConfigAssetUnitName; int 1", ep)
testApp(t, "itxn_begin; int 7; bzero; itxn_field ConfigAssetUnitName; int 1", ep,
"value is too long")

testApp(t, "itxn_begin; int 12; bzero; itxn_field ConfigAssetName; int 1", ep)
testApp(t, "itxn_begin; int 13; bzero; itxn_field ConfigAssetName; int 1", ep,
"value is too long")
}
2 changes: 1 addition & 1 deletion data/transactions/logic/evalStateful_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2493,7 +2493,7 @@ func TestPooledAppCallsVerifyOp(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

source := `#pragma version 5
source := `
global CurrentApplicationID
pop
byte 0x01
Expand Down
Loading