From 850182caf3040ca06a590900918fed9c15eb9028 Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:15:34 +0100 Subject: [PATCH 1/7] fix(gnovm): forbid star expression when value is not a pointer (#2984) closes: #1088
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests
--- gnovm/pkg/gnolang/preprocess.go | 7 ++++++- gnovm/tests/files/ptr9.gno | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 gnovm/tests/files/ptr9.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 2d65dfa5cb1..10c55979520 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1757,7 +1757,12 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { case *KeyValueExpr: // NOTE: For simplicity we just // use the *CompositeLitExpr. - + // TRANS_LEAVE ----------------------- + case *StarExpr: + xt := evalStaticTypeOf(store, last, n.X) + if xt.Kind() != PointerKind && xt.Kind() != TypeKind { + panic(fmt.Sprintf("invalid operation: cannot indirect %s (variable of type %s)", n.X.String(), xt.String())) + } // TRANS_LEAVE ----------------------- case *SelectorExpr: xt := evalStaticTypeOf(store, last, n.X) diff --git a/gnovm/tests/files/ptr9.gno b/gnovm/tests/files/ptr9.gno new file mode 100644 index 00000000000..6e104942d81 --- /dev/null +++ b/gnovm/tests/files/ptr9.gno @@ -0,0 +1,9 @@ +package main + +func main() { + v := 1 + println(*v) +} + +// Error: +// main/files/ptr9.gno:5:10: invalid operation: cannot indirect v (variable of type int) From 494976da31d8c4c9d00a5074e9254429463b6291 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 31 Oct 2024 05:46:10 +0900 Subject: [PATCH 2/7] feat(p/json): remove unnecessary code and optimize (#2939) # Description Optimized the JSON package and simplified JSON node creation using the builder pattern. - in `buffer.gno` and `escape.gno` files are modified the use of map for lookup tables to use slice array instead. - refactor the `Unquote` function in `escape.gno` file - modified the existing functions that parsed numbers to use `strconv` package, and deleted related files and functions - especially, the `eisel_lemire` and `ryu` packages were deleted since they were files that had been added to handle `ParseUint` and `ParseFloat` in `strconv` package. ## JSON Generate Example **Plain JSON** ```go node := Builder(). WithString("name", "Alice"). WithNumber("age", 30). WithBool("is_student", false). Node() value, err := Marshal(node) if err != nil { t.Errorf("unexpected error: %s", err) } Output: {"name":"Alice","age":30,"is_student":false} ``` **Nested Structure** ```go node := Builder(). WriteString("name", "Alice"). WriteObject("address", func(b *NodeBuilder) { b.WriteString("city", "New York"). WriteNumber("zipcode", 10001) }). Node() // ... Output: {"name":"Alice","address":{"city":"New York","zipcode":10001}} ``` ## Benchmark Result for Unquote **Before** ```plain BenchmarkUnquote-8 12433488 98.06 ns/op 144 B/op 2 allocs/op BenchmarkUnquoteWorstCase-8 24727736 50.46 ns/op 48 B/op 1 allocs/op BenchmarkUnquoteBestCase-8 22542354 52.69 ns/op 48 B/op 1 allocs/op BenchmarkUnquoteEmptyString-8 394868628 3.067 ns/op 0 B/op 0 allocs/op ``` **After** ```plain BenchmarkUnquote-8 12464704 96.61 ns/op 144 B/op 2 allocs/op BenchmarkUnquoteWorstCase-8 25084070 48.02 ns/op 48 B/op 1 allocs/op BenchmarkUnquoteBestCase-8 23383227 52.66 ns/op 48 B/op 1 allocs/op BenchmarkUnquoteEmptyString-8 400496838 2.968 ns/op 0 B/op 0 allocs/op ``` --- examples/gno.land/p/demo/json/buffer.gno | 65 +- examples/gno.land/p/demo/json/buffer_test.gno | 35 +- examples/gno.land/p/demo/json/builder.gno | 89 ++ .../gno.land/p/demo/json/builder_test.gno | 103 +++ examples/gno.land/p/demo/json/decode_test.gno | 2 +- .../p/demo/json/eisel_lemire/eisel_lemire.gno | 839 ------------------ .../gno.land/p/demo/json/eisel_lemire/gno.mod | 1 - examples/gno.land/p/demo/json/encode.gno | 15 +- examples/gno.land/p/demo/json/encode_test.gno | 5 +- examples/gno.land/p/demo/json/errors.gno | 34 + examples/gno.land/p/demo/json/escape.gno | 114 ++- examples/gno.land/p/demo/json/escape_test.gno | 61 +- examples/gno.land/p/demo/json/gno.mod | 6 +- examples/gno.land/p/demo/json/indent.gno | 16 +- examples/gno.land/p/demo/json/node.gno | 56 +- examples/gno.land/p/demo/json/parser.gno | 161 +--- examples/gno.land/p/demo/json/parser_test.gno | 122 --- examples/gno.land/p/demo/json/ryu/License | 21 - .../gno.land/p/demo/json/ryu/floatconv.gno | 143 --- .../p/demo/json/ryu/floatconv_test.gno | 33 - examples/gno.land/p/demo/json/ryu/gno.mod | 1 - examples/gno.land/p/demo/json/ryu/ryu64.gno | 344 ------- examples/gno.land/p/demo/json/ryu/table.gno | 678 -------------- 23 files changed, 383 insertions(+), 2561 deletions(-) create mode 100644 examples/gno.land/p/demo/json/builder.gno create mode 100644 examples/gno.land/p/demo/json/builder_test.gno delete mode 100644 examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno delete mode 100644 examples/gno.land/p/demo/json/eisel_lemire/gno.mod create mode 100644 examples/gno.land/p/demo/json/errors.gno delete mode 100644 examples/gno.land/p/demo/json/ryu/License delete mode 100644 examples/gno.land/p/demo/json/ryu/floatconv.gno delete mode 100644 examples/gno.land/p/demo/json/ryu/floatconv_test.gno delete mode 100644 examples/gno.land/p/demo/json/ryu/gno.mod delete mode 100644 examples/gno.land/p/demo/json/ryu/ryu64.gno delete mode 100644 examples/gno.land/p/demo/json/ryu/table.gno diff --git a/examples/gno.land/p/demo/json/buffer.gno b/examples/gno.land/p/demo/json/buffer.gno index 23fb53fb0ea..a217ee653f9 100644 --- a/examples/gno.land/p/demo/json/buffer.gno +++ b/examples/gno.land/p/demo/json/buffer.gno @@ -3,7 +3,6 @@ package json import ( "errors" "io" - "strings" "gno.land/p/demo/ufmt" ) @@ -112,28 +111,6 @@ func (b *buffer) skip(bs byte) error { return io.EOF } -// skipAny moves the index until it encounters one of the given set of bytes. -func (b *buffer) skipAny(endTokens map[byte]bool) error { - for b.index < b.length { - if _, exists := endTokens[b.data[b.index]]; exists { - return nil - } - - b.index++ - } - - // build error message - var tokens []string - for token := range endTokens { - tokens = append(tokens, string(token)) - } - - return ufmt.Errorf( - "EOF reached before encountering one of the expected tokens: %s", - strings.Join(tokens, ", "), - ) -} - // skipAndReturnIndex moves the buffer index forward by one and returns the new index. func (b *buffer) skipAndReturnIndex() (int, error) { err := b.step() @@ -165,7 +142,7 @@ func (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) { // significantTokens is a map where the keys are the significant characters in a JSON path. // The values in the map are all true, which allows us to use the map as a set for quick lookups. -var significantTokens = map[byte]bool{ +var significantTokens = [256]bool{ dot: true, // access properties of an object dollarSign: true, // root object atSign: true, // current object @@ -174,7 +151,7 @@ var significantTokens = map[byte]bool{ } // filterTokens stores the filter expression tokens. -var filterTokens = map[byte]bool{ +var filterTokens = [256]bool{ aesterisk: true, // wildcard andSign: true, orSign: true, @@ -186,7 +163,7 @@ func (b *buffer) skipToNextSignificantToken() { for b.index < b.length { current := b.data[b.index] - if _, ok := significantTokens[current]; ok { + if significantTokens[current] { break } @@ -205,7 +182,7 @@ func (b *buffer) backslash() bool { count := 0 for i := b.index - 1; ; i-- { - if i >= b.length || b.data[i] != backSlash { + if b.data[i] != backSlash { break } @@ -220,7 +197,7 @@ func (b *buffer) backslash() bool { } // numIndex holds a map of valid numeric characters -var numIndex = map[byte]bool{ +var numIndex = [256]bool{ '0': true, '1': true, '2': true, @@ -255,11 +232,11 @@ func (b *buffer) pathToken() error { } if err := b.skip(c); err != nil { - return errors.New("unmatched quote in path") + return errUnmatchedQuotePath } if b.index >= b.length { - return errors.New("unmatched quote in path") + return errUnmatchedQuotePath } case c == bracketOpen || c == parenOpen: @@ -269,7 +246,7 @@ func (b *buffer) pathToken() error { case c == bracketClose || c == parenClose: inToken = true if len(stack) == 0 || (c == bracketClose && stack[len(stack)-1] != bracketOpen) || (c == parenClose && stack[len(stack)-1] != parenOpen) { - return errors.New("mismatched bracket or parenthesis") + return errUnmatchedParenthesis } stack = stack[:len(stack)-1] @@ -284,7 +261,7 @@ func (b *buffer) pathToken() error { inToken = true inNumber = true } else if !inToken { - return errors.New("unexpected operator at start of token") + return errInvalidToken } default: @@ -300,7 +277,7 @@ func (b *buffer) pathToken() error { end: if len(stack) != 0 { - return errors.New("unclosed bracket or parenthesis at end of path") + return errUnmatchedParenthesis } if first == b.index { @@ -315,15 +292,15 @@ end: } func pathStateContainsValidPathToken(c byte) bool { - if _, ok := significantTokens[c]; ok { + if significantTokens[c] { return true } - if _, ok := filterTokens[c]; ok { + if filterTokens[c] { return true } - if _, ok := numIndex[c]; ok { + if numIndex[c] { return true } @@ -342,7 +319,7 @@ func (b *buffer) numeric(token bool) error { for ; b.index < b.length; b.index++ { b.class = b.getClasses(doubleQuote) if b.class == __ { - return errors.New("invalid token found while parsing path") + return errInvalidToken } b.state = StateTransitionTable[b.last][b.class] @@ -351,7 +328,7 @@ func (b *buffer) numeric(token bool) error { break } - return errors.New("invalid token found while parsing path") + return errInvalidToken } if b.state < __ { @@ -366,7 +343,7 @@ func (b *buffer) numeric(token bool) error { } if b.last != ZE && b.last != IN && b.last != FR && b.last != E3 { - return errors.New("invalid token found while parsing path") + return errInvalidToken } return nil @@ -407,12 +384,12 @@ func (b *buffer) string(search byte, token bool) error { b.class = b.getClasses(search) if b.class == __ { - return errors.New("invalid token found while parsing path") + return errInvalidToken } b.state = StateTransitionTable[b.last][b.class] if b.state == __ { - return errors.New("invalid token found while parsing path") + return errInvalidToken } if b.state < __ { @@ -431,11 +408,11 @@ func (b *buffer) word(bs []byte) error { max := len(bs) index := 0 - for ; b.index < b.length; b.index++ { + for ; b.index < b.length && index < max; b.index++ { c = b.data[b.index] if c != bs[index] { - return errors.New("invalid token found while parsing path") + return errInvalidToken } index++ @@ -445,7 +422,7 @@ func (b *buffer) word(bs []byte) error { } if index != max { - return errors.New("invalid token found while parsing path") + return errInvalidToken } return nil diff --git a/examples/gno.land/p/demo/json/buffer_test.gno b/examples/gno.land/p/demo/json/buffer_test.gno index b8dce390a61..f4102040be5 100644 --- a/examples/gno.land/p/demo/json/buffer_test.gno +++ b/examples/gno.land/p/demo/json/buffer_test.gno @@ -1,6 +1,8 @@ package json -import "testing" +import ( + "testing" +) func TestBufferCurrent(t *testing.T) { tests := []struct { @@ -242,37 +244,6 @@ func TestBufferSkip(t *testing.T) { } } -func TestBufferSkipAny(t *testing.T) { - tests := []struct { - name string - buffer *buffer - s map[byte]bool - wantErr bool - }{ - { - name: "Skip any valid byte", - buffer: &buffer{data: []byte("test"), length: 4, index: 0}, - s: map[byte]bool{'e': true, 'o': true}, - wantErr: false, - }, - { - name: "Skip any to EOF", - buffer: &buffer{data: []byte("test"), length: 4, index: 0}, - s: map[byte]bool{'x': true, 'y': true}, - wantErr: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.buffer.skipAny(tt.s) - if (err != nil) != tt.wantErr { - t.Errorf("buffer.skipAny() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - func TestSkipToNextSignificantToken(t *testing.T) { tests := []struct { name string diff --git a/examples/gno.land/p/demo/json/builder.gno b/examples/gno.land/p/demo/json/builder.gno new file mode 100644 index 00000000000..4693d5ec550 --- /dev/null +++ b/examples/gno.land/p/demo/json/builder.gno @@ -0,0 +1,89 @@ +package json + +type NodeBuilder struct { + node *Node +} + +func Builder() *NodeBuilder { + return &NodeBuilder{node: ObjectNode("", nil)} +} + +func (b *NodeBuilder) WriteString(key, value string) *NodeBuilder { + b.node.AppendObject(key, StringNode("", value)) + return b +} + +func (b *NodeBuilder) WriteNumber(key string, value float64) *NodeBuilder { + b.node.AppendObject(key, NumberNode("", value)) + return b +} + +func (b *NodeBuilder) WriteBool(key string, value bool) *NodeBuilder { + b.node.AppendObject(key, BoolNode("", value)) + return b +} + +func (b *NodeBuilder) WriteNull(key string) *NodeBuilder { + b.node.AppendObject(key, NullNode("")) + return b +} + +func (b *NodeBuilder) WriteObject(key string, fn func(*NodeBuilder)) *NodeBuilder { + nestedBuilder := &NodeBuilder{node: ObjectNode("", nil)} + fn(nestedBuilder) + b.node.AppendObject(key, nestedBuilder.node) + return b +} + +func (b *NodeBuilder) WriteArray(key string, fn func(*ArrayBuilder)) *NodeBuilder { + arrayBuilder := &ArrayBuilder{nodes: []*Node{}} + fn(arrayBuilder) + b.node.AppendObject(key, ArrayNode("", arrayBuilder.nodes)) + return b +} + +func (b *NodeBuilder) Node() *Node { + return b.node +} + +type ArrayBuilder struct { + nodes []*Node +} + +func (ab *ArrayBuilder) WriteString(value string) *ArrayBuilder { + ab.nodes = append(ab.nodes, StringNode("", value)) + return ab +} + +func (ab *ArrayBuilder) WriteNumber(value float64) *ArrayBuilder { + ab.nodes = append(ab.nodes, NumberNode("", value)) + return ab +} + +func (ab *ArrayBuilder) WriteInt(value int) *ArrayBuilder { + return ab.WriteNumber(float64(value)) +} + +func (ab *ArrayBuilder) WriteBool(value bool) *ArrayBuilder { + ab.nodes = append(ab.nodes, BoolNode("", value)) + return ab +} + +func (ab *ArrayBuilder) WriteNull() *ArrayBuilder { + ab.nodes = append(ab.nodes, NullNode("")) + return ab +} + +func (ab *ArrayBuilder) WriteObject(fn func(*NodeBuilder)) *ArrayBuilder { + nestedBuilder := &NodeBuilder{node: ObjectNode("", nil)} + fn(nestedBuilder) + ab.nodes = append(ab.nodes, nestedBuilder.node) + return ab +} + +func (ab *ArrayBuilder) WriteArray(fn func(*ArrayBuilder)) *ArrayBuilder { + nestedArrayBuilder := &ArrayBuilder{nodes: []*Node{}} + fn(nestedArrayBuilder) + ab.nodes = append(ab.nodes, ArrayNode("", nestedArrayBuilder.nodes)) + return ab +} diff --git a/examples/gno.land/p/demo/json/builder_test.gno b/examples/gno.land/p/demo/json/builder_test.gno new file mode 100644 index 00000000000..4c882d0d6c8 --- /dev/null +++ b/examples/gno.land/p/demo/json/builder_test.gno @@ -0,0 +1,103 @@ +package json + +import ( + "testing" +) + +func TestNodeBuilder(t *testing.T) { + tests := []struct { + name string + build func() *Node + expected string + }{ + { + name: "plain object", + build: func() *Node { + return Builder(). + WriteString("name", "Alice"). + WriteNumber("age", 30). + WriteBool("is_student", false). + Node() + }, + expected: `{"name":"Alice","age":30,"is_student":false}`, + }, + { + name: "nested object", + build: func() *Node { + return Builder(). + WriteString("name", "Alice"). + WriteObject("address", func(b *NodeBuilder) { + b.WriteString("city", "New York"). + WriteNumber("zipcode", 10001) + }). + Node() + }, + expected: `{"name":"Alice","address":{"city":"New York","zipcode":10001}}`, + }, + { + name: "null node", + build: func() *Node { + return Builder().WriteNull("foo").Node() + }, + expected: `{"foo":null}`, + }, + { + name: "array node", + build: func() *Node { + return Builder(). + WriteArray("items", func(ab *ArrayBuilder) { + ab.WriteString("item1"). + WriteString("item2"). + WriteString("item3") + }). + Node() + }, + expected: `{"items":["item1","item2","item3"]}`, + }, + { + name: "array with objects", + build: func() *Node { + return Builder(). + WriteArray("users", func(ab *ArrayBuilder) { + ab.WriteObject(func(b *NodeBuilder) { + b.WriteString("name", "Bob"). + WriteNumber("age", 25) + }). + WriteObject(func(b *NodeBuilder) { + b.WriteString("name", "Carol"). + WriteNumber("age", 27) + }) + }). + Node() + }, + expected: `{"users":[{"name":"Bob","age":25},{"name":"Carol","age":27}]}`, + }, + { + name: "array with various types", + build: func() *Node { + return Builder(). + WriteArray("values", func(ab *ArrayBuilder) { + ab.WriteString("item1"). + WriteNumber(123). + WriteBool(true). + WriteNull() + }). + Node() + }, + expected: `{"values":["item1",123,true,null]}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + node := tt.build() + value, err := Marshal(node) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if string(value) != tt.expected { + t.Errorf("expected %s, got %s", tt.expected, string(value)) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/decode_test.gno b/examples/gno.land/p/demo/json/decode_test.gno index 8aad07169f2..dc92f1f84cd 100644 --- a/examples/gno.land/p/demo/json/decode_test.gno +++ b/examples/gno.land/p/demo/json/decode_test.gno @@ -8,8 +8,8 @@ import ( type testNode struct { name string input []byte - _type ValueType value []byte + _type ValueType } func simpleValid(test *testNode, t *testing.T) { diff --git a/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno b/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno deleted file mode 100644 index 6a29f7f1350..00000000000 --- a/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno +++ /dev/null @@ -1,839 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package eisel_lemire - -// This file implements the Eisel-Lemire ParseFloat algorithm, published in -// 2020 and discussed extensively at -// https://nigeltao.github.io/blog/2020/eisel-lemire.html -// -// The original C++ implementation is at -// https://github.com/lemire/fast_double_parser/blob/644bef4306059d3be01a04e77d3cc84b379c596f/include/fast_double_parser.h#L840 -// -// This Go re-implementation closely follows the C re-implementation at -// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/internal/cgen/base/floatconv-submodule-code.c#L990 -// -// Additional testing (on over several million test strings) is done by -// https://github.com/nigeltao/parse-number-fxx-test-data/blob/5280dcfccf6d0b02a65ae282dad0b6d9de50e039/script/test-go-strconv.go - -import ( - "math" - "math/bits" -) - -const ( - float32ExponentBias = 127 - float64ExponentBias = 1023 -) - -// eiselLemire64 parses a floating-point number from its mantissa and exponent representation. -// This implementation is based on the Eisel-Lemire ParseFloat algorithm, which is efficient -// and precise for converting strings to floating-point numbers. -// -// Arguments: -// man (uint64): The mantissa part of the floating-point number. -// exp10 (int): The exponent part, representing the power of 10. -// neg (bool): Indicates if the number is negative. -// -// Returns: -// f (float64): The parsed floating-point number. -// ok (bool): Indicates whether the parsing was successful. -// -// The function starts by handling special cases, such as zero mantissa. -// It then checks if the exponent is within the allowed range. -// After that, it normalizes the mantissa by left-shifting it to fill -// the leading zeros. This is followed by the main algorithm logic that -// converts the normalized mantissa and exponent into a 64-bit floating-point number. -// The function returns this number along with a boolean indicating the success of the operation. -func EiselLemire64(man uint64, exp10 int, neg bool) (f float64, ok bool) { - // The terse comments in this function body refer to sections of the - // https://nigeltao.github.io/blog/2020/eisel-lemire.html blog post. - - // Exp10 Range. - if man == 0 { - if neg { - f = math.Float64frombits(0x80000000_00000000) // Negative zero. - } - - return f, true - } - - if exp10 < detailedPowersOfTenMinExp10 || detailedPowersOfTenMaxExp10 < exp10 { - return 0, false - } - - // Normalization. - clz := bits.LeadingZeros64(man) - man <<= uint(clz) - retExp2 := uint64(217706*exp10>>16+64+float64ExponentBias) - uint64(clz) - - // Multiplication. - xHi, xLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][1]) - - // Wider Approximation. - if xHi&0x1FF == 0x1FF && xLo+man < man { - yHi, yLo := bits.Mul64(man, detailedPowersOfTen[exp10-detailedPowersOfTenMinExp10][0]) - mergedHi, mergedLo := xHi, xLo+yHi - if mergedLo < xLo { - mergedHi++ - } - - if mergedHi&0x1FF == 0x1FF && mergedLo+1 == 0 && yLo+man < man { - return 0, false - } - - xHi, xLo = mergedHi, mergedLo - } - - // Shifting to 54 Bits. - msb := xHi >> 63 - retMantissa := xHi >> (msb + 9) - retExp2 -= 1 ^ msb - - // Half-way Ambiguity. - if xLo == 0 && xHi&0x1FF == 0 && retMantissa&3 == 1 { - return 0, false - } - - // From 54 to 53 Bits. - retMantissa += retMantissa & 1 - retMantissa >>= 1 - if retMantissa>>53 > 0 { - retMantissa >>= 1 - retExp2 += 1 - } - - // retExp2 is a uint64. Zero or underflow means that we're in subnormal - // float64 space. 0x7FF or above means that we're in Inf/NaN float64 space. - // - // The if block is equivalent to (but has fewer branches than): - // if retExp2 <= 0 || retExp2 >= 0x7FF { etc } - if retExp2-1 >= 0x7FF-1 { - return 0, false - } - - retBits := retExp2<<52 | retMantissa&0x000FFFFF_FFFFFFFF - if neg { - retBits |= 0x80000000_00000000 - } - - return math.Float64frombits(retBits), true -} - -// detailedPowersOfTen{Min,Max}Exp10 is the power of 10 represented by the -// first and last rows of detailedPowersOfTen. Both bounds are inclusive. -const ( - detailedPowersOfTenMinExp10 = -348 - detailedPowersOfTenMaxExp10 = +347 -) - -// detailedPowersOfTen contains 128-bit mantissa approximations (rounded down) -// to the powers of 10. For example: -// -// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79)) -// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15)) -// -// The mantissas are explicitly listed. The exponents are implied by a linear -// expression with slope 217706.0/65536.0 ≈ log(10)/log(2). -// -// The table was generated by -// https://github.com/google/wuffs/blob/ba3818cb6b473a2ed0b38ecfc07dbbd3a97e8ae7/script/print-mpb-powers-of-10.go -var detailedPowersOfTen = [...][2]uint64{ - {0x1732C869CD60E453, 0xFA8FD5A0081C0288}, // 1e-348 - {0x0E7FBD42205C8EB4, 0x9C99E58405118195}, // 1e-347 - {0x521FAC92A873B261, 0xC3C05EE50655E1FA}, // 1e-346 - {0xE6A797B752909EF9, 0xF4B0769E47EB5A78}, // 1e-345 - {0x9028BED2939A635C, 0x98EE4A22ECF3188B}, // 1e-344 - {0x7432EE873880FC33, 0xBF29DCABA82FDEAE}, // 1e-343 - {0x113FAA2906A13B3F, 0xEEF453D6923BD65A}, // 1e-342 - {0x4AC7CA59A424C507, 0x9558B4661B6565F8}, // 1e-341 - {0x5D79BCF00D2DF649, 0xBAAEE17FA23EBF76}, // 1e-340 - {0xF4D82C2C107973DC, 0xE95A99DF8ACE6F53}, // 1e-339 - {0x79071B9B8A4BE869, 0x91D8A02BB6C10594}, // 1e-338 - {0x9748E2826CDEE284, 0xB64EC836A47146F9}, // 1e-337 - {0xFD1B1B2308169B25, 0xE3E27A444D8D98B7}, // 1e-336 - {0xFE30F0F5E50E20F7, 0x8E6D8C6AB0787F72}, // 1e-335 - {0xBDBD2D335E51A935, 0xB208EF855C969F4F}, // 1e-334 - {0xAD2C788035E61382, 0xDE8B2B66B3BC4723}, // 1e-333 - {0x4C3BCB5021AFCC31, 0x8B16FB203055AC76}, // 1e-332 - {0xDF4ABE242A1BBF3D, 0xADDCB9E83C6B1793}, // 1e-331 - {0xD71D6DAD34A2AF0D, 0xD953E8624B85DD78}, // 1e-330 - {0x8672648C40E5AD68, 0x87D4713D6F33AA6B}, // 1e-329 - {0x680EFDAF511F18C2, 0xA9C98D8CCB009506}, // 1e-328 - {0x0212BD1B2566DEF2, 0xD43BF0EFFDC0BA48}, // 1e-327 - {0x014BB630F7604B57, 0x84A57695FE98746D}, // 1e-326 - {0x419EA3BD35385E2D, 0xA5CED43B7E3E9188}, // 1e-325 - {0x52064CAC828675B9, 0xCF42894A5DCE35EA}, // 1e-324 - {0x7343EFEBD1940993, 0x818995CE7AA0E1B2}, // 1e-323 - {0x1014EBE6C5F90BF8, 0xA1EBFB4219491A1F}, // 1e-322 - {0xD41A26E077774EF6, 0xCA66FA129F9B60A6}, // 1e-321 - {0x8920B098955522B4, 0xFD00B897478238D0}, // 1e-320 - {0x55B46E5F5D5535B0, 0x9E20735E8CB16382}, // 1e-319 - {0xEB2189F734AA831D, 0xC5A890362FDDBC62}, // 1e-318 - {0xA5E9EC7501D523E4, 0xF712B443BBD52B7B}, // 1e-317 - {0x47B233C92125366E, 0x9A6BB0AA55653B2D}, // 1e-316 - {0x999EC0BB696E840A, 0xC1069CD4EABE89F8}, // 1e-315 - {0xC00670EA43CA250D, 0xF148440A256E2C76}, // 1e-314 - {0x380406926A5E5728, 0x96CD2A865764DBCA}, // 1e-313 - {0xC605083704F5ECF2, 0xBC807527ED3E12BC}, // 1e-312 - {0xF7864A44C633682E, 0xEBA09271E88D976B}, // 1e-311 - {0x7AB3EE6AFBE0211D, 0x93445B8731587EA3}, // 1e-310 - {0x5960EA05BAD82964, 0xB8157268FDAE9E4C}, // 1e-309 - {0x6FB92487298E33BD, 0xE61ACF033D1A45DF}, // 1e-308 - {0xA5D3B6D479F8E056, 0x8FD0C16206306BAB}, // 1e-307 - {0x8F48A4899877186C, 0xB3C4F1BA87BC8696}, // 1e-306 - {0x331ACDABFE94DE87, 0xE0B62E2929ABA83C}, // 1e-305 - {0x9FF0C08B7F1D0B14, 0x8C71DCD9BA0B4925}, // 1e-304 - {0x07ECF0AE5EE44DD9, 0xAF8E5410288E1B6F}, // 1e-303 - {0xC9E82CD9F69D6150, 0xDB71E91432B1A24A}, // 1e-302 - {0xBE311C083A225CD2, 0x892731AC9FAF056E}, // 1e-301 - {0x6DBD630A48AAF406, 0xAB70FE17C79AC6CA}, // 1e-300 - {0x092CBBCCDAD5B108, 0xD64D3D9DB981787D}, // 1e-299 - {0x25BBF56008C58EA5, 0x85F0468293F0EB4E}, // 1e-298 - {0xAF2AF2B80AF6F24E, 0xA76C582338ED2621}, // 1e-297 - {0x1AF5AF660DB4AEE1, 0xD1476E2C07286FAA}, // 1e-296 - {0x50D98D9FC890ED4D, 0x82CCA4DB847945CA}, // 1e-295 - {0xE50FF107BAB528A0, 0xA37FCE126597973C}, // 1e-294 - {0x1E53ED49A96272C8, 0xCC5FC196FEFD7D0C}, // 1e-293 - {0x25E8E89C13BB0F7A, 0xFF77B1FCBEBCDC4F}, // 1e-292 - {0x77B191618C54E9AC, 0x9FAACF3DF73609B1}, // 1e-291 - {0xD59DF5B9EF6A2417, 0xC795830D75038C1D}, // 1e-290 - {0x4B0573286B44AD1D, 0xF97AE3D0D2446F25}, // 1e-289 - {0x4EE367F9430AEC32, 0x9BECCE62836AC577}, // 1e-288 - {0x229C41F793CDA73F, 0xC2E801FB244576D5}, // 1e-287 - {0x6B43527578C1110F, 0xF3A20279ED56D48A}, // 1e-286 - {0x830A13896B78AAA9, 0x9845418C345644D6}, // 1e-285 - {0x23CC986BC656D553, 0xBE5691EF416BD60C}, // 1e-284 - {0x2CBFBE86B7EC8AA8, 0xEDEC366B11C6CB8F}, // 1e-283 - {0x7BF7D71432F3D6A9, 0x94B3A202EB1C3F39}, // 1e-282 - {0xDAF5CCD93FB0CC53, 0xB9E08A83A5E34F07}, // 1e-281 - {0xD1B3400F8F9CFF68, 0xE858AD248F5C22C9}, // 1e-280 - {0x23100809B9C21FA1, 0x91376C36D99995BE}, // 1e-279 - {0xABD40A0C2832A78A, 0xB58547448FFFFB2D}, // 1e-278 - {0x16C90C8F323F516C, 0xE2E69915B3FFF9F9}, // 1e-277 - {0xAE3DA7D97F6792E3, 0x8DD01FAD907FFC3B}, // 1e-276 - {0x99CD11CFDF41779C, 0xB1442798F49FFB4A}, // 1e-275 - {0x40405643D711D583, 0xDD95317F31C7FA1D}, // 1e-274 - {0x482835EA666B2572, 0x8A7D3EEF7F1CFC52}, // 1e-273 - {0xDA3243650005EECF, 0xAD1C8EAB5EE43B66}, // 1e-272 - {0x90BED43E40076A82, 0xD863B256369D4A40}, // 1e-271 - {0x5A7744A6E804A291, 0x873E4F75E2224E68}, // 1e-270 - {0x711515D0A205CB36, 0xA90DE3535AAAE202}, // 1e-269 - {0x0D5A5B44CA873E03, 0xD3515C2831559A83}, // 1e-268 - {0xE858790AFE9486C2, 0x8412D9991ED58091}, // 1e-267 - {0x626E974DBE39A872, 0xA5178FFF668AE0B6}, // 1e-266 - {0xFB0A3D212DC8128F, 0xCE5D73FF402D98E3}, // 1e-265 - {0x7CE66634BC9D0B99, 0x80FA687F881C7F8E}, // 1e-264 - {0x1C1FFFC1EBC44E80, 0xA139029F6A239F72}, // 1e-263 - {0xA327FFB266B56220, 0xC987434744AC874E}, // 1e-262 - {0x4BF1FF9F0062BAA8, 0xFBE9141915D7A922}, // 1e-261 - {0x6F773FC3603DB4A9, 0x9D71AC8FADA6C9B5}, // 1e-260 - {0xCB550FB4384D21D3, 0xC4CE17B399107C22}, // 1e-259 - {0x7E2A53A146606A48, 0xF6019DA07F549B2B}, // 1e-258 - {0x2EDA7444CBFC426D, 0x99C102844F94E0FB}, // 1e-257 - {0xFA911155FEFB5308, 0xC0314325637A1939}, // 1e-256 - {0x793555AB7EBA27CA, 0xF03D93EEBC589F88}, // 1e-255 - {0x4BC1558B2F3458DE, 0x96267C7535B763B5}, // 1e-254 - {0x9EB1AAEDFB016F16, 0xBBB01B9283253CA2}, // 1e-253 - {0x465E15A979C1CADC, 0xEA9C227723EE8BCB}, // 1e-252 - {0x0BFACD89EC191EC9, 0x92A1958A7675175F}, // 1e-251 - {0xCEF980EC671F667B, 0xB749FAED14125D36}, // 1e-250 - {0x82B7E12780E7401A, 0xE51C79A85916F484}, // 1e-249 - {0xD1B2ECB8B0908810, 0x8F31CC0937AE58D2}, // 1e-248 - {0x861FA7E6DCB4AA15, 0xB2FE3F0B8599EF07}, // 1e-247 - {0x67A791E093E1D49A, 0xDFBDCECE67006AC9}, // 1e-246 - {0xE0C8BB2C5C6D24E0, 0x8BD6A141006042BD}, // 1e-245 - {0x58FAE9F773886E18, 0xAECC49914078536D}, // 1e-244 - {0xAF39A475506A899E, 0xDA7F5BF590966848}, // 1e-243 - {0x6D8406C952429603, 0x888F99797A5E012D}, // 1e-242 - {0xC8E5087BA6D33B83, 0xAAB37FD7D8F58178}, // 1e-241 - {0xFB1E4A9A90880A64, 0xD5605FCDCF32E1D6}, // 1e-240 - {0x5CF2EEA09A55067F, 0x855C3BE0A17FCD26}, // 1e-239 - {0xF42FAA48C0EA481E, 0xA6B34AD8C9DFC06F}, // 1e-238 - {0xF13B94DAF124DA26, 0xD0601D8EFC57B08B}, // 1e-237 - {0x76C53D08D6B70858, 0x823C12795DB6CE57}, // 1e-236 - {0x54768C4B0C64CA6E, 0xA2CB1717B52481ED}, // 1e-235 - {0xA9942F5DCF7DFD09, 0xCB7DDCDDA26DA268}, // 1e-234 - {0xD3F93B35435D7C4C, 0xFE5D54150B090B02}, // 1e-233 - {0xC47BC5014A1A6DAF, 0x9EFA548D26E5A6E1}, // 1e-232 - {0x359AB6419CA1091B, 0xC6B8E9B0709F109A}, // 1e-231 - {0xC30163D203C94B62, 0xF867241C8CC6D4C0}, // 1e-230 - {0x79E0DE63425DCF1D, 0x9B407691D7FC44F8}, // 1e-229 - {0x985915FC12F542E4, 0xC21094364DFB5636}, // 1e-228 - {0x3E6F5B7B17B2939D, 0xF294B943E17A2BC4}, // 1e-227 - {0xA705992CEECF9C42, 0x979CF3CA6CEC5B5A}, // 1e-226 - {0x50C6FF782A838353, 0xBD8430BD08277231}, // 1e-225 - {0xA4F8BF5635246428, 0xECE53CEC4A314EBD}, // 1e-224 - {0x871B7795E136BE99, 0x940F4613AE5ED136}, // 1e-223 - {0x28E2557B59846E3F, 0xB913179899F68584}, // 1e-222 - {0x331AEADA2FE589CF, 0xE757DD7EC07426E5}, // 1e-221 - {0x3FF0D2C85DEF7621, 0x9096EA6F3848984F}, // 1e-220 - {0x0FED077A756B53A9, 0xB4BCA50B065ABE63}, // 1e-219 - {0xD3E8495912C62894, 0xE1EBCE4DC7F16DFB}, // 1e-218 - {0x64712DD7ABBBD95C, 0x8D3360F09CF6E4BD}, // 1e-217 - {0xBD8D794D96AACFB3, 0xB080392CC4349DEC}, // 1e-216 - {0xECF0D7A0FC5583A0, 0xDCA04777F541C567}, // 1e-215 - {0xF41686C49DB57244, 0x89E42CAAF9491B60}, // 1e-214 - {0x311C2875C522CED5, 0xAC5D37D5B79B6239}, // 1e-213 - {0x7D633293366B828B, 0xD77485CB25823AC7}, // 1e-212 - {0xAE5DFF9C02033197, 0x86A8D39EF77164BC}, // 1e-211 - {0xD9F57F830283FDFC, 0xA8530886B54DBDEB}, // 1e-210 - {0xD072DF63C324FD7B, 0xD267CAA862A12D66}, // 1e-209 - {0x4247CB9E59F71E6D, 0x8380DEA93DA4BC60}, // 1e-208 - {0x52D9BE85F074E608, 0xA46116538D0DEB78}, // 1e-207 - {0x67902E276C921F8B, 0xCD795BE870516656}, // 1e-206 - {0x00BA1CD8A3DB53B6, 0x806BD9714632DFF6}, // 1e-205 - {0x80E8A40ECCD228A4, 0xA086CFCD97BF97F3}, // 1e-204 - {0x6122CD128006B2CD, 0xC8A883C0FDAF7DF0}, // 1e-203 - {0x796B805720085F81, 0xFAD2A4B13D1B5D6C}, // 1e-202 - {0xCBE3303674053BB0, 0x9CC3A6EEC6311A63}, // 1e-201 - {0xBEDBFC4411068A9C, 0xC3F490AA77BD60FC}, // 1e-200 - {0xEE92FB5515482D44, 0xF4F1B4D515ACB93B}, // 1e-199 - {0x751BDD152D4D1C4A, 0x991711052D8BF3C5}, // 1e-198 - {0xD262D45A78A0635D, 0xBF5CD54678EEF0B6}, // 1e-197 - {0x86FB897116C87C34, 0xEF340A98172AACE4}, // 1e-196 - {0xD45D35E6AE3D4DA0, 0x9580869F0E7AAC0E}, // 1e-195 - {0x8974836059CCA109, 0xBAE0A846D2195712}, // 1e-194 - {0x2BD1A438703FC94B, 0xE998D258869FACD7}, // 1e-193 - {0x7B6306A34627DDCF, 0x91FF83775423CC06}, // 1e-192 - {0x1A3BC84C17B1D542, 0xB67F6455292CBF08}, // 1e-191 - {0x20CABA5F1D9E4A93, 0xE41F3D6A7377EECA}, // 1e-190 - {0x547EB47B7282EE9C, 0x8E938662882AF53E}, // 1e-189 - {0xE99E619A4F23AA43, 0xB23867FB2A35B28D}, // 1e-188 - {0x6405FA00E2EC94D4, 0xDEC681F9F4C31F31}, // 1e-187 - {0xDE83BC408DD3DD04, 0x8B3C113C38F9F37E}, // 1e-186 - {0x9624AB50B148D445, 0xAE0B158B4738705E}, // 1e-185 - {0x3BADD624DD9B0957, 0xD98DDAEE19068C76}, // 1e-184 - {0xE54CA5D70A80E5D6, 0x87F8A8D4CFA417C9}, // 1e-183 - {0x5E9FCF4CCD211F4C, 0xA9F6D30A038D1DBC}, // 1e-182 - {0x7647C3200069671F, 0xD47487CC8470652B}, // 1e-181 - {0x29ECD9F40041E073, 0x84C8D4DFD2C63F3B}, // 1e-180 - {0xF468107100525890, 0xA5FB0A17C777CF09}, // 1e-179 - {0x7182148D4066EEB4, 0xCF79CC9DB955C2CC}, // 1e-178 - {0xC6F14CD848405530, 0x81AC1FE293D599BF}, // 1e-177 - {0xB8ADA00E5A506A7C, 0xA21727DB38CB002F}, // 1e-176 - {0xA6D90811F0E4851C, 0xCA9CF1D206FDC03B}, // 1e-175 - {0x908F4A166D1DA663, 0xFD442E4688BD304A}, // 1e-174 - {0x9A598E4E043287FE, 0x9E4A9CEC15763E2E}, // 1e-173 - {0x40EFF1E1853F29FD, 0xC5DD44271AD3CDBA}, // 1e-172 - {0xD12BEE59E68EF47C, 0xF7549530E188C128}, // 1e-171 - {0x82BB74F8301958CE, 0x9A94DD3E8CF578B9}, // 1e-170 - {0xE36A52363C1FAF01, 0xC13A148E3032D6E7}, // 1e-169 - {0xDC44E6C3CB279AC1, 0xF18899B1BC3F8CA1}, // 1e-168 - {0x29AB103A5EF8C0B9, 0x96F5600F15A7B7E5}, // 1e-167 - {0x7415D448F6B6F0E7, 0xBCB2B812DB11A5DE}, // 1e-166 - {0x111B495B3464AD21, 0xEBDF661791D60F56}, // 1e-165 - {0xCAB10DD900BEEC34, 0x936B9FCEBB25C995}, // 1e-164 - {0x3D5D514F40EEA742, 0xB84687C269EF3BFB}, // 1e-163 - {0x0CB4A5A3112A5112, 0xE65829B3046B0AFA}, // 1e-162 - {0x47F0E785EABA72AB, 0x8FF71A0FE2C2E6DC}, // 1e-161 - {0x59ED216765690F56, 0xB3F4E093DB73A093}, // 1e-160 - {0x306869C13EC3532C, 0xE0F218B8D25088B8}, // 1e-159 - {0x1E414218C73A13FB, 0x8C974F7383725573}, // 1e-158 - {0xE5D1929EF90898FA, 0xAFBD2350644EEACF}, // 1e-157 - {0xDF45F746B74ABF39, 0xDBAC6C247D62A583}, // 1e-156 - {0x6B8BBA8C328EB783, 0x894BC396CE5DA772}, // 1e-155 - {0x066EA92F3F326564, 0xAB9EB47C81F5114F}, // 1e-154 - {0xC80A537B0EFEFEBD, 0xD686619BA27255A2}, // 1e-153 - {0xBD06742CE95F5F36, 0x8613FD0145877585}, // 1e-152 - {0x2C48113823B73704, 0xA798FC4196E952E7}, // 1e-151 - {0xF75A15862CA504C5, 0xD17F3B51FCA3A7A0}, // 1e-150 - {0x9A984D73DBE722FB, 0x82EF85133DE648C4}, // 1e-149 - {0xC13E60D0D2E0EBBA, 0xA3AB66580D5FDAF5}, // 1e-148 - {0x318DF905079926A8, 0xCC963FEE10B7D1B3}, // 1e-147 - {0xFDF17746497F7052, 0xFFBBCFE994E5C61F}, // 1e-146 - {0xFEB6EA8BEDEFA633, 0x9FD561F1FD0F9BD3}, // 1e-145 - {0xFE64A52EE96B8FC0, 0xC7CABA6E7C5382C8}, // 1e-144 - {0x3DFDCE7AA3C673B0, 0xF9BD690A1B68637B}, // 1e-143 - {0x06BEA10CA65C084E, 0x9C1661A651213E2D}, // 1e-142 - {0x486E494FCFF30A62, 0xC31BFA0FE5698DB8}, // 1e-141 - {0x5A89DBA3C3EFCCFA, 0xF3E2F893DEC3F126}, // 1e-140 - {0xF89629465A75E01C, 0x986DDB5C6B3A76B7}, // 1e-139 - {0xF6BBB397F1135823, 0xBE89523386091465}, // 1e-138 - {0x746AA07DED582E2C, 0xEE2BA6C0678B597F}, // 1e-137 - {0xA8C2A44EB4571CDC, 0x94DB483840B717EF}, // 1e-136 - {0x92F34D62616CE413, 0xBA121A4650E4DDEB}, // 1e-135 - {0x77B020BAF9C81D17, 0xE896A0D7E51E1566}, // 1e-134 - {0x0ACE1474DC1D122E, 0x915E2486EF32CD60}, // 1e-133 - {0x0D819992132456BA, 0xB5B5ADA8AAFF80B8}, // 1e-132 - {0x10E1FFF697ED6C69, 0xE3231912D5BF60E6}, // 1e-131 - {0xCA8D3FFA1EF463C1, 0x8DF5EFABC5979C8F}, // 1e-130 - {0xBD308FF8A6B17CB2, 0xB1736B96B6FD83B3}, // 1e-129 - {0xAC7CB3F6D05DDBDE, 0xDDD0467C64BCE4A0}, // 1e-128 - {0x6BCDF07A423AA96B, 0x8AA22C0DBEF60EE4}, // 1e-127 - {0x86C16C98D2C953C6, 0xAD4AB7112EB3929D}, // 1e-126 - {0xE871C7BF077BA8B7, 0xD89D64D57A607744}, // 1e-125 - {0x11471CD764AD4972, 0x87625F056C7C4A8B}, // 1e-124 - {0xD598E40D3DD89BCF, 0xA93AF6C6C79B5D2D}, // 1e-123 - {0x4AFF1D108D4EC2C3, 0xD389B47879823479}, // 1e-122 - {0xCEDF722A585139BA, 0x843610CB4BF160CB}, // 1e-121 - {0xC2974EB4EE658828, 0xA54394FE1EEDB8FE}, // 1e-120 - {0x733D226229FEEA32, 0xCE947A3DA6A9273E}, // 1e-119 - {0x0806357D5A3F525F, 0x811CCC668829B887}, // 1e-118 - {0xCA07C2DCB0CF26F7, 0xA163FF802A3426A8}, // 1e-117 - {0xFC89B393DD02F0B5, 0xC9BCFF6034C13052}, // 1e-116 - {0xBBAC2078D443ACE2, 0xFC2C3F3841F17C67}, // 1e-115 - {0xD54B944B84AA4C0D, 0x9D9BA7832936EDC0}, // 1e-114 - {0x0A9E795E65D4DF11, 0xC5029163F384A931}, // 1e-113 - {0x4D4617B5FF4A16D5, 0xF64335BCF065D37D}, // 1e-112 - {0x504BCED1BF8E4E45, 0x99EA0196163FA42E}, // 1e-111 - {0xE45EC2862F71E1D6, 0xC06481FB9BCF8D39}, // 1e-110 - {0x5D767327BB4E5A4C, 0xF07DA27A82C37088}, // 1e-109 - {0x3A6A07F8D510F86F, 0x964E858C91BA2655}, // 1e-108 - {0x890489F70A55368B, 0xBBE226EFB628AFEA}, // 1e-107 - {0x2B45AC74CCEA842E, 0xEADAB0ABA3B2DBE5}, // 1e-106 - {0x3B0B8BC90012929D, 0x92C8AE6B464FC96F}, // 1e-105 - {0x09CE6EBB40173744, 0xB77ADA0617E3BBCB}, // 1e-104 - {0xCC420A6A101D0515, 0xE55990879DDCAABD}, // 1e-103 - {0x9FA946824A12232D, 0x8F57FA54C2A9EAB6}, // 1e-102 - {0x47939822DC96ABF9, 0xB32DF8E9F3546564}, // 1e-101 - {0x59787E2B93BC56F7, 0xDFF9772470297EBD}, // 1e-100 - {0x57EB4EDB3C55B65A, 0x8BFBEA76C619EF36}, // 1e-99 - {0xEDE622920B6B23F1, 0xAEFAE51477A06B03}, // 1e-98 - {0xE95FAB368E45ECED, 0xDAB99E59958885C4}, // 1e-97 - {0x11DBCB0218EBB414, 0x88B402F7FD75539B}, // 1e-96 - {0xD652BDC29F26A119, 0xAAE103B5FCD2A881}, // 1e-95 - {0x4BE76D3346F0495F, 0xD59944A37C0752A2}, // 1e-94 - {0x6F70A4400C562DDB, 0x857FCAE62D8493A5}, // 1e-93 - {0xCB4CCD500F6BB952, 0xA6DFBD9FB8E5B88E}, // 1e-92 - {0x7E2000A41346A7A7, 0xD097AD07A71F26B2}, // 1e-91 - {0x8ED400668C0C28C8, 0x825ECC24C873782F}, // 1e-90 - {0x728900802F0F32FA, 0xA2F67F2DFA90563B}, // 1e-89 - {0x4F2B40A03AD2FFB9, 0xCBB41EF979346BCA}, // 1e-88 - {0xE2F610C84987BFA8, 0xFEA126B7D78186BC}, // 1e-87 - {0x0DD9CA7D2DF4D7C9, 0x9F24B832E6B0F436}, // 1e-86 - {0x91503D1C79720DBB, 0xC6EDE63FA05D3143}, // 1e-85 - {0x75A44C6397CE912A, 0xF8A95FCF88747D94}, // 1e-84 - {0xC986AFBE3EE11ABA, 0x9B69DBE1B548CE7C}, // 1e-83 - {0xFBE85BADCE996168, 0xC24452DA229B021B}, // 1e-82 - {0xFAE27299423FB9C3, 0xF2D56790AB41C2A2}, // 1e-81 - {0xDCCD879FC967D41A, 0x97C560BA6B0919A5}, // 1e-80 - {0x5400E987BBC1C920, 0xBDB6B8E905CB600F}, // 1e-79 - {0x290123E9AAB23B68, 0xED246723473E3813}, // 1e-78 - {0xF9A0B6720AAF6521, 0x9436C0760C86E30B}, // 1e-77 - {0xF808E40E8D5B3E69, 0xB94470938FA89BCE}, // 1e-76 - {0xB60B1D1230B20E04, 0xE7958CB87392C2C2}, // 1e-75 - {0xB1C6F22B5E6F48C2, 0x90BD77F3483BB9B9}, // 1e-74 - {0x1E38AEB6360B1AF3, 0xB4ECD5F01A4AA828}, // 1e-73 - {0x25C6DA63C38DE1B0, 0xE2280B6C20DD5232}, // 1e-72 - {0x579C487E5A38AD0E, 0x8D590723948A535F}, // 1e-71 - {0x2D835A9DF0C6D851, 0xB0AF48EC79ACE837}, // 1e-70 - {0xF8E431456CF88E65, 0xDCDB1B2798182244}, // 1e-69 - {0x1B8E9ECB641B58FF, 0x8A08F0F8BF0F156B}, // 1e-68 - {0xE272467E3D222F3F, 0xAC8B2D36EED2DAC5}, // 1e-67 - {0x5B0ED81DCC6ABB0F, 0xD7ADF884AA879177}, // 1e-66 - {0x98E947129FC2B4E9, 0x86CCBB52EA94BAEA}, // 1e-65 - {0x3F2398D747B36224, 0xA87FEA27A539E9A5}, // 1e-64 - {0x8EEC7F0D19A03AAD, 0xD29FE4B18E88640E}, // 1e-63 - {0x1953CF68300424AC, 0x83A3EEEEF9153E89}, // 1e-62 - {0x5FA8C3423C052DD7, 0xA48CEAAAB75A8E2B}, // 1e-61 - {0x3792F412CB06794D, 0xCDB02555653131B6}, // 1e-60 - {0xE2BBD88BBEE40BD0, 0x808E17555F3EBF11}, // 1e-59 - {0x5B6ACEAEAE9D0EC4, 0xA0B19D2AB70E6ED6}, // 1e-58 - {0xF245825A5A445275, 0xC8DE047564D20A8B}, // 1e-57 - {0xEED6E2F0F0D56712, 0xFB158592BE068D2E}, // 1e-56 - {0x55464DD69685606B, 0x9CED737BB6C4183D}, // 1e-55 - {0xAA97E14C3C26B886, 0xC428D05AA4751E4C}, // 1e-54 - {0xD53DD99F4B3066A8, 0xF53304714D9265DF}, // 1e-53 - {0xE546A8038EFE4029, 0x993FE2C6D07B7FAB}, // 1e-52 - {0xDE98520472BDD033, 0xBF8FDB78849A5F96}, // 1e-51 - {0x963E66858F6D4440, 0xEF73D256A5C0F77C}, // 1e-50 - {0xDDE7001379A44AA8, 0x95A8637627989AAD}, // 1e-49 - {0x5560C018580D5D52, 0xBB127C53B17EC159}, // 1e-48 - {0xAAB8F01E6E10B4A6, 0xE9D71B689DDE71AF}, // 1e-47 - {0xCAB3961304CA70E8, 0x9226712162AB070D}, // 1e-46 - {0x3D607B97C5FD0D22, 0xB6B00D69BB55C8D1}, // 1e-45 - {0x8CB89A7DB77C506A, 0xE45C10C42A2B3B05}, // 1e-44 - {0x77F3608E92ADB242, 0x8EB98A7A9A5B04E3}, // 1e-43 - {0x55F038B237591ED3, 0xB267ED1940F1C61C}, // 1e-42 - {0x6B6C46DEC52F6688, 0xDF01E85F912E37A3}, // 1e-41 - {0x2323AC4B3B3DA015, 0x8B61313BBABCE2C6}, // 1e-40 - {0xABEC975E0A0D081A, 0xAE397D8AA96C1B77}, // 1e-39 - {0x96E7BD358C904A21, 0xD9C7DCED53C72255}, // 1e-38 - {0x7E50D64177DA2E54, 0x881CEA14545C7575}, // 1e-37 - {0xDDE50BD1D5D0B9E9, 0xAA242499697392D2}, // 1e-36 - {0x955E4EC64B44E864, 0xD4AD2DBFC3D07787}, // 1e-35 - {0xBD5AF13BEF0B113E, 0x84EC3C97DA624AB4}, // 1e-34 - {0xECB1AD8AEACDD58E, 0xA6274BBDD0FADD61}, // 1e-33 - {0x67DE18EDA5814AF2, 0xCFB11EAD453994BA}, // 1e-32 - {0x80EACF948770CED7, 0x81CEB32C4B43FCF4}, // 1e-31 - {0xA1258379A94D028D, 0xA2425FF75E14FC31}, // 1e-30 - {0x096EE45813A04330, 0xCAD2F7F5359A3B3E}, // 1e-29 - {0x8BCA9D6E188853FC, 0xFD87B5F28300CA0D}, // 1e-28 - {0x775EA264CF55347D, 0x9E74D1B791E07E48}, // 1e-27 - {0x95364AFE032A819D, 0xC612062576589DDA}, // 1e-26 - {0x3A83DDBD83F52204, 0xF79687AED3EEC551}, // 1e-25 - {0xC4926A9672793542, 0x9ABE14CD44753B52}, // 1e-24 - {0x75B7053C0F178293, 0xC16D9A0095928A27}, // 1e-23 - {0x5324C68B12DD6338, 0xF1C90080BAF72CB1}, // 1e-22 - {0xD3F6FC16EBCA5E03, 0x971DA05074DA7BEE}, // 1e-21 - {0x88F4BB1CA6BCF584, 0xBCE5086492111AEA}, // 1e-20 - {0x2B31E9E3D06C32E5, 0xEC1E4A7DB69561A5}, // 1e-19 - {0x3AFF322E62439FCF, 0x9392EE8E921D5D07}, // 1e-18 - {0x09BEFEB9FAD487C2, 0xB877AA3236A4B449}, // 1e-17 - {0x4C2EBE687989A9B3, 0xE69594BEC44DE15B}, // 1e-16 - {0x0F9D37014BF60A10, 0x901D7CF73AB0ACD9}, // 1e-15 - {0x538484C19EF38C94, 0xB424DC35095CD80F}, // 1e-14 - {0x2865A5F206B06FB9, 0xE12E13424BB40E13}, // 1e-13 - {0xF93F87B7442E45D3, 0x8CBCCC096F5088CB}, // 1e-12 - {0xF78F69A51539D748, 0xAFEBFF0BCB24AAFE}, // 1e-11 - {0xB573440E5A884D1B, 0xDBE6FECEBDEDD5BE}, // 1e-10 - {0x31680A88F8953030, 0x89705F4136B4A597}, // 1e-9 - {0xFDC20D2B36BA7C3D, 0xABCC77118461CEFC}, // 1e-8 - {0x3D32907604691B4C, 0xD6BF94D5E57A42BC}, // 1e-7 - {0xA63F9A49C2C1B10F, 0x8637BD05AF6C69B5}, // 1e-6 - {0x0FCF80DC33721D53, 0xA7C5AC471B478423}, // 1e-5 - {0xD3C36113404EA4A8, 0xD1B71758E219652B}, // 1e-4 - {0x645A1CAC083126E9, 0x83126E978D4FDF3B}, // 1e-3 - {0x3D70A3D70A3D70A3, 0xA3D70A3D70A3D70A}, // 1e-2 - {0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC}, // 1e-1 - {0x0000000000000000, 0x8000000000000000}, // 1e0 - {0x0000000000000000, 0xA000000000000000}, // 1e1 - {0x0000000000000000, 0xC800000000000000}, // 1e2 - {0x0000000000000000, 0xFA00000000000000}, // 1e3 - {0x0000000000000000, 0x9C40000000000000}, // 1e4 - {0x0000000000000000, 0xC350000000000000}, // 1e5 - {0x0000000000000000, 0xF424000000000000}, // 1e6 - {0x0000000000000000, 0x9896800000000000}, // 1e7 - {0x0000000000000000, 0xBEBC200000000000}, // 1e8 - {0x0000000000000000, 0xEE6B280000000000}, // 1e9 - {0x0000000000000000, 0x9502F90000000000}, // 1e10 - {0x0000000000000000, 0xBA43B74000000000}, // 1e11 - {0x0000000000000000, 0xE8D4A51000000000}, // 1e12 - {0x0000000000000000, 0x9184E72A00000000}, // 1e13 - {0x0000000000000000, 0xB5E620F480000000}, // 1e14 - {0x0000000000000000, 0xE35FA931A0000000}, // 1e15 - {0x0000000000000000, 0x8E1BC9BF04000000}, // 1e16 - {0x0000000000000000, 0xB1A2BC2EC5000000}, // 1e17 - {0x0000000000000000, 0xDE0B6B3A76400000}, // 1e18 - {0x0000000000000000, 0x8AC7230489E80000}, // 1e19 - {0x0000000000000000, 0xAD78EBC5AC620000}, // 1e20 - {0x0000000000000000, 0xD8D726B7177A8000}, // 1e21 - {0x0000000000000000, 0x878678326EAC9000}, // 1e22 - {0x0000000000000000, 0xA968163F0A57B400}, // 1e23 - {0x0000000000000000, 0xD3C21BCECCEDA100}, // 1e24 - {0x0000000000000000, 0x84595161401484A0}, // 1e25 - {0x0000000000000000, 0xA56FA5B99019A5C8}, // 1e26 - {0x0000000000000000, 0xCECB8F27F4200F3A}, // 1e27 - {0x4000000000000000, 0x813F3978F8940984}, // 1e28 - {0x5000000000000000, 0xA18F07D736B90BE5}, // 1e29 - {0xA400000000000000, 0xC9F2C9CD04674EDE}, // 1e30 - {0x4D00000000000000, 0xFC6F7C4045812296}, // 1e31 - {0xF020000000000000, 0x9DC5ADA82B70B59D}, // 1e32 - {0x6C28000000000000, 0xC5371912364CE305}, // 1e33 - {0xC732000000000000, 0xF684DF56C3E01BC6}, // 1e34 - {0x3C7F400000000000, 0x9A130B963A6C115C}, // 1e35 - {0x4B9F100000000000, 0xC097CE7BC90715B3}, // 1e36 - {0x1E86D40000000000, 0xF0BDC21ABB48DB20}, // 1e37 - {0x1314448000000000, 0x96769950B50D88F4}, // 1e38 - {0x17D955A000000000, 0xBC143FA4E250EB31}, // 1e39 - {0x5DCFAB0800000000, 0xEB194F8E1AE525FD}, // 1e40 - {0x5AA1CAE500000000, 0x92EFD1B8D0CF37BE}, // 1e41 - {0xF14A3D9E40000000, 0xB7ABC627050305AD}, // 1e42 - {0x6D9CCD05D0000000, 0xE596B7B0C643C719}, // 1e43 - {0xE4820023A2000000, 0x8F7E32CE7BEA5C6F}, // 1e44 - {0xDDA2802C8A800000, 0xB35DBF821AE4F38B}, // 1e45 - {0xD50B2037AD200000, 0xE0352F62A19E306E}, // 1e46 - {0x4526F422CC340000, 0x8C213D9DA502DE45}, // 1e47 - {0x9670B12B7F410000, 0xAF298D050E4395D6}, // 1e48 - {0x3C0CDD765F114000, 0xDAF3F04651D47B4C}, // 1e49 - {0xA5880A69FB6AC800, 0x88D8762BF324CD0F}, // 1e50 - {0x8EEA0D047A457A00, 0xAB0E93B6EFEE0053}, // 1e51 - {0x72A4904598D6D880, 0xD5D238A4ABE98068}, // 1e52 - {0x47A6DA2B7F864750, 0x85A36366EB71F041}, // 1e53 - {0x999090B65F67D924, 0xA70C3C40A64E6C51}, // 1e54 - {0xFFF4B4E3F741CF6D, 0xD0CF4B50CFE20765}, // 1e55 - {0xBFF8F10E7A8921A4, 0x82818F1281ED449F}, // 1e56 - {0xAFF72D52192B6A0D, 0xA321F2D7226895C7}, // 1e57 - {0x9BF4F8A69F764490, 0xCBEA6F8CEB02BB39}, // 1e58 - {0x02F236D04753D5B4, 0xFEE50B7025C36A08}, // 1e59 - {0x01D762422C946590, 0x9F4F2726179A2245}, // 1e60 - {0x424D3AD2B7B97EF5, 0xC722F0EF9D80AAD6}, // 1e61 - {0xD2E0898765A7DEB2, 0xF8EBAD2B84E0D58B}, // 1e62 - {0x63CC55F49F88EB2F, 0x9B934C3B330C8577}, // 1e63 - {0x3CBF6B71C76B25FB, 0xC2781F49FFCFA6D5}, // 1e64 - {0x8BEF464E3945EF7A, 0xF316271C7FC3908A}, // 1e65 - {0x97758BF0E3CBB5AC, 0x97EDD871CFDA3A56}, // 1e66 - {0x3D52EEED1CBEA317, 0xBDE94E8E43D0C8EC}, // 1e67 - {0x4CA7AAA863EE4BDD, 0xED63A231D4C4FB27}, // 1e68 - {0x8FE8CAA93E74EF6A, 0x945E455F24FB1CF8}, // 1e69 - {0xB3E2FD538E122B44, 0xB975D6B6EE39E436}, // 1e70 - {0x60DBBCA87196B616, 0xE7D34C64A9C85D44}, // 1e71 - {0xBC8955E946FE31CD, 0x90E40FBEEA1D3A4A}, // 1e72 - {0x6BABAB6398BDBE41, 0xB51D13AEA4A488DD}, // 1e73 - {0xC696963C7EED2DD1, 0xE264589A4DCDAB14}, // 1e74 - {0xFC1E1DE5CF543CA2, 0x8D7EB76070A08AEC}, // 1e75 - {0x3B25A55F43294BCB, 0xB0DE65388CC8ADA8}, // 1e76 - {0x49EF0EB713F39EBE, 0xDD15FE86AFFAD912}, // 1e77 - {0x6E3569326C784337, 0x8A2DBF142DFCC7AB}, // 1e78 - {0x49C2C37F07965404, 0xACB92ED9397BF996}, // 1e79 - {0xDC33745EC97BE906, 0xD7E77A8F87DAF7FB}, // 1e80 - {0x69A028BB3DED71A3, 0x86F0AC99B4E8DAFD}, // 1e81 - {0xC40832EA0D68CE0C, 0xA8ACD7C0222311BC}, // 1e82 - {0xF50A3FA490C30190, 0xD2D80DB02AABD62B}, // 1e83 - {0x792667C6DA79E0FA, 0x83C7088E1AAB65DB}, // 1e84 - {0x577001B891185938, 0xA4B8CAB1A1563F52}, // 1e85 - {0xED4C0226B55E6F86, 0xCDE6FD5E09ABCF26}, // 1e86 - {0x544F8158315B05B4, 0x80B05E5AC60B6178}, // 1e87 - {0x696361AE3DB1C721, 0xA0DC75F1778E39D6}, // 1e88 - {0x03BC3A19CD1E38E9, 0xC913936DD571C84C}, // 1e89 - {0x04AB48A04065C723, 0xFB5878494ACE3A5F}, // 1e90 - {0x62EB0D64283F9C76, 0x9D174B2DCEC0E47B}, // 1e91 - {0x3BA5D0BD324F8394, 0xC45D1DF942711D9A}, // 1e92 - {0xCA8F44EC7EE36479, 0xF5746577930D6500}, // 1e93 - {0x7E998B13CF4E1ECB, 0x9968BF6ABBE85F20}, // 1e94 - {0x9E3FEDD8C321A67E, 0xBFC2EF456AE276E8}, // 1e95 - {0xC5CFE94EF3EA101E, 0xEFB3AB16C59B14A2}, // 1e96 - {0xBBA1F1D158724A12, 0x95D04AEE3B80ECE5}, // 1e97 - {0x2A8A6E45AE8EDC97, 0xBB445DA9CA61281F}, // 1e98 - {0xF52D09D71A3293BD, 0xEA1575143CF97226}, // 1e99 - {0x593C2626705F9C56, 0x924D692CA61BE758}, // 1e100 - {0x6F8B2FB00C77836C, 0xB6E0C377CFA2E12E}, // 1e101 - {0x0B6DFB9C0F956447, 0xE498F455C38B997A}, // 1e102 - {0x4724BD4189BD5EAC, 0x8EDF98B59A373FEC}, // 1e103 - {0x58EDEC91EC2CB657, 0xB2977EE300C50FE7}, // 1e104 - {0x2F2967B66737E3ED, 0xDF3D5E9BC0F653E1}, // 1e105 - {0xBD79E0D20082EE74, 0x8B865B215899F46C}, // 1e106 - {0xECD8590680A3AA11, 0xAE67F1E9AEC07187}, // 1e107 - {0xE80E6F4820CC9495, 0xDA01EE641A708DE9}, // 1e108 - {0x3109058D147FDCDD, 0x884134FE908658B2}, // 1e109 - {0xBD4B46F0599FD415, 0xAA51823E34A7EEDE}, // 1e110 - {0x6C9E18AC7007C91A, 0xD4E5E2CDC1D1EA96}, // 1e111 - {0x03E2CF6BC604DDB0, 0x850FADC09923329E}, // 1e112 - {0x84DB8346B786151C, 0xA6539930BF6BFF45}, // 1e113 - {0xE612641865679A63, 0xCFE87F7CEF46FF16}, // 1e114 - {0x4FCB7E8F3F60C07E, 0x81F14FAE158C5F6E}, // 1e115 - {0xE3BE5E330F38F09D, 0xA26DA3999AEF7749}, // 1e116 - {0x5CADF5BFD3072CC5, 0xCB090C8001AB551C}, // 1e117 - {0x73D9732FC7C8F7F6, 0xFDCB4FA002162A63}, // 1e118 - {0x2867E7FDDCDD9AFA, 0x9E9F11C4014DDA7E}, // 1e119 - {0xB281E1FD541501B8, 0xC646D63501A1511D}, // 1e120 - {0x1F225A7CA91A4226, 0xF7D88BC24209A565}, // 1e121 - {0x3375788DE9B06958, 0x9AE757596946075F}, // 1e122 - {0x0052D6B1641C83AE, 0xC1A12D2FC3978937}, // 1e123 - {0xC0678C5DBD23A49A, 0xF209787BB47D6B84}, // 1e124 - {0xF840B7BA963646E0, 0x9745EB4D50CE6332}, // 1e125 - {0xB650E5A93BC3D898, 0xBD176620A501FBFF}, // 1e126 - {0xA3E51F138AB4CEBE, 0xEC5D3FA8CE427AFF}, // 1e127 - {0xC66F336C36B10137, 0x93BA47C980E98CDF}, // 1e128 - {0xB80B0047445D4184, 0xB8A8D9BBE123F017}, // 1e129 - {0xA60DC059157491E5, 0xE6D3102AD96CEC1D}, // 1e130 - {0x87C89837AD68DB2F, 0x9043EA1AC7E41392}, // 1e131 - {0x29BABE4598C311FB, 0xB454E4A179DD1877}, // 1e132 - {0xF4296DD6FEF3D67A, 0xE16A1DC9D8545E94}, // 1e133 - {0x1899E4A65F58660C, 0x8CE2529E2734BB1D}, // 1e134 - {0x5EC05DCFF72E7F8F, 0xB01AE745B101E9E4}, // 1e135 - {0x76707543F4FA1F73, 0xDC21A1171D42645D}, // 1e136 - {0x6A06494A791C53A8, 0x899504AE72497EBA}, // 1e137 - {0x0487DB9D17636892, 0xABFA45DA0EDBDE69}, // 1e138 - {0x45A9D2845D3C42B6, 0xD6F8D7509292D603}, // 1e139 - {0x0B8A2392BA45A9B2, 0x865B86925B9BC5C2}, // 1e140 - {0x8E6CAC7768D7141E, 0xA7F26836F282B732}, // 1e141 - {0x3207D795430CD926, 0xD1EF0244AF2364FF}, // 1e142 - {0x7F44E6BD49E807B8, 0x8335616AED761F1F}, // 1e143 - {0x5F16206C9C6209A6, 0xA402B9C5A8D3A6E7}, // 1e144 - {0x36DBA887C37A8C0F, 0xCD036837130890A1}, // 1e145 - {0xC2494954DA2C9789, 0x802221226BE55A64}, // 1e146 - {0xF2DB9BAA10B7BD6C, 0xA02AA96B06DEB0FD}, // 1e147 - {0x6F92829494E5ACC7, 0xC83553C5C8965D3D}, // 1e148 - {0xCB772339BA1F17F9, 0xFA42A8B73ABBF48C}, // 1e149 - {0xFF2A760414536EFB, 0x9C69A97284B578D7}, // 1e150 - {0xFEF5138519684ABA, 0xC38413CF25E2D70D}, // 1e151 - {0x7EB258665FC25D69, 0xF46518C2EF5B8CD1}, // 1e152 - {0xEF2F773FFBD97A61, 0x98BF2F79D5993802}, // 1e153 - {0xAAFB550FFACFD8FA, 0xBEEEFB584AFF8603}, // 1e154 - {0x95BA2A53F983CF38, 0xEEAABA2E5DBF6784}, // 1e155 - {0xDD945A747BF26183, 0x952AB45CFA97A0B2}, // 1e156 - {0x94F971119AEEF9E4, 0xBA756174393D88DF}, // 1e157 - {0x7A37CD5601AAB85D, 0xE912B9D1478CEB17}, // 1e158 - {0xAC62E055C10AB33A, 0x91ABB422CCB812EE}, // 1e159 - {0x577B986B314D6009, 0xB616A12B7FE617AA}, // 1e160 - {0xED5A7E85FDA0B80B, 0xE39C49765FDF9D94}, // 1e161 - {0x14588F13BE847307, 0x8E41ADE9FBEBC27D}, // 1e162 - {0x596EB2D8AE258FC8, 0xB1D219647AE6B31C}, // 1e163 - {0x6FCA5F8ED9AEF3BB, 0xDE469FBD99A05FE3}, // 1e164 - {0x25DE7BB9480D5854, 0x8AEC23D680043BEE}, // 1e165 - {0xAF561AA79A10AE6A, 0xADA72CCC20054AE9}, // 1e166 - {0x1B2BA1518094DA04, 0xD910F7FF28069DA4}, // 1e167 - {0x90FB44D2F05D0842, 0x87AA9AFF79042286}, // 1e168 - {0x353A1607AC744A53, 0xA99541BF57452B28}, // 1e169 - {0x42889B8997915CE8, 0xD3FA922F2D1675F2}, // 1e170 - {0x69956135FEBADA11, 0x847C9B5D7C2E09B7}, // 1e171 - {0x43FAB9837E699095, 0xA59BC234DB398C25}, // 1e172 - {0x94F967E45E03F4BB, 0xCF02B2C21207EF2E}, // 1e173 - {0x1D1BE0EEBAC278F5, 0x8161AFB94B44F57D}, // 1e174 - {0x6462D92A69731732, 0xA1BA1BA79E1632DC}, // 1e175 - {0x7D7B8F7503CFDCFE, 0xCA28A291859BBF93}, // 1e176 - {0x5CDA735244C3D43E, 0xFCB2CB35E702AF78}, // 1e177 - {0x3A0888136AFA64A7, 0x9DEFBF01B061ADAB}, // 1e178 - {0x088AAA1845B8FDD0, 0xC56BAEC21C7A1916}, // 1e179 - {0x8AAD549E57273D45, 0xF6C69A72A3989F5B}, // 1e180 - {0x36AC54E2F678864B, 0x9A3C2087A63F6399}, // 1e181 - {0x84576A1BB416A7DD, 0xC0CB28A98FCF3C7F}, // 1e182 - {0x656D44A2A11C51D5, 0xF0FDF2D3F3C30B9F}, // 1e183 - {0x9F644AE5A4B1B325, 0x969EB7C47859E743}, // 1e184 - {0x873D5D9F0DDE1FEE, 0xBC4665B596706114}, // 1e185 - {0xA90CB506D155A7EA, 0xEB57FF22FC0C7959}, // 1e186 - {0x09A7F12442D588F2, 0x9316FF75DD87CBD8}, // 1e187 - {0x0C11ED6D538AEB2F, 0xB7DCBF5354E9BECE}, // 1e188 - {0x8F1668C8A86DA5FA, 0xE5D3EF282A242E81}, // 1e189 - {0xF96E017D694487BC, 0x8FA475791A569D10}, // 1e190 - {0x37C981DCC395A9AC, 0xB38D92D760EC4455}, // 1e191 - {0x85BBE253F47B1417, 0xE070F78D3927556A}, // 1e192 - {0x93956D7478CCEC8E, 0x8C469AB843B89562}, // 1e193 - {0x387AC8D1970027B2, 0xAF58416654A6BABB}, // 1e194 - {0x06997B05FCC0319E, 0xDB2E51BFE9D0696A}, // 1e195 - {0x441FECE3BDF81F03, 0x88FCF317F22241E2}, // 1e196 - {0xD527E81CAD7626C3, 0xAB3C2FDDEEAAD25A}, // 1e197 - {0x8A71E223D8D3B074, 0xD60B3BD56A5586F1}, // 1e198 - {0xF6872D5667844E49, 0x85C7056562757456}, // 1e199 - {0xB428F8AC016561DB, 0xA738C6BEBB12D16C}, // 1e200 - {0xE13336D701BEBA52, 0xD106F86E69D785C7}, // 1e201 - {0xECC0024661173473, 0x82A45B450226B39C}, // 1e202 - {0x27F002D7F95D0190, 0xA34D721642B06084}, // 1e203 - {0x31EC038DF7B441F4, 0xCC20CE9BD35C78A5}, // 1e204 - {0x7E67047175A15271, 0xFF290242C83396CE}, // 1e205 - {0x0F0062C6E984D386, 0x9F79A169BD203E41}, // 1e206 - {0x52C07B78A3E60868, 0xC75809C42C684DD1}, // 1e207 - {0xA7709A56CCDF8A82, 0xF92E0C3537826145}, // 1e208 - {0x88A66076400BB691, 0x9BBCC7A142B17CCB}, // 1e209 - {0x6ACFF893D00EA435, 0xC2ABF989935DDBFE}, // 1e210 - {0x0583F6B8C4124D43, 0xF356F7EBF83552FE}, // 1e211 - {0xC3727A337A8B704A, 0x98165AF37B2153DE}, // 1e212 - {0x744F18C0592E4C5C, 0xBE1BF1B059E9A8D6}, // 1e213 - {0x1162DEF06F79DF73, 0xEDA2EE1C7064130C}, // 1e214 - {0x8ADDCB5645AC2BA8, 0x9485D4D1C63E8BE7}, // 1e215 - {0x6D953E2BD7173692, 0xB9A74A0637CE2EE1}, // 1e216 - {0xC8FA8DB6CCDD0437, 0xE8111C87C5C1BA99}, // 1e217 - {0x1D9C9892400A22A2, 0x910AB1D4DB9914A0}, // 1e218 - {0x2503BEB6D00CAB4B, 0xB54D5E4A127F59C8}, // 1e219 - {0x2E44AE64840FD61D, 0xE2A0B5DC971F303A}, // 1e220 - {0x5CEAECFED289E5D2, 0x8DA471A9DE737E24}, // 1e221 - {0x7425A83E872C5F47, 0xB10D8E1456105DAD}, // 1e222 - {0xD12F124E28F77719, 0xDD50F1996B947518}, // 1e223 - {0x82BD6B70D99AAA6F, 0x8A5296FFE33CC92F}, // 1e224 - {0x636CC64D1001550B, 0xACE73CBFDC0BFB7B}, // 1e225 - {0x3C47F7E05401AA4E, 0xD8210BEFD30EFA5A}, // 1e226 - {0x65ACFAEC34810A71, 0x8714A775E3E95C78}, // 1e227 - {0x7F1839A741A14D0D, 0xA8D9D1535CE3B396}, // 1e228 - {0x1EDE48111209A050, 0xD31045A8341CA07C}, // 1e229 - {0x934AED0AAB460432, 0x83EA2B892091E44D}, // 1e230 - {0xF81DA84D5617853F, 0xA4E4B66B68B65D60}, // 1e231 - {0x36251260AB9D668E, 0xCE1DE40642E3F4B9}, // 1e232 - {0xC1D72B7C6B426019, 0x80D2AE83E9CE78F3}, // 1e233 - {0xB24CF65B8612F81F, 0xA1075A24E4421730}, // 1e234 - {0xDEE033F26797B627, 0xC94930AE1D529CFC}, // 1e235 - {0x169840EF017DA3B1, 0xFB9B7CD9A4A7443C}, // 1e236 - {0x8E1F289560EE864E, 0x9D412E0806E88AA5}, // 1e237 - {0xF1A6F2BAB92A27E2, 0xC491798A08A2AD4E}, // 1e238 - {0xAE10AF696774B1DB, 0xF5B5D7EC8ACB58A2}, // 1e239 - {0xACCA6DA1E0A8EF29, 0x9991A6F3D6BF1765}, // 1e240 - {0x17FD090A58D32AF3, 0xBFF610B0CC6EDD3F}, // 1e241 - {0xDDFC4B4CEF07F5B0, 0xEFF394DCFF8A948E}, // 1e242 - {0x4ABDAF101564F98E, 0x95F83D0A1FB69CD9}, // 1e243 - {0x9D6D1AD41ABE37F1, 0xBB764C4CA7A4440F}, // 1e244 - {0x84C86189216DC5ED, 0xEA53DF5FD18D5513}, // 1e245 - {0x32FD3CF5B4E49BB4, 0x92746B9BE2F8552C}, // 1e246 - {0x3FBC8C33221DC2A1, 0xB7118682DBB66A77}, // 1e247 - {0x0FABAF3FEAA5334A, 0xE4D5E82392A40515}, // 1e248 - {0x29CB4D87F2A7400E, 0x8F05B1163BA6832D}, // 1e249 - {0x743E20E9EF511012, 0xB2C71D5BCA9023F8}, // 1e250 - {0x914DA9246B255416, 0xDF78E4B2BD342CF6}, // 1e251 - {0x1AD089B6C2F7548E, 0x8BAB8EEFB6409C1A}, // 1e252 - {0xA184AC2473B529B1, 0xAE9672ABA3D0C320}, // 1e253 - {0xC9E5D72D90A2741E, 0xDA3C0F568CC4F3E8}, // 1e254 - {0x7E2FA67C7A658892, 0x8865899617FB1871}, // 1e255 - {0xDDBB901B98FEEAB7, 0xAA7EEBFB9DF9DE8D}, // 1e256 - {0x552A74227F3EA565, 0xD51EA6FA85785631}, // 1e257 - {0xD53A88958F87275F, 0x8533285C936B35DE}, // 1e258 - {0x8A892ABAF368F137, 0xA67FF273B8460356}, // 1e259 - {0x2D2B7569B0432D85, 0xD01FEF10A657842C}, // 1e260 - {0x9C3B29620E29FC73, 0x8213F56A67F6B29B}, // 1e261 - {0x8349F3BA91B47B8F, 0xA298F2C501F45F42}, // 1e262 - {0x241C70A936219A73, 0xCB3F2F7642717713}, // 1e263 - {0xED238CD383AA0110, 0xFE0EFB53D30DD4D7}, // 1e264 - {0xF4363804324A40AA, 0x9EC95D1463E8A506}, // 1e265 - {0xB143C6053EDCD0D5, 0xC67BB4597CE2CE48}, // 1e266 - {0xDD94B7868E94050A, 0xF81AA16FDC1B81DA}, // 1e267 - {0xCA7CF2B4191C8326, 0x9B10A4E5E9913128}, // 1e268 - {0xFD1C2F611F63A3F0, 0xC1D4CE1F63F57D72}, // 1e269 - {0xBC633B39673C8CEC, 0xF24A01A73CF2DCCF}, // 1e270 - {0xD5BE0503E085D813, 0x976E41088617CA01}, // 1e271 - {0x4B2D8644D8A74E18, 0xBD49D14AA79DBC82}, // 1e272 - {0xDDF8E7D60ED1219E, 0xEC9C459D51852BA2}, // 1e273 - {0xCABB90E5C942B503, 0x93E1AB8252F33B45}, // 1e274 - {0x3D6A751F3B936243, 0xB8DA1662E7B00A17}, // 1e275 - {0x0CC512670A783AD4, 0xE7109BFBA19C0C9D}, // 1e276 - {0x27FB2B80668B24C5, 0x906A617D450187E2}, // 1e277 - {0xB1F9F660802DEDF6, 0xB484F9DC9641E9DA}, // 1e278 - {0x5E7873F8A0396973, 0xE1A63853BBD26451}, // 1e279 - {0xDB0B487B6423E1E8, 0x8D07E33455637EB2}, // 1e280 - {0x91CE1A9A3D2CDA62, 0xB049DC016ABC5E5F}, // 1e281 - {0x7641A140CC7810FB, 0xDC5C5301C56B75F7}, // 1e282 - {0xA9E904C87FCB0A9D, 0x89B9B3E11B6329BA}, // 1e283 - {0x546345FA9FBDCD44, 0xAC2820D9623BF429}, // 1e284 - {0xA97C177947AD4095, 0xD732290FBACAF133}, // 1e285 - {0x49ED8EABCCCC485D, 0x867F59A9D4BED6C0}, // 1e286 - {0x5C68F256BFFF5A74, 0xA81F301449EE8C70}, // 1e287 - {0x73832EEC6FFF3111, 0xD226FC195C6A2F8C}, // 1e288 - {0xC831FD53C5FF7EAB, 0x83585D8FD9C25DB7}, // 1e289 - {0xBA3E7CA8B77F5E55, 0xA42E74F3D032F525}, // 1e290 - {0x28CE1BD2E55F35EB, 0xCD3A1230C43FB26F}, // 1e291 - {0x7980D163CF5B81B3, 0x80444B5E7AA7CF85}, // 1e292 - {0xD7E105BCC332621F, 0xA0555E361951C366}, // 1e293 - {0x8DD9472BF3FEFAA7, 0xC86AB5C39FA63440}, // 1e294 - {0xB14F98F6F0FEB951, 0xFA856334878FC150}, // 1e295 - {0x6ED1BF9A569F33D3, 0x9C935E00D4B9D8D2}, // 1e296 - {0x0A862F80EC4700C8, 0xC3B8358109E84F07}, // 1e297 - {0xCD27BB612758C0FA, 0xF4A642E14C6262C8}, // 1e298 - {0x8038D51CB897789C, 0x98E7E9CCCFBD7DBD}, // 1e299 - {0xE0470A63E6BD56C3, 0xBF21E44003ACDD2C}, // 1e300 - {0x1858CCFCE06CAC74, 0xEEEA5D5004981478}, // 1e301 - {0x0F37801E0C43EBC8, 0x95527A5202DF0CCB}, // 1e302 - {0xD30560258F54E6BA, 0xBAA718E68396CFFD}, // 1e303 - {0x47C6B82EF32A2069, 0xE950DF20247C83FD}, // 1e304 - {0x4CDC331D57FA5441, 0x91D28B7416CDD27E}, // 1e305 - {0xE0133FE4ADF8E952, 0xB6472E511C81471D}, // 1e306 - {0x58180FDDD97723A6, 0xE3D8F9E563A198E5}, // 1e307 - {0x570F09EAA7EA7648, 0x8E679C2F5E44FF8F}, // 1e308 - {0x2CD2CC6551E513DA, 0xB201833B35D63F73}, // 1e309 - {0xF8077F7EA65E58D1, 0xDE81E40A034BCF4F}, // 1e310 - {0xFB04AFAF27FAF782, 0x8B112E86420F6191}, // 1e311 - {0x79C5DB9AF1F9B563, 0xADD57A27D29339F6}, // 1e312 - {0x18375281AE7822BC, 0xD94AD8B1C7380874}, // 1e313 - {0x8F2293910D0B15B5, 0x87CEC76F1C830548}, // 1e314 - {0xB2EB3875504DDB22, 0xA9C2794AE3A3C69A}, // 1e315 - {0x5FA60692A46151EB, 0xD433179D9C8CB841}, // 1e316 - {0xDBC7C41BA6BCD333, 0x849FEEC281D7F328}, // 1e317 - {0x12B9B522906C0800, 0xA5C7EA73224DEFF3}, // 1e318 - {0xD768226B34870A00, 0xCF39E50FEAE16BEF}, // 1e319 - {0xE6A1158300D46640, 0x81842F29F2CCE375}, // 1e320 - {0x60495AE3C1097FD0, 0xA1E53AF46F801C53}, // 1e321 - {0x385BB19CB14BDFC4, 0xCA5E89B18B602368}, // 1e322 - {0x46729E03DD9ED7B5, 0xFCF62C1DEE382C42}, // 1e323 - {0x6C07A2C26A8346D1, 0x9E19DB92B4E31BA9}, // 1e324 - {0xC7098B7305241885, 0xC5A05277621BE293}, // 1e325 - {0xB8CBEE4FC66D1EA7, 0xF70867153AA2DB38}, // 1e326 - {0x737F74F1DC043328, 0x9A65406D44A5C903}, // 1e327 - {0x505F522E53053FF2, 0xC0FE908895CF3B44}, // 1e328 - {0x647726B9E7C68FEF, 0xF13E34AABB430A15}, // 1e329 - {0x5ECA783430DC19F5, 0x96C6E0EAB509E64D}, // 1e330 - {0xB67D16413D132072, 0xBC789925624C5FE0}, // 1e331 - {0xE41C5BD18C57E88F, 0xEB96BF6EBADF77D8}, // 1e332 - {0x8E91B962F7B6F159, 0x933E37A534CBAAE7}, // 1e333 - {0x723627BBB5A4ADB0, 0xB80DC58E81FE95A1}, // 1e334 - {0xCEC3B1AAA30DD91C, 0xE61136F2227E3B09}, // 1e335 - {0x213A4F0AA5E8A7B1, 0x8FCAC257558EE4E6}, // 1e336 - {0xA988E2CD4F62D19D, 0xB3BD72ED2AF29E1F}, // 1e337 - {0x93EB1B80A33B8605, 0xE0ACCFA875AF45A7}, // 1e338 - {0xBC72F130660533C3, 0x8C6C01C9498D8B88}, // 1e339 - {0xEB8FAD7C7F8680B4, 0xAF87023B9BF0EE6A}, // 1e340 - {0xA67398DB9F6820E1, 0xDB68C2CA82ED2A05}, // 1e341 - {0x88083F8943A1148C, 0x892179BE91D43A43}, // 1e342 - {0x6A0A4F6B948959B0, 0xAB69D82E364948D4}, // 1e343 - {0x848CE34679ABB01C, 0xD6444E39C3DB9B09}, // 1e344 - {0xF2D80E0C0C0B4E11, 0x85EAB0E41A6940E5}, // 1e345 - {0x6F8E118F0F0E2195, 0xA7655D1D2103911F}, // 1e346 - {0x4B7195F2D2D1A9FB, 0xD13EB46469447567}, // 1e347 -} diff --git a/examples/gno.land/p/demo/json/eisel_lemire/gno.mod b/examples/gno.land/p/demo/json/eisel_lemire/gno.mod deleted file mode 100644 index d6670de82e2..00000000000 --- a/examples/gno.land/p/demo/json/eisel_lemire/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/p/demo/json/eisel_lemire diff --git a/examples/gno.land/p/demo/json/encode.gno b/examples/gno.land/p/demo/json/encode.gno index be90d7aa73d..55828650e22 100644 --- a/examples/gno.land/p/demo/json/encode.gno +++ b/examples/gno.land/p/demo/json/encode.gno @@ -3,10 +3,8 @@ package json import ( "bytes" "errors" - "math" "strconv" - "gno.land/p/demo/json/ryu" "gno.land/p/demo/ufmt" ) @@ -44,17 +42,8 @@ func Marshal(node *Node) ([]byte, error) { return nil, err } - // ufmt does not support %g. by doing so, we need to check if the number is an integer - // after then, apply the correct format for each float and integer numbers. - if math.Mod(nVal, 1.0) == 0 { - // must convert float to integer. otherwise it will be overflowed. - num := ufmt.Sprintf("%d", int(nVal)) - buf.WriteString(num) - } else { - // use ryu algorithm to convert float to string - num := ryu.FormatFloat64(nVal) - buf.WriteString(num) - } + num := strconv.FormatFloat(nVal, 'f', -1, 64) + buf.WriteString(num) case String: sVal, err = node.GetString() diff --git a/examples/gno.land/p/demo/json/encode_test.gno b/examples/gno.land/p/demo/json/encode_test.gno index e8e53993b5c..831a9e0e0a2 100644 --- a/examples/gno.land/p/demo/json/encode_test.gno +++ b/examples/gno.land/p/demo/json/encode_test.gno @@ -37,10 +37,9 @@ func TestMarshal_Primitive(t *testing.T) { name: "42", node: NumberNode("", 42), }, - // TODO: fix output for not to use scientific notation { - name: "1.005e+02", - node: NumberNode("", 100.5), + name: "3.14", + node: NumberNode("", 3.14), }, { name: `[1,2,3]`, diff --git a/examples/gno.land/p/demo/json/errors.gno b/examples/gno.land/p/demo/json/errors.gno new file mode 100644 index 00000000000..e0836dccdff --- /dev/null +++ b/examples/gno.land/p/demo/json/errors.gno @@ -0,0 +1,34 @@ +package json + +import "errors" + +var ( + errNilNode = errors.New("node is nil") + errNotArrayNode = errors.New("node is not array") + errNotBoolNode = errors.New("node is not boolean") + errNotNullNode = errors.New("node is not null") + errNotNumberNode = errors.New("node is not number") + errNotObjectNode = errors.New("node is not object") + errNotStringNode = errors.New("node is not string") + errInvalidToken = errors.New("invalid token") + errIndexNotFound = errors.New("index not found") + errInvalidAppend = errors.New("can't append value to non-appendable node") + errInvalidAppendCycle = errors.New("appending value to itself or its children or parents will cause a cycle") + errInvalidEscapeSequence = errors.New("invalid escape sequence") + errInvalidStringValue = errors.New("invalid string value") + errEmptyBooleanNode = errors.New("boolean node is empty") + errEmptyStringNode = errors.New("string node is empty") + errKeyRequired = errors.New("key is required for object") + errUnmatchedParenthesis = errors.New("mismatched bracket or parenthesis") + errUnmatchedQuotePath = errors.New("unmatched quote in path") +) + +var ( + errInvalidStringInput = errors.New("invalid string input") + errMalformedBooleanValue = errors.New("malformed boolean value") + errEmptyByteSlice = errors.New("empty byte slice") + errInvalidExponentValue = errors.New("invalid exponent value") + errNonDigitCharacters = errors.New("non-digit characters found") + errNumericRangeExceeded = errors.New("numeric value exceeds the range limit") + errMultipleDecimalPoints = errors.New("multiple decimal points found") +) diff --git a/examples/gno.land/p/demo/json/escape.gno b/examples/gno.land/p/demo/json/escape.gno index 5a834068127..ee3e4a79855 100644 --- a/examples/gno.land/p/demo/json/escape.gno +++ b/examples/gno.land/p/demo/json/escape.gno @@ -1,8 +1,6 @@ package json import ( - "bytes" - "errors" "unicode/utf8" ) @@ -13,6 +11,9 @@ const ( surrogateEnd = 0xDFFF basicMultilingualPlaneOffset = 0xFFFF badHex = -1 + + singleUnicodeEscapeLen = 6 + surrogatePairLen = 12 ) var hexLookupTable = [256]int{ @@ -42,48 +43,32 @@ func h2i(c byte) int { // // it returns the processed slice and any error encountered during the Unescape operation. func Unescape(input, output []byte) ([]byte, error) { - // find the index of the first backslash in the input slice. - firstBackslash := bytes.IndexByte(input, backSlash) - if firstBackslash == -1 { - return input, nil - } - - // ensure the output slice has enough capacity to hold the result. + // ensure the output slice has enough capacity to hold the input slice. inputLen := len(input) if cap(output) < inputLen { output = make([]byte, inputLen) } - output = output[:inputLen] - copy(output, input[:firstBackslash]) - - input = input[firstBackslash:] - buf := output[firstBackslash:] - - for len(input) > 0 { - inLen, bufLen, err := processEscapedUTF8(input, buf) - if err != nil { - return nil, err - } - - input = input[inLen:] // the number of bytes consumed in the input - buf = buf[bufLen:] // the number of bytes written to buf + inPos, outPos := 0, 0 - // find the next backslash in the remaining input - nextBackslash := bytes.IndexByte(input, backSlash) - if nextBackslash == -1 { - copy(buf, input) - buf = buf[len(input):] - break + for inPos < len(input) { + c := input[inPos] + if c != backSlash { + output[outPos] = c + inPos++ + outPos++ + } else { + // process escape sequence + inLen, outLen, err := processEscapedUTF8(input[inPos:], output[outPos:]) + if err != nil { + return nil, err + } + inPos += inLen + outPos += outLen } - - copy(buf, input[:nextBackslash]) - - input = input[nextBackslash:] - buf = buf[nextBackslash:] } - return output[:len(output)-len(buf)], nil + return output[:outPos], nil } // isSurrogatePair returns true if the rune is a surrogate pair. @@ -94,6 +79,16 @@ func isSurrogatePair(r rune) bool { return highSurrogateOffset <= r && r <= surrogateEnd } +// isHighSurrogate checks if the rune is a high surrogate (U+D800 to U+DBFF). +func isHighSurrogate(r rune) bool { + return r >= highSurrogateOffset && r <= 0xDBFF +} + +// isLowSurrogate checks if the rune is a low surrogate (U+DC00 to U+DFFF). +func isLowSurrogate(r rune) bool { + return r >= lowSurrogateOffset && r <= surrogateEnd +} + // combineSurrogates reconstruct the original unicode code points in the // supplemental plane by combinin the high and low surrogate. // @@ -122,28 +117,41 @@ func decodeSingleUnicodeEscape(b []byte) (rune, bool) { } // decodeUnicodeEscape decodes a Unicode escape sequence from a byte slice. +// It handles both single Unicode escape sequences and surrogate pairs. func decodeUnicodeEscape(b []byte) (rune, int) { + // decode the first Unicode escape sequence. r, ok := decodeSingleUnicodeEscape(b) if !ok { return utf8.RuneError, -1 } - // determine valid unicode escapes within the BMP + // if the rune is within the BMP and not a surrogate, return it if r <= basicMultilingualPlaneOffset && !isSurrogatePair(r) { return r, 6 } - // Decode the following escape sequence to verify a UTF-16 susergate pair. - r2, ok := decodeSingleUnicodeEscape(b[6:]) - if !ok { + if !isHighSurrogate(r) { + // invalid surrogate pair. return utf8.RuneError, -1 } - if r2 < lowSurrogateOffset { + // if the rune is a high surrogate, need to decode the next escape sequence. + + // ensure there are enough bytes for the next escape sequence. + if len(b) < surrogatePairLen { return utf8.RuneError, -1 } - - return combineSurrogates(r, r2), 12 + // decode the second Unicode escape sequence. + r2, ok := decodeSingleUnicodeEscape(b[singleUnicodeEscapeLen:]) + if !ok { + return utf8.RuneError, -1 + } + // check if the second rune is a low surrogate. + if isLowSurrogate(r2) { + combined := combineSurrogates(r, r2) + return combined, surrogatePairLen + } + return utf8.RuneError, -1 } var escapeByteSet = [256]byte{ @@ -165,7 +173,6 @@ func Unquote(s []byte, border byte) (string, bool) { } // unquoteBytes takes a byte slice and unquotes it by removing -// TODO: consider to move this function to the strconv package. func unquoteBytes(s []byte, border byte) ([]byte, bool) { if len(s) < 2 || s[0] != border || s[len(s)-1] != border { return nil, false @@ -259,21 +266,12 @@ func unquoteBytes(s []byte, border byte) ([]byte, bool) { return b[:w], true } -// processEscapedUTF8 processes the escape sequence in the given byte slice and -// and converts them to UTF-8 characters. The function returns the length of the processed input and output. -// -// The input 'in' must contain the escape sequence to be processed, -// and 'out' provides a space to store the converted characters. -// -// The function returns (input length, output length) if the escape sequence is correct. -// Unicode escape sequences (e.g. \uXXXX) are decoded to UTF-8, other default escape sequences are -// converted to their corresponding special characters (e.g. \n -> newline). -// -// If the escape sequence is invalid, or if 'in' does not completely enclose the escape sequence, -// function returns (-1, -1) to indicate an error. +// processEscapedUTF8 converts escape sequences to UTF-8 characters. +// It decodes Unicode escape sequences (\uXXXX) to UTF-8 and +// converts standard escape sequences (e.g., \n) to their corresponding special characters. func processEscapedUTF8(in, out []byte) (int, int, error) { if len(in) < 2 || in[0] != backSlash { - return -1, -1, errors.New("invalid escape sequence") + return -1, -1, errInvalidEscapeSequence } escapeSeqLen := 2 @@ -282,7 +280,7 @@ func processEscapedUTF8(in, out []byte) (int, int, error) { if escapeChar != 'u' { val := escapeByteSet[escapeChar] if val == 0 { - return -1, -1, errors.New("invalid escape sequence") + return -1, -1, errInvalidEscapeSequence } out[0] = val @@ -291,7 +289,7 @@ func processEscapedUTF8(in, out []byte) (int, int, error) { r, size := decodeUnicodeEscape(in) if size == -1 { - return -1, -1, errors.New("invalid escape sequence") + return -1, -1, errInvalidEscapeSequence } outLen := utf8.EncodeRune(out, r) diff --git a/examples/gno.land/p/demo/json/escape_test.gno b/examples/gno.land/p/demo/json/escape_test.gno index 40c118d93ce..0e2e696e83c 100644 --- a/examples/gno.land/p/demo/json/escape_test.gno +++ b/examples/gno.land/p/demo/json/escape_test.gno @@ -103,24 +103,25 @@ func TestDecodeSingleUnicodeEscape(t *testing.T) { } func TestDecodeUnicodeEscape(t *testing.T) { - testCases := []struct { - input string + tests := []struct { + input []byte expected rune size int }{ - {"\\u0041", 'A', 6}, - {"\\u03B1", 'α', 6}, - {"\\u1F600", 0x1F60, 6}, - {"\\uD830\\uDE03", 0x1C203, 12}, - {"\\uD800\\uDC00", 0x00010000, 12}, - - {"\\u004", utf8.RuneError, -1}, - {"\\uXYZW", utf8.RuneError, -1}, - {"\\uD83D\\u0041", utf8.RuneError, -1}, + {[]byte(`\u0041`), 'A', 6}, + {[]byte(`\uD83D\uDE00`), 0x1F600, 12}, // 😀 + {[]byte(`\uD834\uDD1E`), 0x1D11E, 12}, // 𝄞 + {[]byte(`\uFFFF`), '\uFFFF', 6}, + {[]byte(`\uXYZW`), utf8.RuneError, -1}, + {[]byte(`\uD800`), utf8.RuneError, -1}, // single high surrogate + {[]byte(`\uDC00`), utf8.RuneError, -1}, // single low surrogate + {[]byte(`\uD800\uDC00`), 0x10000, 12}, // First code point above U+FFFF + {[]byte(`\uDBFF\uDFFF`), 0x10FFFF, 12}, // Maximum code point + {[]byte(`\uD83D\u0041`), utf8.RuneError, -1}, // invalid surrogate pair } - for _, tc := range testCases { - r, size := decodeUnicodeEscape([]byte(tc.input)) + for _, tc := range tests { + r, size := decodeUnicodeEscape(tc.input) if r != tc.expected || size != tc.size { t.Errorf("decodeUnicodeEscape(%q) = (%U, %d); want (%U, %d)", tc.input, r, size, tc.expected, tc.size) } @@ -128,7 +129,7 @@ func TestDecodeUnicodeEscape(t *testing.T) { } func TestUnescapeToUTF8(t *testing.T) { - testCases := []struct { + tests := []struct { input []byte expectedIn int expectedOut int @@ -150,7 +151,7 @@ func TestUnescapeToUTF8(t *testing.T) { {[]byte(`\uD83D\u0041`), -1, -1, true}, // invalid unicode escape sequence } - for _, tc := range testCases { + for _, tc := range tests { input := make([]byte, len(tc.input)) copy(input, tc.input) output := make([]byte, utf8.UTFMax) @@ -166,23 +167,32 @@ func TestUnescapeToUTF8(t *testing.T) { } func TestUnescape(t *testing.T) { - testCases := []struct { + tests := []struct { name string input []byte expected []byte + isError bool }{ - {"NoEscape", []byte("hello world"), []byte("hello world")}, - {"SingleEscape", []byte("hello\\nworld"), []byte("hello\nworld")}, - {"MultipleEscapes", []byte("line1\\nline2\\r\\nline3"), []byte("line1\nline2\r\nline3")}, - {"UnicodeEscape", []byte("snowman:\\u2603"), []byte("snowman:\u2603")}, - {"Complex", []byte("tc\\n\\u2603\\r\\nend"), []byte("tc\n\u2603\r\nend")}, + {"NoEscape", []byte("hello world"), []byte("hello world"), false}, + {"SingleEscape", []byte("hello\\nworld"), []byte("hello\nworld"), false}, + {"MultipleEscapes", []byte("line1\\nline2\\r\\nline3"), []byte("line1\nline2\r\nline3"), false}, + {"UnicodeEscape", []byte("snowman:\\u2603"), []byte("snowman:\u2603"), false}, + {"SurrogatePair", []byte("emoji:\\uD83D\\uDE00"), []byte("emoji:😀"), false}, + {"InvalidEscape", []byte("hello\\xworld"), nil, true}, + {"IncompleteUnicode", []byte("incomplete:\\u123"), nil, true}, + {"InvalidSurrogatePair", []byte("invalid:\\uD83D\\u0041"), nil, true}, } - for _, tc := range testCases { + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - output, _ := Unescape(tc.input, make([]byte, len(tc.input)+10)) - if !bytes.Equal(output, tc.expected) { - t.Errorf("unescape(%q) = %q; want %q", tc.input, output, tc.expected) + output := make([]byte, len(tc.input)*2) // Allocate extra space for possible expansion + result, err := Unescape(tc.input, output) + if (err != nil) != tc.isError { + t.Errorf("Unescape(%q) error = %v; want error = %v", tc.input, err, tc.isError) + } + + if !tc.isError && !bytes.Equal(result, tc.expected) { + t.Errorf("Unescape(%q) = %q; want %q", tc.input, result, tc.expected) } }) } @@ -206,6 +216,7 @@ func TestUnquoteBytes(t *testing.T) { {[]byte("\"\\u0041\""), '"', []byte("A"), true}, {[]byte(`"Hello, 世界"`), '"', []byte("Hello, 世界"), true}, {[]byte(`"Hello, \x80"`), '"', nil, false}, + {[]byte(`"invalid surrogate: \uD83D\u0041"`), '"', nil, false}, } for _, tc := range tests { diff --git a/examples/gno.land/p/demo/json/gno.mod b/examples/gno.land/p/demo/json/gno.mod index 8a380644acc..ef794458c56 100644 --- a/examples/gno.land/p/demo/json/gno.mod +++ b/examples/gno.land/p/demo/json/gno.mod @@ -1,7 +1,3 @@ module gno.land/p/demo/json -require ( - gno.land/p/demo/json/eisel_lemire v0.0.0-latest - gno.land/p/demo/json/ryu v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/p/demo/json/indent.gno b/examples/gno.land/p/demo/json/indent.gno index cdcfd4524ee..cdf9d5e976f 100644 --- a/examples/gno.land/p/demo/json/indent.gno +++ b/examples/gno.land/p/demo/json/indent.gno @@ -9,21 +9,7 @@ import ( // A factor no higher than 2 ensures that wasted space never exceeds 50%. const indentGrowthFactor = 2 -// IndentJSON takes a JSON byte slice and a string for indentation, -// then formats the JSON according to the specified indent string. -// This function applies indentation rules as follows: -// -// 1. For top-level arrays and objects, no additional indentation is applied. -// -// 2. For nested structures like arrays within arrays or objects, indentation increases. -// -// 3. Indentation is applied after opening brackets ('[' or '{') and before closing brackets (']' or '}'). -// -// 4. Commas and colons are handled appropriately to maintain valid JSON format. -// -// 5. Nested arrays within objects or arrays receive new lines and indentation based on their depth level. -// -// The function returns the formatted JSON as a byte slice and an error if any issues occurred during formatting. +// IndentJSON formats the JSON data with the specified indentation. func Indent(data []byte, indent string) ([]byte, error) { var ( out bytes.Buffer diff --git a/examples/gno.land/p/demo/json/node.gno b/examples/gno.land/p/demo/json/node.gno index 1e71a101e62..c917150bc3d 100644 --- a/examples/gno.land/p/demo/json/node.gno +++ b/examples/gno.land/p/demo/json/node.gno @@ -44,7 +44,7 @@ func NewNode(prev *Node, b *buffer, typ ValueType, key **string) (*Node, error) prev.next[strconv.Itoa(size)] = curr } else if prev.IsObject() { if key == nil { - return nil, errors.New("key is required for object") + return nil, errKeyRequired } prev.next[**key] = curr @@ -88,7 +88,7 @@ func (n *Node) HasKey(key string) bool { // GetKey returns the value of the given key from the current object node. func (n *Node) GetKey(key string) (*Node, error) { if n == nil { - return nil, errors.New("node is nil") + return nil, errNilNode } if n.Type() != Object { @@ -174,7 +174,7 @@ func (n *Node) Value() (value interface{}, err error) { return nil, nil case Number: - value, err = ParseFloatLiteral(n.source()) + value, err = strconv.ParseFloat(string(n.source()), 64) if err != nil { return nil, err } @@ -185,14 +185,14 @@ func (n *Node) Value() (value interface{}, err error) { var ok bool value, ok = Unquote(n.source(), doubleQuote) if !ok { - return "", errors.New("invalid string value") + return "", errInvalidStringValue } n.value = value case Boolean: if len(n.source()) == 0 { - return nil, errors.New("empty boolean value") + return nil, errEmptyBooleanNode } b := n.source()[0] @@ -319,11 +319,11 @@ func (n *Node) MustIndex(expectIdx int) *Node { // if the index is negative, it returns the index is from the end of the array. func (n *Node) GetIndex(idx int) (*Node, error) { if n == nil { - return nil, errors.New("node is nil") + return nil, errNilNode } if !n.IsArray() { - return nil, errors.New("node is not array") + return nil, errNotArrayNode } if idx > n.Size() { @@ -336,7 +336,7 @@ func (n *Node) GetIndex(idx int) (*Node, error) { child, ok := n.next[strconv.Itoa(idx)] if !ok { - return nil, errors.New("index not found") + return nil, errIndexNotFound } return child, nil @@ -556,11 +556,11 @@ func (n *Node) root() *Node { // } func (n *Node) GetNull() (interface{}, error) { if n == nil { - return nil, errors.New("node is nil") + return nil, errNilNode } if !n.IsNull() { - return nil, errors.New("node is not null") + return nil, errNotNullNode } return nil, nil @@ -590,11 +590,11 @@ func (n *Node) MustNull() interface{} { // println(val) // 10.5 func (n *Node) GetNumeric() (float64, error) { if n == nil { - return 0, errors.New("node is nil") + return 0, errNilNode } if n.nodeType != Number { - return 0, errors.New("node is not number") + return 0, errNotNumberNode } val, err := n.Value() @@ -604,7 +604,7 @@ func (n *Node) GetNumeric() (float64, error) { v, ok := val.(float64) if !ok { - return 0, errors.New("node is not number") + return 0, errNotNumberNode } return v, nil @@ -639,11 +639,11 @@ func (n *Node) MustNumeric() float64 { // println(str) // "foo" func (n *Node) GetString() (string, error) { if n == nil { - return "", errors.New("string node is empty") + return "", errEmptyStringNode } if !n.IsString() { - return "", errors.New("node type is not string") + return "", errNotStringNode } val, err := n.Value() @@ -653,7 +653,7 @@ func (n *Node) GetString() (string, error) { v, ok := val.(string) if !ok { - return "", errors.New("node is not string") + return "", errNotStringNode } return v, nil @@ -683,11 +683,11 @@ func (n *Node) MustString() string { // println(val) // true func (n *Node) GetBool() (bool, error) { if n == nil { - return false, errors.New("node is nil") + return false, errNilNode } if n.nodeType != Boolean { - return false, errors.New("node is not boolean") + return false, errNotBoolNode } val, err := n.Value() @@ -697,7 +697,7 @@ func (n *Node) GetBool() (bool, error) { v, ok := val.(bool) if !ok { - return false, errors.New("node is not boolean") + return false, errNotBoolNode } return v, nil @@ -732,11 +732,11 @@ func (n *Node) MustBool() bool { // result: "foo", 1 func (n *Node) GetArray() ([]*Node, error) { if n == nil { - return nil, errors.New("node is nil") + return nil, errNilNode } if n.nodeType != Array { - return nil, errors.New("node is not array") + return nil, errNotArrayNode } val, err := n.Value() @@ -746,7 +746,7 @@ func (n *Node) GetArray() ([]*Node, error) { v, ok := val.([]*Node) if !ok { - return nil, errors.New("node is not array") + return nil, errNotArrayNode } return v, nil @@ -788,7 +788,7 @@ func (n *Node) MustArray() []*Node { // result: ["bar", "baz", 1, "foo"] func (n *Node) AppendArray(value ...*Node) error { if !n.IsArray() { - return errors.New("can't append value to non-array node") + return errInvalidAppend } for _, val := range value { @@ -836,11 +836,11 @@ func (n *Node) ArrayEach(callback func(i int, target *Node)) { // result: map[string]*Node{"key": StringNode("key", "value")} func (n *Node) GetObject() (map[string]*Node, error) { if n == nil { - return nil, errors.New("node is nil") + return nil, errNilNode } if !n.IsObject() { - return nil, errors.New("node is not object") + return nil, errNotObjectNode } val, err := n.Value() @@ -850,7 +850,7 @@ func (n *Node) GetObject() (map[string]*Node, error) { v, ok := val.(map[string]*Node) if !ok { - return nil, errors.New("node is not object") + return nil, errNotObjectNode } return v, nil @@ -873,7 +873,7 @@ func (n *Node) MustObject() map[string]*Node { // If the current node is not object type, it returns an error. func (n *Node) AppendObject(key string, value *Node) error { if !n.IsObject() { - return errors.New("can't append value to non-object node") + return errInvalidAppend } if err := n.append(&key, value); err != nil { @@ -1003,7 +1003,7 @@ func (n *Node) dropIndex(idx int) { // append is a helper function to append the given value to the current container type node. func (n *Node) append(key *string, val *Node) error { if n.isSameOrParentNode(val) { - return errors.New("can't append same or parent node") + return errInvalidAppendCycle } if val.prev != nil { diff --git a/examples/gno.land/p/demo/json/parser.gno b/examples/gno.land/p/demo/json/parser.gno index 9a2c3a8c817..bae06cb3789 100644 --- a/examples/gno.land/p/demo/json/parser.gno +++ b/examples/gno.land/p/demo/json/parser.gno @@ -2,27 +2,22 @@ package json import ( "bytes" - "errors" - "strconv" - - el "gno.land/p/demo/json/eisel_lemire" ) const ( - absMinInt64 = 1 << 63 - maxInt64 = absMinInt64 - 1 - maxUint64 = 1<<64 - 1 + unescapeStackBufSize = 64 + absMinInt64 = 1 << 63 + maxInt64 = absMinInt64 - 1 + maxUint64 = 1<<64 - 1 ) -const unescapeStackBufSize = 64 - // PaseStringLiteral parses a string from the given byte slice. func ParseStringLiteral(data []byte) (string, error) { var buf [unescapeStackBufSize]byte bf, err := Unescape(data, buf[:]) if err != nil { - return "", errors.New("invalid string input found while parsing string value") + return "", errInvalidStringInput } return string(bf), nil @@ -36,150 +31,6 @@ func ParseBoolLiteral(data []byte) (bool, error) { case bytes.Equal(data, falseLiteral): return false, nil default: - return false, errors.New("JSON Error: malformed boolean value found while parsing boolean value") - } -} - -// PaseFloatLiteral parses a float64 from the given byte slice. -// -// It utilizes double-precision (64-bit) floating-point format as defined -// by the IEEE 754 standard, providing a decimal precision of approximately 15 digits. -func ParseFloatLiteral(bytes []byte) (float64, error) { - if len(bytes) == 0 { - return -1, errors.New("JSON Error: empty byte slice found while parsing float value") - } - - neg, bytes := trimNegativeSign(bytes) - - var exponentPart []byte - for i, c := range bytes { - if lower(c) == 'e' { - exponentPart = bytes[i+1:] - bytes = bytes[:i] - break - } - } - - man, exp10, err := extractMantissaAndExp10(bytes) - if err != nil { - return -1, err - } - - if len(exponentPart) > 0 { - exp, err := strconv.Atoi(string(exponentPart)) - if err != nil { - return -1, errors.New("JSON Error: invalid exponent value found while parsing float value") - } - exp10 += exp - } - - // for fast float64 conversion - f, success := el.EiselLemire64(man, exp10, neg) - if !success { - return 0, nil - } - - return f, nil -} - -func ParseIntLiteral(bytes []byte) (int64, error) { - if len(bytes) == 0 { - return 0, errors.New("JSON Error: empty byte slice found while parsing integer value") - } - - neg, bytes := trimNegativeSign(bytes) - - var n uint64 = 0 - for _, c := range bytes { - if notDigit(c) { - return 0, errors.New("JSON Error: non-digit characters found while parsing integer value") - } - - if n > maxUint64/10 { - return 0, errors.New("JSON Error: numeric value exceeds the range limit") - } - - n *= 10 - - n1 := n + uint64(c-'0') - if n1 < n { - return 0, errors.New("JSON Error: numeric value exceeds the range limit") - } - - n = n1 - } - - if n > maxInt64 { - if neg && n == absMinInt64 { - return -absMinInt64, nil - } - - return 0, errors.New("JSON Error: numeric value exceeds the range limit") + return false, errMalformedBooleanValue } - - if neg { - return -int64(n), nil - } - - return int64(n), nil -} - -// extractMantissaAndExp10 parses a byte slice representing a decimal number and extracts the mantissa and the exponent of its base-10 representation. -// It iterates through the bytes, constructing the mantissa by treating each byte as a digit. -// If a decimal point is encountered, the function keeps track of the position of the decimal point to calculate the exponent. -// The function ensures that: -// - The number contains at most one decimal point. -// - All characters in the byte slice are digits or a single decimal point. -// - The resulting mantissa does not overflow a uint64. -func extractMantissaAndExp10(bytes []byte) (uint64, int, error) { - var ( - man uint64 - exp10 int - decimalFound bool - ) - - for _, c := range bytes { - if c == dot { - if decimalFound { - return 0, 0, errors.New("JSON Error: multiple decimal points found while parsing float value") - } - decimalFound = true - continue - } - - if notDigit(c) { - return 0, 0, errors.New("JSON Error: non-digit characters found while parsing integer value") - } - - digit := uint64(c - '0') - - if man > (maxUint64-digit)/10 { - return 0, 0, errors.New("JSON Error: numeric value exceeds the range limit") - } - - man = man*10 + digit - - if decimalFound { - exp10-- - } - } - - return man, exp10, nil -} - -func trimNegativeSign(bytes []byte) (bool, []byte) { - if bytes[0] == minus { - return true, bytes[1:] - } - - return false, bytes -} - -func notDigit(c byte) bool { - return (c & 0xF0) != 0x30 -} - -// lower converts a byte to lower case if it is an uppercase letter. -func lower(c byte) byte { - return c | 0x20 } diff --git a/examples/gno.land/p/demo/json/parser_test.gno b/examples/gno.land/p/demo/json/parser_test.gno index 078aa048a61..a05e313f67b 100644 --- a/examples/gno.land/p/demo/json/parser_test.gno +++ b/examples/gno.land/p/demo/json/parser_test.gno @@ -64,125 +64,3 @@ func TestParseBoolLiteral(t *testing.T) { } } } - -func TestParseFloatLiteral(t *testing.T) { - tests := []struct { - input string - expected float64 - }{ - {"123", 123}, - {"-123", -123}, - {"123.456", 123.456}, - {"-123.456", -123.456}, - {"12345678.1234567890", 12345678.1234567890}, - {"-12345678.09123456789", -12345678.09123456789}, - {"0.123", 0.123}, - {"-0.123", -0.123}, - {"", -1}, - {"abc", -1}, - {"123.45.6", -1}, - {"999999999999999999999", -1}, - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - got, _ := ParseFloatLiteral([]byte(tt.input)) - if got != tt.expected { - t.Errorf("ParseFloatLiteral(%s): got %v, want %v", tt.input, got, tt.expected) - } - }) - } -} - -func TestParseFloatWithScientificNotation(t *testing.T) { - tests := []struct { - input string - expected float64 - }{ - {"1e6", 1000000}, - {"1E6", 1000000}, - {"1.23e10", 1.23e10}, - {"1.23E10", 1.23e10}, - {"-1.23e10", -1.23e10}, - {"-1.23E10", -1.23e10}, - {"2.45e-8", 2.45e-8}, - {"2.45E-8", 2.45e-8}, - {"-2.45e-8", -2.45e-8}, - {"-2.45E-8", -2.45e-8}, - {"5e0", 5}, - {"-5e0", -5}, - {"5E+0", 5}, - {"5e+1", 50}, - {"1e-1", 0.1}, - {"1E-1", 0.1}, - {"-1e-1", -0.1}, - {"-1E-1", -0.1}, - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - got, err := ParseFloatLiteral([]byte(tt.input)) - if got != tt.expected { - t.Errorf("ParseFloatLiteral(%s): got %v, want %v", tt.input, got, tt.expected) - } - - if err != nil { - t.Errorf("ParseFloatLiteral(%s): got error %v", tt.input, err) - } - }) - } -} - -func TestParseFloat_May_Interoperability_Problem(t *testing.T) { - tests := []struct { - input string - shouldErr bool - }{ - {"3.141592653589793238462643383279", true}, - {"1E400", false}, // TODO: should error - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - _, err := ParseFloatLiteral([]byte(tt.input)) - if tt.shouldErr && err == nil { - t.Errorf("ParseFloatLiteral(%s): expected error, but not error", tt.input) - } - }) - } -} - -func TestParseIntLiteral(t *testing.T) { - tests := []struct { - input string - expected int64 - }{ - {"0", 0}, - {"1", 1}, - {"-1", -1}, - {"12345", 12345}, - {"-12345", -12345}, - {"9223372036854775807", 9223372036854775807}, - {"-9223372036854775808", -9223372036854775808}, - {"-92233720368547758081", 0}, - {"18446744073709551616", 0}, - {"9223372036854775808", 0}, - {"-9223372036854775809", 0}, - {"", 0}, - {"abc", 0}, - {"12345x", 0}, - {"123e5", 0}, - {"9223372036854775807x", 0}, - {"27670116110564327410", 0}, - {"-27670116110564327410", 0}, - } - - for _, tt := range tests { - t.Run(tt.input, func(t *testing.T) { - got, _ := ParseIntLiteral([]byte(tt.input)) - if got != tt.expected { - t.Errorf("ParseIntLiteral(%s): got %v, want %v", tt.input, got, tt.expected) - } - }) - } -} diff --git a/examples/gno.land/p/demo/json/ryu/License b/examples/gno.land/p/demo/json/ryu/License deleted file mode 100644 index 55beeadce54..00000000000 --- a/examples/gno.land/p/demo/json/ryu/License +++ /dev/null @@ -1,21 +0,0 @@ -# Apache License - -Copyright 2018 Ulf Adams -Modifications copyright 2019 Caleb Spare - -The contents of this file may be used under the terms of the Apache License, -Version 2.0. - - (See accompanying file LICENSE or copy at - ) - -Unless required by applicable law or agreed to in writing, this software -is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. - -The code in this file is part of a Go translation of the C code originally written by -Ulf Adams, which can be found at . The original source -code is licensed under the Apache License 2.0. This code is a derivative work thereof, -adapted and modified to meet the specifications of the Gno language project. - -Please note that the modifications are also under the Apache License 2.0 unless otherwise specified. diff --git a/examples/gno.land/p/demo/json/ryu/floatconv.gno b/examples/gno.land/p/demo/json/ryu/floatconv.gno deleted file mode 100644 index 617141d2734..00000000000 --- a/examples/gno.land/p/demo/json/ryu/floatconv.gno +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2018 Ulf Adams -// Modifications copyright 2019 Caleb Spare -// -// The contents of this file may be used under the terms of the Apache License, -// Version 2.0. -// -// (See accompanying file LICENSE or copy at -// http://www.apache.org/licenses/LICENSE-2.0) -// -// Unless required by applicable law or agreed to in writing, this software -// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. -// -// The code in this file is part of a Go translation of the C code originally written by -// Ulf Adams, which can be found at https://github.com/ulfjack/ryu. The original source -// code is licensed under the Apache License 2.0. This code is a derivative work thereof, -// adapted and modified to meet the specifications of the Gno language project. -// -// original Go implementation can be found at https://github.com/cespare/ryu. -// -// Please note that the modifications are also under the Apache License 2.0 unless -// otherwise specified. - -// Package ryu implements the Ryu algorithm for quickly converting floating -// point numbers into strings. -package ryu - -import ( - "math" -) - -const ( - mantBits32 = 23 - expBits32 = 8 - bias32 = 127 - - mantBits64 = 52 - expBits64 = 11 - bias64 = 1023 -) - -// FormatFloat64 converts a 64-bit floating point number f to a string. -// It behaves like strconv.FormatFloat(f, 'e', -1, 64). -func FormatFloat64(f float64) string { - b := make([]byte, 0, 24) - b = AppendFloat64(b, f) - return string(b) -} - -// AppendFloat64 appends the string form of the 64-bit floating point number f, -// as generated by FormatFloat64, to b and returns the extended buffer. -func AppendFloat64(b []byte, f float64) []byte { - // Step 1: Decode the floating-point number. - // Unify normalized and subnormal cases. - u := math.Float64bits(f) - neg := u>>(mantBits64+expBits64) != 0 - mant := u & (uint64(1)<> mantBits64) & (uint64(1)<= 0, "e >= 0") - assert(e <= 1650, "e <= 1650") - return (uint32(e) * 78913) >> 18 -} - -// log10Pow5 returns floor(log_10(5^e)). -func log10Pow5(e int32) uint32 { - // The first value this approximation fails for is 5^2621 - // which is just greater than 10^1832. - assert(e >= 0, "e >= 0") - assert(e <= 2620, "e <= 2620") - return (uint32(e) * 732923) >> 20 -} - -// pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0. -func pow5Bits(e int32) int32 { - // This approximation works up to the point that the multiplication - // overflows at e = 3529. If the multiplication were done in 64 bits, - // it would fail at 5^4004 which is just greater than 2^9297. - assert(e >= 0, "e >= 0") - assert(e <= 3528, "e <= 3528") - return int32((uint32(e)*1217359)>>19 + 1) -} diff --git a/examples/gno.land/p/demo/json/ryu/floatconv_test.gno b/examples/gno.land/p/demo/json/ryu/floatconv_test.gno deleted file mode 100644 index 7f01d4034f7..00000000000 --- a/examples/gno.land/p/demo/json/ryu/floatconv_test.gno +++ /dev/null @@ -1,33 +0,0 @@ -package ryu - -import ( - "math" - "testing" -) - -func TestFormatFloat64(t *testing.T) { - tests := []struct { - name string - value float64 - expected string - }{ - {"positive infinity", math.Inf(1), "+Inf"}, - {"negative infinity", math.Inf(-1), "-Inf"}, - {"NaN", math.NaN(), "NaN"}, - {"zero", 0.0, "0e+00"}, - {"negative zero", -0.0, "0e+00"}, - {"positive number", 3.14159, "3.14159e+00"}, - {"negative number", -2.71828, "-2.71828e+00"}, - {"very small number", 1.23e-20, "1.23e-20"}, - {"very large number", 1.23e+20, "1.23e+20"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - result := FormatFloat64(test.value) - if result != test.expected { - t.Errorf("FormatFloat64(%v) = %q, expected %q", test.value, result, test.expected) - } - }) - } -} diff --git a/examples/gno.land/p/demo/json/ryu/gno.mod b/examples/gno.land/p/demo/json/ryu/gno.mod deleted file mode 100644 index 86a1988b052..00000000000 --- a/examples/gno.land/p/demo/json/ryu/gno.mod +++ /dev/null @@ -1 +0,0 @@ -module gno.land/p/demo/json/ryu diff --git a/examples/gno.land/p/demo/json/ryu/ryu64.gno b/examples/gno.land/p/demo/json/ryu/ryu64.gno deleted file mode 100644 index 249e3d0f526..00000000000 --- a/examples/gno.land/p/demo/json/ryu/ryu64.gno +++ /dev/null @@ -1,344 +0,0 @@ -package ryu - -import ( - "math/bits" -) - -type uint128 struct { - lo uint64 - hi uint64 -} - -// dec64 is a floating decimal type representing m * 10^e. -type dec64 struct { - m uint64 - e int32 -} - -func (d dec64) append(b []byte, neg bool) []byte { - // Step 5: Print the decimal representation. - if neg { - b = append(b, '-') - } - - out := d.m - outLen := decimalLen64(out) - bufLen := outLen - if bufLen > 1 { - bufLen++ // extra space for '.' - } - - // Print the decimal digits. - n := len(b) - if cap(b)-len(b) >= bufLen { - // Avoid function call in the common case. - b = b[:len(b)+bufLen] - } else { - b = append(b, make([]byte, bufLen)...) - } - - // Avoid expensive 64-bit divisions. - // We have at most 17 digits, and uint32 can store 9 digits. - // If the output doesn't fit into a uint32, cut off 8 digits - // so the rest will fit into a uint32. - var i int - if out>>32 > 0 { - var out32 uint32 - out, out32 = out/1e8, uint32(out%1e8) - for ; i < 8; i++ { - b[n+outLen-i] = '0' + byte(out32%10) - out32 /= 10 - } - } - out32 := uint32(out) - for ; i < outLen-1; i++ { - b[n+outLen-i] = '0' + byte(out32%10) - out32 /= 10 - } - b[n] = '0' + byte(out32%10) - - // Print the '.' if needed. - if outLen > 1 { - b[n+1] = '.' - } - - // Print the exponent. - b = append(b, 'e') - exp := d.e + int32(outLen) - 1 - if exp < 0 { - b = append(b, '-') - exp = -exp - } else { - // Unconditionally print a + here to match strconv's formatting. - b = append(b, '+') - } - // Always print at least two digits to match strconv's formatting. - d2 := exp % 10 - exp /= 10 - d1 := exp % 10 - d0 := exp / 10 - if d0 > 0 { - b = append(b, '0'+byte(d0)) - } - b = append(b, '0'+byte(d1), '0'+byte(d2)) - - return b -} - -func float64ToDecimalExactInt(mant, exp uint64) (d dec64, ok bool) { - e := exp - bias64 - if e > mantBits64 { - return d, false - } - shift := mantBits64 - e - mant |= 1 << mantBits64 // implicit 1 - d.m = mant >> shift - if d.m<= 0 { - // This expression is slightly faster than max(0, log10Pow2(e2) - 1). - q := log10Pow2(e2) - boolToUint32(e2 > 3) - e10 = int32(q) - k := pow5InvNumBits64 + pow5Bits(int32(q)) - 1 - i := -e2 + int32(q) + k - mul := pow5InvSplit64[q] - vr = mulShift64(4*m2, mul, i) - vp = mulShift64(4*m2+2, mul, i) - vm = mulShift64(4*m2-1-mmShift, mul, i) - if q <= 21 { - // This should use q <= 22, but I think 21 is also safe. - // Smaller values may still be safe, but it's more - // difficult to reason about them. Only one of mp, mv, - // and mm can be a multiple of 5, if any. - if mv%5 == 0 { - vrIsTrailingZeros = multipleOfPowerOfFive64(mv, q) - } else if acceptBounds { - // Same as min(e2 + (^mm & 1), pow5Factor64(mm)) >= q - // <=> e2 + (^mm & 1) >= q && pow5Factor64(mm) >= q - // <=> true && pow5Factor64(mm) >= q, since e2 >= q. - vmIsTrailingZeros = multipleOfPowerOfFive64(mv-1-mmShift, q) - } else if multipleOfPowerOfFive64(mv+2, q) { - vp-- - } - } - } else { - // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). - q := log10Pow5(-e2) - boolToUint32(-e2 > 1) - e10 = int32(q) + e2 - i := -e2 - int32(q) - k := pow5Bits(i) - pow5NumBits64 - j := int32(q) - k - mul := pow5Split64[i] - vr = mulShift64(4*m2, mul, j) - vp = mulShift64(4*m2+2, mul, j) - vm = mulShift64(4*m2-1-mmShift, mul, j) - if q <= 1 { - // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. - // mv = 4 * m2, so it always has at least two trailing 0 bits. - vrIsTrailingZeros = true - if acceptBounds { - // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. - vmIsTrailingZeros = mmShift == 1 - } else { - // mp = mv + 2, so it always has at least one trailing 0 bit. - vp-- - } - } else if q < 63 { // TODO(ulfjack/cespare): Use a tighter bound here. - // We need to compute min(ntz(mv), pow5Factor64(mv) - e2) >= q - 1 - // <=> ntz(mv) >= q - 1 && pow5Factor64(mv) - e2 >= q - 1 - // <=> ntz(mv) >= q - 1 (e2 is negative and -e2 >= q) - // <=> (mv & ((1 << (q - 1)) - 1)) == 0 - // We also need to make sure that the left shift does not overflow. - vrIsTrailingZeros = multipleOfPowerOfTwo64(mv, q-1) - } - } - - // Step 4: Find the shortest decimal representation - // in the interval of valid representations. - var removed int32 - var lastRemovedDigit uint8 - var out uint64 - // On average, we remove ~2 digits. - if vmIsTrailingZeros || vrIsTrailingZeros { - // General case, which happens rarely (~0.7%). - for { - vpDiv10 := vp / 10 - vmDiv10 := vm / 10 - if vpDiv10 <= vmDiv10 { - break - } - vmMod10 := vm % 10 - vrDiv10 := vr / 10 - vrMod10 := vr % 10 - vmIsTrailingZeros = vmIsTrailingZeros && vmMod10 == 0 - vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0 - lastRemovedDigit = uint8(vrMod10) - vr = vrDiv10 - vp = vpDiv10 - vm = vmDiv10 - removed++ - } - if vmIsTrailingZeros { - for { - vmDiv10 := vm / 10 - vmMod10 := vm % 10 - if vmMod10 != 0 { - break - } - vpDiv10 := vp / 10 - vrDiv10 := vr / 10 - vrMod10 := vr % 10 - vrIsTrailingZeros = vrIsTrailingZeros && lastRemovedDigit == 0 - lastRemovedDigit = uint8(vrMod10) - vr = vrDiv10 - vp = vpDiv10 - vm = vmDiv10 - removed++ - } - } - if vrIsTrailingZeros && lastRemovedDigit == 5 && vr%2 == 0 { - // Round even if the exact number is .....50..0. - lastRemovedDigit = 4 - } - out = vr - // We need to take vr + 1 if vr is outside bounds - // or we need to round up. - if (vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || lastRemovedDigit >= 5 { - out++ - } - } else { - // Specialized for the common case (~99.3%). - // Percentages below are relative to this. - roundUp := false - for vp/100 > vm/100 { - // Optimization: remove two digits at a time (~86.2%). - roundUp = vr%100 >= 50 - vr /= 100 - vp /= 100 - vm /= 100 - removed += 2 - } - // Loop iterations below (approximately), without optimization above: - // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% - // Loop iterations below (approximately), with optimization above: - // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% - for vp/10 > vm/10 { - roundUp = vr%10 >= 5 - vr /= 10 - vp /= 10 - vm /= 10 - removed++ - } - // We need to take vr + 1 if vr is outside bounds - // or we need to round up. - out = vr + boolToUint64(vr == vm || roundUp) - } - - return dec64{m: out, e: e10 + removed} -} - -var powersOf10 = [...]uint64{ - 1e0, - 1e1, - 1e2, - 1e3, - 1e4, - 1e5, - 1e6, - 1e7, - 1e8, - 1e9, - 1e10, - 1e11, - 1e12, - 1e13, - 1e14, - 1e15, - 1e16, - 1e17, - // We only need to find the length of at most 17 digit numbers. -} - -func decimalLen64(u uint64) int { - // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - log2 := 64 - bits.LeadingZeros64(u) - 1 - t := (log2 + 1) * 1233 >> 12 - return t - boolToInt(u < powersOf10[t]) + 1 -} - -func mulShift64(m uint64, mul uint128, shift int32) uint64 { - hihi, hilo := bits.Mul64(m, mul.hi) - lohi, _ := bits.Mul64(m, mul.lo) - sum := uint128{hi: hihi, lo: lohi + hilo} - if sum.lo < lohi { - sum.hi++ // overflow - } - return shiftRight128(sum, shift-64) -} - -func shiftRight128(v uint128, shift int32) uint64 { - // The shift value is always modulo 64. - // In the current implementation of the 64-bit version - // of Ryu, the shift value is always < 64. - // (It is in the range [2, 59].) - // Check this here in case a future change requires larger shift - // values. In this case this function needs to be adjusted. - assert(shift < 64, "shift < 64") - return (v.hi << uint64(64-shift)) | (v.lo >> uint(shift)) -} - -func pow5Factor64(v uint64) uint32 { - for n := uint32(0); ; n++ { - q, r := v/5, v%5 - if r != 0 { - return n - } - v = q - } -} - -func multipleOfPowerOfFive64(v uint64, p uint32) bool { - return pow5Factor64(v) >= p -} - -func multipleOfPowerOfTwo64(v uint64, p uint32) bool { - return uint32(bits.TrailingZeros64(v)) >= p -} diff --git a/examples/gno.land/p/demo/json/ryu/table.gno b/examples/gno.land/p/demo/json/ryu/table.gno deleted file mode 100644 index fe33ad90a57..00000000000 --- a/examples/gno.land/p/demo/json/ryu/table.gno +++ /dev/null @@ -1,678 +0,0 @@ -// Code generated by running "go generate". DO NOT EDIT. - -// Copyright 2018 Ulf Adams -// Modifications copyright 2019 Caleb Spare -// -// The contents of this file may be used under the terms of the Apache License, -// Version 2.0. -// -// (See accompanying file LICENSE or copy at -// http://www.apache.org/licenses/LICENSE-2.0) -// -// Unless required by applicable law or agreed to in writing, this software -// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. -// -// The code in this file is part of a Go translation of the C code written by -// Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source -// code is licensed under Apache 2.0 and this code is derivative work thereof. - -package ryu - -const pow5NumBits32 = 61 - -var pow5Split32 = [...]uint64{ - 1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000, - 1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000, - 1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000, - 2097152000000000000, 1310720000000000000, 1638400000000000000, 2048000000000000000, - 1280000000000000000, 1600000000000000000, 2000000000000000000, 1250000000000000000, - 1562500000000000000, 1953125000000000000, 1220703125000000000, 1525878906250000000, - 1907348632812500000, 1192092895507812500, 1490116119384765625, 1862645149230957031, - 1164153218269348144, 1455191522836685180, 1818989403545856475, 2273736754432320594, - 1421085471520200371, 1776356839400250464, 2220446049250313080, 1387778780781445675, - 1734723475976807094, 2168404344971008868, 1355252715606880542, 1694065894508600678, - 2117582368135750847, 1323488980084844279, 1654361225106055349, 2067951531382569187, - 1292469707114105741, 1615587133892632177, 2019483917365790221, -} - -const pow5InvNumBits32 = 59 - -var pow5InvSplit32 = [...]uint64{ - 576460752303423489, 461168601842738791, 368934881474191033, 295147905179352826, - 472236648286964522, 377789318629571618, 302231454903657294, 483570327845851670, - 386856262276681336, 309485009821345069, 495176015714152110, 396140812571321688, - 316912650057057351, 507060240091291761, 405648192073033409, 324518553658426727, - 519229685853482763, 415383748682786211, 332306998946228969, 531691198313966350, - 425352958651173080, 340282366920938464, 544451787073501542, 435561429658801234, - 348449143727040987, 557518629963265579, 446014903970612463, 356811923176489971, - 570899077082383953, 456719261665907162, 365375409332725730, -} - -const pow5NumBits64 = 121 - -var pow5Split64 = [...]uint128{ - {0, 72057594037927936}, - {0, 90071992547409920}, - {0, 112589990684262400}, - {0, 140737488355328000}, - {0, 87960930222080000}, - {0, 109951162777600000}, - {0, 137438953472000000}, - {0, 85899345920000000}, - {0, 107374182400000000}, - {0, 134217728000000000}, - {0, 83886080000000000}, - {0, 104857600000000000}, - {0, 131072000000000000}, - {0, 81920000000000000}, - {0, 102400000000000000}, - {0, 128000000000000000}, - {0, 80000000000000000}, - {0, 100000000000000000}, - {0, 125000000000000000}, - {0, 78125000000000000}, - {0, 97656250000000000}, - {0, 122070312500000000}, - {0, 76293945312500000}, - {0, 95367431640625000}, - {0, 119209289550781250}, - {4611686018427387904, 74505805969238281}, - {10376293541461622784, 93132257461547851}, - {8358680908399640576, 116415321826934814}, - {612489549322387456, 72759576141834259}, - {14600669991935148032, 90949470177292823}, - {13639151471491547136, 113686837721616029}, - {3213881284082270208, 142108547152020037}, - {4314518811765112832, 88817841970012523}, - {781462496279003136, 111022302462515654}, - {10200200157203529728, 138777878078144567}, - {13292654125893287936, 86736173798840354}, - {7392445620511834112, 108420217248550443}, - {4628871007212404736, 135525271560688054}, - {16728102434789916672, 84703294725430033}, - {7075069988205232128, 105879118406787542}, - {18067209522111315968, 132348898008484427}, - {8986162942105878528, 82718061255302767}, - {6621017659204960256, 103397576569128459}, - {3664586055578812416, 129246970711410574}, - {16125424340018921472, 80779356694631608}, - {1710036351314100224, 100974195868289511}, - {15972603494424788992, 126217744835361888}, - {9982877184015493120, 78886090522101180}, - {12478596480019366400, 98607613152626475}, - {10986559581596820096, 123259516440783094}, - {2254913720070624656, 77037197775489434}, - {12042014186943056628, 96296497219361792}, - {15052517733678820785, 120370621524202240}, - {9407823583549262990, 75231638452626400}, - {11759779479436578738, 94039548065783000}, - {14699724349295723422, 117549435082228750}, - {4575641699882439235, 73468396926392969}, - {10331238143280436948, 91835496157991211}, - {8302361660673158281, 114794370197489014}, - {1154580038986672043, 143492962746861268}, - {9944984561221445835, 89683101716788292}, - {12431230701526807293, 112103877145985365}, - {1703980321626345405, 140129846432481707}, - {17205888765512323542, 87581154020301066}, - {12283988920035628619, 109476442525376333}, - {1519928094762372062, 136845553156720417}, - {12479170105294952299, 85528470722950260}, - {15598962631618690374, 106910588403687825}, - {5663645234241199255, 133638235504609782}, - {17374836326682913246, 83523897190381113}, - {7883487353071477846, 104404871487976392}, - {9854359191339347308, 130506089359970490}, - {10770660513014479971, 81566305849981556}, - {13463325641268099964, 101957882312476945}, - {2994098996302961243, 127447352890596182}, - {15706369927971514489, 79654595556622613}, - {5797904354682229399, 99568244445778267}, - {2635694424925398845, 124460305557222834}, - {6258995034005762182, 77787690973264271}, - {3212057774079814824, 97234613716580339}, - {17850130272881932242, 121543267145725423}, - {18073860448192289507, 75964541966078389}, - {8757267504958198172, 94955677457597987}, - {6334898362770359811, 118694596821997484}, - {13182683513586250689, 74184123013748427}, - {11866668373555425458, 92730153767185534}, - {5609963430089506015, 115912692208981918}, - {17341285199088104971, 72445432630613698}, - {12453234462005355406, 90556790788267123}, - {10954857059079306353, 113195988485333904}, - {13693571323849132942, 141494985606667380}, - {17781854114260483896, 88434366004167112}, - {3780573569116053255, 110542957505208891}, - {114030942967678664, 138178696881511114}, - {4682955357782187069, 86361685550944446}, - {15077066234082509644, 107952106938680557}, - {5011274737320973344, 134940133673350697}, - {14661261756894078100, 84337583545844185}, - {4491519140835433913, 105421979432305232}, - {5614398926044292391, 131777474290381540}, - {12732371365632458552, 82360921431488462}, - {6692092170185797382, 102951151789360578}, - {17588487249587022536, 128688939736700722}, - {15604490549419276989, 80430587335437951}, - {14893927168346708332, 100538234169297439}, - {14005722942005997511, 125672792711621799}, - {15671105866394830300, 78545495444763624}, - {1142138259283986260, 98181869305954531}, - {15262730879387146537, 122727336632443163}, - {7233363790403272633, 76704585395276977}, - {13653390756431478696, 95880731744096221}, - {3231680390257184658, 119850914680120277}, - {4325643253124434363, 74906821675075173}, - {10018740084832930858, 93633527093843966}, - {3300053069186387764, 117041908867304958}, - {15897591223523656064, 73151193042065598}, - {10648616992549794273, 91438991302581998}, - {4087399203832467033, 114298739128227498}, - {14332621041645359599, 142873423910284372}, - {18181260187883125557, 89295889943927732}, - {4279831161144355331, 111619862429909666}, - {14573160988285219972, 139524828037387082}, - {13719911636105650386, 87203017523366926}, - {7926517508277287175, 109003771904208658}, - {684774848491833161, 136254714880260823}, - {7345513307948477581, 85159196800163014}, - {18405263671790372785, 106448996000203767}, - {18394893571310578077, 133061245000254709}, - {13802651491282805250, 83163278125159193}, - {3418256308821342851, 103954097656448992}, - {4272820386026678563, 129942622070561240}, - {2670512741266674102, 81214138794100775}, - {17173198981865506339, 101517673492625968}, - {3019754653622331308, 126897091865782461}, - {4193189667727651020, 79310682416114038}, - {14464859121514339583, 99138353020142547}, - {13469387883465536574, 123922941275178184}, - {8418367427165960359, 77451838296986365}, - {15134645302384838353, 96814797871232956}, - {471562554271496325, 121018497339041196}, - {9518098633274461011, 75636560836900747}, - {7285937273165688360, 94545701046125934}, - {18330793628311886258, 118182126307657417}, - {4539216990053847055, 73863828942285886}, - {14897393274422084627, 92329786177857357}, - {4786683537745442072, 115412232722321697}, - {14520892257159371055, 72132645451451060}, - {18151115321449213818, 90165806814313825}, - {8853836096529353561, 112707258517892282}, - {1843923083806916143, 140884073147365353}, - {12681666973447792349, 88052545717103345}, - {2017025661527576725, 110065682146379182}, - {11744654113764246714, 137582102682973977}, - {422879793461572340, 85988814176858736}, - {528599741826965425, 107486017721073420}, - {660749677283706782, 134357522151341775}, - {7330497575943398595, 83973451344588609}, - {13774807988356636147, 104966814180735761}, - {3383451930163631472, 131208517725919702}, - {15949715511634433382, 82005323578699813}, - {6102086334260878016, 102506654473374767}, - {3015921899398709616, 128133318091718459}, - {18025852251620051174, 80083323807324036}, - {4085571240815512351, 100104154759155046}, - {14330336087874166247, 125130193448943807}, - {15873989082562435760, 78206370905589879}, - {15230800334775656796, 97757963631987349}, - {5203442363187407284, 122197454539984187}, - {946308467778435600, 76373409087490117}, - {5794571603150432404, 95466761359362646}, - {16466586540792816313, 119333451699203307}, - {7985773578781816244, 74583407312002067}, - {5370530955049882401, 93229259140002584}, - {6713163693812353001, 116536573925003230}, - {18030785363914884337, 72835358703127018}, - {13315109668038829614, 91044198378908773}, - {2808829029766373305, 113805247973635967}, - {17346094342490130344, 142256559967044958}, - {6229622945628943561, 88910349979403099}, - {3175342663608791547, 111137937474253874}, - {13192550366365765242, 138922421842817342}, - {3633657960551215372, 86826513651760839}, - {18377130505971182927, 108533142064701048}, - {4524669058754427043, 135666427580876311}, - {9745447189362598758, 84791517238047694}, - {2958436949848472639, 105989396547559618}, - {12921418224165366607, 132486745684449522}, - {12687572408530742033, 82804216052780951}, - {11247779492236039638, 103505270065976189}, - {224666310012885835, 129381587582470237}, - {2446259452971747599, 80863492239043898}, - {12281196353069460307, 101079365298804872}, - {15351495441336825384, 126349206623506090}, - {14206370669262903769, 78968254139691306}, - {8534591299723853903, 98710317674614133}, - {15279925143082205283, 123387897093267666}, - {14161639232853766206, 77117435683292291}, - {13090363022639819853, 96396794604115364}, - {16362953778299774816, 120495993255144205}, - {12532689120651053212, 75309995784465128}, - {15665861400813816515, 94137494730581410}, - {10358954714162494836, 117671868413226763}, - {4168503687137865320, 73544917758266727}, - {598943590494943747, 91931147197833409}, - {5360365506546067587, 114913933997291761}, - {11312142901609972388, 143642417496614701}, - {9375932322719926695, 89776510935384188}, - {11719915403399908368, 112220638669230235}, - {10038208235822497557, 140275798336537794}, - {10885566165816448877, 87672373960336121}, - {18218643725697949000, 109590467450420151}, - {18161618638695048346, 136988084313025189}, - {13656854658398099168, 85617552695640743}, - {12459382304570236056, 107021940869550929}, - {1739169825430631358, 133777426086938662}, - {14922039196176308311, 83610891304336663}, - {14040862976792997485, 104513614130420829}, - {3716020665709083144, 130642017663026037}, - {4628355925281870917, 81651261039391273}, - {10397130925029726550, 102064076299239091}, - {8384727637859770284, 127580095374048864}, - {5240454773662356427, 79737559608780540}, - {6550568467077945534, 99671949510975675}, - {3576524565420044014, 124589936888719594}, - {6847013871814915412, 77868710555449746}, - {17782139376623420074, 97335888194312182}, - {13004302183924499284, 121669860242890228}, - {17351060901807587860, 76043662651806392}, - {3242082053549933210, 95054578314757991}, - {17887660622219580224, 118818222893447488}, - {11179787888887237640, 74261389308404680}, - {13974734861109047050, 92826736635505850}, - {8245046539531533005, 116033420794382313}, - {16682369133275677888, 72520887996488945}, - {7017903361312433648, 90651109995611182}, - {17995751238495317868, 113313887494513977}, - {8659630992836983623, 141642359368142472}, - {5412269370523114764, 88526474605089045}, - {11377022731581281359, 110658093256361306}, - {4997906377621825891, 138322616570451633}, - {14652906532082110942, 86451635356532270}, - {9092761128247862869, 108064544195665338}, - {2142579373455052779, 135080680244581673}, - {12868327154477877747, 84425425152863545}, - {2250350887815183471, 105531781441079432}, - {2812938609768979339, 131914726801349290}, - {6369772649532999991, 82446704250843306}, - {17185587848771025797, 103058380313554132}, - {3035240737254230630, 128822975391942666}, - {6508711479211282048, 80514359619964166}, - {17359261385868878368, 100642949524955207}, - {17087390713908710056, 125803686906194009}, - {3762090168551861929, 78627304316371256}, - {4702612710689827411, 98284130395464070}, - {15101637925217060072, 122855162994330087}, - {16356052730901744401, 76784476871456304}, - {1998321839917628885, 95980596089320381}, - {7109588318324424010, 119975745111650476}, - {13666864735807540814, 74984840694781547}, - {12471894901332038114, 93731050868476934}, - {6366496589810271835, 117163813585596168}, - {3979060368631419896, 73227383490997605}, - {9585511479216662775, 91534229363747006}, - {2758517312166052660, 114417786704683758}, - {12671518677062341634, 143022233380854697}, - {1002170145522881665, 89388895863034186}, - {10476084718758377889, 111736119828792732}, - {13095105898447972362, 139670149785990915}, - {5878598177316288774, 87293843616244322}, - {16571619758500136775, 109117304520305402}, - {11491152661270395161, 136396630650381753}, - {264441385652915120, 85247894156488596}, - {330551732066143900, 106559867695610745}, - {5024875683510067779, 133199834619513431}, - {10058076329834874218, 83249896637195894}, - {3349223375438816964, 104062370796494868}, - {4186529219298521205, 130077963495618585}, - {14145795808130045513, 81298727184761615}, - {13070558741735168987, 101623408980952019}, - {11726512408741573330, 127029261226190024}, - {7329070255463483331, 79393288266368765}, - {13773023837756742068, 99241610332960956}, - {17216279797195927585, 124052012916201195}, - {8454331864033760789, 77532508072625747}, - {5956228811614813082, 96915635090782184}, - {7445286014518516353, 121144543863477730}, - {9264989777501460624, 75715339914673581}, - {16192923240304213684, 94644174893341976}, - {1794409976670715490, 118305218616677471}, - {8039035263060279037, 73940761635423419}, - {5437108060397960892, 92425952044279274}, - {16019757112352226923, 115532440055349092}, - {788976158365366019, 72207775034593183}, - {14821278253238871236, 90259718793241478}, - {9303225779693813237, 112824648491551848}, - {11629032224617266546, 141030810614439810}, - {11879831158813179495, 88144256634024881}, - {1014730893234310657, 110180320792531102}, - {10491785653397664129, 137725400990663877}, - {8863209042587234033, 86078375619164923}, - {6467325284806654637, 107597969523956154}, - {17307528642863094104, 134497461904945192}, - {10817205401789433815, 84060913690590745}, - {18133192770664180173, 105076142113238431}, - {18054804944902837312, 131345177641548039}, - {18201782118205355176, 82090736025967524}, - {4305483574047142354, 102613420032459406}, - {14605226504413703751, 128266775040574257}, - {2210737537617482988, 80166734400358911}, - {16598479977304017447, 100208418000448638}, - {11524727934775246001, 125260522500560798}, - {2591268940807140847, 78287826562850499}, - {17074144231291089770, 97859783203563123}, - {16730994270686474309, 122324729004453904}, - {10456871419179046443, 76452955627783690}, - {3847717237119032246, 95566194534729613}, - {9421332564826178211, 119457743168412016}, - {5888332853016361382, 74661089480257510}, - {16583788103125227536, 93326361850321887}, - {16118049110479146516, 116657952312902359}, - {16991309721690548428, 72911220195563974}, - {12015765115258409727, 91139025244454968}, - {15019706394073012159, 113923781555568710}, - {9551260955736489391, 142404726944460888}, - {5969538097335305869, 89002954340288055}, - {2850236603241744433, 111253692925360069}, -} - -const pow5InvNumBits64 = 122 - -var pow5InvSplit64 = [...]uint128{ - {1, 288230376151711744}, - {3689348814741910324, 230584300921369395}, - {2951479051793528259, 184467440737095516}, - {17118578500402463900, 147573952589676412}, - {12632330341676300947, 236118324143482260}, - {10105864273341040758, 188894659314785808}, - {15463389048156653253, 151115727451828646}, - {17362724847566824558, 241785163922925834}, - {17579528692795369969, 193428131138340667}, - {6684925324752475329, 154742504910672534}, - {18074578149087781173, 247588007857076054}, - {18149011334012135262, 198070406285660843}, - {3451162622983977240, 158456325028528675}, - {5521860196774363583, 253530120045645880}, - {4417488157419490867, 202824096036516704}, - {7223339340677503017, 162259276829213363}, - {7867994130342094503, 259614842926741381}, - {2605046489531765280, 207691874341393105}, - {2084037191625412224, 166153499473114484}, - {10713157136084480204, 265845599156983174}, - {12259874523609494487, 212676479325586539}, - {13497248433629505913, 170141183460469231}, - {14216899864323388813, 272225893536750770}, - {11373519891458711051, 217780714829400616}, - {5409467098425058518, 174224571863520493}, - {4965798542738183305, 278759314981632789}, - {7661987648932456967, 223007451985306231}, - {2440241304404055250, 178405961588244985}, - {3904386087046488400, 285449538541191976}, - {17880904128604832013, 228359630832953580}, - {14304723302883865611, 182687704666362864}, - {15133127457049002812, 146150163733090291}, - {16834306301794583852, 233840261972944466}, - {9778096226693756759, 187072209578355573}, - {15201174610838826053, 149657767662684458}, - {2185786488890659746, 239452428260295134}, - {5437978005854438120, 191561942608236107}, - {15418428848909281466, 153249554086588885}, - {6222742084545298729, 245199286538542217}, - {16046240111861969953, 196159429230833773}, - {1768945645263844993, 156927543384667019}, - {10209010661905972635, 251084069415467230}, - {8167208529524778108, 200867255532373784}, - {10223115638361732810, 160693804425899027}, - {1599589762411131202, 257110087081438444}, - {4969020624670815285, 205688069665150755}, - {3975216499736652228, 164550455732120604}, - {13739044029062464211, 263280729171392966}, - {7301886408508061046, 210624583337114373}, - {13220206756290269483, 168499666669691498}, - {17462981995322520850, 269599466671506397}, - {6591687966774196033, 215679573337205118}, - {12652048002903177473, 172543658669764094}, - {9175230360419352987, 276069853871622551}, - {3650835473593572067, 220855883097298041}, - {17678063637842498946, 176684706477838432}, - {13527506561580357021, 282695530364541492}, - {3443307619780464970, 226156424291633194}, - {6443994910566282300, 180925139433306555}, - {5155195928453025840, 144740111546645244}, - {15627011115008661990, 231584178474632390}, - {12501608892006929592, 185267342779705912}, - {2622589484121723027, 148213874223764730}, - {4196143174594756843, 237142198758023568}, - {10735612169159626121, 189713759006418854}, - {12277838550069611220, 151771007205135083}, - {15955192865369467629, 242833611528216133}, - {1696107848069843133, 194266889222572907}, - {12424932722681605476, 155413511378058325}, - {1433148282581017146, 248661618204893321}, - {15903913885032455010, 198929294563914656}, - {9033782293284053685, 159143435651131725}, - {14454051669254485895, 254629497041810760}, - {11563241335403588716, 203703597633448608}, - {16629290697806691620, 162962878106758886}, - {781423413297334329, 260740604970814219}, - {4314487545379777786, 208592483976651375}, - {3451590036303822229, 166873987181321100}, - {5522544058086115566, 266998379490113760}, - {4418035246468892453, 213598703592091008}, - {10913125826658934609, 170878962873672806}, - {10082303693170474728, 273406340597876490}, - {8065842954536379782, 218725072478301192}, - {17520720807854834795, 174980057982640953}, - {5897060404116273733, 279968092772225526}, - {1028299508551108663, 223974474217780421}, - {15580034865808528224, 179179579374224336}, - {17549358155809824511, 286687326998758938}, - {2971440080422128639, 229349861599007151}, - {17134547323305344204, 183479889279205720}, - {13707637858644275364, 146783911423364576}, - {14553522944347019935, 234854258277383322}, - {4264120725993795302, 187883406621906658}, - {10789994210278856888, 150306725297525326}, - {9885293106962350374, 240490760476040522}, - {529536856086059653, 192392608380832418}, - {7802327114352668369, 153914086704665934}, - {1415676938738538420, 246262538727465495}, - {1132541550990830736, 197010030981972396}, - {15663428499760305882, 157608024785577916}, - {17682787970132668764, 252172839656924666}, - {10456881561364224688, 201738271725539733}, - {15744202878575200397, 161390617380431786}, - {17812026976236499989, 258224987808690858}, - {3181575136763469022, 206579990246952687}, - {13613306553636506187, 165263992197562149}, - {10713244041592678929, 264422387516099439}, - {12259944048016053467, 211537910012879551}, - {6118606423670932450, 169230328010303641}, - {2411072648389671274, 270768524816485826}, - {16686253377679378312, 216614819853188660}, - {13349002702143502650, 173291855882550928}, - {17669055508687693916, 277266969412081485}, - {14135244406950155133, 221813575529665188}, - {240149081334393137, 177450860423732151}, - {11452284974360759988, 283921376677971441}, - {5472479164746697667, 227137101342377153}, - {11756680961281178780, 181709681073901722}, - {2026647139541122378, 145367744859121378}, - {18000030682233437097, 232588391774594204}, - {18089373360528660001, 186070713419675363}, - {3403452244197197031, 148856570735740291}, - {16513570034941246220, 238170513177184465}, - {13210856027952996976, 190536410541747572}, - {3189987192878576934, 152429128433398058}, - {1414630693863812771, 243886605493436893}, - {8510402184574870864, 195109284394749514}, - {10497670562401807014, 156087427515799611}, - {9417575270359070576, 249739884025279378}, - {14912757845771077107, 199791907220223502}, - {4551508647133041040, 159833525776178802}, - {10971762650154775986, 255733641241886083}, - {16156107749607641435, 204586912993508866}, - {9235537384944202825, 163669530394807093}, - {11087511001168814197, 261871248631691349}, - {12559357615676961681, 209496998905353079}, - {13736834907283479668, 167597599124282463}, - {18289587036911657145, 268156158598851941}, - {10942320814787415393, 214524926879081553}, - {16132554281313752961, 171619941503265242}, - {11054691591134363444, 274591906405224388}, - {16222450902391311402, 219673525124179510}, - {12977960721913049122, 175738820099343608}, - {17075388340318968271, 281182112158949773}, - {2592264228029443648, 224945689727159819}, - {5763160197165465241, 179956551781727855}, - {9221056315464744386, 287930482850764568}, - {14755542681855616155, 230344386280611654}, - {15493782960226403247, 184275509024489323}, - {1326979923955391628, 147420407219591459}, - {9501865507812447252, 235872651551346334}, - {11290841220991868125, 188698121241077067}, - {1653975347309673853, 150958496992861654}, - {10025058185179298811, 241533595188578646}, - {4330697733401528726, 193226876150862917}, - {14532604630946953951, 154581500920690333}, - {1116074521063664381, 247330401473104534}, - {4582208431592841828, 197864321178483627}, - {14733813189500004432, 158291456942786901}, - {16195403473716186445, 253266331108459042}, - {5577625149489128510, 202613064886767234}, - {8151448934333213131, 162090451909413787}, - {16731667109675051333, 259344723055062059}, - {17074682502481951390, 207475778444049647}, - {6281048372501740465, 165980622755239718}, - {6360328581260874421, 265568996408383549}, - {8777611679750609860, 212455197126706839}, - {10711438158542398211, 169964157701365471}, - {9759603424184016492, 271942652322184754}, - {11497031554089123517, 217554121857747803}, - {16576322872755119460, 174043297486198242}, - {11764721337440549842, 278469275977917188}, - {16790474699436260520, 222775420782333750}, - {13432379759549008416, 178220336625867000}, - {3045063541568861850, 285152538601387201}, - {17193446092222730773, 228122030881109760}, - {13754756873778184618, 182497624704887808}, - {18382503128506368341, 145998099763910246}, - {3586563302416817083, 233596959622256395}, - {2869250641933453667, 186877567697805116}, - {17052795772514404226, 149502054158244092}, - {12527077977055405469, 239203286653190548}, - {17400360011128145022, 191362629322552438}, - {2852241564676785048, 153090103458041951}, - {15631632947708587046, 244944165532867121}, - {8815957543424959314, 195955332426293697}, - {18120812478965698421, 156764265941034957}, - {14235904707377476180, 250822825505655932}, - {4010026136418160298, 200658260404524746}, - {17965416168102169531, 160526608323619796}, - {2919224165770098987, 256842573317791675}, - {2335379332616079190, 205474058654233340}, - {1868303466092863352, 164379246923386672}, - {6678634360490491686, 263006795077418675}, - {5342907488392393349, 210405436061934940}, - {4274325990713914679, 168324348849547952}, - {10528270399884173809, 269318958159276723}, - {15801313949391159694, 215455166527421378}, - {1573004715287196786, 172364133221937103}, - {17274202803427156150, 275782613155099364}, - {17508711057483635243, 220626090524079491}, - {10317620031244997871, 176500872419263593}, - {12818843235250086271, 282401395870821749}, - {13944423402941979340, 225921116696657399}, - {14844887537095493795, 180736893357325919}, - {15565258844418305359, 144589514685860735}, - {6457670077359736959, 231343223497377177}, - {16234182506113520537, 185074578797901741}, - {9297997190148906106, 148059663038321393}, - {11187446689496339446, 236895460861314229}, - {12639306166338981880, 189516368689051383}, - {17490142562555006151, 151613094951241106}, - {2158786396894637579, 242580951921985771}, - {16484424376483351356, 194064761537588616}, - {9498190686444770762, 155251809230070893}, - {11507756283569722895, 248402894768113429}, - {12895553841597688639, 198722315814490743}, - {17695140702761971558, 158977852651592594}, - {17244178680193423523, 254364564242548151}, - {10105994129412828495, 203491651394038521}, - {4395446488788352473, 162793321115230817}, - {10722063196803274280, 260469313784369307}, - {1198952927958798777, 208375451027495446}, - {15716557601334680315, 166700360821996356}, - {17767794532651667857, 266720577315194170}, - {14214235626121334286, 213376461852155336}, - {7682039686155157106, 170701169481724269}, - {1223217053622520399, 273121871170758831}, - {15735968901865657612, 218497496936607064}, - {16278123936234436413, 174797997549285651}, - {219556594781725998, 279676796078857043}, - {7554342905309201445, 223741436863085634}, - {9732823138989271479, 178993149490468507}, - {815121763415193074, 286389039184749612}, - {11720143854957885429, 229111231347799689}, - {13065463898708218666, 183288985078239751}, - {6763022304224664610, 146631188062591801}, - {3442138057275642729, 234609900900146882}, - {13821756890046245153, 187687920720117505}, - {11057405512036996122, 150150336576094004}, - {6623802375033462826, 240240538521750407}, - {16367088344252501231, 192192430817400325}, - {13093670675402000985, 153753944653920260}, - {2503129006933649959, 246006311446272417}, - {13070549649772650937, 196805049157017933}, - {17835137349301941396, 157444039325614346}, - {2710778055689733971, 251910462920982955}, - {2168622444551787177, 201528370336786364}, - {5424246770383340065, 161222696269429091}, - {1300097203129523457, 257956314031086546}, - {15797473021471260058, 206365051224869236}, - {8948629602435097724, 165092040979895389}, - {3249760919670425388, 264147265567832623}, - {9978506365220160957, 211317812454266098}, - {15361502721659949412, 169054249963412878}, - {2442311466204457120, 270486799941460606}, - {16711244431931206989, 216389439953168484}, - {17058344360286875914, 173111551962534787}, - {12535955717491360170, 276978483140055660}, - {10028764573993088136, 221582786512044528}, - {15401709288678291155, 177266229209635622}, - {9885339602917624555, 283625966735416996}, - {4218922867592189321, 226900773388333597}, - {14443184738299482427, 181520618710666877}, - {4175850161155765295, 145216494968533502}, - {10370709072591134795, 232346391949653603}, - {15675264887556728482, 185877113559722882}, - {5161514280561562140, 148701690847778306}, - {879725219414678777, 237922705356445290}, - {703780175531743021, 190338164285156232}, - {11631070584651125387, 152270531428124985}, - {162968861732249003, 243632850284999977}, - {11198421533611530172, 194906280227999981}, - {5269388412147313814, 155925024182399985}, - {8431021459435702103, 249480038691839976}, - {3055468352806651359, 199584030953471981}, - {17201769941212962380, 159667224762777584}, - {16454785461715008838, 255467559620444135}, - {13163828369372007071, 204374047696355308}, - {17909760324981426303, 163499238157084246}, - {2830174816776909822, 261598781051334795}, - {2264139853421527858, 209279024841067836}, - {16568707141704863579, 167423219872854268}, - {4373838538276319787, 267877151796566830}, - {3499070830621055830, 214301721437253464}, - {6488605479238754987, 171441377149802771}, - {3003071137298187333, 274306203439684434}, - {6091805724580460189, 219444962751747547}, - {15941491023890099121, 175555970201398037}, - {10748990379256517301, 280889552322236860}, - {8599192303405213841, 224711641857789488}, - {14258051472207991719, 179769313486231590}, -} From e9640ef55dc3d0bb0022a50557550bf6316b9953 Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:21:43 +0100 Subject: [PATCH 3/7] test(gno.land): add unit tests for sdk/vm.vmHandler (#2459) ```console gnome$ go test -v ./sdk/vm -run TestVmHandler === RUN TestVmHandlerQuery_Eval === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.Echo("hello") === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.PubString === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.ConstString === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.pvString === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.counter === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.GetCounter() === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.Inc() === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.pvEcho("hello") === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.1337 === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.13.37 === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.float64(1337) === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.myStructInst === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.myStructInst.Foo() === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.myStruct === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.Inc === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.fn()("hi") === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.sl === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.sl[1] === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.println(1234) 1234 === RUN TestVmHandlerQuery_Eval/gno.land/r/hello === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.doesnotexist === RUN TestVmHandlerQuery_Eval/gno.land/r/doesnotexist.Foo === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.Panic() === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.panic("bar") === RUN TestVmHandlerQuery_Eval/gno.land/r/hello.sl[6] --- PASS: TestVmHandlerQuery_Eval (0.03s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.Echo("hello") (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.PubString (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.ConstString (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.pvString (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.counter (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.GetCounter() (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.Inc() (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.pvEcho("hello") (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.1337 (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.13.37 (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.float64(1337) (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.myStructInst (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.myStructInst.Foo() (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.myStruct (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.Inc (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.fn()("hi") (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.sl (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.sl[1] (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.println(1234) (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.doesnotexist (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/doesnotexist.Foo (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.Panic() (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.panic("bar") (0.00s) --- PASS: TestVmHandlerQuery_Eval/gno.land/r/hello.sl[6] (0.00s) === RUN TestVmHandlerQuery_Funcs === RUN TestVmHandlerQuery_Funcs/gno.land/r/hello === RUN TestVmHandlerQuery_Funcs/gno.land/r/doesnotexist === RUN TestVmHandlerQuery_Funcs/std === RUN TestVmHandlerQuery_Funcs/strings --- PASS: TestVmHandlerQuery_Funcs (0.00s) --- PASS: TestVmHandlerQuery_Funcs/gno.land/r/hello (0.00s) --- PASS: TestVmHandlerQuery_Funcs/gno.land/r/doesnotexist (0.00s) --- PASS: TestVmHandlerQuery_Funcs/std (0.00s) --- PASS: TestVmHandlerQuery_Funcs/strings (0.00s) === RUN TestVmHandlerQuery_File === RUN TestVmHandlerQuery_File/gno.land/r/hello/hello.gno === RUN TestVmHandlerQuery_File/gno.land/r/hello/README.md === RUN TestVmHandlerQuery_File/gno.land/r/hello/doesnotexist.gno === RUN TestVmHandlerQuery_File/gno.land/r/hello === RUN TestVmHandlerQuery_File/gno.land/r/doesnotexist === RUN TestVmHandlerQuery_File/gno.land/r/doesnotexist/hello.gno --- PASS: TestVmHandlerQuery_File (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/hello/hello.gno (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/hello/README.md (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/hello/doesnotexist.gno (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/hello (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/doesnotexist (0.00s) --- PASS: TestVmHandlerQuery_File/gno.land/r/doesnotexist/hello.gno (0.00s) PASS ok github.com/gnolang/gno/gno.land/pkg/sdk/vm (cached) ``` --------- Signed-off-by: moul <94029+moul@users.noreply.github.com> --- gno.land/pkg/sdk/vm/common_test.go | 4 +- gno.land/pkg/sdk/vm/gas_test.go | 3 +- gno.land/pkg/sdk/vm/handler_test.go | 274 ++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 3 deletions(-) diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index 7380d3e0f72..8b1b7d909c1 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -23,6 +23,7 @@ type testEnv struct { vmk *VMKeeper bank bankm.BankKeeper acck authm.AccountKeeper + vmh vmHandler } func setupTestEnv() testEnv { @@ -62,6 +63,7 @@ func _setupTestEnv(cacheStdlibs bool) testEnv { } vmk.CommitGnoTransactionStore(stdlibCtx) mcw.MultiWrite() + vmh := NewHandler(vmk) - return testEnv{ctx: ctx, vmk: vmk, bank: bank, acck: acck} + return testEnv{ctx: ctx, vmk: vmk, bank: bank, acck: acck, vmh: vmh} } diff --git a/gno.land/pkg/sdk/vm/gas_test.go b/gno.land/pkg/sdk/vm/gas_test.go index a199f12898a..ff924610627 100644 --- a/gno.land/pkg/sdk/vm/gas_test.go +++ b/gno.land/pkg/sdk/vm/gas_test.go @@ -143,7 +143,6 @@ func setupAddPkg(success bool) (sdk.Context, sdk.Tx, vmHandler) { ctx := env.ctx // conduct base gas meter tests from a non-genesis block since genesis block use infinite gas meter instead. ctx = ctx.WithBlockHeader(&bft.Header{Height: int64(1)}) - vmHandler := NewHandler(env.vmk) // Create an account with 10M ugnot (10gnot) addr := crypto.AddressFromPreimage([]byte("test1")) acc := env.acck.NewAccountWithAddress(ctx, addr) @@ -183,5 +182,5 @@ func Echo() UnknowType { fee := std.NewFee(500000, std.MustParseCoin(ugnot.ValueString(1))) tx := std.NewTx(msgs, fee, []std.Signature{}, "") - return ctx, tx, vmHandler + return ctx, tx, env.vmh } diff --git a/gno.land/pkg/sdk/vm/handler_test.go b/gno.land/pkg/sdk/vm/handler_test.go index 38ac8fa61b9..7e029f4cacb 100644 --- a/gno.land/pkg/sdk/vm/handler_test.go +++ b/gno.land/pkg/sdk/vm/handler_test.go @@ -1,8 +1,13 @@ package vm import ( + "fmt" "testing" + "github.com/gnolang/gno/gnovm" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/stretchr/testify/assert" ) @@ -48,3 +53,272 @@ func Test_parseQueryEval_panic(t *testing.T) { parseQueryEvalData("gno.land/r/demo/users") }) } + +func TestVmHandlerQuery_Eval(t *testing.T) { + tt := []struct { + input []byte + expectedResult string + expectedResultMatch string + expectedErrorMatch string + expectedPanicMatch string + // XXX: expectedEvents + }{ + // valid queries + {input: []byte(`gno.land/r/hello.Echo("hello")`), expectedResult: `("echo:hello" string)`}, + {input: []byte(`gno.land/r/hello.caller()`), expectedResult: `("" std.Address)`}, // FIXME? + {input: []byte(`gno.land/r/hello.GetHeight()`), expectedResult: `(0 int64)`}, + // {input: []byte(`gno.land/r/hello.time.RFC3339`), expectedResult: `test`}, // not working, but should we care? + {input: []byte(`gno.land/r/hello.PubString`), expectedResult: `("public string" string)`}, + {input: []byte(`gno.land/r/hello.ConstString`), expectedResult: `("const string" string)`}, + {input: []byte(`gno.land/r/hello.pvString`), expectedResult: `("private string" string)`}, + {input: []byte(`gno.land/r/hello.counter`), expectedResult: `(42 int)`}, + {input: []byte(`gno.land/r/hello.GetCounter()`), expectedResult: `(42 int)`}, + {input: []byte(`gno.land/r/hello.Inc()`), expectedResult: `(43 int)`}, + {input: []byte(`gno.land/r/hello.pvEcho("hello")`), expectedResult: `("pvecho:hello" string)`}, + {input: []byte(`gno.land/r/hello.1337`), expectedResult: `(1337 int)`}, + {input: []byte(`gno.land/r/hello.13.37`), expectedResult: `(13.37 float64)`}, + {input: []byte(`gno.land/r/hello.float64(1337)`), expectedResult: `(1337 float64)`}, + {input: []byte(`gno.land/r/hello.myStructInst`), expectedResult: `(struct{(1000 int)} gno.land/r/hello.myStruct)`}, + {input: []byte(`gno.land/r/hello.myStructInst.Foo()`), expectedResult: `("myStruct.Foo" string)`}, + {input: []byte(`gno.land/r/hello.myStruct`), expectedResultMatch: `\(typeval{gno.land/r/hello.myStruct \(0x.*\)} type{}\)`}, + {input: []byte(`gno.land/r/hello.Inc`), expectedResult: `(Inc func()( int))`}, + {input: []byte(`gno.land/r/hello.fn()("hi")`), expectedResult: `("echo:hi" string)`}, + {input: []byte(`gno.land/r/hello.sl`), expectedResultMatch: `(slice[ref(.*)] []int)`}, // XXX: should return the actual value + {input: []byte(`gno.land/r/hello.sl[1]`), expectedResultMatch: `(slice[ref(.*)] []int)`}, // XXX: should return the actual value + {input: []byte(`gno.land/r/hello.println(1234)`), expectedResultMatch: `^$`}, // XXX: compare stdout? + + // panics + {input: []byte(`gno.land/r/hello`), expectedPanicMatch: `expected . syntax in query input data`}, + + // errors + {input: []byte(`gno.land/r/hello.doesnotexist`), expectedErrorMatch: `^/:0:0: name doesnotexist not declared:`}, // multiline error + {input: []byte(`gno.land/r/doesnotexist.Foo`), expectedErrorMatch: `^invalid package path$`}, + {input: []byte(`gno.land/r/hello.Panic()`), expectedErrorMatch: `^foo$`}, + {input: []byte(`gno.land/r/hello.sl[6]`), expectedErrorMatch: `^slice index out of bounds: 6 \(len=5\)$`}, + } + + for _, tc := range tt { + name := string(tc.input) + t.Run(name, func(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + vmHandler := env.vmh + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot")) + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot"))) + + // Create test package. + files := []*gnovm.MemFile{ + {"hello.gno", ` +package hello + +import "std" +import "time" + +var _ = time.RFC3339 +func caller() std.Address { return std.GetOrigCaller() } +var GetHeight = std.GetHeight +var sl = []int{1,2,3,4,5} +func fn() func(string) string { return Echo } +type myStruct struct{a int} +var myStructInst = myStruct{a: 1000} +func (ms myStruct) Foo() string { return "myStruct.Foo" } +func Panic() { panic("foo") } +var counter int = 42 +var pvString = "private string" +var PubString = "public string" +const ConstString = "const string" +func Echo(msg string) string { return "echo:"+msg } +func GetCounter() int { return counter } +func Inc() int { counter += 1; return counter } +func pvEcho(msg string) string { return "pvecho:"+msg } +`}, + } + pkgPath := "gno.land/r/hello" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + env.vmk.CommitGnoTransactionStore(ctx) + + req := abci.RequestQuery{ + Path: "vm/qeval", + Data: tc.input, + } + + defer func() { + if r := recover(); r != nil { + output := fmt.Sprintf("%v", r) + assert.Regexp(t, tc.expectedPanicMatch, output) + } else { + assert.Equal(t, tc.expectedPanicMatch, "", "should not panic") + } + }() + res := vmHandler.Query(env.ctx, req) + if tc.expectedPanicMatch == "" { + if tc.expectedErrorMatch == "" { + assert.True(t, res.IsOK(), "should not have error") + if tc.expectedResult != "" { + assert.Equal(t, string(res.Data), tc.expectedResult) + } + if tc.expectedResultMatch != "" { + assert.Regexp(t, tc.expectedResultMatch, string(res.Data)) + } + } else { + assert.False(t, res.IsOK(), "should have an error") + errmsg := res.Error.Error() + assert.Regexp(t, tc.expectedErrorMatch, errmsg) + } + } + }) + } +} + +func TestVmHandlerQuery_Funcs(t *testing.T) { + tt := []struct { + input []byte + expectedResult string + expectedErrorMatch string + }{ + // valid queries + {input: []byte(`gno.land/r/hello`), expectedResult: `[{"FuncName":"Panic","Params":null,"Results":null},{"FuncName":"Echo","Params":[{"Name":"msg","Type":"string","Value":""}],"Results":[{"Name":"_","Type":"string","Value":""}]},{"FuncName":"GetCounter","Params":null,"Results":[{"Name":"_","Type":"int","Value":""}]},{"FuncName":"Inc","Params":null,"Results":[{"Name":"_","Type":"int","Value":""}]}]`}, + {input: []byte(`gno.land/r/doesnotexist`), expectedErrorMatch: `invalid package path`}, + {input: []byte(`std`), expectedErrorMatch: `invalid package path`}, + {input: []byte(`strings`), expectedErrorMatch: `invalid package path`}, + } + + for _, tc := range tt { + name := string(tc.input) + t.Run(name, func(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + vmHandler := env.vmh + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot")) + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot"))) + + // Create test package. + files := []*gnovm.MemFile{ + {"hello.gno", ` +package hello + +var sl = []int{1,2,3,4,5} +func fn() func(string) string { return Echo } +type myStruct struct{a int} +var myStructInst = myStruct{a: 1000} +func (ms myStruct) Foo() string { return "myStruct.Foo" } +func Panic() { panic("foo") } +var counter int = 42 +var pvString = "private string" +var PubString = "public string" +const ConstString = "const string" +func Echo(msg string) string { return "echo:"+msg } +func GetCounter() int { return counter } +func Inc() int { counter += 1; return counter } +func pvEcho(msg string) string { return "pvecho:"+msg } +`}, + } + pkgPath := "gno.land/r/hello" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + req := abci.RequestQuery{ + Path: "vm/qfuncs", + Data: tc.input, + } + + res := vmHandler.Query(env.ctx, req) + if tc.expectedErrorMatch == "" { + assert.True(t, res.IsOK(), "should not have error") + if tc.expectedResult != "" { + assert.Equal(t, string(res.Data), tc.expectedResult) + } + } else { + assert.False(t, res.IsOK(), "should have an error") + errmsg := res.Error.Error() + assert.Regexp(t, tc.expectedErrorMatch, errmsg) + } + }) + } +} + +func TestVmHandlerQuery_File(t *testing.T) { + tt := []struct { + input []byte + expectedResult string + expectedResultMatch string + expectedErrorMatch string + expectedPanicMatch string + // XXX: expectedEvents + }{ + // valid queries + {input: []byte(`gno.land/r/hello/hello.gno`), expectedResult: "package hello\n\nfunc Hello() string { return \"hello\" }\n"}, + {input: []byte(`gno.land/r/hello/README.md`), expectedResult: "# Hello"}, + {input: []byte(`gno.land/r/hello/doesnotexist.gno`), expectedErrorMatch: `file "gno.land/r/hello/doesnotexist.gno" is not available`}, + {input: []byte(`gno.land/r/hello`), expectedResult: "README.md\nhello.gno"}, + {input: []byte(`gno.land/r/doesnotexist`), expectedErrorMatch: `package "gno.land/r/doesnotexist" is not available`}, + {input: []byte(`gno.land/r/doesnotexist/hello.gno`), expectedErrorMatch: `file "gno.land/r/doesnotexist/hello.gno" is not available`}, + } + + for _, tc := range tt { + name := string(tc.input) + t.Run(name, func(t *testing.T) { + env := setupTestEnv() + ctx := env.vmk.MakeGnoTransactionStore(env.ctx) + vmHandler := env.vmh + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot")) + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot"))) + + // Create test package. + files := []*gnovm.MemFile{ + {"README.md", "# Hello"}, + {"hello.gno", "package hello\n\nfunc Hello() string { return \"hello\" }\n"}, + } + pkgPath := "gno.land/r/hello" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + req := abci.RequestQuery{ + Path: "vm/qfile", + Data: tc.input, + } + + defer func() { + if r := recover(); r != nil { + output := fmt.Sprintf("%v", r) + assert.Regexp(t, tc.expectedPanicMatch, output) + } else { + assert.Equal(t, "", tc.expectedPanicMatch, "should not panic") + } + }() + res := vmHandler.Query(env.ctx, req) + if tc.expectedErrorMatch == "" { + assert.True(t, res.IsOK(), "should not have error") + if tc.expectedResult != "" { + assert.Equal(t, string(res.Data), tc.expectedResult) + } + if tc.expectedResultMatch != "" { + assert.Regexp(t, tc.expectedResultMatch, string(res.Data)) + } + } else { + assert.False(t, res.IsOK(), "should have an error") + errmsg := res.Error.Error() + assert.Regexp(t, tc.expectedErrorMatch, errmsg) + } + }) + } +} From 8ec556e603ed5c21796409b5b487cbf9bf28751f Mon Sep 17 00:00:00 2001 From: 6h057 <15034695+omarsy@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:56:47 +0100 Subject: [PATCH 4/7] fix(gnovm): forbid star expression when value is nil (#3053) close: #3052
Contributors' checklist... - [ ] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [ ] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests
--- gnovm/pkg/gnolang/preprocess.go | 3 +++ gnovm/tests/files/ptr10.gno | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 gnovm/tests/files/ptr10.gno diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 10c55979520..a7a1e15bbf3 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -1760,6 +1760,9 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // TRANS_LEAVE ----------------------- case *StarExpr: xt := evalStaticTypeOf(store, last, n.X) + if xt == nil { + panic(fmt.Sprintf("invalid operation: cannot indirect nil")) + } if xt.Kind() != PointerKind && xt.Kind() != TypeKind { panic(fmt.Sprintf("invalid operation: cannot indirect %s (variable of type %s)", n.X.String(), xt.String())) } diff --git a/gnovm/tests/files/ptr10.gno b/gnovm/tests/files/ptr10.gno new file mode 100644 index 00000000000..807fc149808 --- /dev/null +++ b/gnovm/tests/files/ptr10.gno @@ -0,0 +1,8 @@ +package main + +func main() { + println(*nil) +} + +// Error: +// main/files/ptr10.gno:4:10: invalid operation: cannot indirect nil From 287335461efc8857b8a56ab0d9749bc3c6244fdd Mon Sep 17 00:00:00 2001 From: Manfred Touron <94029+moul@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:12:41 +0100 Subject: [PATCH 5/7] test(gnovm): indented json on filetests's Events: directives (#3055) Closer to the current `// Realm:` output, and more git-friendly. Bigger example in #3003. Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/r/demo/event/z1_filetest.gno | 25 ++++++++++++++++++- .../testdata/gno_test/filetest_events.txtar | 20 ++++++++++++++- gnovm/tests/file.go | 2 +- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/r/demo/event/z1_filetest.gno b/examples/gno.land/r/demo/event/z1_filetest.gno index 1fcfa1a0e4f..b138aa4351c 100644 --- a/examples/gno.land/r/demo/event/z1_filetest.gno +++ b/examples/gno.land/r/demo/event/z1_filetest.gno @@ -8,4 +8,27 @@ func main() { } // Events: -// [{"type":"TAG","attrs":[{"key":"key","value":"foo"}],"pkg_path":"gno.land/r/demo/event","func":"Emit"},{"type":"TAG","attrs":[{"key":"key","value":"bar"}],"pkg_path":"gno.land/r/demo/event","func":"Emit"}] +// [ +// { +// "type": "TAG", +// "attrs": [ +// { +// "key": "key", +// "value": "foo" +// } +// ], +// "pkg_path": "gno.land/r/demo/event", +// "func": "Emit" +// }, +// { +// "type": "TAG", +// "attrs": [ +// { +// "key": "key", +// "value": "bar" +// } +// ], +// "pkg_path": "gno.land/r/demo/event", +// "func": "Emit" +// } +// ] diff --git a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar b/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar index 5e0520a2e85..0236872e78a 100644 --- a/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/filetest_events.txtar @@ -30,4 +30,22 @@ func main() { // test // Events: -// [{"type":"EventA","attrs":[],"pkg_path":"","func":"main"},{"type":"EventB","attrs":[{"key":"keyA","value":"valA"}],"pkg_path":"","func":"main"}] +// [ +// { +// "type": "EventA", +// "attrs": [], +// "pkg_path": "", +// "func": "main" +// }, +// { +// "type": "EventB", +// "attrs": [ +// { +// "key": "keyA", +// "value": "valA" +// } +// ], +// "pkg_path": "", +// "func": "main" +// } +// ] diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 9df982d4fd8..98e54114af9 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -374,7 +374,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { } // check result events := m.Context.(*teststd.TestExecContext).EventLogger.Events() - evtjson, err := json.Marshal(events) + evtjson, err := json.MarshalIndent(events, "", " ") if err != nil { panic(err) } From af057801de3657c5910101b14b7996591f31cec6 Mon Sep 17 00:00:00 2001 From: Antonio Navarro Perez Date: Thu, 31 Oct 2024 18:50:05 +0100 Subject: [PATCH 6/7] ci: publish master Docker images without `--snapshot` flag (#3057) --- .github/workflows/releaser-master.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/releaser-master.yml b/.github/workflows/releaser-master.yml index 535cac5d965..7f81ef1ad1a 100644 --- a/.github/workflows/releaser-master.yml +++ b/.github/workflows/releaser-master.yml @@ -18,6 +18,8 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Create a dummy tag to avoid goreleaser failure + run: git tag v0.0.0 master - uses: actions/setup-go@v5 with: @@ -40,7 +42,7 @@ jobs: with: distribution: goreleaser-pro version: ~> v2 - args: release --clean --snapshot --nightly --config ./.github/goreleaser.yaml + args: release --clean --nightly --config ./.github/goreleaser.yaml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} From c776e32b08430487083de8ca2ac34c4e435878db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20=C5=BDivkovi=C4=87?= Date: Mon, 4 Nov 2024 17:22:46 +0100 Subject: [PATCH 7/7] feat: support metadata for genesis txs (#2941) ## Description This PR introduces metadata support for genesis transactions (such as timestamps), in the form of a new Gno genesis state that's easily generate-able. Shoutout to @clockworkgr for sanity checking the idea, and providing insights that ultimately led to this PR materializing. **BREAKING CHANGE** The `GnoGenesisState` is now modified, and all functionality that references it (ex. `gnogenesis`, `tx-archive`) will need to adapt. ### What we wanted to accomplish The Portal Loop does not save "time" information upon restarting (from block 0). This means that any transaction that resulted in a Realm / Package calling `time.Now()` will get differing results when these same transactions are "replayed" as part of the loop (in the genesis state). We wanted to somehow preserve this timestamp information when the transactions (from a previous loop), are executed as part of the genesis building process. For example: - Portal Loop chain is on block 100 - tx A results in a call to `time.Now()`, which returns time T, and saves it somewhere (Realm state) - the Portal Loop restarts, and uses all the transactions it encountered in the past iteration as genesis txs - the genesis is generated by executing the transactions - when tx A is re-executed (this time as part of the genesis block), **it should return time T, as with the original execution context** It is worth noting that this functionality is something we want in `gnodev`, so simple helpers on the Realms to update the timestamps wouldn't work. ### What this PR does I've tried to follow a couple of principles when working on this PR: - don't break backwards compatibility - don't modify critical APIs such as the SDK ABCI, but preserve existing, working, functionality in its original form - don't add another layer to the complexity pancake - don't implement a solution that looks (and works) like a hack I'm not a huge fan of execution hooks, so the solution proposed in this PR doesn't rely on any hook mechanism. Not going with the hook approach also significantly decreases the complexity and preserves readability. The base of this solution is enabling execution context modification, with minimal / no API changes. Having functionality like this, we can tailor the context during critical segments such as genesis generation, and we're not just limited to timestamps (which is the primary use-case). We also introduce a new type of `AppState`, called `MetadataGenesisState`, where metadata is associated with the transactions. We hide the actual `AppState` implementation behind an interface, so existing tools and flows don't break, and work as normal. ### What this PR doesn't do There is more work to be done if this PR is merged: - we need to add support to `tx-archive` for supporting exporting txs with metadata. Should be straightforward to do - the portal loop also needs to be restarted with this new "mode" enabled - we need to add support to existing `gnoland genesis` commands to support the new `MetadataGenesisState`. It is also straightforward, but definitely a bit of work - if we want support for something like this in gnodev, the export / import code of gnodev also needs to be modified to support the new genesis state type (cc @gfanton) - https://github.com/gnolang/gno/pull/2943 Related PRs and issues: - #2751 - #2744 cc @moul @thehowl @jeronimoalbi @ilgooz
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [x] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--------- Co-authored-by: Manfred Touron <94029+moul@users.noreply.github.com> --- contribs/gnodev/cmd/gnodev/setup_node.go | 10 +- contribs/gnodev/cmd/gnodev/txs.go | 23 -- contribs/gnodev/pkg/dev/node.go | 26 ++- contribs/gnodev/pkg/dev/node_state.go | 5 +- contribs/gnodev/pkg/dev/packages.go | 24 +- contribs/gnogenesis/internal/txs/txs.go | 9 +- .../internal/txs/txs_add_packages.go | 2 +- .../internal/txs/txs_add_packages_test.go | 4 +- .../gnogenesis/internal/txs/txs_add_sheet.go | 21 +- .../internal/txs/txs_add_sheet_test.go | 33 +-- .../internal/txs/txs_export_test.go | 5 +- .../gnogenesis/internal/txs/txs_list_test.go | 2 +- .../gnogenesis/internal/txs/txs_remove.go | 2 +- .../internal/txs/txs_remove_test.go | 4 +- contribs/gnogenesis/internal/verify/verify.go | 2 +- .../gnogenesis/internal/verify/verify_test.go | 7 +- gno.land/genesis/genesis_txs.jsonl | 34 +-- gno.land/pkg/gnoland/app.go | 27 ++- gno.land/pkg/gnoland/app_test.go | 205 +++++++++++++++++- gno.land/pkg/gnoland/balance_test.go | 23 +- gno.land/pkg/gnoland/genesis.go | 15 +- gno.land/pkg/gnoland/node_inmemory.go | 3 +- gno.land/pkg/gnoland/package.go | 2 + gno.land/pkg/gnoland/types.go | 62 +++++- gno.land/pkg/gnoland/types_test.go | 131 +++++++++++ .../pkg/integration/testing_integration.go | 10 +- gno.land/pkg/integration/testing_node.go | 10 +- tm2/pkg/sdk/baseapp.go | 26 ++- tm2/pkg/sdk/helpers.go | 27 ++- 29 files changed, 581 insertions(+), 173 deletions(-) delete mode 100644 contribs/gnodev/cmd/gnodev/txs.go create mode 100644 gno.land/pkg/gnoland/types_test.go diff --git a/contribs/gnodev/cmd/gnodev/setup_node.go b/contribs/gnodev/cmd/gnodev/setup_node.go index 578cf525751..4b3619b4a7d 100644 --- a/contribs/gnodev/cmd/gnodev/setup_node.go +++ b/contribs/gnodev/cmd/gnodev/setup_node.go @@ -23,7 +23,7 @@ func setupDevNode( if devCfg.txsFile != "" { // Load txs files var err error - nodeConfig.InitialTxs, err = parseTxs(devCfg.txsFile) + nodeConfig.InitialTxs, err = gnoland.ReadGenesisTxs(ctx, devCfg.txsFile) if err != nil { return nil, fmt.Errorf("unable to load transactions: %w", err) } @@ -35,7 +35,13 @@ func setupDevNode( // Override balances and txs nodeConfig.BalancesList = state.Balances - nodeConfig.InitialTxs = state.Txs + + stateTxs := state.Txs + nodeConfig.InitialTxs = make([]gnoland.TxWithMetadata, len(stateTxs)) + + for index, nodeTx := range stateTxs { + nodeConfig.InitialTxs[index] = nodeTx + } logger.Info("genesis file loaded", "path", devCfg.genesisFile, "txs", len(nodeConfig.InitialTxs)) } diff --git a/contribs/gnodev/cmd/gnodev/txs.go b/contribs/gnodev/cmd/gnodev/txs.go deleted file mode 100644 index 0be33b68702..00000000000 --- a/contribs/gnodev/cmd/gnodev/txs.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - - "github.com/gnolang/gno/tm2/pkg/std" -) - -func parseTxs(txFile string) ([]std.Tx, error) { - if txFile == "" { - return nil, nil - } - - file, loadErr := os.Open(txFile) - if loadErr != nil { - return nil, fmt.Errorf("unable to open tx file %s: %w", txFile, loadErr) - } - defer file.Close() - - return std.ParseTxs(context.Background(), file) -} diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index 0e1099eef88..54baa2ea774 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -35,7 +35,7 @@ type NodeConfig struct { BalancesList []gnoland.Balance PackagesPathList []PackagePath Emitter emitter.Emitter - InitialTxs []std.Tx + InitialTxs []gnoland.TxWithMetadata TMConfig *tmcfg.Config SkipFailingGenesisTxs bool NoReplay bool @@ -84,7 +84,7 @@ type Node struct { loadedPackages int // state - initialState, state []std.Tx + initialState, state []gnoland.TxWithMetadata currentStateIndex int } @@ -154,7 +154,7 @@ func (n *Node) GetRemoteAddress() string { // GetBlockTransactions returns the transactions contained // within the specified block, if any -func (n *Node) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) { +func (n *Node) GetBlockTransactions(blockNum uint64) ([]gnoland.TxWithMetadata, error) { n.muNode.RLock() defer n.muNode.RUnlock() @@ -163,21 +163,26 @@ func (n *Node) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) { // GetBlockTransactions returns the transactions contained // within the specified block, if any -func (n *Node) getBlockTransactions(blockNum uint64) ([]std.Tx, error) { +func (n *Node) getBlockTransactions(blockNum uint64) ([]gnoland.TxWithMetadata, error) { int64BlockNum := int64(blockNum) b, err := n.client.Block(&int64BlockNum) if err != nil { - return []std.Tx{}, fmt.Errorf("unable to load block at height %d: %w", blockNum, err) // nothing to see here + return []gnoland.TxWithMetadata{}, fmt.Errorf("unable to load block at height %d: %w", blockNum, err) // nothing to see here } - txs := make([]std.Tx, len(b.Block.Data.Txs)) + txs := make([]gnoland.TxWithMetadata, len(b.Block.Data.Txs)) for i, encodedTx := range b.Block.Data.Txs { var tx std.Tx if unmarshalErr := amino.Unmarshal(encodedTx, &tx); unmarshalErr != nil { return nil, fmt.Errorf("unable to unmarshal amino tx, %w", unmarshalErr) } - txs[i] = tx + txs[i] = gnoland.TxWithMetadata{ + Tx: tx, + Metadata: &gnoland.GnoTxMetadata{ + Timestamp: b.BlockMeta.Header.Time.Unix(), + }, + } } return txs, nil @@ -347,11 +352,14 @@ func (n *Node) SendTransaction(tx *std.Tx) error { return nil } -func (n *Node) getBlockStoreState(ctx context.Context) ([]std.Tx, error) { +func (n *Node) getBlockStoreState(ctx context.Context) ([]gnoland.TxWithMetadata, error) { // get current genesis state genesis := n.GenesisDoc().AppState.(gnoland.GnoGenesisState) - state := genesis.Txs[n.loadedPackages:] // ignore previously loaded packages + initialTxs := genesis.Txs[n.loadedPackages:] // ignore previously loaded packages + + state := append([]gnoland.TxWithMetadata{}, initialTxs...) + lastBlock := n.getLatestBlockNumber() var blocnum uint64 = 1 for ; blocnum <= lastBlock; blocnum++ { diff --git a/contribs/gnodev/pkg/dev/node_state.go b/contribs/gnodev/pkg/dev/node_state.go index 846c4857784..7504580b333 100644 --- a/contribs/gnodev/pkg/dev/node_state.go +++ b/contribs/gnodev/pkg/dev/node_state.go @@ -8,7 +8,6 @@ import ( "github.com/gnolang/gno/contribs/gnodev/pkg/events" "github.com/gnolang/gno/gno.land/pkg/gnoland" bft "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/std" ) var ErrEmptyState = errors.New("empty state") @@ -29,7 +28,7 @@ func (n *Node) SaveCurrentState(ctx context.Context) error { } // Export the current state as list of txs -func (n *Node) ExportCurrentState(ctx context.Context) ([]std.Tx, error) { +func (n *Node) ExportCurrentState(ctx context.Context) ([]gnoland.TxWithMetadata, error) { n.muNode.RLock() defer n.muNode.RUnlock() @@ -42,7 +41,7 @@ func (n *Node) ExportCurrentState(ctx context.Context) ([]std.Tx, error) { return state[:n.currentStateIndex], nil } -func (n *Node) getState(ctx context.Context) ([]std.Tx, error) { +func (n *Node) getState(ctx context.Context) ([]gnoland.TxWithMetadata, error) { if n.state == nil { var err error n.state, err = n.getBlockStoreState(ctx) diff --git a/contribs/gnodev/pkg/dev/packages.go b/contribs/gnodev/pkg/dev/packages.go index 7b560c21e09..7ee628ce39e 100644 --- a/contribs/gnodev/pkg/dev/packages.go +++ b/contribs/gnodev/pkg/dev/packages.go @@ -7,6 +7,7 @@ import ( "path/filepath" "github.com/gnolang/gno/contribs/gnodev/pkg/address" + "github.com/gnolang/gno/gno.land/pkg/gnoland" vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/pkg/gnomod" @@ -118,7 +119,7 @@ func (pm PackagesMap) toList() gnomod.PkgList { return list } -func (pm PackagesMap) Load(fee std.Fee) ([]std.Tx, error) { +func (pm PackagesMap) Load(fee std.Fee) ([]gnoland.TxWithMetadata, error) { pkgs := pm.toList() sorted, err := pkgs.Sort() @@ -127,7 +128,8 @@ func (pm PackagesMap) Load(fee std.Fee) ([]std.Tx, error) { } nonDraft := sorted.GetNonDraftPkgs() - txs := []std.Tx{} + txs := make([]gnoland.TxWithMetadata, 0, len(nonDraft)) + for _, modPkg := range nonDraft { pkg := pm[modPkg.Dir] if pkg.Creator.IsZero() { @@ -141,18 +143,20 @@ func (pm PackagesMap) Load(fee std.Fee) ([]std.Tx, error) { } // Create transaction - tx := std.Tx{ - Fee: fee, - Msgs: []std.Msg{ - vmm.MsgAddPackage{ - Creator: pkg.Creator, - Deposit: pkg.Deposit, - Package: memPkg, + tx := gnoland.TxWithMetadata{ + Tx: std.Tx{ + Fee: fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: pkg.Creator, + Deposit: pkg.Deposit, + Package: memPkg, + }, }, }, } - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + tx.Tx.Signatures = make([]std.Signature, len(tx.Tx.GetSigners())) txs = append(txs, tx) } diff --git a/contribs/gnogenesis/internal/txs/txs.go b/contribs/gnogenesis/internal/txs/txs.go index f8a14eafefc..fbf4c6ea3c7 100644 --- a/contribs/gnogenesis/internal/txs/txs.go +++ b/contribs/gnogenesis/internal/txs/txs.go @@ -8,7 +8,6 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/std" ) type txsCfg struct { @@ -47,7 +46,7 @@ func (c *txsCfg) RegisterFlags(fs *flag.FlagSet) { } // appendGenesisTxs saves the given transactions to the genesis doc -func appendGenesisTxs(genesis *types.GenesisDoc, txs []std.Tx) error { +func appendGenesisTxs(genesis *types.GenesisDoc, txs []gnoland.TxWithMetadata) error { // Initialize the app state if it's not present if genesis.AppState == nil { genesis.AppState = gnoland.GnoGenesisState{} @@ -77,7 +76,7 @@ func appendGenesisTxs(genesis *types.GenesisDoc, txs []std.Tx) error { } // txStore is a wrapper for TM2 transactions -type txStore []std.Tx +type txStore []gnoland.TxWithMetadata // leftMerge merges the two tx stores, with // preference to the left @@ -86,7 +85,7 @@ func (i *txStore) leftMerge(b txStore) error { txHashMap := make(map[string]struct{}, len(*i)) for _, tx := range *i { - txHash, err := getTxHash(tx) + txHash, err := getTxHash(tx.Tx) if err != nil { return err } @@ -95,7 +94,7 @@ func (i *txStore) leftMerge(b txStore) error { } for _, tx := range b { - txHash, err := getTxHash(tx) + txHash, err := getTxHash(tx.Tx) if err != nil { return err } diff --git a/contribs/gnogenesis/internal/txs/txs_add_packages.go b/contribs/gnogenesis/internal/txs/txs_add_packages.go index b07adc777a7..1b4e6e7cffb 100644 --- a/contribs/gnogenesis/internal/txs/txs_add_packages.go +++ b/contribs/gnogenesis/internal/txs/txs_add_packages.go @@ -53,7 +53,7 @@ func execTxsAddPackages( return errInvalidPackageDir } - parsedTxs := make([]std.Tx, 0) + parsedTxs := make([]gnoland.TxWithMetadata, 0) for _, path := range args { // Generate transactions from the packages (recursively) txs, err := gnoland.LoadPackagesFromDir(path, genesisDeployAddress, genesisDeployFee) diff --git a/contribs/gnogenesis/internal/txs/txs_add_packages_test.go b/contribs/gnogenesis/internal/txs/txs_add_packages_test.go index c814ccde957..12a9287f171 100644 --- a/contribs/gnogenesis/internal/txs/txs_add_packages_test.go +++ b/contribs/gnogenesis/internal/txs/txs_add_packages_test.go @@ -118,9 +118,9 @@ func TestGenesis_Txs_Add_Packages(t *testing.T) { state := updatedGenesis.AppState.(gnoland.GnoGenesisState) require.Equal(t, 1, len(state.Txs)) - require.Equal(t, 1, len(state.Txs[0].Msgs)) + require.Equal(t, 1, len(state.Txs[0].Tx.Msgs)) - msgAddPkg, ok := state.Txs[0].Msgs[0].(vmm.MsgAddPackage) + msgAddPkg, ok := state.Txs[0].Tx.Msgs[0].(vmm.MsgAddPackage) require.True(t, ok) assert.Equal(t, packagePath, msgAddPkg.Package.Path) diff --git a/contribs/gnogenesis/internal/txs/txs_add_sheet.go b/contribs/gnogenesis/internal/txs/txs_add_sheet.go index 88673bc29bd..0bbd4b578cc 100644 --- a/contribs/gnogenesis/internal/txs/txs_add_sheet.go +++ b/contribs/gnogenesis/internal/txs/txs_add_sheet.go @@ -4,17 +4,13 @@ import ( "context" "errors" "fmt" - "os" + "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/std" ) -var ( - errInvalidTxsFile = errors.New("unable to open transactions file") - errNoTxsFileSpecified = errors.New("no txs file specified") -) +var errNoTxsFileSpecified = errors.New("no txs file specified") // newTxsAddSheetCmd creates the genesis txs add sheet subcommand func newTxsAddSheetCmd(txsCfg *txsCfg, io commands.IO) *commands.Command { @@ -49,22 +45,13 @@ func execTxsAddSheet( return errNoTxsFileSpecified } - parsedTxs := make([]std.Tx, 0) + parsedTxs := make([]gnoland.TxWithMetadata, 0) for _, file := range args { - file, loadErr := os.Open(file) - if loadErr != nil { - return fmt.Errorf("%w, %w", errInvalidTxsFile, loadErr) - } - - txs, err := std.ParseTxs(ctx, file) + txs, err := gnoland.ReadGenesisTxs(ctx, file) if err != nil { return fmt.Errorf("unable to parse file, %w", err) } - if err = file.Close(); err != nil { - return fmt.Errorf("unable to gracefully close file, %w", err) - } - parsedTxs = append(parsedTxs, txs...) } diff --git a/contribs/gnogenesis/internal/txs/txs_add_sheet_test.go b/contribs/gnogenesis/internal/txs/txs_add_sheet_test.go index a174905c237..6da3faea6ed 100644 --- a/contribs/gnogenesis/internal/txs/txs_add_sheet_test.go +++ b/contribs/gnogenesis/internal/txs/txs_add_sheet_test.go @@ -21,25 +21,27 @@ import ( ) // generateDummyTxs generates dummy transactions -func generateDummyTxs(t *testing.T, count int) []std.Tx { +func generateDummyTxs(t *testing.T, count int) []gnoland.TxWithMetadata { t.Helper() - txs := make([]std.Tx, count) + txs := make([]gnoland.TxWithMetadata, count) for i := 0; i < count; i++ { - txs[i] = std.Tx{ - Msgs: []std.Msg{ - bank.MsgSend{ - FromAddress: crypto.Address{byte(i)}, - ToAddress: crypto.Address{byte((i + 1) % count)}, - Amount: std.NewCoins(std.NewCoin(ugnot.Denom, 1)), + txs[i] = gnoland.TxWithMetadata{ + Tx: std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: crypto.Address{byte(i)}, + ToAddress: crypto.Address{byte((i + 1) % count)}, + Amount: std.NewCoins(std.NewCoin(ugnot.Denom, 1)), + }, }, + Fee: std.Fee{ + GasWanted: 1, + GasFee: std.NewCoin(ugnot.Denom, 1000000), + }, + Memo: fmt.Sprintf("tx %d", i), }, - Fee: std.Fee{ - GasWanted: 1, - GasFee: std.NewCoin(ugnot.Denom, 1000000), - }, - Memo: fmt.Sprintf("tx %d", i), } } @@ -47,7 +49,7 @@ func generateDummyTxs(t *testing.T, count int) []std.Tx { } // encodeDummyTxs encodes the transactions into amino JSON -func encodeDummyTxs(t *testing.T, txs []std.Tx) []string { +func encodeDummyTxs(t *testing.T, txs []gnoland.TxWithMetadata) []string { t.Helper() encodedTxs := make([]string, 0, len(txs)) @@ -104,8 +106,7 @@ func TestGenesis_Txs_Add_Sheets(t *testing.T) { } // Run the command - cmdErr := cmd.ParseAndRun(context.Background(), args) - assert.ErrorContains(t, cmdErr, errInvalidTxsFile.Error()) + assert.Error(t, cmd.ParseAndRun(context.Background(), args)) }) t.Run("no txs file", func(t *testing.T) { diff --git a/contribs/gnogenesis/internal/txs/txs_export_test.go b/contribs/gnogenesis/internal/txs/txs_export_test.go index 47fc594d2ec..ad738cd95f7 100644 --- a/contribs/gnogenesis/internal/txs/txs_export_test.go +++ b/contribs/gnogenesis/internal/txs/txs_export_test.go @@ -9,7 +9,6 @@ import ( "github.com/gnolang/gno/gno.land/pkg/gnoland" "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -117,9 +116,9 @@ func TestGenesis_Txs_Export(t *testing.T) { // Validate the transactions were written down scanner := bufio.NewScanner(outputFile) - outputTxs := make([]std.Tx, 0) + outputTxs := make([]gnoland.TxWithMetadata, 0) for scanner.Scan() { - var tx std.Tx + var tx gnoland.TxWithMetadata require.NoError(t, amino.UnmarshalJSON(scanner.Bytes(), &tx)) diff --git a/contribs/gnogenesis/internal/txs/txs_list_test.go b/contribs/gnogenesis/internal/txs/txs_list_test.go index 5129533dc8f..d4d9f4d7ba8 100644 --- a/contribs/gnogenesis/internal/txs/txs_list_test.go +++ b/contribs/gnogenesis/internal/txs/txs_list_test.go @@ -63,6 +63,6 @@ func TestGenesis_List_All(t *testing.T) { cmdErr := cmd.ParseAndRun(context.Background(), args) require.NoError(t, cmdErr) - require.Len(t, buf.String(), 4442) + require.Len(t, buf.String(), 5262) }) } diff --git a/contribs/gnogenesis/internal/txs/txs_remove.go b/contribs/gnogenesis/internal/txs/txs_remove.go index f767e19bc90..dbfc90fb1dc 100644 --- a/contribs/gnogenesis/internal/txs/txs_remove.go +++ b/contribs/gnogenesis/internal/txs/txs_remove.go @@ -59,7 +59,7 @@ func execTxsRemove(cfg *txsCfg, io commands.IO, args []string) error { for indx, tx := range state.Txs { // Find the hash of the transaction - hash, err := getTxHash(tx) + hash, err := getTxHash(tx.Tx) if err != nil { return fmt.Errorf("unable to generate tx hash, %w", err) } diff --git a/contribs/gnogenesis/internal/txs/txs_remove_test.go b/contribs/gnogenesis/internal/txs/txs_remove_test.go index c031e0d342e..16edbb83e3c 100644 --- a/contribs/gnogenesis/internal/txs/txs_remove_test.go +++ b/contribs/gnogenesis/internal/txs/txs_remove_test.go @@ -97,7 +97,7 @@ func TestGenesis_Txs_Remove(t *testing.T) { } require.NoError(t, genesis.SaveAs(tempGenesis.Name())) - txHash, err := getTxHash(txs[0]) + txHash, err := getTxHash(txs[0].Tx) require.NoError(t, err) // Create the command @@ -124,7 +124,7 @@ func TestGenesis_Txs_Remove(t *testing.T) { assert.Len(t, state.Txs, len(txs)-1) for _, tx := range state.Txs { - genesisTxHash, err := getTxHash(tx) + genesisTxHash, err := getTxHash(tx.Tx) require.NoError(t, err) assert.NotEqual(t, txHash, genesisTxHash) diff --git a/contribs/gnogenesis/internal/verify/verify.go b/contribs/gnogenesis/internal/verify/verify.go index 97ad14cb7f6..9022711ce49 100644 --- a/contribs/gnogenesis/internal/verify/verify.go +++ b/contribs/gnogenesis/internal/verify/verify.go @@ -61,7 +61,7 @@ func execVerify(cfg *verifyCfg, io commands.IO) error { // Validate the initial transactions for _, tx := range state.Txs { - if validateErr := tx.ValidateBasic(); validateErr != nil { + if validateErr := tx.Tx.ValidateBasic(); validateErr != nil { return fmt.Errorf("invalid transacton, %w", validateErr) } } diff --git a/contribs/gnogenesis/internal/verify/verify_test.go b/contribs/gnogenesis/internal/verify/verify_test.go index 76009c34c94..130bd5e09bc 100644 --- a/contribs/gnogenesis/internal/verify/verify_test.go +++ b/contribs/gnogenesis/internal/verify/verify_test.go @@ -9,7 +9,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/mock" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/require" ) @@ -45,7 +44,7 @@ func TestGenesis_Verify(t *testing.T) { g.AppState = gnoland.GnoGenesisState{ Balances: []gnoland.Balance{}, - Txs: []std.Tx{ + Txs: []gnoland.TxWithMetadata{ {}, }, } @@ -76,7 +75,7 @@ func TestGenesis_Verify(t *testing.T) { Balances: []gnoland.Balance{ {}, }, - Txs: []std.Tx{}, + Txs: []gnoland.TxWithMetadata{}, } require.NoError(t, g.SaveAs(tempFile.Name())) @@ -102,7 +101,7 @@ func TestGenesis_Verify(t *testing.T) { g := getValidTestGenesis() g.AppState = gnoland.GnoGenesisState{ Balances: []gnoland.Balance{}, - Txs: []std.Tx{}, + Txs: []gnoland.TxWithMetadata{}, } require.NoError(t, g.SaveAs(tempFile.Name())) diff --git a/gno.land/genesis/genesis_txs.jsonl b/gno.land/genesis/genesis_txs.jsonl index daf9fbdc5d4..43453dcd2fc 100644 --- a/gno.land/genesis/genesis_txs.jsonl +++ b/gno.land/genesis/genesis_txs.jsonl @@ -1,17 +1,17 @@ -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj:10\ng1589c8cekvmjfmy0qrd4f3z52r7fn7rgk02667s:1\ng13sm84nuqed3fuank8huh7x9mupgw22uft3lcl8:1\ng1m6732pkrngu9vrt0g7056lvr9kcqc4mv83xl5q:1\ng1wg88rhzlwxjd2z4j5de5v5xq30dcf6rjq3dhsj:1\ng18pmaskasz7mxj6rmgrl3al58xu45a7w0l5nmc0:1\ng19wwhkmqlns70604ksp6rkuuu42qhtvyh05lffz:1\ng187982000zsc493znqt828s90cmp6hcp2erhu6m:1\ng1ndpsnrspdnauckytvkfv8s823t3gmpqmtky8pl:1\ng16ja66d65emkr0zxd2tu7xjvm7utthyhpej0037:1\ng1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5:1\ng1trkzq75ntamsnw9xnrav2v7gy2lt5g6p29yhdr:1\ng1rrf8s5mrmu00sx04fzfsvc399fklpeg2x0a7mz:1\ng19p5ntfvpt4lwq4jqsmnxsnelhf3tff9scy3w8w:1\ng1tue8l73d6rq4vhqdsp2sr3zhuzpure3k2rnwpz:1\ng14hhsss4ngx5kq77je5g0tl4vftg8qp45ceadk3:1\ng1768hvkh7anhd40ch4h7jdh6j3mpcs7hrat4gl0:1\ng15fa8kyjhu88t9dr8zzua8fwdvkngv5n8yqsm0n:1\ng1xhccdjcscuhgmt3quww6qdy3j3czqt3urc2eac:1\ng1z629z04f85k4t5gnkk5egpxw9tqxeec435esap:1\ng1pfldkplz9puq0v82lu9vqcve9nwrxuq9qe5ttv:1\ng152pn0g5qfgxr7yx8zlwjq48hytkafd8x7egsfv:1\ng1cf2ye686ke38vjyqakreprljum4xu6rwf5jskq:1\ng1c5shztyaj4gjrc5zlwmh9xhex5w7l4asffs2w6:1\ng1lhpx2ktk0ha3qw42raxq4m24a4c4xqxyrgv54q:1\ng1026p54q0j902059sm2zsv37krf0ghcl7gmhyv7:1\ng1n4yvwnv77frq2ccuw27dmtjkd7u4p4jg0pgm7k:1\ng13m7f2e6r3lh3ykxupacdt9sem2tlvmaamwjhll:1\ng19uxluuecjlsqvwmwu8sp6pxaaqfhk972q975xd:1\ng1j80fpcsumfkxypvydvtwtz3j4sdwr8c2u0lr64:1\ng1tjdpptuk9eysq6z38nscqyycr998xjyx3w8jvw:1\ng19t3n89slfemgd3mwuat4lajwcp0yxrkadgeg7a:1\ng1yqndt8xx92l9h494jfruz2w79swzjes3n4wqjc:1\ng13278z0a5ufeg80ffqxpda9dlp599t7ekregcy6:1\ng1ht236wjd83x96uqwh9rh3fq6pylyn78mtwq9v6:1\ng1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9:1\ng1wwppuzdns5u6c6jqpkzua24zh6ppsus6399cea:1\ng1k8pjnguyu36pkc8hy0ufzgpzfmj2jl78la7ek3:1\ng1e8umkzumtxgs8399lw0us4rclea3xl5gxy9spp:1\ng14qekdkj2nmmwea4ufg9n002a3pud23y8k7ugs5:1\ng19w2488ntfgpduzqq3sk4j5x387zynwknqdvjqf:1\ng1495y3z7zrej4rendysnw5kaeu4g3d7x7w0734g:1\ng1hygx8ga9qakhkczyrzs9drm8j8tu4qds9y5e3r:1\ng1f977l6wxdh3qu60kzl75vx2wmzswu68l03r8su:1\ng1644qje5rx6jsdqfkzmgnfcegx4dxkjh6rwqd69:1\ng1mzjajymvmtksdwh3wkrndwj6zls2awl9q83dh6:1\ng14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa:10\ng14vhcdsyf83ngsrrqc92kmw8q9xakqjm0v8448t:5\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"S8iMMzlOMK8dmox78R9Z8+pSsS8YaTCXrIcaHDpiOgkOy7gqoQJ0oftM0zf8zAz4xpezK8Lzg8Q0fCdXJxV76w=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1thlf3yct7n7ex70k0p62user0kn6mj6d3s0cg3\ng1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"njczE6xYdp01+CaUU/8/v0YC/NuZD06+qLind+ZZEEMNaRe/4Ln+4z7dG6HYlaWUMsyI1KCoB6NIehoE0PZ44Q=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1589c8cekvmjfmy0qrd4f3z52r7fn7rgk02667s\ng13sm84nuqed3fuank8huh7x9mupgw22uft3lcl8\ng1m6732pkrngu9vrt0g7056lvr9kcqc4mv83xl5q\ng1wg88rhzlwxjd2z4j5de5v5xq30dcf6rjq3dhsj\ng18pmaskasz7mxj6rmgrl3al58xu45a7w0l5nmc0\ng19wwhkmqlns70604ksp6rkuuu42qhtvyh05lffz\ng187982000zsc493znqt828s90cmp6hcp2erhu6m\ng1ndpsnrspdnauckytvkfv8s823t3gmpqmtky8pl\ng16ja66d65emkr0zxd2tu7xjvm7utthyhpej0037\ng1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5\ng1trkzq75ntamsnw9xnrav2v7gy2lt5g6p29yhdr\ng1rrf8s5mrmu00sx04fzfsvc399fklpeg2x0a7mz\ng19p5ntfvpt4lwq4jqsmnxsnelhf3tff9scy3w8w\ng1tue8l73d6rq4vhqdsp2sr3zhuzpure3k2rnwpz\ng14hhsss4ngx5kq77je5g0tl4vftg8qp45ceadk3\ng1768hvkh7anhd40ch4h7jdh6j3mpcs7hrat4gl0\ng15fa8kyjhu88t9dr8zzua8fwdvkngv5n8yqsm0n\ng1xhccdjcscuhgmt3quww6qdy3j3czqt3urc2eac\ng1z629z04f85k4t5gnkk5egpxw9tqxeec435esap\ng1pfldkplz9puq0v82lu9vqcve9nwrxuq9qe5ttv\ng152pn0g5qfgxr7yx8zlwjq48hytkafd8x7egsfv\ng1cf2ye686ke38vjyqakreprljum4xu6rwf5jskq\ng1c5shztyaj4gjrc5zlwmh9xhex5w7l4asffs2w6\ng1lhpx2ktk0ha3qw42raxq4m24a4c4xqxyrgv54q\ng1026p54q0j902059sm2zsv37krf0ghcl7gmhyv7\ng1n4yvwnv77frq2ccuw27dmtjkd7u4p4jg0pgm7k\ng13m7f2e6r3lh3ykxupacdt9sem2tlvmaamwjhll\ng19uxluuecjlsqvwmwu8sp6pxaaqfhk972q975xd\ng1j80fpcsumfkxypvydvtwtz3j4sdwr8c2u0lr64\ng1tjdpptuk9eysq6z38nscqyycr998xjyx3w8jvw\ng19t3n89slfemgd3mwuat4lajwcp0yxrkadgeg7a\ng1yqndt8xx92l9h494jfruz2w79swzjes3n4wqjc\ng13278z0a5ufeg80ffqxpda9dlp599t7ekregcy6\ng1ht236wjd83x96uqwh9rh3fq6pylyn78mtwq9v6\ng1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9\ng1wwppuzdns5u6c6jqpkzua24zh6ppsus6399cea\ng1k8pjnguyu36pkc8hy0ufzgpzfmj2jl78la7ek3\ng1e8umkzumtxgs8399lw0us4rclea3xl5gxy9spp\ng14qekdkj2nmmwea4ufg9n002a3pud23y8k7ugs5\ng19w2488ntfgpduzqq3sk4j5x387zynwknqdvjqf\ng1495y3z7zrej4rendysnw5kaeu4g3d7x7w0734g\ng1hygx8ga9qakhkczyrzs9drm8j8tu4qds9y5e3r\ng1f977l6wxdh3qu60kzl75vx2wmzswu68l03r8su\ng1644qje5rx6jsdqfkzmgnfcegx4dxkjh6rwqd69\ng1mzjajymvmtksdwh3wkrndwj6zls2awl9q83dh6\ng1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\ng14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa\ng14vhcdsyf83ngsrrqc92kmw8q9xakqjm0v8448t\n"]}],"fee":{"gas_wanted":"4000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"7AmlhZhsVkxCUl0bbpvpPMnIKihwtG7A5IFR6Tg4xStWLgaUr05XmWRKlO2xjstTtwbVKQT5mFL4h5wyX4SQzw=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","administrator","g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"AqCqe0cS55Ym7/BvPDoCDyPP5q8284gecVQ2PMOlq/4lJpO9Q18SOWKI15dMEBY1pT0AYyhCeTirlsM1I3Y4Cg=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","zo_oma","Love is the encryption key\u003c3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A6yg5/iiktruezVw5vZJwLlGwyrvw8RlqOToTRMWXkE2"},"signature":"GGp+bVL2eEvKecPqgcULSABYOSnSMnJzfIsR8ZIRER1GGX/fOiCReX4WKMrGLVROJVfbLQkDRwvhS4TLHlSoSQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","manfred","https://github.com/moul"]}],"fee":{"gas_wanted":"2000000","gas_fee":"200000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"9CWeNbKx+hEL+RdHplAVAFntcrAVx5mK9tMqoywuHVoreH844n3yOxddQrGfBk6T2tMBmNWakERRqWZfS+bYAQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","piupiu","@piux2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"Ar68lqbU2YC63fbMcYUtJhYO3/66APM/EqF7m0nUjGyz"},"signature":"pTUpP0d/XlfVe3TH1hlaoLhKadzIKG1gtQ/Ueuat72p+659RWRea58Z0mk6GgPE/EeTbhMEY45zufevBdGJVoQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5","send":"","pkg_path":"gno.land/r/demo/users","func":"Register","args":["g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","anarcher","https://twitter.com/anarcher"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AjpLbKdQeH+yB/1OCB148l5GlRRrXma71hdA8EES3H7f"},"signature":"pf5xm8oWIQIOEwSGw4icPmynLXb1P1HxKfjeh8UStU1mlIBPKa7yppeIMPpAflC0o2zjFR7Axe7CimAebm3BHg=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g15gdm49ktawvkrl88jadqpucng37yxutucuwaef","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","ideamour","\u003c3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AhClx4AsDuX3DNCPxhDwWnrfd4MIZmxJE4vt47ClVvT2"},"signature":"IQe64af878k6HjLDqIJeg27GXAVF6xS+96cDe2jMlxNV6+8sOcuUctp0GiWVnYfN4tpthC6d4WhBo+VlpHqkbg=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateBoard","args":["testboard"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"vzlSxEFh5jOkaSdv3rsV91v/OJKEF2qSuoCpri1u5tRWq62T7xr3KHRCF5qFnn4aQX/yE8g8f/Y//WPOCUGhJw=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Hello World","This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm \nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n## Starting the `gnoland` node node/validator.\n\nNOTE: Where you see `--remote %%REMOTE%%` here, that flag can be replaced\nwith `--remote localhost:26657` for local testnets.\n\n### build gnoland.\n\n```bash\ngit clone git@github.com:gnolang/gno.git\ncd ./gno\nmake \n```\n\n### add test account.\n\n```bash\n./build/gnokey add test1 --recover\n```\n\nUse this mnemonic:\n\u003e source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast\n\n### start gnoland validator node.\n\n```bash\n./build/gnoland\n```\n\n(This can be reset with `make reset`).\n\n### start gnoland web server (optional).\n\n```bash\ngo run ./gnoland/website\n```\n\n## Signing and broadcasting transactions.\n\n### publish the \"gno.land/p/demo/avl\" package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/p/demo/avl\" --pkgdir \"examples/gno.land/p/demo/avl\" --deposit 100ugnot --gas-fee 1ugnot --gas-wanted 2000000 \u003e addpkg.avl.unsigned.txt\n./build/gnokey query \"auth/accounts/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"\n./build/gnokey sign test1 --txpath addpkg.avl.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 0 \u003e addpkg.avl.signed.txt\n./build/gnokey broadcast addpkg.avl.signed.txt --remote %%REMOTE%%\n```\n\n### publish the \"gno.land/r/demo/boards\" realm package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/r/demo/boards\" --pkgdir \"examples/gno.land/r/demo/boards\" --deposit 100ugnot --gas-fee 1ugnot --gas-wanted 300000000 \u003e addpkg.boards.unsigned.txt\n./build/gnokey sign test1 --txpath addpkg.boards.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 1 \u003e addpkg.boards.signed.txt\n./build/gnokey broadcast addpkg.boards.signed.txt --remote %%REMOTE%%\n```\n\n### create a board with a smart contract call.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreateBoard --args \"testboard\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createboard.unsigned.txt\n./build/gnokey sign test1 --txpath createboard.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 2 \u003e createboard.signed.txt\n./build/gnokey broadcast createboard.signed.txt --remote %%REMOTE%%\n```\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" --data \"gno.land/r/demo/boards.GetBoardIDFromName(\\\"testboard\\\")\"\n```\n\n### create a post of a board with a smart contract call.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreatePost --args 1 --args \"Hello World\" --args#file \"./examples/gno.land/r/demo/boards/README.md\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createpost.unsigned.txt\n./build/gnokey sign test1 --txpath createpost.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 3 \u003e createpost.signed.txt\n./build/gnokey broadcast createpost.signed.txt --remote %%REMOTE%%\n```\n\n### create a comment to a post.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreateReply --args 1 --args 1 --args \"A comment\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createcomment.unsigned.txt\n./build/gnokey sign test1 --txpath createcomment.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 4 \u003e createcomment.signed.txt\n./build/gnokey broadcast createcomment.signed.txt --remote %%REMOTE%%\n```\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:testboard/1\"\n```\n\n### render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:testboard` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:testboard\"\n```\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"V43B1waFxhzheW9TfmCpjLdrC4dC1yjUGES5y3J6QsNar6hRpNz4G1thzWmWK7xXhg8u1PCIpxLxGczKQYhuPw=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","NFT example","NFT's are all the rage these days, for various reasons.\n\nI read over EIP-721 which appears to be the de-facto NFT standard on Ethereum. Then, made a sample implementation of EIP-721 (let's here called GRC-721). The implementation isn't complete, but it demonstrates the main functionality.\n\n - [EIP-721](https://eips.ethereum.org/EIPS/eip-721)\n - [gno.land/r/demo/nft/nft.gno](https://gno.land/r/demo/nft/nft.gno)\n - [zrealm_nft3.gno test](https://github.com/gnolang/gno/blob/master/examples/gno.land/r/demo/nft/z_3_filetest.gno)\n\nIn short, this demonstrates how to implement Ethereum contract interfaces in gno.land; by using only standard Go language features.\n\nPlease leave a comment ([guide](https://gno.land/r/demo/boards:testboard/1)).\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"ZXfrTiHxPFQL8uSm+Tv7WXIHPMca9okhm94RAlC6YgNbB1VHQYYpoP4w+cnL3YskVzGrOZxensXa9CAZ+cNNeg=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Simple echo example with coins","This is a simple test realm contract that demonstrates how to use the banker.\n\nSee [gno.land/r/demo/banktest/banktest.gno](/r/demo/banktest/banktest.gno) to see the original contract code.\n\nThis article will go through each line to explain how it works.\n\n```go\npackage banktest\n```\n\nThis package is locally named \"banktest\" (could be anything).\n\n```go\nimport (\n\t\"std\"\n)\n```\n\nThe \"std\" package is defined by the gno code in stdlibs/std/. \u003c/br\u003e\nSelf explanatory; and you'll see more usage from std later.\n\n```go\ntype activity struct {\n\tcaller std.Address\n\tsent std.Coins\n\treturned std.Coins\n\ttime std.Time\n}\n\nfunc (act *activity) String() string {\n\treturn act.caller.String() + \" \" +\n\t\tact.sent.String() + \" sent, \" +\n\t\tact.returned.String() + \" returned, at \" +\n\t\tstd.FormatTimestamp(act.time, \"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n```\n\nThis is just maintaining a list of recent activity to this contract.\nNotice that the \"latest\" variable is defined \"globally\" within\nthe context of the realm with path \"gno.land/r/demo/banktest\".\n\nThis means that calls to functions defined within this package\nare encapsulated within this \"data realm\", where the data is \nmutated based on transactions that can potentially cross many\nrealm and non-realm packge boundaries (in the call stack).\n\n```go\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tsend := std.Coins{{returnDenom, returnAmount}}\n```\n\nThis is the beginning of the definition of the contract function named\n\"Deposit\". `std.AssertOriginCall() asserts that this function was called by a\ngno transactional Message. The caller is the user who signed off on this\ntransactional message. Send is the amount of deposit sent along with this\nmessage.\n\n```go\n\t// record activity\n\tact := \u0026activity{\n\t\tcaller: caller,\n\t\tsent: std.GetOrigSend(),\n\t\treturned: send,\n\t\ttime: std.GetTimestamp(),\n\t}\n\tfor i := len(latest) - 2; i \u003e= 0; i-- {\n\t\tlatest[i+1] = latest[i] // shift by +1.\n\t}\n\tlatest[0] = act\n```\n\nUpdating the \"latest\" array for viewing at gno.land/r/demo/banktest: (w/ trailing colon).\n\n```go\n\t// return if any.\n\tif returnAmount \u003e 0 {\n```\n\nIf the user requested the return of coins...\n\n```go\n\t\tbanker := std.GetBanker(std.BankerTypeOrigSend)\n```\n\nuse a std.Banker instance to return any deposited coins to the original sender.\n\n```go\n\t\tpkgaddr := std.GetOrigPkgAddr()\n\t\t// TODO: use std.Coins constructors, this isn't generally safe.\n\t\tbanker.SendCoins(pkgaddr, caller, send)\n\t\treturn \"returned!\"\n```\n\nNotice that each realm package has an associated Cosmos address.\n\n\nFinally, the results are rendered via an ABCI query call when you visit [/r/demo/banktest:](/r/demo/banktest:).\n\n```go\nfunc Render(path string) string {\n\t// get realm coins.\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(std.GetOrigPkgAddr())\n\n\t// render\n\tres := \"\"\n\tres += \"## recent activity\\n\"\n\tres += \"\\n\"\n\tfor _, act := range latest {\n\t\tif act == nil {\n\t\t\tbreak\n\t\t}\n\t\tres += \" * \" + act.String() + \"\\n\"\n\t}\n\tres += \"\\n\"\n\tres += \"## total deposits\\n\"\n\tres += coins.String()\n\treturn res\n}\n```\n\nYou can call this contract yourself, by vistiing [/r/demo/banktest](/r/demo/banktest) and the [quickstart guide](/r/demo/boards:testboard/4).\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"iZX/llZlNTdZMLv1goCTgK2bWqzT8enlTq56wMTCpVxJGA0BTvuEM5Nnt9vrnlG6Taqj2GuTrmEnJBkDFTmt9g=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","TASK: Describe in your words","Describe in an essay (250+ words), on your favorite medium, why you are interested in gno.land and gnolang.\n\nReply here with a URL link to your written piece as a comment, for rewards.\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"4HBNtrta8HdeHj4JTN56PBTRK8GOe31NMRRXDiyYtjozuyRdWfOGEsGjGgHWcoBUJq6DepBgD4FetdqfhZ6TNQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Getting Started","This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm\nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n\n\n## Build `gnokey`, create your account, and interact with Gno.\n\nNOTE: Where you see `--remote %%REMOTE%%` here, that flag can be replaced\nwith `--remote localhost:26657` for local testnets.\n\n### Build `gnokey`.\n\n```bash\ngit clone git@github.com:gnolang/gno.git\ncd ./gno\nmake\n```\n\n### Generate a seed/mnemonic code.\n\n```bash\n./build/gnokey generate\n```\n\nNOTE: You can generate 24 words with any good bip39 generator.\n\n### Create a new account using your mnemonic.\n\n```bash\n./build/gnokey add KEYNAME --recover\n```\n\nNOTE: `KEYNAME` is your key identifier, and should be changed.\n\n### Verify that you can see your account locally.\n\n```bash\n./build/gnokey list\n```\n\n## Interact with the blockchain:\n\n### Get your current balance, account number, and sequence number.\n\n```bash\n./build/gnokey query auth/accounts/ACCOUNT_ADDR --remote %%REMOTE%%\n```\n\nNOTE: you can retrieve your `ACCOUNT_ADDR` with `./build/gnokey list`.\n\n### Acquire testnet tokens using the official faucet.\n\nGo to https://gno.land/faucet\n\n### Create a board with a smart contract call.\n\nNOTE: `BOARDNAME` will be the slug of the board, and should be changed.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateBoard\" --args \"BOARDNAME\" --gas-fee \"1000000ugnot\" --gas-wanted \"2000000\" --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateBoard\n\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" --data \"gno.land/r/demo/boards.GetBoardIDFromName(\\\"BOARDNAME\\\")\" --remote %%REMOTE%%\n```\n\n### Create a post of a board with a smart contract call.\n\nNOTE: If a board was created successfully, your SEQUENCE_NUMBER would have increased.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateThread\" --args BOARD_ID --args \"Hello gno.land\" --args\\#file \"./examples/gno.land/r/demo/boards/example_post.md\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateThread\n\n### Create a comment to a post.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateReply\" --args \"BOARD_ID\" --args \"1\" --args \"1\" --args \"Nice to meet you too.\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateReply\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:BOARDNAME/1\" --remote %%REMOTE%%\n```\n\n### Render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:gnolang` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:gnolang\"\n```\n\n## Starting a local `gnoland` node:\n\n### Add test account.\n\n```bash\n./build/gnokey add test1 --recover\n```\n\nUse this mneonic:\n\u003e source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast\n\n### Start `gnoland` node.\n\n```bash\n./build/gnoland\n```\n\nNOTE: This can be reset with `make reset`\n\n### Publish the \"gno.land/p/demo/avl\" package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/p/demo/avl\" --pkgdir \"examples/gno.land/p/demo/avl\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote localhost:26657\n```\n\n### Publish the \"gno.land/r/demo/boards\" realm package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/r/demo/boards\" --pkgdir \"examples/gno.land/r/demo/boards\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 300000000 --broadcast=true --chainid %%CHAINID%% --remote localhost:26657\n```\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post1","First post","Lorem Ipsum","2022-05-20T13:17:22Z","","tag1,tag2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""} -{"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post2","Second post","Lorem Ipsum","2022-05-20T13:17:23Z","","tag1,tag3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj:10\ng1589c8cekvmjfmy0qrd4f3z52r7fn7rgk02667s:1\ng13sm84nuqed3fuank8huh7x9mupgw22uft3lcl8:1\ng1m6732pkrngu9vrt0g7056lvr9kcqc4mv83xl5q:1\ng1wg88rhzlwxjd2z4j5de5v5xq30dcf6rjq3dhsj:1\ng18pmaskasz7mxj6rmgrl3al58xu45a7w0l5nmc0:1\ng19wwhkmqlns70604ksp6rkuuu42qhtvyh05lffz:1\ng187982000zsc493znqt828s90cmp6hcp2erhu6m:1\ng1ndpsnrspdnauckytvkfv8s823t3gmpqmtky8pl:1\ng16ja66d65emkr0zxd2tu7xjvm7utthyhpej0037:1\ng1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5:1\ng1trkzq75ntamsnw9xnrav2v7gy2lt5g6p29yhdr:1\ng1rrf8s5mrmu00sx04fzfsvc399fklpeg2x0a7mz:1\ng19p5ntfvpt4lwq4jqsmnxsnelhf3tff9scy3w8w:1\ng1tue8l73d6rq4vhqdsp2sr3zhuzpure3k2rnwpz:1\ng14hhsss4ngx5kq77je5g0tl4vftg8qp45ceadk3:1\ng1768hvkh7anhd40ch4h7jdh6j3mpcs7hrat4gl0:1\ng15fa8kyjhu88t9dr8zzua8fwdvkngv5n8yqsm0n:1\ng1xhccdjcscuhgmt3quww6qdy3j3czqt3urc2eac:1\ng1z629z04f85k4t5gnkk5egpxw9tqxeec435esap:1\ng1pfldkplz9puq0v82lu9vqcve9nwrxuq9qe5ttv:1\ng152pn0g5qfgxr7yx8zlwjq48hytkafd8x7egsfv:1\ng1cf2ye686ke38vjyqakreprljum4xu6rwf5jskq:1\ng1c5shztyaj4gjrc5zlwmh9xhex5w7l4asffs2w6:1\ng1lhpx2ktk0ha3qw42raxq4m24a4c4xqxyrgv54q:1\ng1026p54q0j902059sm2zsv37krf0ghcl7gmhyv7:1\ng1n4yvwnv77frq2ccuw27dmtjkd7u4p4jg0pgm7k:1\ng13m7f2e6r3lh3ykxupacdt9sem2tlvmaamwjhll:1\ng19uxluuecjlsqvwmwu8sp6pxaaqfhk972q975xd:1\ng1j80fpcsumfkxypvydvtwtz3j4sdwr8c2u0lr64:1\ng1tjdpptuk9eysq6z38nscqyycr998xjyx3w8jvw:1\ng19t3n89slfemgd3mwuat4lajwcp0yxrkadgeg7a:1\ng1yqndt8xx92l9h494jfruz2w79swzjes3n4wqjc:1\ng13278z0a5ufeg80ffqxpda9dlp599t7ekregcy6:1\ng1ht236wjd83x96uqwh9rh3fq6pylyn78mtwq9v6:1\ng1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9:1\ng1wwppuzdns5u6c6jqpkzua24zh6ppsus6399cea:1\ng1k8pjnguyu36pkc8hy0ufzgpzfmj2jl78la7ek3:1\ng1e8umkzumtxgs8399lw0us4rclea3xl5gxy9spp:1\ng14qekdkj2nmmwea4ufg9n002a3pud23y8k7ugs5:1\ng19w2488ntfgpduzqq3sk4j5x387zynwknqdvjqf:1\ng1495y3z7zrej4rendysnw5kaeu4g3d7x7w0734g:1\ng1hygx8ga9qakhkczyrzs9drm8j8tu4qds9y5e3r:1\ng1f977l6wxdh3qu60kzl75vx2wmzswu68l03r8su:1\ng1644qje5rx6jsdqfkzmgnfcegx4dxkjh6rwqd69:1\ng1mzjajymvmtksdwh3wkrndwj6zls2awl9q83dh6:1\ng14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa:10\ng14vhcdsyf83ngsrrqc92kmw8q9xakqjm0v8448t:5\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"S8iMMzlOMK8dmox78R9Z8+pSsS8YaTCXrIcaHDpiOgkOy7gqoQJ0oftM0zf8zAz4xpezK8Lzg8Q0fCdXJxV76w=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1thlf3yct7n7ex70k0p62user0kn6mj6d3s0cg3\ng1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"njczE6xYdp01+CaUU/8/v0YC/NuZD06+qLind+ZZEEMNaRe/4Ln+4z7dG6HYlaWUMsyI1KCoB6NIehoE0PZ44Q=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/users","func":"Invite","args":["g1589c8cekvmjfmy0qrd4f3z52r7fn7rgk02667s\ng13sm84nuqed3fuank8huh7x9mupgw22uft3lcl8\ng1m6732pkrngu9vrt0g7056lvr9kcqc4mv83xl5q\ng1wg88rhzlwxjd2z4j5de5v5xq30dcf6rjq3dhsj\ng18pmaskasz7mxj6rmgrl3al58xu45a7w0l5nmc0\ng19wwhkmqlns70604ksp6rkuuu42qhtvyh05lffz\ng187982000zsc493znqt828s90cmp6hcp2erhu6m\ng1ndpsnrspdnauckytvkfv8s823t3gmpqmtky8pl\ng16ja66d65emkr0zxd2tu7xjvm7utthyhpej0037\ng1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5\ng1trkzq75ntamsnw9xnrav2v7gy2lt5g6p29yhdr\ng1rrf8s5mrmu00sx04fzfsvc399fklpeg2x0a7mz\ng19p5ntfvpt4lwq4jqsmnxsnelhf3tff9scy3w8w\ng1tue8l73d6rq4vhqdsp2sr3zhuzpure3k2rnwpz\ng14hhsss4ngx5kq77je5g0tl4vftg8qp45ceadk3\ng1768hvkh7anhd40ch4h7jdh6j3mpcs7hrat4gl0\ng15fa8kyjhu88t9dr8zzua8fwdvkngv5n8yqsm0n\ng1xhccdjcscuhgmt3quww6qdy3j3czqt3urc2eac\ng1z629z04f85k4t5gnkk5egpxw9tqxeec435esap\ng1pfldkplz9puq0v82lu9vqcve9nwrxuq9qe5ttv\ng152pn0g5qfgxr7yx8zlwjq48hytkafd8x7egsfv\ng1cf2ye686ke38vjyqakreprljum4xu6rwf5jskq\ng1c5shztyaj4gjrc5zlwmh9xhex5w7l4asffs2w6\ng1lhpx2ktk0ha3qw42raxq4m24a4c4xqxyrgv54q\ng1026p54q0j902059sm2zsv37krf0ghcl7gmhyv7\ng1n4yvwnv77frq2ccuw27dmtjkd7u4p4jg0pgm7k\ng13m7f2e6r3lh3ykxupacdt9sem2tlvmaamwjhll\ng19uxluuecjlsqvwmwu8sp6pxaaqfhk972q975xd\ng1j80fpcsumfkxypvydvtwtz3j4sdwr8c2u0lr64\ng1tjdpptuk9eysq6z38nscqyycr998xjyx3w8jvw\ng19t3n89slfemgd3mwuat4lajwcp0yxrkadgeg7a\ng1yqndt8xx92l9h494jfruz2w79swzjes3n4wqjc\ng13278z0a5ufeg80ffqxpda9dlp599t7ekregcy6\ng1ht236wjd83x96uqwh9rh3fq6pylyn78mtwq9v6\ng1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9\ng1wwppuzdns5u6c6jqpkzua24zh6ppsus6399cea\ng1k8pjnguyu36pkc8hy0ufzgpzfmj2jl78la7ek3\ng1e8umkzumtxgs8399lw0us4rclea3xl5gxy9spp\ng14qekdkj2nmmwea4ufg9n002a3pud23y8k7ugs5\ng19w2488ntfgpduzqq3sk4j5x387zynwknqdvjqf\ng1495y3z7zrej4rendysnw5kaeu4g3d7x7w0734g\ng1hygx8ga9qakhkczyrzs9drm8j8tu4qds9y5e3r\ng1f977l6wxdh3qu60kzl75vx2wmzswu68l03r8su\ng1644qje5rx6jsdqfkzmgnfcegx4dxkjh6rwqd69\ng1mzjajymvmtksdwh3wkrndwj6zls2awl9q83dh6\ng1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq\ng14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa\ng14vhcdsyf83ngsrrqc92kmw8q9xakqjm0v8448t\n"]}],"fee":{"gas_wanted":"4000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"7AmlhZhsVkxCUl0bbpvpPMnIKihwtG7A5IFR6Tg4xStWLgaUr05XmWRKlO2xjstTtwbVKQT5mFL4h5wyX4SQzw=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","administrator","g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"AqCqe0cS55Ym7/BvPDoCDyPP5q8284gecVQ2PMOlq/4lJpO9Q18SOWKI15dMEBY1pT0AYyhCeTirlsM1I3Y4Cg=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","zo_oma","Love is the encryption key\u003c3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A6yg5/iiktruezVw5vZJwLlGwyrvw8RlqOToTRMWXkE2"},"signature":"GGp+bVL2eEvKecPqgcULSABYOSnSMnJzfIsR8ZIRER1GGX/fOiCReX4WKMrGLVROJVfbLQkDRwvhS4TLHlSoSQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","manfred","https://github.com/moul"]}],"fee":{"gas_wanted":"2000000","gas_fee":"200000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"9CWeNbKx+hEL+RdHplAVAFntcrAVx5mK9tMqoywuHVoreH844n3yOxddQrGfBk6T2tMBmNWakERRqWZfS+bYAQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1fj9jccm3zjnqspq7lp2g7lj4czyfq0s35600g9","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","piupiu","@piux2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"Ar68lqbU2YC63fbMcYUtJhYO3/66APM/EqF7m0nUjGyz"},"signature":"pTUpP0d/XlfVe3TH1hlaoLhKadzIKG1gtQ/Ueuat72p+659RWRea58Z0mk6GgPE/EeTbhMEY45zufevBdGJVoQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1ds24jj9kqjcskd0gzu24r9e4n62ggye230zuv5","send":"","pkg_path":"gno.land/r/demo/users","func":"Register","args":["g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","anarcher","https://twitter.com/anarcher"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AjpLbKdQeH+yB/1OCB148l5GlRRrXma71hdA8EES3H7f"},"signature":"pf5xm8oWIQIOEwSGw4icPmynLXb1P1HxKfjeh8UStU1mlIBPKa7yppeIMPpAflC0o2zjFR7Axe7CimAebm3BHg=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g15gdm49ktawvkrl88jadqpucng37yxutucuwaef","send":"200000000ugnot","pkg_path":"gno.land/r/demo/users","func":"Register","args":["","ideamour","\u003c3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AhClx4AsDuX3DNCPxhDwWnrfd4MIZmxJE4vt47ClVvT2"},"signature":"IQe64af878k6HjLDqIJeg27GXAVF6xS+96cDe2jMlxNV6+8sOcuUctp0GiWVnYfN4tpthC6d4WhBo+VlpHqkbg=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateBoard","args":["testboard"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"vzlSxEFh5jOkaSdv3rsV91v/OJKEF2qSuoCpri1u5tRWq62T7xr3KHRCF5qFnn4aQX/yE8g8f/Y//WPOCUGhJw=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Hello World","This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm \nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n## Starting the `gnoland` node node/validator.\n\nNOTE: Where you see `--remote %%REMOTE%%` here, that flag can be replaced\nwith `--remote localhost:26657` for local testnets.\n\n### build gnoland.\n\n```bash\ngit clone git@github.com:gnolang/gno.git\ncd ./gno\nmake \n```\n\n### add test account.\n\n```bash\n./build/gnokey add test1 --recover\n```\n\nUse this mnemonic:\n\u003e source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast\n\n### start gnoland validator node.\n\n```bash\n./build/gnoland\n```\n\n(This can be reset with `make reset`).\n\n### start gnoland web server (optional).\n\n```bash\ngo run ./gnoland/website\n```\n\n## Signing and broadcasting transactions.\n\n### publish the \"gno.land/p/demo/avl\" package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/p/demo/avl\" --pkgdir \"examples/gno.land/p/demo/avl\" --deposit 100ugnot --gas-fee 1ugnot --gas-wanted 2000000 \u003e addpkg.avl.unsigned.txt\n./build/gnokey query \"auth/accounts/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\"\n./build/gnokey sign test1 --txpath addpkg.avl.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 0 \u003e addpkg.avl.signed.txt\n./build/gnokey broadcast addpkg.avl.signed.txt --remote %%REMOTE%%\n```\n\n### publish the \"gno.land/r/demo/boards\" realm package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/r/demo/boards\" --pkgdir \"examples/gno.land/r/demo/boards\" --deposit 100ugnot --gas-fee 1ugnot --gas-wanted 300000000 \u003e addpkg.boards.unsigned.txt\n./build/gnokey sign test1 --txpath addpkg.boards.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 1 \u003e addpkg.boards.signed.txt\n./build/gnokey broadcast addpkg.boards.signed.txt --remote %%REMOTE%%\n```\n\n### create a board with a smart contract call.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreateBoard --args \"testboard\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createboard.unsigned.txt\n./build/gnokey sign test1 --txpath createboard.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 2 \u003e createboard.signed.txt\n./build/gnokey broadcast createboard.signed.txt --remote %%REMOTE%%\n```\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" --data \"gno.land/r/demo/boards.GetBoardIDFromName(\\\"testboard\\\")\"\n```\n\n### create a post of a board with a smart contract call.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreatePost --args 1 --args \"Hello World\" --args#file \"./examples/gno.land/r/demo/boards/README.md\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createpost.unsigned.txt\n./build/gnokey sign test1 --txpath createpost.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 3 \u003e createpost.signed.txt\n./build/gnokey broadcast createpost.signed.txt --remote %%REMOTE%%\n```\n\n### create a comment to a post.\n\n```bash\n./build/gnokey maketx call test1 --pkgpath \"gno.land/r/demo/boards\" --func CreateReply --args 1 --args 1 --args \"A comment\" --gas-fee 1ugnot --gas-wanted 2000000 \u003e createcomment.unsigned.txt\n./build/gnokey sign test1 --txpath createcomment.unsigned.txt --chainid \"%%CHAINID%%\" --number 0 --sequence 4 \u003e createcomment.signed.txt\n./build/gnokey broadcast createcomment.signed.txt --remote %%REMOTE%%\n```\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:testboard/1\"\n```\n\n### render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:testboard` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:testboard\"\n```\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"V43B1waFxhzheW9TfmCpjLdrC4dC1yjUGES5y3J6QsNar6hRpNz4G1thzWmWK7xXhg8u1PCIpxLxGczKQYhuPw=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","NFT example","NFT's are all the rage these days, for various reasons.\n\nI read over EIP-721 which appears to be the de-facto NFT standard on Ethereum. Then, made a sample implementation of EIP-721 (let's here called GRC-721). The implementation isn't complete, but it demonstrates the main functionality.\n\n - [EIP-721](https://eips.ethereum.org/EIPS/eip-721)\n - [gno.land/r/demo/nft/nft.gno](https://gno.land/r/demo/nft/nft.gno)\n - [zrealm_nft3.gno test](https://github.com/gnolang/gno/blob/master/examples/gno.land/r/demo/nft/z_3_filetest.gno)\n\nIn short, this demonstrates how to implement Ethereum contract interfaces in gno.land; by using only standard Go language features.\n\nPlease leave a comment ([guide](https://gno.land/r/demo/boards:testboard/1)).\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"ZXfrTiHxPFQL8uSm+Tv7WXIHPMca9okhm94RAlC6YgNbB1VHQYYpoP4w+cnL3YskVzGrOZxensXa9CAZ+cNNeg=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Simple echo example with coins","This is a simple test realm contract that demonstrates how to use the banker.\n\nSee [gno.land/r/demo/banktest/banktest.gno](/r/demo/banktest/banktest.gno) to see the original contract code.\n\nThis article will go through each line to explain how it works.\n\n```go\npackage banktest\n```\n\nThis package is locally named \"banktest\" (could be anything).\n\n```go\nimport (\n\t\"std\"\n)\n```\n\nThe \"std\" package is defined by the gno code in stdlibs/std/. \u003c/br\u003e\nSelf explanatory; and you'll see more usage from std later.\n\n```go\ntype activity struct {\n\tcaller std.Address\n\tsent std.Coins\n\treturned std.Coins\n\ttime std.Time\n}\n\nfunc (act *activity) String() string {\n\treturn act.caller.String() + \" \" +\n\t\tact.sent.String() + \" sent, \" +\n\t\tact.returned.String() + \" returned, at \" +\n\t\tstd.FormatTimestamp(act.time, \"2006-01-02 3:04pm MST\")\n}\n\nvar latest [10]*activity\n```\n\nThis is just maintaining a list of recent activity to this contract.\nNotice that the \"latest\" variable is defined \"globally\" within\nthe context of the realm with path \"gno.land/r/demo/banktest\".\n\nThis means that calls to functions defined within this package\nare encapsulated within this \"data realm\", where the data is \nmutated based on transactions that can potentially cross many\nrealm and non-realm packge boundaries (in the call stack).\n\n```go\n// Deposit will take the coins (to the realm's pkgaddr) or return them to user.\nfunc Deposit(returnDenom string, returnAmount int64) string {\n\tstd.AssertOriginCall()\n\tcaller := std.GetOrigCaller()\n\tsend := std.Coins{{returnDenom, returnAmount}}\n```\n\nThis is the beginning of the definition of the contract function named\n\"Deposit\". `std.AssertOriginCall() asserts that this function was called by a\ngno transactional Message. The caller is the user who signed off on this\ntransactional message. Send is the amount of deposit sent along with this\nmessage.\n\n```go\n\t// record activity\n\tact := \u0026activity{\n\t\tcaller: caller,\n\t\tsent: std.GetOrigSend(),\n\t\treturned: send,\n\t\ttime: std.GetTimestamp(),\n\t}\n\tfor i := len(latest) - 2; i \u003e= 0; i-- {\n\t\tlatest[i+1] = latest[i] // shift by +1.\n\t}\n\tlatest[0] = act\n```\n\nUpdating the \"latest\" array for viewing at gno.land/r/demo/banktest: (w/ trailing colon).\n\n```go\n\t// return if any.\n\tif returnAmount \u003e 0 {\n```\n\nIf the user requested the return of coins...\n\n```go\n\t\tbanker := std.GetBanker(std.BankerTypeOrigSend)\n```\n\nuse a std.Banker instance to return any deposited coins to the original sender.\n\n```go\n\t\tpkgaddr := std.GetOrigPkgAddr()\n\t\t// TODO: use std.Coins constructors, this isn't generally safe.\n\t\tbanker.SendCoins(pkgaddr, caller, send)\n\t\treturn \"returned!\"\n```\n\nNotice that each realm package has an associated Cosmos address.\n\n\nFinally, the results are rendered via an ABCI query call when you visit [/r/demo/banktest:](/r/demo/banktest:).\n\n```go\nfunc Render(path string) string {\n\t// get realm coins.\n\tbanker := std.GetBanker(std.BankerTypeReadonly)\n\tcoins := banker.GetCoins(std.GetOrigPkgAddr())\n\n\t// render\n\tres := \"\"\n\tres += \"## recent activity\\n\"\n\tres += \"\\n\"\n\tfor _, act := range latest {\n\t\tif act == nil {\n\t\t\tbreak\n\t\t}\n\t\tres += \" * \" + act.String() + \"\\n\"\n\t}\n\tres += \"\\n\"\n\tres += \"## total deposits\\n\"\n\tres += coins.String()\n\treturn res\n}\n```\n\nYou can call this contract yourself, by vistiing [/r/demo/banktest](/r/demo/banktest) and the [quickstart guide](/r/demo/boards:testboard/4).\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A+FhNtsXHjLfSJk1lB8FbiL4mGPjc50Kt81J7EKDnJ2y"},"signature":"iZX/llZlNTdZMLv1goCTgK2bWqzT8enlTq56wMTCpVxJGA0BTvuEM5Nnt9vrnlG6Taqj2GuTrmEnJBkDFTmt9g=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","TASK: Describe in your words","Describe in an essay (250+ words), on your favorite medium, why you are interested in gno.land and gnolang.\n\nReply here with a URL link to your written piece as a comment, for rewards.\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AmG6kzznyo1uNqWPAYU6wDpsmzQKDaEOrVRaZ08vOyX0"},"signature":"4HBNtrta8HdeHj4JTN56PBTRK8GOe31NMRRXDiyYtjozuyRdWfOGEsGjGgHWcoBUJq6DepBgD4FetdqfhZ6TNQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/demo/boards","func":"CreateThread","args":["1","Getting Started","This is a demo of Gno smart contract programming. This document was\nconstructed by Gno onto a smart contract hosted on the data Realm\nname [\"gno.land/r/demo/boards\"](https://gno.land/r/demo/boards/)\n([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)).\n\n\n\n## Build `gnokey`, create your account, and interact with Gno.\n\nNOTE: Where you see `--remote %%REMOTE%%` here, that flag can be replaced\nwith `--remote localhost:26657` for local testnets.\n\n### Build `gnokey`.\n\n```bash\ngit clone git@github.com:gnolang/gno.git\ncd ./gno\nmake\n```\n\n### Generate a seed/mnemonic code.\n\n```bash\n./build/gnokey generate\n```\n\nNOTE: You can generate 24 words with any good bip39 generator.\n\n### Create a new account using your mnemonic.\n\n```bash\n./build/gnokey add KEYNAME --recover\n```\n\nNOTE: `KEYNAME` is your key identifier, and should be changed.\n\n### Verify that you can see your account locally.\n\n```bash\n./build/gnokey list\n```\n\n## Interact with the blockchain:\n\n### Get your current balance, account number, and sequence number.\n\n```bash\n./build/gnokey query auth/accounts/ACCOUNT_ADDR --remote %%REMOTE%%\n```\n\nNOTE: you can retrieve your `ACCOUNT_ADDR` with `./build/gnokey list`.\n\n### Acquire testnet tokens using the official faucet.\n\nGo to https://gno.land/faucet\n\n### Create a board with a smart contract call.\n\nNOTE: `BOARDNAME` will be the slug of the board, and should be changed.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateBoard\" --args \"BOARDNAME\" --gas-fee \"1000000ugnot\" --gas-wanted \"2000000\" --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateBoard\n\nNext, query for the permanent board ID by querying (you need this to create a new post):\n\n```bash\n./build/gnokey query \"vm/qeval\" --data \"gno.land/r/demo/boards.GetBoardIDFromName(\\\"BOARDNAME\\\")\" --remote %%REMOTE%%\n```\n\n### Create a post of a board with a smart contract call.\n\nNOTE: If a board was created successfully, your SEQUENCE_NUMBER would have increased.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateThread\" --args BOARD_ID --args \"Hello gno.land\" --args\\#file \"./examples/gno.land/r/demo/boards/example_post.md\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateThread\n\n### Create a comment to a post.\n\n```bash\n./build/gnokey maketx call KEYNAME --pkgpath \"gno.land/r/demo/boards\" --func \"CreateReply\" --args \"BOARD_ID\" --args \"1\" --args \"1\" --args \"Nice to meet you too.\" --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote %%REMOTE%%\n```\n\nInteractive documentation: https://gno.land/r/demo/boards?help\u0026__func=CreateReply\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:BOARDNAME/1\" --remote %%REMOTE%%\n```\n\n### Render page with optional path expression.\n\nThe contents of `https://gno.land/r/demo/boards:` and `https://gno.land/r/demo/boards:gnolang` are rendered by calling\nthe `Render(path string)` function like so:\n\n```bash\n./build/gnokey query \"vm/qrender\" --data \"gno.land/r/demo/boards:gnolang\"\n```\n\n## Starting a local `gnoland` node:\n\n### Add test account.\n\n```bash\n./build/gnokey add test1 --recover\n```\n\nUse this mneonic:\n\u003e source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast\n\n### Start `gnoland` node.\n\n```bash\n./build/gnoland\n```\n\nNOTE: This can be reset with `make reset`\n\n### Publish the \"gno.land/p/demo/avl\" package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/p/demo/avl\" --pkgdir \"examples/gno.land/p/demo/avl\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 2000000 --broadcast=true --chainid %%CHAINID%% --remote localhost:26657\n```\n\n### Publish the \"gno.land/r/demo/boards\" realm package.\n\n```bash\n./build/gnokey maketx addpkg test1 --pkgpath \"gno.land/r/demo/boards\" --pkgdir \"examples/gno.land/r/demo/boards\" --deposit 100000000ugnot --gas-fee 1000000ugnot --gas-wanted 300000000 --broadcast=true --chainid %%CHAINID%% --remote localhost:26657\n```\n"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post1","First post","Lorem Ipsum","2022-05-20T13:17:22Z","","tag1,tag2"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}} +{"tx": {"msg":[{"@type":"/vm.m_call","caller":"g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["post2","Second post","Lorem Ipsum","2022-05-20T13:17:23Z","","tag1,tag3"]}],"fee":{"gas_wanted":"2000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AnK+a6mcFDjY6b/v6p7r8QFW1M1PgIoQxBgrwOoyY7v3"},"signature":"sHjOGXZEi9wt2FSXFHmkDDoVQyepvFHKRDDU0zgedHYnCYPx5/YndyihsDD5Y2Z7/RgNYBh4JlJwDMGFNStzBQ=="}],"memo":""}} diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index e784f2148aa..ff1b5a88fea 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -12,6 +12,7 @@ import ( "github.com/gnolang/gno/gnovm/pkg/gnoenv" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/config" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/events" @@ -301,9 +302,31 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci } txResponses := make([]abci.ResponseDeliverTx, 0, len(state.Txs)) + // Run genesis txs for _, tx := range state.Txs { - res := cfg.baseApp.Deliver(tx) + var ( + stdTx = tx.Tx + metadata = tx.Metadata + + ctxFn sdk.ContextFn + ) + + // Check if there is metadata associated with the tx + if metadata != nil { + // Create a custom context modifier + ctxFn = func(ctx sdk.Context) sdk.Context { + // Create a copy of the header, in + // which only the timestamp information is modified + header := ctx.BlockHeader().(*bft.Header).Copy() + header.Time = time.Unix(metadata.Timestamp, 0) + + // Save the modified header + return ctx.WithBlockHeader(header) + } + } + + res := cfg.baseApp.Deliver(stdTx, ctxFn) if res.IsErr() { ctx.Logger().Error( "Unable to deliver genesis tx", @@ -319,7 +342,7 @@ func (cfg InitChainerConfig) loadAppState(ctx sdk.Context, appState any) ([]abci GasUsed: res.GasUsed, }) - cfg.GenesisTxResultHandler(ctx, tx, res) + cfg.GenesisTxResultHandler(ctx, stdTx, res) } return txResponses, nil } diff --git a/gno.land/pkg/gnoland/app_test.go b/gno.land/pkg/gnoland/app_test.go index 87b624280c6..5e4dcb2b449 100644 --- a/gno.land/pkg/gnoland/app_test.go +++ b/gno.land/pkg/gnoland/app_test.go @@ -52,16 +52,18 @@ func TestNewAppWithOptions(t *testing.T) { Amount: []std.Coin{{Amount: 1e15, Denom: "ugnot"}}, }, }, - Txs: []std.Tx{ + Txs: []TxWithMetadata{ { - Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*gnovm.MemFile{ - { - Name: "demo.gno", - Body: "package demo; func Hello() string { return `hello`; }", - }, - })}, - Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}}, - Signatures: []std.Signature{{}}, // one empty signature + Tx: std.Tx{ + Msgs: []std.Msg{vm.NewMsgAddPackage(addr, "gno.land/r/demo", []*gnovm.MemFile{ + { + Name: "demo.gno", + Body: "package demo; func Hello() string { return `hello`; }", + }, + })}, + Fee: std.Fee{GasWanted: 1e6, GasFee: std.Coin{Amount: 1e6, Denom: "ugnot"}}, + Signatures: []std.Signature{{}}, // one empty signature + }, }, }, }, @@ -206,7 +208,7 @@ func generateValidatorUpdates(t *testing.T, count int) []abci.ValidatorUpdate { for i := 0; i < count; i++ { // Generate a random private key - key := getDummyKey(t) + key := getDummyKey(t).PubKey() validator := abci.ValidatorUpdate{ Address: key.Address(), @@ -220,6 +222,189 @@ func generateValidatorUpdates(t *testing.T, count int) []abci.ValidatorUpdate { return validators } +func createAndSignTx( + t *testing.T, + msgs []std.Msg, + chainID string, + key crypto.PrivKey, +) std.Tx { + t.Helper() + + tx := std.Tx{ + Msgs: msgs, + Fee: std.Fee{ + GasFee: std.NewCoin("ugnot", 2000000), + GasWanted: 10000000, + }, + } + + signBytes, err := tx.GetSignBytes(chainID, 0, 0) + require.NoError(t, err) + + // Sign the tx + signedTx, err := key.Sign(signBytes) + require.NoError(t, err) + + tx.Signatures = []std.Signature{ + { + PubKey: key.PubKey(), + Signature: signedTx, + }, + } + + return tx +} + +func TestInitChainer_MetadataTxs(t *testing.T) { + var ( + currentTimestamp = time.Now() + laterTimestamp = currentTimestamp.Add(10 * 24 * time.Hour) // 10 days + + getMetadataState = func(tx std.Tx, balances []Balance) GnoGenesisState { + return GnoGenesisState{ + // Set the package deployment as the genesis tx + Txs: []TxWithMetadata{ + { + Tx: tx, + Metadata: &GnoTxMetadata{ + Timestamp: laterTimestamp.Unix(), + }, + }, + }, + // Make sure the deployer account has a balance + Balances: balances, + } + } + + getNonMetadataState = func(tx std.Tx, balances []Balance) GnoGenesisState { + return GnoGenesisState{ + Txs: []TxWithMetadata{ + { + Tx: tx, + }, + }, + Balances: balances, + } + } + ) + + testTable := []struct { + name string + genesisTime time.Time + expectedTime time.Time + stateFn func(std.Tx, []Balance) GnoGenesisState + }{ + { + "non-metadata transaction", + currentTimestamp, + currentTimestamp, + getNonMetadataState, + }, + { + "metadata transaction", + currentTimestamp, + laterTimestamp, + getMetadataState, + }, + } + + for _, testCase := range testTable { + t.Run(testCase.name, func(t *testing.T) { + var ( + db = memdb.NewMemDB() + + key = getDummyKey(t) // user account, and genesis deployer + chainID = "test" + + path = "gno.land/r/demo/metadatatx" + body = `package metadatatx + + import "time" + + // Time is initialized on deployment (genesis) + var t time.Time = time.Now() + + // GetT returns the time that was saved from genesis + func GetT() int64 { return t.Unix() } +` + ) + + // Create a fresh app instance + app, err := NewAppWithOptions(TestAppOptions(db)) + require.NoError(t, err) + + // Prepare the deploy transaction + msg := vm.MsgAddPackage{ + Creator: key.PubKey().Address(), + Package: &gnovm.MemPackage{ + Name: "metadatatx", + Path: path, + Files: []*gnovm.MemFile{ + { + Name: "file.gno", + Body: body, + }, + }, + }, + Deposit: nil, + } + + // Create the initial genesis tx + tx := createAndSignTx(t, []std.Msg{msg}, chainID, key) + + // Run the top-level init chain process + app.InitChain(abci.RequestInitChain{ + ChainID: chainID, + Time: testCase.genesisTime, + ConsensusParams: &abci.ConsensusParams{ + Block: defaultBlockParams(), + Validator: &abci.ValidatorParams{ + PubKeyTypeURLs: []string{}, + }, + }, + // Set the package deployment as the genesis tx, + // and make sure the deployer account has a balance + AppState: testCase.stateFn(tx, []Balance{ + { + // Make sure the deployer account has a balance + Address: key.PubKey().Address(), + Amount: std.NewCoins(std.NewCoin("ugnot", 20_000_000)), + }, + }), + }) + + // Prepare the call transaction + callMsg := vm.MsgCall{ + Caller: key.PubKey().Address(), + PkgPath: path, + Func: "GetT", + } + + tx = createAndSignTx(t, []std.Msg{callMsg}, chainID, key) + + // Marshal the transaction to Amino binary + marshalledTx, err := amino.Marshal(tx) + require.NoError(t, err) + + // Execute the call to the "GetT" method + // on the deployed Realm + resp := app.DeliverTx(abci.RequestDeliverTx{ + Tx: marshalledTx, + }) + + require.True(t, resp.IsOK()) + + // Make sure the initialized Realm state is + // the injected context timestamp from the tx metadata + assert.Contains( + t, + string(resp.Data), + fmt.Sprintf("(%d int64)", testCase.expectedTime.Unix()), + ) + }) + } +} + func TestEndBlocker(t *testing.T) { t.Parallel() diff --git a/gno.land/pkg/gnoland/balance_test.go b/gno.land/pkg/gnoland/balance_test.go index 99a348e9f2f..489384196ad 100644 --- a/gno.land/pkg/gnoland/balance_test.go +++ b/gno.land/pkg/gnoland/balance_test.go @@ -120,7 +120,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { for index, key := range dummyKeys { entries[index] = fmt.Sprintf( "%s=%s", - key.Address().String(), + key.PubKey().Address().String(), ugnot.ValueString(amount.AmountOf(ugnot.Denom)), ) } @@ -131,7 +131,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()].Amount) + assert.Equal(t, amount, balanceMap[key.PubKey().Address()].Amount) } }) @@ -162,7 +162,7 @@ func TestBalances_GetBalancesFromEntries(t *testing.T) { t.Run("malformed balance, invalid amount", func(t *testing.T) { t.Parallel() - dummyKey := getDummyKey(t) + dummyKey := getDummyKey(t).PubKey() balances := []string{ fmt.Sprintf( @@ -194,7 +194,7 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { for index, key := range dummyKeys { balances[index] = fmt.Sprintf( "%s=%s", - key.Address().String(), + key.PubKey().Address().String(), ugnot.ValueString(amount.AmountOf(ugnot.Denom)), ) } @@ -206,14 +206,14 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { // Validate the balance map assert.Len(t, balanceMap, len(dummyKeys)) for _, key := range dummyKeys { - assert.Equal(t, amount, balanceMap[key.Address()].Amount) + assert.Equal(t, amount, balanceMap[key.PubKey().Address()].Amount) } }) t.Run("malformed balance, invalid amount", func(t *testing.T) { t.Parallel() - dummyKey := getDummyKey(t) + dummyKey := getDummyKey(t).PubKey() balances := []string{ fmt.Sprintf( @@ -236,9 +236,8 @@ func TestBalances_GetBalancesFromSheet(t *testing.T) { // XXX: this function should probably be exposed somewhere as it's duplicate of // cmd/genesis/... -// getDummyKey generates a random public key, -// and returns the key info -func getDummyKey(t *testing.T) crypto.PubKey { +// getDummyKey generates a random private key +func getDummyKey(t *testing.T) crypto.PrivKey { t.Helper() mnemonic, err := client.GenerateMnemonic(256) @@ -246,14 +245,14 @@ func getDummyKey(t *testing.T) crypto.PubKey { seed := bip39.NewSeed(mnemonic, "") - return generateKeyFromSeed(seed, 0).PubKey() + return generateKeyFromSeed(seed, 0) } // getDummyKeys generates random keys for testing -func getDummyKeys(t *testing.T, count int) []crypto.PubKey { +func getDummyKeys(t *testing.T, count int) []crypto.PrivKey { t.Helper() - dummyKeys := make([]crypto.PubKey, count) + dummyKeys := make([]crypto.PrivKey, count) for i := 0; i < count; i++ { dummyKeys[i] = getDummyKey(t) diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go index f5f0aa56758..613fba51c37 100644 --- a/gno.land/pkg/gnoland/genesis.go +++ b/gno.land/pkg/gnoland/genesis.go @@ -60,8 +60,9 @@ func LoadGenesisBalancesFile(path string) ([]Balance, error) { // LoadGenesisTxsFile loads genesis transactions from the provided file path. // XXX: Improve the way we generate and load this file -func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) { - txs := []std.Tx{} +func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]TxWithMetadata, error) { + txs := make([]TxWithMetadata, 0) + txsBz := osm.MustReadFile(path) txsLines := strings.Split(string(txsBz), "\n") for _, txLine := range txsLines { @@ -73,7 +74,7 @@ func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]st txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - var tx std.Tx + var tx TxWithMetadata if err := amino.UnmarshalJSON([]byte(txLine), &tx); err != nil { return nil, fmt.Errorf("unable to Unmarshall txs file: %w", err) } @@ -86,7 +87,7 @@ func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]st // LoadPackagesFromDir loads gno packages from a directory. // It creates and returns a list of transactions based on these packages. -func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]std.Tx, error) { +func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]TxWithMetadata, error) { // list all packages from target path pkgs, err := gnomod.ListPkgs(dir) if err != nil { @@ -101,14 +102,16 @@ func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee) ([]std.Tx // Filter out draft packages. nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - txs := []std.Tx{} + txs := make([]TxWithMetadata, 0, len(nonDraftPkgs)) for _, pkg := range nonDraftPkgs { tx, err := LoadPackage(pkg, creator, fee, nil) if err != nil { return nil, fmt.Errorf("unable to load package %q: %w", pkg.Dir, err) } - txs = append(txs, tx) + txs = append(txs, TxWithMetadata{ + Tx: tx, + }) } return txs, nil diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index f81838e1eb3..9dccbbfac8d 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -16,7 +16,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/db/memdb" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/p2p" - "github.com/gnolang/gno/tm2/pkg/std" ) type InMemoryNodeConfig struct { @@ -44,7 +43,7 @@ func NewDefaultGenesisConfig(chainid string) *bft.GenesisDoc { }, AppState: &GnoGenesisState{ Balances: []Balance{}, - Txs: []std.Tx{}, + Txs: []TxWithMetadata{}, }, } } diff --git a/gno.land/pkg/gnoland/package.go b/gno.land/pkg/gnoland/package.go index fd1afbde136..e4b2449c972 100644 --- a/gno.land/pkg/gnoland/package.go +++ b/gno.land/pkg/gnoland/package.go @@ -11,4 +11,6 @@ var Package = amino.RegisterPackage(amino.NewPackage( ).WithDependencies().WithTypes( &GnoAccount{}, "Account", GnoGenesisState{}, "GenesisState", + TxWithMetadata{}, "TxWithMetadata", + GnoTxMetadata{}, "GnoTxMetadata", )) diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 016f3279dbd..b1bedb541aa 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,8 +1,13 @@ package gnoland import ( + "bufio" + "context" "errors" + "fmt" + "os" + "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/std" ) @@ -20,6 +25,59 @@ func ProtoGnoAccount() std.Account { } type GnoGenesisState struct { - Balances []Balance `json:"balances"` - Txs []std.Tx `json:"txs"` + Balances []Balance `json:"balances"` + Txs []TxWithMetadata `json:"txs"` +} + +type TxWithMetadata struct { + Tx std.Tx `json:"tx"` + Metadata *GnoTxMetadata `json:"metadata,omitempty"` +} + +type GnoTxMetadata struct { + Timestamp int64 `json:"timestamp"` +} + +// ReadGenesisTxs reads the genesis txs from the given file path +func ReadGenesisTxs(ctx context.Context, path string) ([]TxWithMetadata, error) { + // Open the txs file + file, loadErr := os.Open(path) + if loadErr != nil { + return nil, fmt.Errorf("unable to open tx file %s: %w", path, loadErr) + } + defer file.Close() + + var ( + txs []TxWithMetadata + + scanner = bufio.NewScanner(file) + ) + + for scanner.Scan() { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + // Parse the amino JSON + var tx TxWithMetadata + if err := amino.UnmarshalJSON(scanner.Bytes(), &tx); err != nil { + return nil, fmt.Errorf( + "unable to unmarshal amino JSON, %w", + err, + ) + } + + txs = append(txs, tx) + } + } + + // Check for scanning errors + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf( + "error encountered while reading file, %w", + err, + ) + } + + return txs, nil } diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go new file mode 100644 index 00000000000..b4625d6d7d6 --- /dev/null +++ b/gno.land/pkg/gnoland/types_test.go @@ -0,0 +1,131 @@ +package gnoland + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/gnolang/gno/gno.land/pkg/gnoland/ugnot" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// generateTxs generates dummy transactions +func generateTxs(t *testing.T, count int) []TxWithMetadata { + t.Helper() + + txs := make([]TxWithMetadata, count) + + for i := 0; i < count; i++ { + txs[i] = TxWithMetadata{ + Tx: std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: crypto.Address{byte(i)}, + ToAddress: crypto.Address{byte(i)}, + Amount: std.NewCoins(std.NewCoin(ugnot.Denom, 1)), + }, + }, + Fee: std.Fee{ + GasWanted: 10, + GasFee: std.NewCoin(ugnot.Denom, 1000000), + }, + Memo: fmt.Sprintf("tx %d", i), + }, + } + } + + return txs +} + +func TestReadGenesisTxs(t *testing.T) { + t.Parallel() + + createFile := func(path, data string) { + file, err := os.Create(path) + require.NoError(t, err) + + _, err = file.WriteString(data) + require.NoError(t, err) + } + + t.Run("invalid path", func(t *testing.T) { + t.Parallel() + + path := "" // invalid + + ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelFn() + + txs, err := ReadGenesisTxs(ctx, path) + assert.Nil(t, txs) + + assert.Error(t, err) + }) + + t.Run("invalid tx format", func(t *testing.T) { + t.Parallel() + + var ( + dir = t.TempDir() + path = filepath.Join(dir, "txs.jsonl") + ) + + // Create the file + createFile( + path, + "random data", + ) + + ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelFn() + + txs, err := ReadGenesisTxs(ctx, path) + assert.Nil(t, txs) + + assert.Error(t, err) + }) + + t.Run("valid txs", func(t *testing.T) { + t.Parallel() + + var ( + dir = t.TempDir() + path = filepath.Join(dir, "txs.jsonl") + txs = generateTxs(t, 1000) + ) + + // Create the file + file, err := os.Create(path) + require.NoError(t, err) + + // Write the transactions + for _, tx := range txs { + encodedTx, err := amino.MarshalJSON(tx) + require.NoError(t, err) + + _, err = file.WriteString(fmt.Sprintf("%s\n", encodedTx)) + require.NoError(t, err) + } + + ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelFn() + + // Load the transactions + readTxs, err := ReadGenesisTxs(ctx, path) + require.NoError(t, err) + + require.Len(t, readTxs, len(txs)) + + for index, readTx := range readTxs { + assert.Equal(t, txs[index], readTx) + } + }) +} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go index d3f55cfadf7..56b298e4541 100644 --- a/gno.land/pkg/integration/testing_integration.go +++ b/gno.land/pkg/integration/testing_integration.go @@ -134,7 +134,7 @@ func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { // This genesis will be use when node is started. genesis := &gnoland.GnoGenesisState{ Balances: LoadDefaultGenesisBalanceFile(t, gnoRootDir), - Txs: []std.Tx{}, + Txs: []gnoland.TxWithMetadata{}, } // test1 must be created outside of the loop below because it is already included in genesis so @@ -660,13 +660,13 @@ func (pl *pkgsLoader) SetPatch(replace, with string) { pl.patchs[replace] = with } -func (pl *pkgsLoader) LoadPackages(creator bft.Address, fee std.Fee, deposit std.Coins) ([]std.Tx, error) { +func (pl *pkgsLoader) LoadPackages(creator bft.Address, fee std.Fee, deposit std.Coins) ([]gnoland.TxWithMetadata, error) { pkgslist, err := pl.List().Sort() // sorts packages by their dependencies. if err != nil { return nil, fmt.Errorf("unable to sort packages: %w", err) } - txs := make([]std.Tx, len(pkgslist)) + txs := make([]gnoland.TxWithMetadata, len(pkgslist)) for i, pkg := range pkgslist { tx, err := gnoland.LoadPackage(pkg, creator, fee, deposit) if err != nil { @@ -693,7 +693,9 @@ func (pl *pkgsLoader) LoadPackages(creator bft.Address, fee std.Fee, deposit std } } - txs[i] = tx + txs[i] = gnoland.TxWithMetadata{ + Tx: tx, + } } return txs, nil diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go index 5e9e2272049..ef7e05d0787 100644 --- a/gno.land/pkg/integration/testing_node.go +++ b/gno.land/pkg/integration/testing_node.go @@ -54,13 +54,13 @@ func TestingInMemoryNode(t TestingTS, logger *slog.Logger, config *gnoland.InMem // TestingNodeConfig constructs an in-memory node configuration // with default packages and genesis transactions already loaded. // It will return the default creator address of the loaded packages. -func TestingNodeConfig(t TestingTS, gnoroot string, additionalTxs ...std.Tx) (*gnoland.InMemoryNodeConfig, bft.Address) { +func TestingNodeConfig(t TestingTS, gnoroot string, additionalTxs ...gnoland.TxWithMetadata) (*gnoland.InMemoryNodeConfig, bft.Address) { cfg := TestingMinimalNodeConfig(t, gnoroot) creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 balances := LoadDefaultGenesisBalanceFile(t, gnoroot) - txs := []std.Tx{} + txs := make([]gnoland.TxWithMetadata, 0) txs = append(txs, LoadDefaultPackages(t, creator, gnoroot)...) txs = append(txs, additionalTxs...) @@ -121,13 +121,13 @@ func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey Amount: std.MustParseCoins(ugnot.ValueString(10000000000000)), }, }, - Txs: []std.Tx{}, + Txs: []gnoland.TxWithMetadata{}, }, } } // LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. -func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []std.Tx { +func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []gnoland.TxWithMetadata { examplesDir := filepath.Join(gnoroot, "examples") defaultFee := std.NewFee(50000, std.MustParseCoin(ugnot.ValueString(1000000))) @@ -148,7 +148,7 @@ func LoadDefaultGenesisBalanceFile(t TestingTS, gnoroot string) []gnoland.Balanc } // LoadDefaultGenesisTXsFile loads the default genesis transactions file for testing. -func LoadDefaultGenesisTXsFile(t TestingTS, chainid string, gnoroot string) []std.Tx { +func LoadDefaultGenesisTXsFile(t TestingTS, chainid string, gnoroot string) []gnoland.TxWithMetadata { txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.jsonl") // NOTE: We dont care about giving a correct address here, as it's only for display diff --git a/tm2/pkg/sdk/baseapp.go b/tm2/pkg/sdk/baseapp.go index 3cabc9df336..c11f81d852a 100644 --- a/tm2/pkg/sdk/baseapp.go +++ b/tm2/pkg/sdk/baseapp.go @@ -336,6 +336,7 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC app.deliverState.ctx = app.deliverState.ctx. WithBlockGasMeter(store.NewInfiniteGasMeter()) + // Run the set chain initializer res = app.initChainer(app.deliverState.ctx, req) // sanity check @@ -557,7 +558,9 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) (res abci.ResponseCheckTx) res.Error = ABCIError(std.ErrTxDecode(err.Error())) return } else { - result := app.runTx(RunTxModeCheck, req.Tx, tx) + ctx := app.getContextForTx(RunTxModeCheck, req.Tx) + + result := app.runTx(ctx, tx) res.ResponseBase = result.ResponseBase res.GasWanted = result.GasWanted res.GasUsed = result.GasUsed @@ -573,7 +576,9 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliv res.Error = ABCIError(std.ErrTxDecode(err.Error())) return } else { - result := app.runTx(RunTxModeDeliver, req.Tx, tx) + ctx := app.getContextForTx(RunTxModeDeliver, req.Tx) + + result := app.runTx(ctx, tx) res.ResponseBase = result.ResponseBase res.GasWanted = result.GasWanted res.GasUsed = result.GasUsed @@ -701,14 +706,17 @@ func (app *BaseApp) cacheTxContext(ctx Context) (Context, store.MultiStore) { // anteHandler. The provided txBytes may be nil in some cases, eg. in tests. For // further details on transaction execution, reference the BaseApp SDK // documentation. -func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx Tx) (result Result) { - // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is - // determined by the GasMeter. We need access to the context to get the gas - // meter so we initialize upfront. - var gasWanted int64 +func (app *BaseApp) runTx(ctx Context, tx Tx) (result Result) { + var ( + // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is + // determined by the GasMeter. We need access to the context to get the gas + // meter so we initialize upfront. + gasWanted int64 + + ms = ctx.MultiStore() + mode = ctx.Mode() + ) - ctx := app.getContextForTx(mode, txBytes) - ms := ctx.MultiStore() if mode == RunTxModeDeliver { gasleft := ctx.BlockGasMeter().Remaining() ctx = ctx.WithGasMeter(store.NewPassthroughGasMeter( diff --git a/tm2/pkg/sdk/helpers.go b/tm2/pkg/sdk/helpers.go index d61e8115671..ea5cdfea2da 100644 --- a/tm2/pkg/sdk/helpers.go +++ b/tm2/pkg/sdk/helpers.go @@ -10,17 +10,36 @@ import ( var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString func (app *BaseApp) Check(tx Tx) (result Result) { - return app.runTx(RunTxModeCheck, nil, tx) + ctx := app.getContextForTx(RunTxModeCheck, nil) + + return app.runTx(ctx, tx) } func (app *BaseApp) Simulate(txBytes []byte, tx Tx) (result Result) { - return app.runTx(RunTxModeSimulate, txBytes, tx) + ctx := app.getContextForTx(RunTxModeSimulate, txBytes) + + return app.runTx(ctx, tx) } -func (app *BaseApp) Deliver(tx Tx) (result Result) { - return app.runTx(RunTxModeDeliver, nil, tx) +func (app *BaseApp) Deliver(tx Tx, ctxFns ...ContextFn) (result Result) { + ctx := app.getContextForTx(RunTxModeDeliver, nil) + + for _, ctxFn := range ctxFns { + if ctxFn == nil { + continue + } + + ctx = ctxFn(ctx) + } + + return app.runTx(ctx, tx) } +// ContextFn is the custom execution context builder. +// It can be used to add custom metadata when replaying transactions +// during InitChainer or in the context of a unit test. +type ContextFn func(ctx Context) Context + // Context with current {check, deliver}State of the app // used by tests func (app *BaseApp) NewContext(mode RunTxMode, header abci.Header) Context {