diff --git a/Dockerfile b/Dockerfile index 89296cf6ec..ab55a90940 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ ARG TARGETARCH ADD https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz /go.tar.gz # Basic dependencies. -ENV HOME="/node" DEBIAN_FRONTEND="noninteractive" GOPATH="/node" +ENV HOME="/node" DEBIAN_FRONTEND="noninteractive" GOPATH="/dist" RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -27,15 +27,15 @@ RUN apt-get update && \ ENV PATH="/usr/local/go/bin:${PATH}" -COPY ./docker/files/ /node/files -COPY ./installer/genesis /node/files/run/genesis -COPY ./cmd/updater/update.sh /node/files/build/update.sh -COPY ./installer/config.json.example /node/files/run/config.json.example +COPY ./docker/files/ /dist/files +COPY ./installer/genesis /dist/files/run/genesis +COPY ./cmd/updater/update.sh /dist/files/build/update.sh +COPY ./installer/config.json.example /dist/files/run/config.json.example # Install algod binaries. -RUN /node/files/build/install.sh \ +RUN /dist/files/build/install.sh \ -p "${GOPATH}/bin" \ - -d "/node/data" \ + -d "/algod/data" \ -c "${CHANNEL}" \ -u "${URL}" \ -b "${BRANCH}" \ @@ -55,10 +55,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates USER algorand -COPY --chown=algorand:algorand --from=builder "/node/bin/" "/node/bin/" -COPY --chown=algorand:algorand --from=builder "/node/files/run/" "/node/run/" +COPY --chown=algorand:algorand --from=builder "/dist/bin/" "/node/bin/" +COPY --chown=algorand:algorand --from=builder "/dist/files/run/" "/node/run/" # Expose Algod REST API, Algod Gossip, and Prometheus Metrics ports EXPOSE $ALGOD_PORT 4160 9100 +WORKDIR /algod + CMD ["/node/run/run.sh"] diff --git a/cmd/catchpointdump/file.go b/cmd/catchpointdump/file.go index 2a39f98d37..79a426c0a5 100644 --- a/cmd/catchpointdump/file.go +++ b/cmd/catchpointdump/file.go @@ -32,11 +32,11 @@ import ( "github.com/spf13/cobra" + "github.com/algorand/avm-abi/apps" cmdutil "github.com/algorand/go-algorand/cmd/util" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" - "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/ledger" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store" @@ -436,7 +436,7 @@ func printAccountsDatabase(databaseName string, stagingTables bool, fileHeader l func printKeyValue(writer *bufio.Writer, key, value []byte) { var pretty string - ai, rest, err := logic.SplitBoxKey(string(key)) + ai, rest, err := apps.SplitBoxKey(string(key)) if err == nil { pretty = fmt.Sprintf("box(%d, %s)", ai, base64.StdEncoding.EncodeToString([]byte(rest))) } else { diff --git a/cmd/goal/application.go b/cmd/goal/application.go index 00a35eeac5..e01e1fdee5 100644 --- a/cmd/goal/application.go +++ b/cmd/goal/application.go @@ -31,6 +31,7 @@ import ( "github.com/spf13/cobra" "github.com/algorand/avm-abi/abi" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/crypto" apiclient "github.com/algorand/go-algorand/daemon/algod/api/client" "github.com/algorand/go-algorand/data/basics" @@ -198,8 +199,8 @@ func panicIfErr(err error) { } } -func newAppCallBytes(arg string) logic.AppCallBytes { - appBytes, err := logic.NewAppCallBytes(arg) +func newAppCallBytes(arg string) apps.AppCallBytes { + appBytes, err := apps.NewAppCallBytes(arg) if err != nil { reportErrorf(err.Error()) } @@ -207,16 +208,16 @@ func newAppCallBytes(arg string) logic.AppCallBytes { } type appCallInputs struct { - Accounts []string `codec:"accounts"` - ForeignApps []uint64 `codec:"foreignapps"` - ForeignAssets []uint64 `codec:"foreignassets"` - Boxes []boxRef `codec:"boxes"` - Args []logic.AppCallBytes `codec:"args"` + Accounts []string `codec:"accounts"` + ForeignApps []uint64 `codec:"foreignapps"` + ForeignAssets []uint64 `codec:"foreignassets"` + Boxes []boxRef `codec:"boxes"` + Args []apps.AppCallBytes `codec:"args"` } type boxRef struct { - appID uint64 `codec:"app"` - name logic.AppCallBytes `codec:"name"` + appID uint64 `codec:"app"` + name apps.AppCallBytes `codec:"name"` } // newBoxRef parses a command-line box ref, which is an optional appId, a comma, @@ -335,7 +336,7 @@ func processAppInputFile() (args [][]byte, accounts []string, foreignApps []uint return parseAppInputs(inputs) } -func getAppInputs() (args [][]byte, accounts []string, apps []uint64, assets []uint64, boxes []transactions.BoxRef) { +func getAppInputs() (args [][]byte, accounts []string, _ []uint64, assets []uint64, boxes []transactions.BoxRef) { if appInputFilename != "" { if appArgs != nil || appStrAccounts != nil || foreignApps != nil || foreignAssets != nil { reportErrorf("Cannot specify both command-line arguments/resources and JSON input filename") @@ -348,7 +349,7 @@ func getAppInputs() (args [][]byte, accounts []string, apps []uint64, assets []u // on it. appArgs became `StringArrayVar` in order to support abi arguments // which contain commas. - var encodedArgs []logic.AppCallBytes + var encodedArgs []apps.AppCallBytes for _, arg := range appArgs { if len(arg) > 0 { encodedArgs = append(encodedArgs, newAppCallBytes(arg)) diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go index e467064ee1..dce74a81d4 100644 --- a/cmd/goal/clerk.go +++ b/cmd/goal/clerk.go @@ -962,7 +962,7 @@ func assembleFileImpl(fname string, printWarnings bool) *logic.OpStream { } ops, err := logic.AssembleString(string(text)) if err != nil { - ops.ReportProblems(fname, os.Stderr) + ops.ReportMultipleErrors(fname, os.Stderr) reportErrorf("%s: %s", fname, err) } _, params := getProto(protoVersion) diff --git a/cmd/goal/interact.go b/cmd/goal/interact.go index e578713d29..000b7e8459 100644 --- a/cmd/goal/interact.go +++ b/cmd/goal/interact.go @@ -29,6 +29,7 @@ import ( "github.com/spf13/cobra" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/crypto" apiclient "github.com/algorand/go-algorand/daemon/algod/api/client" "github.com/algorand/go-algorand/data/basics" @@ -513,7 +514,7 @@ var appExecuteCmd = &cobra.Command{ var inputs appCallInputs for _, arg := range proc.Args { - var callArg logic.AppCallBytes + var callArg apps.AppCallBytes callArg.Encoding = arg.Kind if !procFlags.Changed(arg.Name) && arg.Default != "" { diff --git a/cmd/goal/multisig.go b/cmd/goal/multisig.go index e4526f44f8..5db3d09af9 100644 --- a/cmd/goal/multisig.go +++ b/cmd/goal/multisig.go @@ -163,7 +163,7 @@ var signProgramCmd = &cobra.Command{ } ops, err := logic.AssembleString(string(text)) if err != nil { - ops.ReportProblems(programSource, os.Stderr) + ops.ReportMultipleErrors(programSource, os.Stderr) reportErrorf("%s: %s", programSource, err) } if outname == "" { diff --git a/cmd/pingpong/runCmd.go b/cmd/pingpong/runCmd.go index ac95b16e07..1ebf4fc95c 100644 --- a/cmd/pingpong/runCmd.go +++ b/cmd/pingpong/runCmd.go @@ -316,7 +316,7 @@ var runCmd = &cobra.Command{ } ops, err := logic.AssembleString(programStr) if err != nil { - ops.ReportProblems(teal, os.Stderr) + ops.ReportMultipleErrors(teal, os.Stderr) reportErrorf("Internal error, cannot assemble %v \n", programStr) } cfg.Program = ops.Program diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index e314de31a7..21e1f91703 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -30,6 +30,7 @@ import ( "github.com/labstack/echo/v4" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-codec/codec" "github.com/algorand/go-algorand/agreement" @@ -1382,7 +1383,7 @@ func (v2 *Handlers) GetApplicationBoxes(ctx echo.Context, applicationID uint64, appIdx := basics.AppIndex(applicationID) ledger := v2.Node.LedgerForAPI() lastRound := ledger.Latest() - keyPrefix := logic.MakeBoxKey(appIdx, "") + keyPrefix := apps.MakeBoxKey(uint64(appIdx), "") requestedMax, algodMax := nilToZero(params.Max), v2.Node.Config().MaxAPIBoxPerApplication max := applicationBoxesMaxKeys(requestedMax, algodMax) @@ -1428,7 +1429,7 @@ func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint lastRound := ledger.Latest() encodedBoxName := params.Name - boxNameBytes, err := logic.NewAppCallBytes(encodedBoxName) + boxNameBytes, err := apps.NewAppCallBytes(encodedBoxName) if err != nil { return badRequest(ctx, err, err.Error(), v2.Log) } @@ -1437,7 +1438,7 @@ func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint return badRequest(ctx, err, err.Error(), v2.Log) } - value, err := ledger.LookupKv(lastRound, logic.MakeBoxKey(appIdx, string(boxName))) + value, err := ledger.LookupKv(lastRound, apps.MakeBoxKey(uint64(appIdx), string(boxName))) if err != nil { return internalError(ctx, err, errFailedLookingUpLedger, v2.Log) } @@ -1528,7 +1529,7 @@ func (v2 *Handlers) TealCompile(ctx echo.Context, params model.TealCompileParams ops, err := logic.AssembleString(source) if err != nil { sb := strings.Builder{} - ops.ReportProblems("", &sb) + ops.ReportMultipleErrors("", &sb) return badRequest(ctx, err, sb.String(), v2.Log) } pd := logic.HashProgram(ops.Program) diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index 5584224941..dedd902c94 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -815,7 +815,7 @@ func TestTealCompile(t *testing.T) { t.Parallel() params := model.TealCompileParams{} - tealCompileTest(t, nil, 200, true, params, nil) // nil program should work + tealCompileTest(t, nil, 400, true, params, nil) // nil program should NOT work goodProgram := fmt.Sprintf(`#pragma version %d int 1 diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 3ed539feab..9e8da08d1a 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -1622,7 +1622,7 @@ func isFullSpec(spec OpSpec) bool { } // mergeProtos allows us to support typetracking of pseudo-ops which are given an improper number of immediates -//by creating a new proto that is a combination of all the pseudo-op's possibilities +// by creating a new proto that is a combination of all the pseudo-op's possibilities func mergeProtos(specs map[int]OpSpec) (Proto, uint64, bool) { var args StackTypes var returns StackTypes @@ -1857,10 +1857,13 @@ func splitTokens(tokens []string) (current, rest []string) { // assemble reads text from an input and accumulates the program func (ops *OpStream) assemble(text string) error { - fin := strings.NewReader(text) if ops.Version > LogicVersion && ops.Version != assemblerNoVersion { return ops.errorf("Can not assemble version %d", ops.Version) } + if strings.TrimSpace(text) == "" { + return ops.errorf("Cannot assemble empty program text") + } + fin := strings.NewReader(text) scanner := bufio.NewScanner(fin) for scanner.Scan() { ops.sourceLine++ @@ -2411,8 +2414,19 @@ func (ops *OpStream) warnf(format string, a ...interface{}) error { return ops.warn(fmt.Errorf(format, a...)) } -// ReportProblems issues accumulated warnings and outputs errors to an io.Writer. -func (ops *OpStream) ReportProblems(fname string, writer io.Writer) { +// ReportMultipleErrors issues accumulated warnings and outputs errors to an io.Writer. +// In the case of exactly 1 error and no warnings, a slightly different format is provided +// to handle the cases when the original error is or isn't reported elsewhere. +// In the case of > 10 errors, only the first 10 errors will be reported. +func (ops *OpStream) ReportMultipleErrors(fname string, writer io.Writer) { + if len(ops.Errors) == 1 && len(ops.Warnings) == 0 { + prefix := "" + if fname != "" { + prefix = fmt.Sprintf("%s: ", fname) + } + fmt.Fprintf(writer, "%s1 error: %s\n", prefix, ops.Errors[0]) + return + } for i, e := range ops.Errors { if i > 9 { break diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 24a3afaf50..cc5cd97a44 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -19,6 +19,7 @@ package logic import ( "bytes" "encoding/hex" + "errors" "fmt" "regexp" "strings" @@ -1457,7 +1458,7 @@ done:` require.Equal(t, expectedProgBytes, ops.Program) } -func TestMultipleErrors(t *testing.T) { +func TestSeveralErrors(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -3053,3 +3054,108 @@ func TestAssemblePushConsts(t *testing.T) { source = `pushbytess "x" "y"; +` testProg(t, source, AssemblerMaxVersion, Expect{1, "+ arg 1 wanted type uint64 got []byte"}) } + +func TestAssembleEmpty(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + emptyExpect := Expect{0, "Cannot assemble empty program text"} + emptyPrograms := []string{ + "", + " ", + " \n\t\t\t\n\n ", + " \n \t \t \t \n \n \n\n", + } + + nonEmpty := " \n \t \t \t int 1 \n \n \t \t \n\n" + + for version := uint64(1); version <= AssemblerMaxVersion; version++ { + for _, prog := range emptyPrograms { + testProg(t, prog, version, emptyExpect) + } + testProg(t, nonEmpty, version) + } +} + +func TestReportMultipleErrors(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + assertWithMsg := func(t *testing.T, expectedOutput string, b bytes.Buffer) { + if b.String() != expectedOutput { + t.Errorf("Unexpected output: got %q, want %q", b.String(), expectedOutput) + } + } + + ops := &OpStream{ + Errors: []lineError{ + {Line: 1, Err: errors.New("error 1")}, + {Err: errors.New("error 2")}, + {Line: 3, Err: errors.New("error 3")}, + }, + Warnings: []error{ + errors.New("warning 1"), + errors.New("warning 2"), + }, + } + + // Test the case where fname is not empty + var b bytes.Buffer + ops.ReportMultipleErrors("test.txt", &b) + expected := `test.txt: 1: error 1 +test.txt: 0: error 2 +test.txt: 3: error 3 +test.txt: warning 1 +test.txt: warning 2 +` + assertWithMsg(t, expected, b) + + // Test the case where fname is empty + b.Reset() + ops.ReportMultipleErrors("", &b) + expected = `1: error 1 +0: error 2 +3: error 3 +warning 1 +warning 2 +` + assertWithMsg(t, expected, b) + + // no errors or warnings at all + ops = &OpStream{} + b.Reset() + ops.ReportMultipleErrors("blah blah", &b) + expected = "" + assertWithMsg(t, expected, b) + + // more than 10 errors: + file := "great-file.go" + les := []lineError{} + expectedStrs := []string{} + for i := 1; i <= 11; i++ { + errS := fmt.Errorf("error %d", i) + les = append(les, lineError{i, errS}) + if i <= 10 { + expectedStrs = append(expectedStrs, fmt.Sprintf("%s: %d: %s", file, i, errS)) + } + } + expected = strings.Join(expectedStrs, "\n") + "\n" + ops = &OpStream{Errors: les} + b.Reset() + ops.ReportMultipleErrors(file, &b) + assertWithMsg(t, expected, b) + + // exactly 1 error + filename + ops = &OpStream{Errors: []lineError{{42, errors.New("super annoying error")}}} + b.Reset() + ops.ReportMultipleErrors("galaxy.py", &b) + expected = "galaxy.py: 1 error: 42: super annoying error\n" + assertWithMsg(t, expected, b) + + // exactly 1 error w/o filename + ops = &OpStream{Errors: []lineError{{42, errors.New("super annoying error")}}} + b.Reset() + ops.ReportMultipleErrors("", &b) + expected = "1 error: 42: super annoying error\n" + assertWithMsg(t, expected, b) +} diff --git a/data/transactions/logic/box.go b/data/transactions/logic/box.go index 0f82f9a560..ebc1c25f2e 100644 --- a/data/transactions/logic/box.go +++ b/data/transactions/logic/box.go @@ -17,7 +17,6 @@ package logic import ( - "encoding/binary" "fmt" "github.com/algorand/go-algorand/data/basics" @@ -283,36 +282,3 @@ func opBoxPut(cx *EvalContext) error { appAddr := cx.getApplicationAddress(cx.appID) return cx.Ledger.NewBox(cx.appID, name, value, appAddr) } - -const boxPrefix = "bx:" -const boxPrefixLength = len(boxPrefix) -const boxNameIndex = boxPrefixLength + 8 // len("bx:") + 8 (appIdx, big-endian) - -// MakeBoxKey creates the key that a box named `name` under app `appIdx` should use. -func MakeBoxKey(appIdx basics.AppIndex, name string) string { - /* This format is chosen so that a simple indexing scheme on the key would - allow for quick lookups of all the boxes of a certain app, or even all - the boxes of a certain app with a certain prefix. - - The "bx:" prefix is so that the kvstore might be usable for things - besides boxes. - */ - key := make([]byte, boxNameIndex+len(name)) - copy(key, boxPrefix) - binary.BigEndian.PutUint64(key[boxPrefixLength:], uint64(appIdx)) - copy(key[boxNameIndex:], name) - return string(key) -} - -// SplitBoxKey extracts an appid and box name from a string that was created by MakeBoxKey() -func SplitBoxKey(key string) (basics.AppIndex, string, error) { - if len(key) < boxNameIndex { - return 0, "", fmt.Errorf("SplitBoxKey() cannot extract AppIndex as key (%s) too short (length=%d)", key, len(key)) - } - if key[:boxPrefixLength] != boxPrefix { - return 0, "", fmt.Errorf("SplitBoxKey() illegal app box prefix in key (%s). Expected prefix '%s'", key, boxPrefix) - } - keyBytes := []byte(key) - app := basics.AppIndex(binary.BigEndian.Uint64(keyBytes[boxPrefixLength:boxNameIndex])) - return app, key[boxNameIndex:], nil -} diff --git a/data/transactions/logic/box_test.go b/data/transactions/logic/box_test.go index fbca75ee0b..77c3adf1b2 100644 --- a/data/transactions/logic/box_test.go +++ b/data/transactions/logic/box_test.go @@ -27,7 +27,6 @@ import ( "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" - "github.com/stretchr/testify/require" ) func TestBoxNewDel(t *testing.T) { @@ -568,45 +567,3 @@ func TestBoxTotals(t *testing.T) { logic.TestApp(t, `int 888; app_params_get AppAddress; assert; acct_params_get AcctTotalBoxBytes; pop; int 35; ==`, ep) } - -func TestMakeBoxKey(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - - type testCase struct { - description string - name string - app basics.AppIndex - key string - err string - } - - pp := func(tc testCase) string { - return fmt.Sprintf("<<<%s>>> (name, app) = (%#v, %d) --should--> key = %#v (err = [%s])", tc.description, tc.name, tc.app, tc.key, tc.err) - } - - var testCases = []testCase{ - // COPACETIC: - {"zero appid", "stranger", 0, "bx:\x00\x00\x00\x00\x00\x00\x00\x00stranger", ""}, - {"typical", "348-8uj", 131231, "bx:\x00\x00\x00\x00\x00\x02\x00\x9f348-8uj", ""}, - {"empty box name", "", 42, "bx:\x00\x00\x00\x00\x00\x00\x00*", ""}, - {"random byteslice", "{\xbb\x04\a\xd1\xe2\xc6I\x81{", 13475904583033571713, "bx:\xbb\x04\a\xd1\xe2\xc6I\x81{\xbb\x04\a\xd1\xe2\xc6I\x81{", ""}, - - // ERRORS: - {"too short", "", 0, "stranger", "SplitBoxKey() cannot extract AppIndex as key (stranger) too short (length=8)"}, - {"wrong prefix", "", 0, "strangersINTHEdark", "SplitBoxKey() illegal app box prefix in key (strangersINTHEdark). Expected prefix 'bx:'"}, - } - - for _, tc := range testCases { - app, name, err := logic.SplitBoxKey(tc.key) - - if tc.err == "" { - key := logic.MakeBoxKey(tc.app, tc.name) - require.Equal(t, tc.app, app, pp(tc)) - require.Equal(t, tc.name, name, pp(tc)) - require.Equal(t, tc.key, key, pp(tc)) - } else { - require.EqualError(t, err, tc.err, pp(tc)) - } - } -} diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 2815c2e97c..f7bb120482 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -1822,6 +1822,15 @@ func opBytesSqrt(cx *EvalContext) error { return nil } +func nonzero(b []byte) []byte { + for i := range b { + if b[i] != 0 { + return b[i:] + } + } + return nil +} + func opBytesLt(cx *EvalContext) error { last := len(cx.stack) - 1 prev := last - 1 @@ -1830,9 +1839,11 @@ func opBytesLt(cx *EvalContext) error { return errors.New("math attempted on large byte-array") } - rhs := new(big.Int).SetBytes(cx.stack[last].Bytes) - lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes) - cx.stack[prev] = boolToSV(lhs.Cmp(rhs) < 0) + rhs := nonzero(cx.stack[last].Bytes) + lhs := nonzero(cx.stack[prev].Bytes) + + cx.stack[prev] = boolToSV(len(lhs) < len(rhs) || bytes.Compare(lhs, rhs) < 0) + cx.stack = cx.stack[:last] return nil } @@ -1866,9 +1877,10 @@ func opBytesEq(cx *EvalContext) error { return errors.New("math attempted on large byte-array") } - rhs := new(big.Int).SetBytes(cx.stack[last].Bytes) - lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes) - cx.stack[prev] = boolToSV(lhs.Cmp(rhs) == 0) + rhs := nonzero(cx.stack[last].Bytes) + lhs := nonzero(cx.stack[prev].Bytes) + + cx.stack[prev] = boolToSV(bytes.Equal(lhs, rhs)) cx.stack = cx.stack[:last] return nil } diff --git a/data/transactions/logic/evalCrypto_test.go b/data/transactions/logic/evalCrypto_test.go index 26665c7041..65f0787a97 100644 --- a/data/transactions/logic/evalCrypto_test.go +++ b/data/transactions/logic/evalCrypto_test.go @@ -147,18 +147,27 @@ func BenchmarkVerify(b *testing.B) { benches := [][]string{ {"pop", "", "int 1234576; int 6712; pop; pop", "int 1"}, {"add", "", "int 1234576; int 6712; +; pop", "int 1"}, - /* - {"ed25519verify_bare", "", `byte 0x - byte 0x - addr - ed25519verify_bare - assert`, "int 1"},*/ - {"ecdsa_verify", "", `byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f + {"ed25519verify_bare", "", ` +byte 0x62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd +byte 0xaab40a8b4f1f386504af2473804abbc03bbd94506e8e0c8db881fc2b2c3aee65b867b25caa47fa25ae2105bf1731398df336213707f2d25f9b1d31b3dc133307; +addr C7ZCK6N2AJQMVEP4FRTK2UW45UFR6DKPRJHJVWB5O4VQOZMFPK2KCMR7M4 +ed25519verify_bare; assert +`, "int 1"}, + {"ecdsa_verify k1", "", ` +byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f byte 0x5eb27782eb1a5df8de9a5d51613ad5ca730840ddf4af919c6feb15cde14f9978 byte 0x0cb3c0d636ed991ee030d09c295de3121eb166cb9e1552cf0ef0fb2358f35f0f byte 0x79de0699673571df1de8486718d06a3e7838f6831ec4ef3fb963788fbfb773b7 byte 0xd76446a3393af3e2eefada16df80cc6a881a56f4cf41fa2ab4769c5708ce878d ecdsa_verify Secp256k1 +assert`, "int 1"}, + {"ecdsa_verify r1", "", ` +byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f +byte 0xc010fc83ea196d6f5ce8a44637060bdcfb5bf1199cfc5bb893684d450c4f160c +byte 0x8e391a7b9cd75a99e8ebfe703036caebd9e91ae8339bd7e2abfb0f273eb8e972 +byte 0x13e49a19378bbfa8d55ac81a35b87d7bae456c79fcf04a78803d8eb45b253fab +byte 0xa2d237cd897ca70787abf04d2155c6dc2fbe26fd642e0472cd75c13dc919ef1a +ecdsa_verify Secp256r1 assert`, "int 1"}, {"vrf_verify", "", `byte 0x72 byte 0xae5b66bdf04b4c010bfe32b2fc126ead2107b697634f6f7337b9bff8785ee111200095ece87dde4dbe87343f6df3b107d91798c8a7eb1245d3bb9c5aafb093358c13e6ae1111a55717e895fd15f99f07 diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 8d23b20416..f63eae7dad 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -3728,27 +3728,37 @@ main: } func BenchmarkByteLogic(b *testing.B) { + e64 := "byte 0x8090a0b0c0d0e0f0;" + o64 := "byte 0x1020304050607080;" + hex128e := "90a0b0c0d0e0f0001020304050607080" + hex128o := "102030405060708090a0b0c0d0e0f000" + e128 := "byte 0x" + strings.Repeat(hex128e, 1) + ";" + o128 := "byte 0x" + strings.Repeat(hex128o, 1) + ";" + e256 := "byte 0x" + strings.Repeat(hex128e, 2) + ";" + o256 := "byte 0x" + strings.Repeat(hex128o, 2) + ";" + e512 := "byte 0x" + strings.Repeat(hex128e, 4) + ";" + o512 := "byte 0x" + strings.Repeat(hex128o, 4) + ";" + benches := [][]string{ - {"b&", "", "byte 0x012345678901feab; byte 0x01ffffffffffffff; b&; pop", "int 1"}, - {"b|", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b|; pop", "int 1"}, - {"b^", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b^; pop", "int 1"}, - {"b~", "byte 0x0123457673624736", "b~", "pop; int 1"}, - - {"b&big", - "byte 0x0123457601234576012345760123457601234576012345760123457601234576", - "byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b&", - "pop; int 1"}, - {"b|big", - "byte 0x0123457601234576012345760123457601234576012345760123457601234576", - "byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b|", - "pop; int 1"}, - {"b^big", "", // u256^u256 - `byte 0x123457601234576012345760123457601234576012345760123457601234576a - byte 0xf123457601234576012345760123457601234576012345760123457601234576; b^; pop`, - "int 1"}, - {"b~big", "byte 0xa123457601234576012345760123457601234576012345760123457601234576", - "b~", - "pop; int 1"}, + {"b& 8", "", e64 + o64 + "b&; pop", "int 1"}, + {"b| 8", "", e64 + o64 + "b|; pop", "int 1"}, + {"b^ 8", "", e64 + o64 + "b^; pop", "int 1"}, + {"b~ 8", e64, "b~", "pop; int 1"}, + + {"b& 16", "", e128 + o128 + "b&; pop", "int 1"}, + {"b| 16", "", e128 + o128 + "b|; pop", "int 1"}, + {"b^ 16", "", e128 + o128 + "b^; pop", "int 1"}, + {"b~ 16", e128, "b~", "pop; int 1"}, + + {"b& 32", "", e256 + o256 + "b&; pop", "int 1"}, + {"b| 32", "", e256 + o256 + "b|; pop", "int 1"}, + {"b^ 32", "", e256 + o256 + "b^; pop", "int 1"}, + {"b~ 32", e256, "b~", "pop; int 1"}, + + {"b& 64", "", e512 + o512 + "b&; pop", "int 1"}, + {"b| 64", "", e512 + o512 + "b|; pop", "int 1"}, + {"b^ 64", "", e512 + o512 + "b^; pop", "int 1"}, + {"b~ 64", e512, "b~", "pop; int 1"}, } for _, bench := range benches { b.Run(bench[0], func(b *testing.B) { @@ -3759,44 +3769,79 @@ func BenchmarkByteLogic(b *testing.B) { } func BenchmarkByteMath(b *testing.B) { + u64 := "byte 0x8090a0b0c0d0e0f0;" + hex128 := "102030405060708090a0b0c0d0e0f000" + u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";" + u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";" + u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";" + benches := [][]string{ - {"bpop", "", "byte 0x01ffffffffffffff; pop", "int 1"}, - - {"b+", "byte 0x01234576", "byte 0x01ffffffffffffff; b+", "pop; int 1"}, - {"b-", "byte 0x0ffff1234576", "byte 0x1202; b-", "pop; int 1"}, - {"b*", "", "byte 0x01234576; byte 0x0223627389; b*; pop", "int 1"}, - {"b/", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"}, - {"b%", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"}, - {"bsqrt", "", "byte 0x0123457673624736; bsqrt; pop", "int 1"}, - - {"b+big", // u256 + u256 - "byte 0x0123457601234576012345760123457601234576012345760123457601234576", - "byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b+", - "pop; int 1"}, - {"b-big", // second is a bit small, so we can subtract it over and over - "byte 0x0123457601234576012345760123457601234576012345760123457601234576", - "byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b-", - "pop; int 1"}, - {"b*big", "", // u256*u256 - `byte 0xa123457601234576012345760123457601234576012345760123457601234576 - byte 0xf123457601234576012345760123457601234576012345760123457601234576; b*; pop`, - "int 1"}, - {"b/big", "", // u256 / u128 (half sized divisor seems pessimal) - `byte 0xa123457601234576012345760123457601234576012345760123457601234576 - byte 0x34576012345760123457601234576312; b/; pop`, - "int 1"}, - {"b%big", "", // u256 / u128 (half sized divisor seems pessimal) - `byte 0xa123457601234576012345760123457601234576012345760123457601234576 - byte 0x34576012345760123457601234576312; b/; pop`, - "int 1"}, - {"bsqrt-big", "", - `byte 0xa123457601234576012345760123457601234576012345760123457601234576 - bsqrt; pop`, - "int 1"}, + {"bytec", u128 + "pop"}, + + {"b+ 128", u128 + u128 + "b+; pop"}, + {"b- 128", u128 + u128 + "b-; pop"}, + {"b* 128", u128 + u128 + "b*; pop"}, + // half sized divisor seems pessimal for / and % + {"b/ 128", u128 + u64 + "b/; pop"}, + {"b% 128", u128 + u64 + "b%; pop"}, + {"bsqrt 128", u128 + "bsqrt; pop"}, + + {"b+ 256", u256 + u256 + "b+; pop"}, + {"b- 256", u256 + u256 + "b-; pop"}, + {"b* 256", u256 + u256 + "b*; pop"}, + {"b/ 256", u256 + u128 + "b/; pop"}, + {"b% 256", u256 + u128 + "b%; pop"}, + {"bsqrt 256", u256 + "bsqrt; pop"}, + + {"b+ 512", u512 + u512 + "b+; pop"}, + {"b- 512", u512 + u512 + "b-; pop"}, + {"b* 512", u512 + u512 + "b*; pop"}, + {"b/ 512", u512 + u256 + "b/; pop"}, + {"b% 512", u512 + u256 + "b%; pop"}, + {"bsqrt 512", u512 + "bsqrt; pop"}, + + {"bytec recheck", u128 + "pop"}, } for _, bench := range benches { b.Run(bench[0], func(b *testing.B) { - benchmarkOperation(b, bench[1], bench[2], bench[3]) + b.ReportAllocs() + benchmarkOperation(b, "", bench[1], "int 1") + }) + } +} + +func BenchmarkByteCompare(b *testing.B) { + u64 := "byte 0x8090a0b0c0d0e0f0;" + hex128 := "102030405060708090a0b0c0d0e0f000" + u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";" + u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";" + u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";" + //u4k := "byte 0x" + strings.Repeat(hex128, 256) + ";" + + benches := [][]string{ + {"b== 64", u64 + u64 + "b==; pop"}, + {"b< 64", u64 + u64 + "b<; pop"}, + {"b<= 64", u64 + u64 + "b<=; pop"}, + {"b== 128", u128 + u128 + "b==; pop"}, + {"b< 128", u128 + u128 + "b<; pop"}, + {"b<= 128", u128 + u128 + "b<=; pop"}, + {"b== 256", u256 + u256 + "b==; pop"}, + {"b< 256", u256 + u256 + "b<; pop"}, + {"b<= 256", u256 + u256 + "b<=; pop"}, + {"b== 512", u512 + u512 + "b==; pop"}, + {"b< 512", u512 + u512 + "b<; pop"}, + {"b<= 512", u512 + u512 + "b<=; pop"}, + // These can only be run with the maxByteMathSize check removed. They + // show that we can remove that check in a later AVM version, as there + // is no appreciable cost to even a 4k compare. + // {"b== 4k", u4k + u4k + "b==; pop"}, + // {"b< 4k", u4k + u4k + "b<; pop"}, + // {"b<= 4k", u4k + u4k + "b<=; pop"}, + } + for _, bench := range benches { + b.Run(bench[0], func(b *testing.B) { + b.ReportAllocs() + benchmarkOperation(b, "", bench[1], "int 1") }) } } @@ -4871,9 +4916,20 @@ func TestBytesCompare(t *testing.T) { testPanics(t, "byte 0x10; int 65; bzero; b<=", 4) testAccepts(t, "byte 0x10; int 64; bzero; b>", 4) testPanics(t, "byte 0x10; int 65; bzero; b>", 4) + testAccepts(t, "byte 0x1010; byte 0x10; b<; !", 4) + + // All zero input are interesting, because they lead to bytes.Compare being + // called with nils. Show that is correct. + testAccepts(t, "byte 0x10; byte 0x00; b<; !", 4) + testAccepts(t, "byte 0x10; byte 0x0000; b<; !", 4) + testAccepts(t, "byte 0x00; byte 0x10; b<", 4) + testAccepts(t, "byte 0x0000; byte 0x10; b<", 4) + testAccepts(t, "byte 0x0000; byte 0x00; b<; !", 4) + testAccepts(t, "byte 0x; byte 0x00; b==", 4) testAccepts(t, "byte 0x11; byte 0x10; b>", 4) testAccepts(t, "byte 0x11; byte 0x0010; b>", 4) + testAccepts(t, "byte 0x1010; byte 0x11; b>", 4) testAccepts(t, "byte 0x11; byte 0x10; b>=", 4) testAccepts(t, "byte 0x11; byte 0x0011; b>=", 4) diff --git a/data/transactions/logic/parsing.go b/data/transactions/logic/parsing.go deleted file mode 100644 index 6bb2c5c714..0000000000 --- a/data/transactions/logic/parsing.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package logic - -import ( - "encoding/base32" - "encoding/base64" - "encoding/binary" - "fmt" - "strconv" - "strings" - - "github.com/algorand/avm-abi/abi" - "github.com/algorand/go-algorand/data/basics" -) - -// AppCallBytes represents an encoding and a value of an app call argument. -type AppCallBytes struct { - Encoding string `codec:"encoding"` - Value string `codec:"value"` -} - -// NewAppCallBytes parses an argument of the form "encoding:value" to AppCallBytes. -func NewAppCallBytes(arg string) (AppCallBytes, error) { - parts := strings.SplitN(arg, ":", 2) - if len(parts) != 2 { - return AppCallBytes{}, fmt.Errorf("all arguments and box names should be of the form 'encoding:value'") - } - return AppCallBytes{ - Encoding: parts[0], - Value: parts[1], - }, nil -} - -// Raw converts an AppCallBytes arg to a byte array. -func (arg AppCallBytes) Raw() (rawValue []byte, parseErr error) { - switch arg.Encoding { - case "str", "string": - rawValue = []byte(arg.Value) - case "int", "integer": - num, err := strconv.ParseUint(arg.Value, 10, 64) - if err != nil { - parseErr = fmt.Errorf("Could not parse uint64 from string (%s): %v", arg.Value, err) - return - } - ibytes := make([]byte, 8) - binary.BigEndian.PutUint64(ibytes, num) - rawValue = ibytes - case "addr", "address": - addr, err := basics.UnmarshalChecksumAddress(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not unmarshal checksummed address from string (%s): %v", arg.Value, err) - return - } - rawValue = addr[:] - case "b32", "base32", "byte base32": - data, err := base32.StdEncoding.DecodeString(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not decode base32-encoded string (%s): %v", arg.Value, err) - return - } - rawValue = data - case "b64", "base64", "byte base64": - data, err := base64.StdEncoding.DecodeString(arg.Value) - if err != nil { - parseErr = fmt.Errorf("Could not decode base64-encoded string (%s): %v", arg.Value, err) - return - } - rawValue = data - case "abi": - typeAndValue := strings.SplitN(arg.Value, ":", 2) - if len(typeAndValue) != 2 { - parseErr = fmt.Errorf("Could not decode abi string (%s): should split abi-type and abi-value with colon", arg.Value) - return - } - abiType, err := abi.TypeOf(typeAndValue[0]) - if err != nil { - parseErr = fmt.Errorf("Could not decode abi type string (%s): %v", typeAndValue[0], err) - return - } - value, err := abiType.UnmarshalFromJSON([]byte(typeAndValue[1])) - if err != nil { - parseErr = fmt.Errorf("Could not decode abi value string (%s):%v ", typeAndValue[1], err) - return - } - return abiType.Encode(value) - default: - parseErr = fmt.Errorf("Unknown encoding: %s", arg.Encoding) - } - return -} diff --git a/data/transactions/logic/parsing_test.go b/data/transactions/logic/parsing_test.go deleted file mode 100644 index 14830b011f..0000000000 --- a/data/transactions/logic/parsing_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package logic - -import ( - "encoding/base32" - "encoding/base64" - "encoding/binary" - "fmt" - "math" - "testing" - - "github.com/algorand/avm-abi/abi" - "github.com/algorand/go-algorand/data/basics" - - "github.com/algorand/go-algorand/test/partitiontest" - "github.com/stretchr/testify/require" -) - -func TestNewAppCallBytes(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - - t.Run("errors", func(t *testing.T) { - t.Parallel() - _, err := NewAppCallBytes("hello") - require.Error(t, err) - - for _, v := range []string{":x", "int:-1"} { - acb, err := NewAppCallBytes(v) - _, err = acb.Raw() - require.Error(t, err) - } - }) - - for _, v := range []string{"hello", "1:2"} { - for _, e := range []string{"str", "string"} { - v, e := v, e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) { - t.Parallel() - acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - require.Equal(t, v, string(r)) - }) - } - - for _, e := range []string{"b32", "base32", "byte base32"} { - ve := base32.StdEncoding.EncodeToString([]byte(v)) - e := e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, ve), func(t *testing.T) { - acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, ve)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - require.Equal(t, ve, base32.StdEncoding.EncodeToString(r)) - }) - } - - for _, e := range []string{"b64", "base64", "byte base64"} { - ve := base64.StdEncoding.EncodeToString([]byte(v)) - e := e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, ve), func(t *testing.T) { - t.Parallel() - acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, ve)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - require.Equal(t, ve, base64.StdEncoding.EncodeToString(r)) - }) - } - } - - for _, v := range []uint64{1, 0, math.MaxUint64} { - for _, e := range []string{"int", "integer"} { - v, e := v, e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) { - t.Parallel() - acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - require.Equal(t, v, binary.BigEndian.Uint64(r)) - }) - } - } - - for _, v := range []string{"737777777777777777777777777777777777777777777777777UFEJ2CI"} { - for _, e := range []string{"addr", "address"} { - v, e := v, e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) { - t.Parallel() - acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - addr, err := basics.UnmarshalChecksumAddress(v) - require.NoError(t, err) - expectedBytes := []byte{} - expectedBytes = addr[:] - require.Equal(t, expectedBytes, r) - }) - } - } - - type abiCase struct { - abiType, rawValue string - } - for _, v := range []abiCase{ - { - `(uint64,string,bool[])`, - `[399,"should pass",[true,false,false,true]]`, - }} { - for _, e := range []string{"abi"} { - v, e := v, e - t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) { - t.Parallel() - acb, err := NewAppCallBytes(fmt.Sprintf( - "%v:%v:%v", e, v.abiType, v.rawValue)) - require.NoError(t, err) - r, err := acb.Raw() - require.NoError(t, err) - require.NotEmpty(t, r) - - // Confirm round-trip works. - abiType, err := abi.TypeOf(v.abiType) - require.NoError(t, err) - d, err := abiType.Decode(r) - require.NoError(t, err) - vv, err := abiType.Encode(d) - require.NoError(t, err) - require.Equal(t, r, vv) - }) - } - } -} diff --git a/docker/README.md b/docker/README.md index b3027655e6..78099e6027 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,5 +1,7 @@ # Algod Container +[![DockerHub](https://img.shields.io/badge/DockerHub-blue)](https://hub.docker.com/r/algorand/algod) + General purpose algod container image. ## Image Configuration @@ -39,11 +41,12 @@ Configuration can be modified by specifying certain files. These can be changed | File | Description | | ---- | ----------- | -| /etc/config.json | Override default configurations by providing your own file. | -| /etc/algod.token | Override default randomized REST API token. | -| /etc/algod.admin.token | Override default randomized REST API admin token. | +| /etc/algorand/config.json | Override default configurations by providing your own file. | +| /etc/algorand/algod.token | Override default randomized REST API token. | +| /etc/algorand/algod.admin.token | Override default randomized REST API admin token. | +| /etc/algorand/logging.config | Use a custom [logging.config](https://developer.algorand.org/docs/run-a-node/reference/telemetry-config/#configuration) file for configuring telemetry. | -TODO: `/etc/template.json` for overriding the private network topology. +TODO: `/etc/algorand/template.json` for overriding the private network topology. ## Example Configuration @@ -66,7 +69,7 @@ Explanation of parts: * `-p 4190:8080` maps the internal algod REST API to local port 4190 * `-e NETWORK=` can be set to any of the supported public networks. * `-e FAST_CATCHUP=` causes fast catchup to start shortly after launching the network. -* `-e TELEMETRY_NAME=` enables telemetry reporting to Algorand for network health analysis. +* `-e TELEMETRY_NAME=` enables telemetry reporting to Algorand for network health analysis. The value of this variable takes precedence over the `name` attribute set in `/etc/algorand/logging.config`. * `-e TOKEN=` sets the REST API token to use. * `-v ${PWD}/data:/algod/data/` mounts a local volume to the data directory, which can be used to restart and upgrade the deployment. @@ -74,6 +77,37 @@ Explanation of parts: The data directory located at `/algod/data`. Mounting a volume at that location will allow you to shutdown and resume the node. +### Volume Permissions + +The container executes in the context of the `algorand` user with it's own UID and GID which is handled differently depending on your operating system. Here are a few options for how to work with this environment: + +#### Named Volume + +Using a named volume will work without any specific configuration in most cases: + +```bash +docker volume create algod-data +docker run -it --rm -d -v algod-data:/algod/data algorand/algod +``` + +#### Local Directory without SELinux + +Explicitly set the UID and GID of the container: + +```bash +docker run -it --rm -d -v /srv/data:/algod/data -u $UID:$GID algorand/algod +``` + +#### Local Directory with SELinux + +Set the UID and GID of the container while add the `Z` option to the volume definition: + +```bash +docker run -it --rm -d -v /srv/data:/algod/data:Z -u $UID:$GID algorand/algod +``` + +> See the documentation on [configuring the selinux label](https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label). + ### Private Network Private networks work a little bit differently. They are configured with, potentially, several data directories. The default topology supplied with this container is installed to `/algod/`, and has a single node named `data`. This means the private network has a data directory at `/algod/data`, matching the production configuration. diff --git a/docker/files/build/install.sh b/docker/files/build/install.sh index 9cfef5e858..abc57cc5ec 100755 --- a/docker/files/build/install.sh +++ b/docker/files/build/install.sh @@ -87,6 +87,6 @@ BUILD_NUMBER="" BRANCH="$BRANCH" make build shopt -s extglob -cd "$BINDIR" && rm -vrf !(algocfg|algod|algoh|algokey|carpenter|catchupsrv|ddconfig.sh|diagcfg|find-nodes.sh|goal|kmd|msgpacktool|node_exporter|tealcut|tealdbg|update.sh|updater|COPYING) +cd "$BINDIR" && rm -vrf !(algocfg|algod|algokey|diagcfg|goal|kmd|msgpacktool|node_exporter|tealdbg|update.sh|updater|COPYING) "$BINDIR"/algod -v diff --git a/docker/files/run/run.sh b/docker/files/run/run.sh index 24e59ae145..627665dd6f 100755 --- a/docker/files/run/run.sh +++ b/docker/files/run/run.sh @@ -13,25 +13,25 @@ function apply_configuration() { cd "$ALGORAND_DATA" # check for config file overrides. - if [ -f "/etc/config.json" ]; then - cp /etc/config.json config.json + if [ -f "/etc/algorand/config.json" ]; then + cp /etc/algorand/config.json config.json fi - if [ -f "/etc/algod.token" ]; then - cp /etc/algod.token algod.token + if [ -f "/etc/algorand/algod.token" ]; then + cp /etc/algorand/algod.token algod.token fi - if [ -f "/etc/algod.admin.token" ]; then - cp /etc/algod.admin.token algod.admin.token + if [ -f "/etc/algorand/algod.admin.token" ]; then + cp /etc/algorand/algod.admin.token algod.admin.token fi - if [ -f "/etc/logging.config" ]; then - cp /etc/logging.config logging.config + if [ -f "/etc/algorand/logging.config" ]; then + cp /etc/algorand/logging.config logging.config fi # check for environment variable overrides. if [ "$TOKEN" != "" ]; then - echo "$TOKEN" > algod.token + echo "$TOKEN" >algod.token fi if [ "$ADMIN_TOKEN" != "" ]; then - echo "$ADMIN_TOKEN" > algod.admin.token + echo "$ADMIN_TOKEN" >algod.admin.token fi # configure telemetry @@ -78,8 +78,8 @@ function configure_data_dir() { } function start_new_public_network() { - cd /node - if [ ! -d "run/genesis/$NETWORK" ]; then + cd /algod + if [ ! -d "/node/run/genesis/${NETWORK}" ]; then echo "No genesis file for '$NETWORK' is available." exit 1 fi @@ -88,7 +88,7 @@ function start_new_public_network() { cd "$ALGORAND_DATA" - cp "/node/run/genesis/$NETWORK/genesis.json" genesis.json + cp "/node/run/genesis/${NETWORK}/genesis.json" genesis.json cp /node/run/config.json.example config.json configure_data_dir @@ -111,18 +111,17 @@ function start_private_network() { apply_configuration # TODO: Is there a way to properly exec a private network? - goal network start -r "$ALGORAND_DATA/.." - tail -f "$ALGORAND_DATA/node.log" + goal network start -r "${ALGORAND_DATA}/.." + tail -f "${ALGORAND_DATA}/node.log" } function start_new_private_network() { - cd /node local TEMPLATE="template.json" if [ "$DEV_MODE" ]; then TEMPLATE="devmode_template.json" fi - sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "run/$TEMPLATE" - goal network create -n dockernet -r "$ALGORAND_DATA/.." -t "run/$TEMPLATE" + sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "/node/run/$TEMPLATE" + goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE" configure_data_dir start_private_network } diff --git a/go.mod b/go.mod index d605367586..a1f20d7dfe 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/DataDog/zstd v1.5.2 - github.com/algorand/avm-abi v0.1.1 + github.com/algorand/avm-abi v0.2.0 github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414 github.com/algorand/go-codec/codec v1.1.8 github.com/algorand/go-deadlock v0.2.2 diff --git a/go.sum b/go.sum index 0cb01bcb2e..68a08e0848 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/algorand/avm-abi v0.1.1 h1:dbyQKzXiyaEbzpmqXFB30yAhyqseBsyqXTyZbNbkh2Y= -github.com/algorand/avm-abi v0.1.1/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= +github.com/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k= +github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414 h1:nwYN+GQ7Z5OOfZwqBO1ma7DSlP7S1YrKWICOyjkwqrc= github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414/go.mod h1:OkQyHlGvS0kLNcIWbC21/uQcnbfwSOQm+wiqWwBG9pQ= github.com/algorand/go-codec v1.1.8/go.mod h1:XhzVs6VVyWMLu6cApb9/192gBjGRVGm5cX5j203Heg4= diff --git a/ledger/acctdeltas_test.go b/ledger/acctdeltas_test.go index de5efc9f54..21ac1e5dcc 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -32,15 +32,14 @@ import ( "testing" "time" - "github.com/algorand/go-algorand/data/transactions/logic" - "github.com/algorand/go-algorand/ledger/encoded" - "github.com/stretchr/testify/require" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/ledger/encoded" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store" storetesting "github.com/algorand/go-algorand/ledger/store/testing" @@ -1181,12 +1180,12 @@ func BenchmarkLookupKeyByPrefix(b *testing.B) { crypto.RandBytes(nameBuffer) crypto.RandBytes(valueBuffer) appID := basics.AppIndex(crypto.RandUint64()) - boxKey := logic.MakeBoxKey(appID, string(nameBuffer)) + boxKey := apps.MakeBoxKey(uint64(appID), string(nameBuffer)) err = writer.UpsertKvPair(boxKey, valueBuffer) require.NoError(b, err) if i == 0 { - prefix = logic.MakeBoxKey(appID, "") + prefix = apps.MakeBoxKey(uint64(appID), "") } } err = tx.Commit() diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 0642bb0096..723e2c1986 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -31,11 +31,11 @@ import ( "github.com/stretchr/testify/require" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" - "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/ledger/internal" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store" @@ -1290,7 +1290,7 @@ func TestBoxNamesByAppIDs(t *testing.T) { boxChange := ledgercore.KvValueDelta{Data: []byte(boxName)} auNewBlock(t, currentRound, au, accts, opts, map[string]ledgercore.KvValueDelta{ - logic.MakeBoxKey(appID, boxName): boxChange, + apps.MakeBoxKey(uint64(appID), boxName): boxChange, }) auCommitSync(t, currentRound, au, ml) @@ -1305,10 +1305,10 @@ func TestBoxNamesByAppIDs(t *testing.T) { // check input, see all present keys are all still there for _, storedBoxName := range testingBoxNames[:i+1] { - res, err := au.LookupKeysByPrefix(currentRound, logic.MakeBoxKey(boxNameToAppID[storedBoxName], ""), 10000) + res, err := au.LookupKeysByPrefix(currentRound, apps.MakeBoxKey(uint64(boxNameToAppID[storedBoxName]), ""), 10000) require.NoError(t, err) require.Len(t, res, 1) - require.Equal(t, logic.MakeBoxKey(boxNameToAppID[storedBoxName], storedBoxName), res[0]) + require.Equal(t, apps.MakeBoxKey(uint64(boxNameToAppID[storedBoxName]), storedBoxName), res[0]) } } @@ -1319,12 +1319,12 @@ func TestBoxNamesByAppIDs(t *testing.T) { // remove inserted box appID := boxNameToAppID[boxName] auNewBlock(t, currentRound, au, accts, opts, map[string]ledgercore.KvValueDelta{ - logic.MakeBoxKey(appID, boxName): {}, + apps.MakeBoxKey(uint64(appID), boxName): {}, }) auCommitSync(t, currentRound, au, ml) // ensure recently removed key is not present, and it is not part of the result - res, err := au.LookupKeysByPrefix(currentRound, logic.MakeBoxKey(boxNameToAppID[boxName], ""), 10000) + res, err := au.LookupKeysByPrefix(currentRound, apps.MakeBoxKey(uint64(boxNameToAppID[boxName]), ""), 10000) require.NoError(t, err) require.Len(t, res, 0) } diff --git a/ledger/catchpointwriter_test.go b/ledger/catchpointwriter_test.go index 0530991e01..478df2bb25 100644 --- a/ledger/catchpointwriter_test.go +++ b/ledger/catchpointwriter_test.go @@ -32,12 +32,12 @@ import ( "github.com/stretchr/testify/require" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/crypto/merkletrie" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" - "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/encoded" "github.com/algorand/go-algorand/ledger/ledgercore" @@ -779,7 +779,7 @@ func TestCatchpointAfterTxns(t *testing.T) { values, err = l.LookupKeysByPrefix(l.Latest(), "bx:", 10) require.NoError(t, err) require.Len(t, values, 1) - v, err := l.LookupKv(l.Latest(), logic.MakeBoxKey(boxApp, "xxx")) + v, err := l.LookupKv(l.Latest(), apps.MakeBoxKey(uint64(boxApp), "xxx")) require.NoError(t, err) require.Equal(t, strings.Repeat("\x00", 24), string(v)) @@ -869,7 +869,7 @@ func TestCatchpointAfterBoxTxns(t *testing.T) { values, err := l.LookupKeysByPrefix(l.Latest(), "bx:", 10) require.NoError(t, err) require.Len(t, values, 1) - v, err := l.LookupKv(l.Latest(), logic.MakeBoxKey(boxApp, "xxx")) + v, err := l.LookupKv(l.Latest(), apps.MakeBoxKey(uint64(boxApp), "xxx")) require.NoError(t, err) require.Equal(t, strings.Repeat("f", 24), string(v)) } diff --git a/ledger/encoded/recordsV6_test.go b/ledger/encoded/recordsV6_test.go index e966bd60fe..9e5dd14384 100644 --- a/ledger/encoded/recordsV6_test.go +++ b/ledger/encoded/recordsV6_test.go @@ -20,10 +20,9 @@ import ( "math" "testing" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" - "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" @@ -36,7 +35,7 @@ func TestEncodedKVRecordV6Allocbounds(t *testing.T) { for version, params := range config.Consensus { require.GreaterOrEqualf(t, uint64(KVRecordV6MaxValueLength), params.MaxBoxSize, "Allocbound constant no longer valid as of consensus version %s", version) longestPossibleBoxName := string(make([]byte, params.MaxAppKeyLen)) - longestPossibleKey := logic.MakeBoxKey(basics.AppIndex(math.MaxUint64), longestPossibleBoxName) + longestPossibleKey := apps.MakeBoxKey(math.MaxUint64, longestPossibleBoxName) require.GreaterOrEqualf(t, KVRecordV6MaxValueLength, len(longestPossibleKey), "Allocbound constant no longer valid as of consensus version %s", version) } } diff --git a/ledger/internal/applications.go b/ledger/internal/applications.go index 37c50d321d..f0466b7d4a 100644 --- a/ledger/internal/applications.go +++ b/ledger/internal/applications.go @@ -19,6 +19,7 @@ package internal import ( "fmt" + "github.com/algorand/avm-abi/apps" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions/logic" @@ -222,7 +223,7 @@ func (cs *roundCowState) NewBox(appIdx basics.AppIndex, key string, value []byte return fmt.Errorf("box size too large: %d, maximum is %d", size, cs.proto.MaxBoxSize) } - fullKey := logic.MakeBoxKey(appIdx, key) + fullKey := apps.MakeBoxKey(uint64(appIdx), key) _, exists, err := cs.kvGet(fullKey) if err != nil { return err @@ -246,12 +247,12 @@ func (cs *roundCowState) NewBox(appIdx basics.AppIndex, key string, value []byte } func (cs *roundCowState) GetBox(appIdx basics.AppIndex, key string) ([]byte, bool, error) { - fullKey := logic.MakeBoxKey(appIdx, key) + fullKey := apps.MakeBoxKey(uint64(appIdx), key) return cs.kvGet(fullKey) } func (cs *roundCowState) SetBox(appIdx basics.AppIndex, key string, value []byte) error { - fullKey := logic.MakeBoxKey(appIdx, key) + fullKey := apps.MakeBoxKey(uint64(appIdx), key) old, ok, err := cs.kvGet(fullKey) if err != nil { return err @@ -267,7 +268,7 @@ func (cs *roundCowState) SetBox(appIdx basics.AppIndex, key string, value []byte } func (cs *roundCowState) DelBox(appIdx basics.AppIndex, key string, appAddr basics.Address) (bool, error) { - fullKey := logic.MakeBoxKey(appIdx, key) + fullKey := apps.MakeBoxKey(uint64(appIdx), key) value, ok, err := cs.kvGet(fullKey) if err != nil { diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go index ebe69e4b5e..4a27b70587 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -765,7 +765,7 @@ func (c *Client) ApplicationBoxes(appID uint64, maxBoxNum uint64) (resp model.Bo } // GetApplicationBoxByName takes an app's index and box name and returns its value. -// The box name should be of the form `encoding:value`. See logic.AppCallBytes for more information. +// The box name should be of the form `encoding:value`. See apps.AppCallBytes for more information. func (c *Client) GetApplicationBoxByName(index uint64, name string) (resp model.BoxResponse, err error) { algod, err := c.ensureAlgodClient() if err == nil { diff --git a/test/scripts/e2e_subs/e2e-app-simple.sh b/test/scripts/e2e_subs/e2e-app-simple.sh index e1f1458ce4..660487621d 100755 --- a/test/scripts/e2e_subs/e2e-app-simple.sh +++ b/test/scripts/e2e_subs/e2e-app-simple.sh @@ -122,3 +122,27 @@ ${gcmd} app optin --app-id $APPID --from $ACCOUNT # Succeed in clearing state for the app ${gcmd} app clear --app-id $APPID --from $ACCOUNT + + +# Empty program: +printf ' ' > "${TEMPDIR}/empty_clear.teal" + +# Fail to compile an empty program +RES=$(${gcmd} clerk compile "${TEMPDIR}/empty_clear.teal" 2>&1 | tr -d '\n' || true) +EXPERROR='Cannot assemble empty program text' +if [[ $RES != *"${EXPERROR}"* ]]; then + echo RES="$RES" + echo EXPERROR="$EXPERROR" + date '+clerk-compile-test FAIL wrong error for compiling empty program %Y%m%d_%H%M%S' + false +fi + +# Fail to create an app because the clear program is empty +RES=$(${gcmd} app create --creator "${ACCOUNT}" --approval-prog "${TEMPDIR}/simple.teal" --clear-prog "${TEMPDIR}/empty_clear.teal" --global-byteslices 0 --global-ints 0 --local-byteslices 0 --local-ints 0 2>&1 | tr -d '\n' || true) +EXPERROR='Cannot assemble empty program text' +if [[ $RES != *"${EXPERROR}"* ]]; then + echo RES="$RES" + echo EXPERROR="$EXPERROR" + date '+app-create-test FAIL wrong error for creating app with empty clear program %Y%m%d_%H%M%S' + false +fi diff --git a/tools/boxkey/convertBoxKey.go b/tools/boxkey/convertBoxKey.go new file mode 100644 index 0000000000..0d249fa1e0 --- /dev/null +++ b/tools/boxkey/convertBoxKey.go @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package main + +import ( + "encoding/base64" + "encoding/hex" + "flag" + "fmt" + + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions/logic" +) + +func main() { + var name string + var appIdx uint64 + flag.Uint64Var(&appIdx, "a", 0, "base64/algorand address to convert to the other") + flag.StringVar(&name, "n", "", "base64 box name") + flag.Parse() + + if appIdx == 0 && name == "" { + fmt.Println("provide input with '-a' and '-k' flags.") + return + } + + nameBytes, err := base64.StdEncoding.DecodeString(name) + if err != nil { + fmt.Println("invalid key value") + return + } + key := logic.MakeBoxKey(basics.AppIndex(appIdx), string(nameBytes)) + fmt.Println(base64.StdEncoding.EncodeToString([]byte(key))) + fmt.Println(hex.EncodeToString([]byte(key))) +}