diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 94525fff505..a8407f57291 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -25,7 +25,7 @@ jobs: with: go-version: "1.22.x" - run: "cd misc/gendocs && make install gen" - - uses: actions/configure-pages@v4 + - uses: actions/configure-pages@v5 id: pages - uses: actions/upload-pages-artifact@v3 with: diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod index 7c2e17012a4..4ec6a63d59c 100644 --- a/contribs/gnodev/go.mod +++ b/contribs/gnodev/go.mod @@ -8,21 +8,20 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/gnolang/gno v0.0.0-00010101000000-000000000000 github.com/gorilla/websocket v1.5.1 - go.uber.org/zap v1.26.0 + go.uber.org/zap v1.27.0 golang.org/x/term v0.18.0 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/gnolang/goleveldb v0.0.9 // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect @@ -35,22 +34,23 @@ require ( github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/rs/cors v1.10.1 // indirect - github.com/stretchr/testify v1.8.4 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - go.uber.org/multierr v1.10.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.2.0 // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.21.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.19.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum index e02d4d6232f..a20a723cc14 100644 --- a/contribs/gnodev/go.sum +++ b/contribs/gnodev/go.sum @@ -3,19 +3,20 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -46,8 +47,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= -github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -57,15 +56,13 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -78,6 +75,7 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= @@ -124,46 +122,47 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -184,8 +183,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -199,10 +198,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go index 23ac66a5f9a..5d5a4c5c5e6 100644 --- a/contribs/gnodev/pkg/dev/node.go +++ b/contribs/gnodev/pkg/dev/node.go @@ -410,9 +410,8 @@ func newNodeConfig(tmc *tmcfg.Config, chainid string, appstate gnoland.GnoGenesi } return &gnoland.InMemoryNodeConfig{ - PrivValidator: pv, - TMConfig: tmc, - Genesis: genesis, - GenesisMaxVMCycles: 10_000_000, + PrivValidator: pv, + TMConfig: tmc, + Genesis: genesis, } } diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod index 79f7747a5cc..66a3c750633 100644 --- a/contribs/gnokeykc/go.mod +++ b/contribs/gnokeykc/go.mod @@ -12,15 +12,14 @@ require ( require ( github.com/alessio/shellescape v1.4.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/gnolang/goleveldb v0.0.9 // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/jaekwon/testify v1.6.1 // indirect @@ -30,18 +29,19 @@ require ( github.com/peterbourgon/ff/v3 v3.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.8.4 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.21.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.17.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum index e2458bb59c6..1bc8ea08a57 100644 --- a/contribs/gnokeykc/go.sum +++ b/contribs/gnokeykc/go.sum @@ -5,19 +5,20 @@ github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVK github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -47,8 +48,6 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= -github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -60,19 +59,18 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -117,12 +115,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= @@ -130,24 +129,24 @@ github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -160,8 +159,8 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -181,10 +180,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/docs/concepts/effective-gno.md b/docs/concepts/effective-gno.md index ca593edf212..72f3081556d 100644 --- a/docs/concepts/effective-gno.md +++ b/docs/concepts/effective-gno.md @@ -466,7 +466,7 @@ other intermediary realm. and then checks if the caller is the admin. If not, it panics and stops the execution. The goal of this approach is to allow a contract to own assets (like grc20 or -native tokens), so that you can create contracts that can be called by another +coins), so that you can create contracts that can be called by another contract, reducing the risk of stealing money from the original caller. This is the behavior of the default grc20 implementation. @@ -596,18 +596,19 @@ func init() { // the object. ``` -### Choosing between native tokens and GRC20 tokens +### Choosing between Coins and GRC20 tokens -In Gno, you've got two main choices for tokens: Native or GRC20. Each has its -own pros and cons, and the best fit depends on your needs. +In Gno, you've got two primary options: Coins or GRC20. Each option +has its unique advantages and disadvantages, and the ideal choice varies based +on individual requirements. -#### Native tokens +#### Coins -Native tokens are managed by the banker module, separate from GnoVM. They're +Coins are managed by the banker module, separate from GnoVM. They're simple, strict, and secure. You can create, transfer, and check balances with an RPC call, no GnoVM needed. -For example, if you're creating a coin for cross-chain transfers, native tokens +For example, if you're creating a coin for cross-chain transfers, Coins are your best bet. They're IBC-ready and their strict rules offer top-notch security. @@ -627,9 +628,8 @@ Remember, GRC20 tokens are more gas-intensive and aren't IBC-ready yet. They also come with shared ownership, meaning the contract retains some control. In the end, your choice depends on your needs: simplicity and security with -Native tokens, or flexibility and control with GRC20 tokens. And if you want the -best of both worlds, you can wrap a native token into a GRC20 compatible -token. +Coins, or flexibility and control with GRC20 tokens. And if you want the +best of both worlds, you can wrap a Coins into a GRC20 compatible token. ```go import "gno.land/p/demo/grc/grc20" @@ -645,11 +645,11 @@ func MyBalance() uint64 { See also: https://gno.land/r/demo/foo20 -#### Wrapping native tokens +#### Wrapping Coins -Want the best of both worlds? Consider wrapping your Native tokens. This gives -your tokens the flexibility of GRC20 while keeping the security of Native -tokens. It's a bit more complex, but it's a powerful option that offers great +Want the best of both worlds? Consider wrapping your Coins. This gives +your coins the flexibility of GRC20 while keeping the security of Coins. +It's a bit more complex, but it's a powerful option that offers great versatility. See also: https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/wugnot diff --git a/docs/gno-tooling/cli/gnokey.md b/docs/gno-tooling/cli/gnokey.md index 9103e6c5801..8abd0722229 100644 --- a/docs/gno-tooling/cli/gnokey.md +++ b/docs/gno-tooling/cli/gnokey.md @@ -25,7 +25,7 @@ gnokey add {KEY_NAME} #### **Options** | Name | Type | Description | -| ----------- | ---------- | -------------------------------------------------------------------------------------- | +|-------------|------------|----------------------------------------------------------------------------------------| | `account` | UInt | Account number for HD derivation. | | `dryrun` | Boolean | Performs action, but doesn't add key to local keystore. | | `index` | UInt | Address index number for HD derivation. | @@ -50,8 +50,6 @@ You can add a ledger device using the following command gnokey add {LEDGER_KEY_NAME} --ledger ``` - - ## List all Known Keys List all keys stored in your keybase with the following command. @@ -71,7 +69,7 @@ gnokey delete {KEY_NAME} #### **Options** | Name | Type | Description | -| ------- | ------- | ---------------------------- | +|---------|---------|------------------------------| | `yes` | Boolean | Skips confirmation prompt. | | `force` | Boolean | Removes key unconditionally. | @@ -87,7 +85,7 @@ gnokey export #### **Options** | Name | Type | Description | -| ------------- | ------ | ------------------------------------------- | +|---------------|--------|---------------------------------------------| | `key` | String | Name or Bech32 address of the private key | | `output-path` | String | The desired output path for the armor file | | `unsafe` | Bool | Export the private key armor as unencrypted | @@ -104,7 +102,7 @@ gnokey import #### **Options** | Name | Type | Description | -| ------------ | ------ | ------------------------------------------- | +|--------------|--------|---------------------------------------------| | `armor-path` | String | The path to the encrypted armor file. | | `name` | String | The name of the private key. | | `unsafe` | Bool | Import the private key armor as unencrypted | @@ -120,21 +118,21 @@ gnokey query {QUERY_PATH} #### **Query** -| Query Path | Description | Example | -| ------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | +| Query Path | Description | Example | +|---------------------------|--------------------------------------------------------------------|----------------------------------------------------------------------------------------| | `auth/accounts/{ADDRESS}` | Returns information about an account. | `gnokey query auth/accounts/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5` | | `bank/balances/{ADDRESS}` | Returns balances of an account. | `gnokey query bank/balances/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5` | | `vm/qfuncs` | Returns public facing function signatures as JSON. | `gnokey query vm/qfuncs --data "gno.land/r/demo/boards"` | | `vm/qfile` | Returns the file bytes, or list of files if directory. | `gnokey query vm/qfile --data "gno.land/r/demo/boards"` | -| `vm/qrender` | Calls .Render(path) in readonly mode. | `gnokey query vm/qrender --data "gno.land/r/demo/boards"` | +| `vm/qrender` | Calls .Render(path) in readonly mode. | `gnokey query vm/qrender --data "gno.land/r/demo/boards"` | | `vm/qeval` | Evaluates any expression in readonly mode and returns the results. | `gnokey query vm/qeval --data "gno.land/r/demo/boards GetBoardIDFromName("my_board")"` | -| `vm/store` | (not yet supported) Fetches items from the store. | - | -| `vm/package` | (not yet supported) Fetches a package's files. | - | +| `vm/store` | (not yet supported) Fetches items from the store. | - | +| `vm/package` | (not yet supported) Fetches a package's files. | - | #### **Options** | Name | Type | Description | -| -------- | --------- | ---------------------------------------- | +|----------|-----------|------------------------------------------| | `data` | UInt8 \[] | Queries data bytes. | | `height` | Int64 | (not yet supported) Queries height. | | `prove` | Boolean | (not yet supported) Proves query result. | @@ -151,7 +149,7 @@ gnokey maketx {SUB_COMMAND} {ADDRESS or KeyName} #### **Subcommands** | Name | Description | -| -------- | ---------------------------- | +|----------|------------------------------| | `addpkg` | Uploads a new package. | | `call` | Calls a public function. | | `send` | The amount of coins to send. | @@ -174,22 +172,21 @@ gnokey maketx addpkg \ #### **SignBroadcast Options** | Name | Type | Description | -| ------------ | ------- | ------------------------------------------------------------------------ | +|--------------|---------|--------------------------------------------------------------------------| | `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | | `gas-fee` | String | The gas fee to pay for the transaction. | | `memo` | String | Any descriptive text. | | `broadcast` | Boolean | Broadcasts the transaction. | | `chainid` | String | Defines the chainid to sign for (should only be used with `--broadcast`) | -#### **makeAddPackageTx Options** +#### **makeTx AddPackage Options** | Name | Type | Description | -| --------- | ------ | ------------------------------------- | +|-----------|--------|---------------------------------------| | `pkgpath` | String | The package path (required). | | `pkgdir` | String | The path to package files (required). | | `deposit` | String | The amount of coins to send. | - ### `call` This subcommand lets you call a public function. @@ -212,14 +209,14 @@ gnokey maketx call \ #### **SignBroadcast Options** | Name | Type | Description | -| ------------ | ------- | ---------------------------------------------------------------- | +|--------------|---------|------------------------------------------------------------------| | `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | | `gas-fee` | String | The gas fee to pay for the transaction. | | `memo` | String | Any descriptive text. | | `broadcast` | Boolean | Broadcasts the transaction. | | `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) | -#### **makeCallTx Options** +#### **makeTx Call Options** | Name | Type | Description | |-----------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -250,17 +247,17 @@ gnokey maketx send \ #### **SignBroadcast Options** | Name | Type | Description | -| ------------ | ------- | ----------------------------------------------------- | +|--------------|---------|-------------------------------------------------------| | `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | | `gas-fee` | String | The gas fee to pay for the transaction. | | `memo` | String | Any descriptive text. | | `broadcast` | Boolean | Broadcasts the transaction. | | `chainid` | String | The chainid to sign for (implies `--broadcast`) | -#### **makeSendTx Options** +#### **makeTx Send Options** | Name | Type | Description | -| ------ | ------ | ------------------------ | +|--------|--------|--------------------------| | `send` | String | Amount of coins to send. | | `to` | String | The destination address. | @@ -276,7 +273,7 @@ gnokey sign #### **Options** | Name | Type | Description | -| ---------------- | ------- | ---------------------------------------------------------- | +|------------------|---------|------------------------------------------------------------| | `txpath` | String | The path to file of tx to sign (default: `-`). | | `chainid` | String | The chainid to sign for (default: `dev`). | | `number` | UInt | The account number of the account to sign with (required) | @@ -295,7 +292,7 @@ gnokey verify #### **Options** | Name | Type | Description | -| --------- | ------ | ---------------------------------------- | +|-----------|--------|------------------------------------------| | `docpath` | String | The path of the document file to verify. | ## Broadcast a Signed Document diff --git a/docs/how-to-guides/connecting-from-go.md b/docs/how-to-guides/connecting-from-go.md new file mode 100644 index 00000000000..d1cdd324683 --- /dev/null +++ b/docs/how-to-guides/connecting-from-go.md @@ -0,0 +1,271 @@ +--- +id: connect-from-go +--- + +# How to connect a Go app to Gno.land + +This guide will show you how to connect to a Gno.land network from your Go application, +using the [gnoclient](../reference/gnoclient/gnoclient.md) package. + +For this guide, we will build a small Go app that will: + +- Get account information from the chain +- Broadcast a state-changing transaction +- Read on-chain state + +## Prerequisites +- A local Gno.land keypair generated using +[gnokey](../getting-started/local-setup/working-with-key-pairs.md) + +## Setup + +To get started, create a new Go project. In a clean directory, run the following: +```bash +go mod init example +``` + +After this, create a new `main.go` file: + +```bash +touch main.go +``` + +Set up your main file with the code below: + +```go +package main + +func main() {} +``` + +Finally, add the `gnoclient` package by running the following command: + +```bash +go get github.com/gnolang/gno/gno.land/pkg/gnoclient +``` + +## Main components + +The `gnoclient` package exposes a `Client` struct containing a `Signer` and +`RPCClient` connector. `Client` exposes all available functionality for talking +to a Gno.land chain. + +```go +type Client struct { + Signer Signer // Signer for transaction authentication + RPCClient rpcclient.Client // gnolang/gno/tm2/pkg/bft/rpc/client +} +``` + +### Signer + +The `Signer` provides functionality to sign transactions with a Gno.land keypair. +The keypair can be accessed from a local keybase, or it can be generated +in-memory from a BIP39 mnemonic. + +:::info +The keybase directory path is set with the `gnokey --home` flag. +::: + +### RPCClient + +The `RPCCLient` provides connectivity to a Gno.land network via HTTP or WebSockets. + + +## Initialize the Signer + +For this example, we will initialize the `Signer` from a local keybase: + +```go +package main + +import ( + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" +) + +func main() { + // Initialize keybase from a directory + keybase, _ := keys.NewKeyBaseFromDir("path/to/keybase/dir") + + // Create signer + signer := gnoclient.SignerFromKeybase{ + Keybase: keybase, + Account: "", // Name of your keypair in keybase + Password: "", // Password to decrypt your keypair + ChainID: "", // id of Gno.land chain + } +} +``` + +A few things to note: +- You can view keys in your local keybase by running `gnokey list`. +- You can get the password from a user input using the IO package. +- `Signer` can also be initialized in-memory from a BIP39 mnemonic, using the +[`SignerFromBip39`](../reference/gnoclient/signer.md#func-signerfrombip39) function. + +## Initialize the RPC connection & Client + +You can initialize the RPC Client used to connect to the Gno.land network with +the following line: +```go +rpc := rpcclient.NewHTTP("", "") +``` + +A list of Gno.land network endpoints & chain IDs can be found in the [Gno RPC +endpoints](../reference/rpc-endpoints.md#network-configurations) page. + +With this, we can initialize the `gnoclient.Client` struct: + +```go +package main + +import ( + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" +) + +func main() { + // Initialize keybase from a directory + keybase, _ := keys.NewKeyBaseFromDir("path/to/keybase/dir") + + // Create signer + signer := gnoclient.SignerFromKeybase{ + Keybase: keybase, + Account: "", // Name of your keypair in keybase + Password: "", // Password to decrypt your keypair + ChainID: "", // id of Gno.land chain + } + + // Initialize the RPC client + rpc := rpcclient.NewHTTP("", "") + + // Initialize the gnoclient + client := gnoclient.Client{ + Signer: signer, + RPCClient: rpc, + } +} +``` + +We can now communicate with the Gno.land chain. Let's explore some of the functionality +`gnoclient` provides. + +## Query account info from a chain + +To send transactions to the chain, we need to know the account number (ID) and +sequence (nonce). We can get this information by querying the chain with the +`QueryAccount` function: + +```go +// Convert Gno address string to `crypto.Address` +addr, err := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // your Gno address +if err != nil { + panic(err) +} + +accountRes, _, err := client.QueryAccount(addr) +if err != nil { + panic(err) +} +``` + +An example result would be as follows: + +```go +fmt.Println(accountRes) +// Output: +// Account: +// Address: g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 +// Pubkey: +// Coins: 9999862000000ugnot +// AccountNumber: 0 +// Sequence: 0 +``` + +We are now ready to send a transaction to the chain. + +## Sending a transaction + +A Gno.land transaction consists of two main parts: +- A set of base transaction fields, such as a gas price, gas limit, account & +sequence number, +- An array of messages to be executed on the chain. + +To construct the base set of transaction fields, we can use the `BaseTxCfg` type: +```go +txCfg := gnoclient.BaseTxCfg{ + GasFee: "1000000ugnot", // gas price + GasWanted: 1000000, // gas limit + AccountNumber: accountRes.GetAccountNumber(), // account ID + SequenceNumber: accountRes.GetSequence(), // account nonce + Memo: "This is a cool how-to guide!", // transaction memo +} +``` + +For calling an exported (public) function in a Gno realm, we can use the `MsgCall` +message type. We will use the wrapped ugnot realm for this example, wrapping +`1000000ugnot` (1 $GNOT) for demonstration purposes. + +```go +msg := gnoclient.MsgCall{ + PkgPath: "gno.land/r/demo/wugnot", // wrapped ugnot realm path + FuncName: "Deposit", // function to call + Args: nil, // arguments in string format + Send: "1000000ugnot", // coins to send along with transaction +} +``` + +Finally, to actually call the function, we can use `Call`: + +```go +res, err := client.Call(txCfg, msg) +if err != nil { + panic(err) +} +``` + +Before running your code, make sure your keypair has enough funds to send the +transaction. + +If everything went well, you've just sent a state-changing transaction to a +Gno.land chain! + + +## Reading on-chain state + +To read on-chain state, you can use the `QEval()` function. This functionality +allows you to evaluate a query expression on a realm, without having to spend gas. + +Let's fetch the balance of wrapped ugnot for our address: +```go +// Evaluate expression +qevalRes, _, err := client.QEval("gno.land/r/demo/wugnot", "BalanceOf(\"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5\")") +if err != nil { + panic(err) +} +``` + +Printing out the result should output: +```go +fmt.Println(qevalRes) +// Output: +// (1000000 uint64) +``` + +To see all functionality the `gnoclient` package provides, see the gnoclient +[reference page](../reference/gnoclient/gnoclient.md). + +## Conclusion + +Congratulations 🎉 + +You've just built a small demo app in Go that connects to a Gno.land chain +to query account info, send a transaction, and read on-chain state. + +Check out the full example app code [here](https://github.com/leohhhn/connect-gno/blob/master/main.go). + +To see a real-world example CLI tool use `gnoclient`, +check out [gnoblog-cli](https://github.com/gnolang/blog/tree/main/cmd/gnoblog-cli). + + diff --git a/docs/reference/gno-js-client/getting-started.md b/docs/reference/gno-js-client/gno-js-client.md similarity index 62% rename from docs/reference/gno-js-client/getting-started.md rename to docs/reference/gno-js-client/gno-js-client.md index 188f084bd56..90b480bdd69 100644 --- a/docs/reference/gno-js-client/getting-started.md +++ b/docs/reference/gno-js-client/gno-js-client.md @@ -1,11 +1,12 @@ --- -id: gno-js-getting-started +id: gno-js-client --- -# Getting Started +# Gno JavaScript Client -[@gnolang/gno-js-client](https://github.com/gnolang/gno-js-client) is a JavaScript/TypeScript client implementation for Gno chains. It is an extension of the -[tm2-js-client](https://github.com/gnolang/tm2-js-client), but with Gno-specific functionality. +[@gnolang/gno-js-client](https://github.com/gnolang/gno-js-client) is a JavaScript/TypeScript client implementation +for Gno chains. It is an extension of the [tm2-js-client](https://github.com/gnolang/tm2-js-client), but with +Gno-specific functionality. ## Key Features diff --git a/docs/reference/gnoclient/client.md b/docs/reference/gnoclient/client.md new file mode 100644 index 00000000000..c4d9affffd5 --- /dev/null +++ b/docs/reference/gnoclient/client.md @@ -0,0 +1,149 @@ +--- +id: client +--- + +# Client + +## type [Client]() + +`Client` provides an interface for interacting with the blockchain. It is the main +struct of the `gnoclient` package, exposing all APIs used to communicate with a +Gno.land chain. + +```go +type Client struct { + Signer Signer // Signer for transaction authentication + RPCClient rpcclient.Client // RPC client for blockchain communication +} +``` + +### func \(\*Client\) [Call]() + +```go +func (c *Client) Call(cfg BaseTxCfg, msgs ...MsgCall) (*ctypes.ResultBroadcastTxCommit, error) +``` + +`Call` executes a one or more [MsgCall](#type-msgcall) calls on the blockchain. + +### func \(\*Client\) [Send]() + +```go +func (c *Client) Send(cfg BaseTxCfg, msgs ...MsgSend) (*ctypes.ResultBroadcastTxCommit, error) +``` + +`Send` executes one or more [MsgSend](#type-msgsend) calls on the blockchain. + +### func \(\*Client\) [Run]() + +```go +func (c *Client) Run(cfg BaseTxCfg, msgs ...MsgRun) (*ctypes.ResultBroadcastTxCommit, error) +``` + +`Run` executes a one or more MsgRun calls on the blockchain. + +### func \(*Client\) [QEval]() + +```go +func (c *Client) QEval(pkgPath string, expression string) (string, *ctypes.ResultABCIQuery, error) +``` + +`QEval` evaluates the given expression with the realm code at `pkgPath`. +The `pkgPath` should include the prefix like `gno.land/`. The expression is +usually a function call like `Render("")`. + +### func \(*Client\) [Query]() + +```go +func (c *Client) Query(cfg QueryCfg) (*ctypes.ResultABCIQuery, error) +``` + +`Query` performs a generic query on the blockchain. + +### func \(*Client\) [QueryAccount]() + +```go +func (c *Client) QueryAccount(addr crypto.Address) (*std.BaseAccount, *ctypes.ResultABCIQuery, error) +``` + +`QueryAccount` retrieves account information for a given address. + +### func \(*Client\) [QueryAppVersion]() + +```go +func (c *Client) QueryAppVersion() (string, *ctypes.ResultABCIQuery, error) +``` + +`QueryAppVersion` retrieves information about the Gno.land app version. + +### func \(*Client\) [Render]() + +```go +func (c *Client) Render(pkgPath string, args string) (string, *ctypes.ResultABCIQuery, error) +``` + +`Render` calls the Render function for pkgPath with optional args. The `pkgPath` +should include the prefix like `gno.land/`. This is similar to using a browser +URL `/:` where `` doesn't have the prefix like +`gno.land/`. + +## type [BaseTxCfg]() + +`BaseTxCfg` defines the base transaction configuration, shared by all message +types. + +```go +type BaseTxCfg struct { + GasFee string // Gas fee + GasWanted int64 // Gas wanted + AccountNumber uint64 // Account number + SequenceNumber uint64 // Sequence number + Memo string // Memo +} +``` + +## type [MsgCall]() + +`MsgCall` \- syntax sugar for `vm.MsgCall`. + +```go +type MsgCall struct { + PkgPath string // Package path + FuncName string // Function name + Args []string // Function arguments + Send string // Send amount +} +``` + +## type [MsgRun]() + +`MsgRun` \- syntax sugar for `vm.MsgRun`. + +```go +type MsgRun struct { + Package *std.MemPackage // Package to run + Send string // Send amount +} +``` + +## type [MsgSend]() + +`MsgSend` \- syntax sugar for `bank.MsgSend`. + +```go +type MsgSend struct { + ToAddress crypto.Address // Send to address + Send string // Send amount +} +``` + +## type [QueryCfg]() + +`QueryCfg` contains configuration options for performing ABCI queries. + +```go +type QueryCfg struct { + Path string // Query path + Data []byte // Query data + rpcclient.ABCIQueryOptions // ABCI query options +} +``` \ No newline at end of file diff --git a/docs/reference/gnoclient/gnoclient.md b/docs/reference/gnoclient/gnoclient.md new file mode 100644 index 00000000000..f5baa9fc03a --- /dev/null +++ b/docs/reference/gnoclient/gnoclient.md @@ -0,0 +1,25 @@ +--- +id: gnoclient +--- + +# Gnoclient + +[Gnoclient](https://github.com/gnolang/gno/tree/master/gno.land/pkg/gnoclient) +allows you to easily access Gno blockchains from your Go code, through exposing +APIs for common functionality. + +## Key Features + +- Connect to a Gno chain via RPC +- Use local keystore to sign & broadcast transactions containing any type of +Gno message +- Sign & broadcast transactions with batch messages +- Use [ABCI queries](../../gno-tooling/cli/gnokey.md#make-an-abci-query) in +your Go code + +## Installation + +To add Gnoclient to your Go project, run the following command: +```bash +go get github.com/gnolang/gno/gno.land/pkg/gnoclient +``` \ No newline at end of file diff --git a/docs/reference/gnoclient/signer.md b/docs/reference/gnoclient/signer.md new file mode 100644 index 00000000000..75c69a9f7c2 --- /dev/null +++ b/docs/reference/gnoclient/signer.md @@ -0,0 +1,86 @@ +--- +id: signer +--- + +# Signer + +`Signer` is an interface that provides functionality for signing transactions. +The signer can be created from a local keybase, or from a bip39 mnemonic phrase. + +Useful types and functions when using the `Signer` can be found below. + +## type [Signer]() + +`Signer` provides an interface for signing transactions. + +```go +type Signer interface { + Sign(SignCfg) (*std.Tx, error) // Signs a transaction and returns a signed tx ready for broadcasting. + Info() keys.Info // Returns key information, including the address. + Validate() error // Checks whether the signer is properly configured. +} +``` + +## type [SignCfg]() + +`SignCfg` provides the signing configuration, containing the unsigned transaction +data, account number, and account sequence. + +```go +type SignCfg struct { + UnsignedTX std.Tx + SequenceNumber uint64 + AccountNumber uint64 +} +``` + +## type [SignerFromKeybase]() + +`SignerFromKeybase` represents a signer created from a Keybase. + +```go +type SignerFromKeybase struct { + Keybase keys.Keybase // Stores keys in memory or on disk + Account string // Account name or bech32 format + Password string // Password for encryption + ChainID string // Chain ID for transaction signing +} +``` + +### func \(SignerFromKeybase\) [Info]() + +```go +func (s SignerFromKeybase) Info() keys.Info +``` + +`Info` gets keypair information. + +### func \(SignerFromKeybase\) [Sign]() + +```go +func (s SignerFromKeybase) Sign(cfg SignCfg) (*std.Tx, error) +``` + +`Sign` implements the Signer interface for SignerFromKeybase. + +### func \(SignerFromKeybase\) [Validate]() + +```go +func (s SignerFromKeybase) Validate() error +``` + +`Validate` checks if the signer is properly configured. + +## func [SignerFromBip39]() + +```go +func SignerFromBip39(mnemonic string, chainID string, passphrase string, account uint32, index uint32) (Signer, error) +``` + +`SignerFromBip39` creates a `Signer` from an in-memory keybase with a single default +account, derived from the given mnemonic. +This can be useful in scenarios where storing private keys in the filesystem +isn't feasible, or for generating a signer for testing. + +> Using `keys.NewKeyBaseFromDir()` to get a keypair from local storage is +recommended where possible, as it is more secure. \ No newline at end of file diff --git a/docs/reference/go-gno-compatibility.md b/docs/reference/go-gno-compatibility.md index 50a4d13b601..fbc7d10668e 100644 --- a/docs/reference/go-gno-compatibility.md +++ b/docs/reference/go-gno-compatibility.md @@ -265,7 +265,7 @@ Legend: | time | `full`[^7] | | time/tzdata | `tbd` | | unicode | `full` | -| unicode/utf16 | `tbd` | +| unicode/utf16 | `full` | | unicode/utf8 | `full` | | unsafe | `nondet` | diff --git a/docs/reference/tm2-js-client/getting-started.md b/docs/reference/tm2-js-client/tm2-js-client.md similarity index 52% rename from docs/reference/tm2-js-client/getting-started.md rename to docs/reference/tm2-js-client/tm2-js-client.md index 4a20ce2684f..f32438f4113 100644 --- a/docs/reference/tm2-js-client/getting-started.md +++ b/docs/reference/tm2-js-client/tm2-js-client.md @@ -1,15 +1,15 @@ --- -id: tm2-js-getting-started +id: tm2-js-client --- -# Getting Started +# Tendermint2 JavaScript Client -[@gnolang/tm2-js-client](https://github.com/gnolang/tm2-js-client) is a JavaScript/TypeScript client implementation for -Tendermint2-based chains. It is designed to make it -easy for developers to interact with TM2 chains, providing a simplified API for account and transaction management. By -doing all the heavy lifting behind the scenes, `@gnolang/tm2-js-client` enables developers to focus on what really -matters - -building their dApps. +[@gnolang/tm2-js-client](https://github.com/gnolang/tm2-js-client) is a JavaScript/TypeScript client implementation +for Tendermint2-based chains. It is designed to make it +easy for developers to interact with TM2 chains, providing a simplified API for +account and transaction management. By doing all the heavy lifting behind the +scenes, `@gnolang/tm2-js-client` enables developers to focus on what really +matters - building their dApps. ## Key Features @@ -33,33 +33,41 @@ npm install @gnolang/tm2-js-client ### Provider -A `Provider` is an interface that abstracts the interaction with the Tendermint2 chain, making it easier for users to -communicate with it. Rather than requiring users to understand which endpoints are exposed, what their return types are, -and how they are parsed, the `Provider` abstraction handles all of this behind the scenes. It exposes useful API methods -that users can use and expects concrete types in return. +A `Provider` is an interface that abstracts the interaction with the Tendermint2 +chain, making it easier for users to communicate with it. Rather than requiring +users to understand which endpoints are exposed, what their return types are, +and how they are parsed, the `Provider` abstraction handles all of this behind +the scenes. It exposes useful API methods that users can use and expects +concrete types in return. -Currently, the `@gnolang/tm2-js-client` package provides support for two Provider implementations: +Currently, the `@gnolang/tm2-js-client` package provides support for two +Provider implementations: - `JSON-RPC Provider`: executes each call as a separate HTTP RPC call. -- `WS Provider`: executes each call through an active WebSocket connection, which requires closing when not needed - anymore. +- `WS Provider`: executes each call through an active WebSocket connection, +which requires closing when not needed anymore. ### Signer -A `Signer` is an interface that abstracts the interaction with a single Secp256k1 key pair. It exposes methods for -signing data, verifying signatures, and getting metadata associated with the key pair, such as the address. +A `Signer` is an interface that abstracts the interaction with a single +Secp256k1 key pair. It exposes methods for signing data, verifying signatures, +and getting metadata associated with the key pair, such as the address. -Currently, the `@gnolang/tm2-js-client` package provides support for two `Signer` implementations: +Currently, the `@gnolang/tm2-js-client` package provides support for two +`Signer` implementations: - `Key`: a signer that is based on a raw Secp256k1 key pair. -- `Ledger`: a signer that is based on a Ledger device, with all interaction flowing through the user's device. +- `Ledger`: a signer that is based on a Ledger device, with all interaction +flowing through the user's device. ### Wallet -A `Wallet` is a user-facing API that is used to interact with an account. A `Wallet` instance is tied to a single key -pair and essentially wraps the given `Provider` for that specific account. +A `Wallet` is a user-facing API that is used to interact with an account. +A `Wallet` instance is tied to a single key pair and essentially wraps the given +`Provider` for that specific account. -A wallet can be generated from a randomly generated seed, a private key, or instantiated using a Ledger device. +A wallet can be generated from a randomly generated seed, a private key, or +instantiated using a Ledger device. -Using the `Wallet`, users can easily interact with the Tendermint2 chain using their account without having to worry -about account management. +Using the `Wallet`, users can easily interact with the Tendermint2 chain using +their account without having to worry about account management. diff --git a/examples/gno.land/p/demo/int256/LICENSE b/examples/gno.land/p/demo/int256/LICENSE new file mode 100644 index 00000000000..fc7e78a4875 --- /dev/null +++ b/examples/gno.land/p/demo/int256/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Trịnh Đức Bảo Linh(Kevin) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/examples/gno.land/p/demo/int256/README.md b/examples/gno.land/p/demo/int256/README.md new file mode 100644 index 00000000000..be467471199 --- /dev/null +++ b/examples/gno.land/p/demo/int256/README.md @@ -0,0 +1,6 @@ +# Fixed size signed 256-bit math library + +1. This is a library specialized at replacing the big.Int library for math based on signed 256-bit types. +2. It uses [uint256](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/uint256) as the underlying type. + +ported from [mempooler/int256](https://github.com/mempooler/int256) diff --git a/examples/gno.land/p/demo/int256/absolute.gno b/examples/gno.land/p/demo/int256/absolute.gno new file mode 100644 index 00000000000..23beb4071a4 --- /dev/null +++ b/examples/gno.land/p/demo/int256/absolute.gno @@ -0,0 +1,20 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +// Abs returns |z| +func (z *Int) Abs() *uint256.Uint { + return z.abs.Clone() +} + +// AbsGt returns true if |z| > x, where x is a uint256 +func (z *Int) AbsGt(x *uint256.Uint) bool { + return z.abs.Gt(x) +} + +// AbsLt returns true if |z| < x, where x is a uint256 +func (z *Int) AbsLt(x *uint256.Uint) bool { + return z.abs.Lt(x) +} diff --git a/examples/gno.land/p/demo/int256/absolute_test.gno b/examples/gno.land/p/demo/int256/absolute_test.gno new file mode 100644 index 00000000000..55f6e41d0c8 --- /dev/null +++ b/examples/gno.land/p/demo/int256/absolute_test.gno @@ -0,0 +1,105 @@ +package int256 + +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestAbs(t *testing.T) { + tests := []struct { + x, want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "1"}, + {"-2", "2"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.Abs() + + if got.ToString() != tc.want { + t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) + } + } +} + +func TestAbsGt(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "0", "false"}, + {"1", "0", "true"}, + {"-1", "0", "true"}, + {"-1", "1", "false"}, + {"-2", "1", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.AbsGt(y) + + if got != (tc.want == "true") { + t.Errorf("AbsGt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestAbsLt(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "0", "false"}, + {"1", "0", "false"}, + {"-1", "0", "false"}, + {"-1", "1", "false"}, + {"-2", "1", "false"}, + {"-5", "10", "true"}, + {"31330", "31337", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "false"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "false"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.AbsLt(y) + + if got != (tc.want == "true") { + t.Errorf("AbsLt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/int256/arithmetic.gno b/examples/gno.land/p/demo/int256/arithmetic.gno new file mode 100644 index 00000000000..ccb494bec9a --- /dev/null +++ b/examples/gno.land/p/demo/int256/arithmetic.gno @@ -0,0 +1,198 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +func (z *Int) Add(x, y *Int) *Int { + z.initiateAbs() + + neg := x.neg + + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.abs = z.abs.Add(x.abs, y.abs) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.abs.Cmp(y.abs) >= 0 { + z.abs = z.abs.Sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.Sub(y.abs, x.abs) + } + } + z.neg = neg // 0 has no sign + return z +} + +// AddUint256 set z to the sum x + y, where y is a uint256, and returns z +func (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int { + if x.neg { + if x.abs.Gt(y) { + z.abs.Sub(x.abs, y) + z.neg = true + } else { + z.abs.Sub(y, x.abs) + z.neg = false + } + } else { + z.abs.Add(x.abs, y) + z.neg = false + } + return z +} + +// Sets z to the sum x + y, where z and x are uint256s and y is an int256. +func AddDelta(z, x *uint256.Uint, y *Int) { + if y.neg { + z.Sub(x, y.abs) + } else { + z.Add(x, y.abs) + } +} + +// Sets z to the sum x + y, where z and x are uint256s and y is an int256. +func AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool { + var overflow bool + if y.neg { + _, overflow = z.SubOverflow(x, y.abs) + } else { + _, overflow = z.AddOverflow(x, y.abs) + } + return overflow +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + z.initiateAbs() + + neg := x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.abs = z.abs.Add(x.abs, y.abs) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.abs.Cmp(y.abs) >= 0 { + z.abs = z.abs.Sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.Sub(y.abs, x.abs) + } + } + z.neg = neg // 0 has no sign + return z +} + +// SubUint256 set z to the difference x - y, where y is a uint256, and returns z +func (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int { + if x.neg { + z.abs.Add(x.abs, y) + z.neg = true + } else { + if x.abs.Lt(y) { + z.abs.Sub(y, x.abs) + z.neg = true + } else { + z.abs.Sub(x.abs, y) + z.neg = false + } + } + return z +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + z.initiateAbs() + + z.abs = z.abs.Mul(x.abs, y.abs) + z.neg = x.neg != y.neg // 0 has no sign + return z +} + +// MulUint256 sets z to the product x*y, where y is a uint256, and returns z +func (z *Int) MulUint256(x *Int, y *uint256.Uint) *Int { + z.abs.Mul(x.abs, y) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = x.neg + } + return z +} + +// Div sets z to the quotient x/y for y != 0 and returns z. +func (z *Int) Div(x, y *Int) *Int { + z.initiateAbs() + + z.abs.Div(x.abs, y.abs) + if x.neg == y.neg { + z.neg = false + } else { + z.neg = true + } + return z +} + +// DivUint256 sets z to the quotient x/y, where y is a uint256, and returns z +// If y == 0, z is set to 0 +func (z *Int) DivUint256(x *Int, y *uint256.Uint) *Int { + z.abs.Div(x.abs, y) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = x.neg + } + return z +} + +// Quo sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// OBS: differs from mempooler int256, we need to panic manually if y == 0 +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + if y.IsZero() { + panic("division by zero") + } + + z.initiateAbs() + + z.abs = z.abs.Div(x.abs, y.abs) + z.neg = !(z.abs.IsZero()) && x.neg != y.neg // 0 has no sign + return z +} + +// Rem sets z to the remainder x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// OBS: differs from mempooler int256, we need to panic manually if y == 0 +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + if y.IsZero() { + panic("division by zero") + } + + z.initiateAbs() + + z.abs.Mod(x.abs, y.abs) + z.neg = z.abs.Sign() > 0 && x.neg // 0 has no sign + return z +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, z is set to 0 (OBS: differs from the big.Int) +func (z *Int) Mod(x, y *Int) *Int { + if x.neg { + z.abs.Div(x.abs, y.abs) + z.abs.Add(z.abs, one) + z.abs.Mul(z.abs, y.abs) + z.abs.Sub(z.abs, x.abs) + z.abs.Mod(z.abs, y.abs) + } else { + z.abs.Mod(x.abs, y.abs) + } + z.neg = false + return z +} diff --git a/examples/gno.land/p/demo/int256/arithmetic_test.gno b/examples/gno.land/p/demo/int256/arithmetic_test.gno new file mode 100644 index 00000000000..c4aeb18e3c5 --- /dev/null +++ b/examples/gno.land/p/demo/int256/arithmetic_test.gno @@ -0,0 +1,571 @@ +package int256 + +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestAdd(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "2", "3"}, + // NEGATIVE + {"-1", "1", "-0"}, // TODO: remove negative sign for 0 ?? + {"1", "-1", "0"}, + {"-1", "-1", "-2"}, + {"-1", "-2", "-3"}, + {"-1", "3", "2"}, + {"3", "-1", "2"}, + // OVERFLOW + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Add(x, y) + + if got.Neq(want) { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestAddUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "2", "3"}, + {"-1", "1", "0"}, + {"-1", "3", "2"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "1"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639934", "-1"}, + // OVERFLOW + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.AddUint256(x, y) + + if got.Neq(want) { + t.Errorf("AddUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestAddDelta(t *testing.T) { + tests := []struct { + z, x, y, want string + }{ + {"0", "0", "0", "0"}, + {"0", "0", "1", "1"}, + {"0", "1", "0", "1"}, + {"0", "1", "1", "2"}, + {"1", "2", "3", "5"}, + {"5", "10", "-3", "7"}, + // underflow + {"1", "2", "-3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + z, err := uint256.FromDecimal(tc.z) + if err != nil { + t.Error(err) + continue + } + + x, err := uint256.FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := uint256.FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + AddDelta(z, x, y) + + if z.Neq(want) { + t.Errorf("AddDelta(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, z.ToString(), want.ToString()) + } + } +} + +func TestAddDeltaOverflow(t *testing.T) { + tests := []struct { + z, x, y string + want bool + }{ + {"0", "0", "0", false}, + // underflow + {"1", "2", "-3", true}, + } + + for _, tc := range tests { + z, err := uint256.FromDecimal(tc.z) + if err != nil { + t.Error(err) + continue + } + + x, err := uint256.FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + result := AddDeltaOverflow(z, x, y) + if result != tc.want { + t.Errorf("AddDeltaOverflow(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, result, tc.want) + } + } +} + +func TestSub(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"1", "0", "1"}, + {"1", "1", "0"}, + {"-1", "1", "-2"}, + {"1", "-1", "2"}, + {"-1", "-1", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {x: "-115792089237316195423570985008687907853269984665640564039457584007913129639935", y: "1", want: "-0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Sub(x, y) + + if got.Neq(want) { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestSubUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "-1"}, + {"1", "0", "1"}, + {"1", "1", "0"}, + {"1", "2", "-1"}, + {"-1", "1", "-2"}, + {"-1", "3", "-4"}, + // underflow + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "2", "-1"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "3", "-2"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.SubUint256(x, y) + + if got.Neq(want) { + t.Errorf("SubUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMul(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"5", "3", "15"}, + {"-5", "3", "-15"}, + {"5", "-3", "-15"}, + {"0", "3", "0"}, + {"3", "0", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Mul(x, y) + + if got.Neq(want) { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMulUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"1", "0", "0"}, + {"1", "1", "1"}, + {"1", "2", "2"}, + {"-1", "1", "-1"}, + {"-1", "3", "-3"}, + {"3", "4", "12"}, + {"-3", "4", "-12"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-115792089237316195423570985008687907853269984665640564039457584007913129639932"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "115792089237316195423570985008687907853269984665640564039457584007913129639932"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.MulUint256(x, y) + + if got.Neq(want) { + t.Errorf("MulUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDiv(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "-0"}, + {"10", "0", "0"}, + {"10", "1", "10"}, + {"10", "-1", "-10"}, + {"-10", "0", "-0"}, + {"-10", "1", "-10"}, + {"-10", "-1", "10"}, + {"10", "-3", "-3"}, + {"10", "3", "3"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Div(x, y) + + if got.Neq(want) { + t.Errorf("Div(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDivUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"1", "0", "0"}, + {"1", "1", "1"}, + {"1", "2", "0"}, + {"-1", "1", "-1"}, + {"-1", "3", "0"}, + {"4", "3", "1"}, + {"25", "5", "5"}, + {"25", "4", "6"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.DivUint256(x, y) + + if got.Neq(want) { + t.Errorf("DivUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestQuo(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "1", "10"}, + {"10", "-1", "-10"}, + {"-10", "1", "-10"}, + {"-10", "-1", "10"}, + {"10", "-3", "-3"}, + {"10", "3", "3"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Quo(x, y) + + if got.Neq(want) { + t.Errorf("Quo(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestRem(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "1", "0"}, + {"10", "-1", "0"}, + {"-10", "1", "0"}, + {"-10", "-1", "0"}, + {"10", "3", "1"}, + {"10", "-3", "1"}, + {"-10", "3", "-1"}, + {"-10", "-3", "-1"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Rem(x, y) + + if got.Neq(want) { + t.Errorf("Rem(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMod(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "0", "0"}, + {"10", "1", "0"}, + {"10", "-1", "0"}, + {"-10", "0", "0"}, + {"-10", "1", "0"}, + {"-10", "-1", "0"}, + {"10", "3", "1"}, + {"10", "-3", "1"}, + {"-10", "3", "2"}, + {"-10", "-3", "2"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Mod(x, y) + + if got.Neq(want) { + t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/int256/bitwise.gno b/examples/gno.land/p/demo/int256/bitwise.gno new file mode 100644 index 00000000000..c0d0f65f78f --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise.gno @@ -0,0 +1,94 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +// Or sets z = x | y and returns z. +func (z *Int) Or(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) + x1 := new(uint256.Uint).Sub(x.abs, one) + y1 := new(uint256.Uint).Sub(y.abs, one) + z.abs = z.abs.Add(z.abs.And(x1, y1), one) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x | y == x | y + z.abs = z.abs.Or(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + if x.neg { + x, y = y, x // | is symmetric + } + + // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) + y1 := new(uint256.Uint).Sub(y.abs, one) + z.abs = z.abs.Add(z.abs.AndNot(y1, x.abs), one) + z.neg = true // z cannot be zero if one of x or y is negative + + return z +} + +// And sets z = x & y and returns z. +func (z *Int) And(x, y *Int) *Int { + if x.neg == y.neg { + if x.neg { + // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) + x1 := new(uint256.Uint).Sub(x.abs, one) + y1 := new(uint256.Uint).Sub(y.abs, one) + z.abs = z.abs.Add(z.abs.Or(x1, y1), one) + z.neg = true // z cannot be zero if x and y are negative + return z + } + + // x & y == x & y + z.abs = z.abs.And(x.abs, y.abs) + z.neg = false + return z + } + + // x.neg != y.neg + // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1192-1202;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 + if x.neg { + x, y = y, x // & is symmetric + } + + // x & (-y) == x & ^(y-1) == x &^ (y-1) + y1 := new(uint256.Uint).Sub(y.abs, uint256.One()) + z.abs = z.abs.AndNot(x.abs, y1) + z.neg = false + return z +} + +// Rsh sets z = x >> n and returns z. +// OBS: Different from original implementation it was using math.Big +func (z *Int) Rsh(x *Int, n uint) *Int { + if !x.neg { + z.abs.Rsh(x.abs, n) + z.neg = x.neg + return z + } + + // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1118-1126;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 + t := NewInt(0).Sub(FromUint256(x.abs), NewInt(1)) + t = t.Rsh(t, n) + + _tmp := t.Add(t, NewInt(1)) + z.abs = _tmp.Abs() + z.neg = true + + return z +} + +// Lsh sets z = x << n and returns z. +func (z *Int) Lsh(x *Int, n uint) *Int { + z.abs.Lsh(x.abs, n) + z.neg = x.neg + return z +} diff --git a/examples/gno.land/p/demo/int256/bitwise_test.gno b/examples/gno.land/p/demo/int256/bitwise_test.gno new file mode 100644 index 00000000000..ea51360ea65 --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise_test.gno @@ -0,0 +1,201 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +import ( + "testing" +) + +func TestOr(t *testing.T) { + tests := []struct { + name string + x, y, want Int + }{ + { + name: "all zeroes", + x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "all ones", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + }, + { + name: "mixed", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + }, + { + name: "one operand all ones", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := New() + got.Or(&tc.x, &tc.y) + + if got.Neq(&tc.want) { + t.Errorf("Or(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) + } + }) + } +} + +func TestAnd(t *testing.T) { + tests := []struct { + name string + x, y, want Int + }{ + { + name: "all zeroes", + x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "all ones", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + }, + { + name: "mixed", + x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "mixed 2", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "mixed 3", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "one operand zero", + x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, + }, + { + name: "one operand all ones", + x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, + y: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, + want: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := New() + got.And(&tc.x, &tc.y) + + if got.Neq(&tc.want) { + t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) + } + }) + } +} + +func TestRsh(t *testing.T) { + tests := []struct { + x string + n uint + want string + }{ + {"1024", 0, "1024"}, + {"1024", 1, "512"}, + {"1024", 2, "256"}, + {"1024", 10, "1"}, + {"1024", 11, "0"}, + {"18446744073709551615", 0, "18446744073709551615"}, + {"18446744073709551615", 1, "9223372036854775807"}, + {"18446744073709551615", 62, "3"}, + {"18446744073709551615", 63, "1"}, + {"18446744073709551615", 64, "0"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 0, "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 1, "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 128, "340282366920938463463374607431768211455"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 255, "1"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 256, "0"}, + {"-1024", 0, "-1024"}, + {"-1024", 1, "-512"}, + {"-1024", 2, "-256"}, + {"-1024", 10, "-1"}, + {"-1024", 10, "-1"}, + {"-9223372036854775808", 0, "-9223372036854775808"}, + {"-9223372036854775808", 1, "-4611686018427387904"}, + {"-9223372036854775808", 62, "-2"}, + {"-9223372036854775808", 63, "-1"}, + {"-9223372036854775808", 64, "-1"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 0, "-57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 1, "-28948022309329048855892746252171976963317496166410141009864396001978282409984"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 253, "-4"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 254, "-2"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 255, "-1"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 256, "-1"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Rsh(x, tc.n) + + if got.ToString() != tc.want { + t.Errorf("Rsh(%s, %d) = %v, want %v", tc.x, tc.n, got.ToString(), tc.want) + } + } +} + +func TestLsh(t *testing.T) { + tests := []struct { + x string + n uint + want string + }{ + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 2, "4"}, + {"2", 0, "2"}, + {"2", 1, "4"}, + {"2", 2, "8"}, + {"-2", 0, "-2"}, + {"-4", 0, "-4"}, + {"-8", 0, "-8"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Lsh(x, tc.n) + + if got.ToString() != tc.want { + t.Errorf("Lsh(%s, %d) = %v, want %v", tc.x, tc.n, got.ToString(), tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/int256/cmp.gno b/examples/gno.land/p/demo/int256/cmp.gno new file mode 100644 index 00000000000..426dfd76485 --- /dev/null +++ b/examples/gno.land/p/demo/int256/cmp.gno @@ -0,0 +1,86 @@ +package int256 + +// Eq returns true if z == x +func (z *Int) Eq(x *Int) bool { + return (z.neg == x.neg) && z.abs.Eq(x.abs) +} + +// Neq returns true if z != x +func (z *Int) Neq(x *Int) bool { + return !z.Eq(x) +} + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +func (z *Int) Cmp(x *Int) (r int) { + // x cmp y == x cmp y + // x cmp (-y) == x + // (-x) cmp y == y + // (-x) cmp (-y) == -(x cmp y) + switch { + case z == x: + // nothing to do + case z.neg == x.neg: + r = z.abs.Cmp(x.abs) + if z.neg { + r = -r + } + case z.neg: + r = -1 + default: + r = 1 + } + return +} + +// IsZero returns true if z == 0 +func (z *Int) IsZero() bool { + return z.abs.IsZero() +} + +// IsNeg returns true if z < 0 +func (z *Int) IsNeg() bool { + return z.neg +} + +// Lt returns true if z < x +func (z *Int) Lt(x *Int) bool { + if z.neg { + if x.neg { + return z.abs.Gt(x.abs) + } else { + return true + } + } else { + if x.neg { + return false + } else { + return z.abs.Lt(x.abs) + } + } +} + +// Gt returns true if z > x +func (z *Int) Gt(x *Int) bool { + if z.neg { + if x.neg { + return z.abs.Lt(x.abs) + } else { + return false + } + } else { + if x.neg { + return true + } else { + return z.abs.Gt(x.abs) + } + } +} + +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + return &Int{z.abs.Clone(), z.neg} +} diff --git a/examples/gno.land/p/demo/int256/cmp_test.gno b/examples/gno.land/p/demo/int256/cmp_test.gno new file mode 100644 index 00000000000..155cfa94b92 --- /dev/null +++ b/examples/gno.land/p/demo/int256/cmp_test.gno @@ -0,0 +1,263 @@ +package int256 + +import ( + "testing" +) + +func TestEq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", true}, + {"0", "1", false}, + {"1", "0", false}, + {"-1", "0", false}, + {"0", "-1", false}, + {"1", "1", true}, + {"-1", "-1", true}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Eq(y) + if got != tc.want { + t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestNeq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", true}, + {"-1", "0", true}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Neq(y) + if got != tc.want { + t.Errorf("Neq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestCmp(t *testing.T) { + tests := []struct { + x, y string + want int + }{ + {"0", "0", 0}, + {"0", "1", -1}, + {"1", "0", 1}, + {"-1", "0", -1}, + {"0", "-1", 1}, + {"1", "1", 0}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", 1}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Cmp(y) + if got != tc.want { + t.Errorf("Cmp(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestIsZero(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"-0", true}, + {"1", false}, + {"-1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsZero() + if got != tc.want { + t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestIsNeg(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", false}, + {"-0", true}, // TODO: should this be false? + {"1", false}, + {"-1", true}, + {"10", false}, + {"-10", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsNeg() + if got != tc.want { + t.Errorf("IsNeg(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestLt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", false}, + {"-1", "0", true}, + {"0", "-1", false}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Lt(y) + if got != tc.want { + t.Errorf("Lt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestGt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", false}, + {"1", "0", true}, + {"-1", "0", false}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Gt(y) + if got != tc.want { + t.Errorf("Gt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestClone(t *testing.T) { + tests := []struct { + x string + }{ + {"0"}, + {"-0"}, + {"1"}, + {"-1"}, + {"10"}, + {"-10"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y := x.Clone() + + if x.Cmp(y) != 0 { + t.Errorf("Clone(%s) = %v, want %v", tc.x, y, x) + } + } +} diff --git a/examples/gno.land/p/demo/int256/conversion.gno b/examples/gno.land/p/demo/int256/conversion.gno new file mode 100644 index 00000000000..c6955f37421 --- /dev/null +++ b/examples/gno.land/p/demo/int256/conversion.gno @@ -0,0 +1,88 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + z.initiateAbs() + + neg := false + if x < 0 { + neg = true + x = -x + } + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(uint64(x)) + z.neg = neg + return z +} + +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + z.initiateAbs() + + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(x) + z.neg = false + return z +} + +// Uint64 returns the lower 64-bits of z +func (z *Int) Uint64() uint64 { + return z.abs.Uint64() +} + +// Int64 returns the lower 64-bits of z +func (z *Int) Int64() int64 { + _abs := z.abs.Clone() + + if z.neg { + return -int64(_abs.Uint64()) + } + return int64(_abs.Uint64()) +} + +// Neg sets z to -x and returns z.) +func (z *Int) Neg(x *Int) *Int { + z.abs.Set(x.abs) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = !x.neg + } + return z +} + +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { + z.abs.Set(x.abs) + z.neg = x.neg + return z +} + +// SetFromUint256 converts a uint256.Uint to Int and sets the value to z. +func (z *Int) SetUint256(x *uint256.Uint) *Int { + z.abs.Set(x) + z.neg = false + return z +} + +// OBS, differs from original mempooler int256 +// ToString returns the decimal representation of z. +func (z *Int) ToString() string { + if z == nil { + panic("int256: nil pointer to ToString()") + } + + t := z.abs.Dec() + if z.neg { + return "-" + t + } + return t +} diff --git a/examples/gno.land/p/demo/int256/conversion_test.gno b/examples/gno.land/p/demo/int256/conversion_test.gno new file mode 100644 index 00000000000..da54c226669 --- /dev/null +++ b/examples/gno.land/p/demo/int256/conversion_test.gno @@ -0,0 +1,171 @@ +package int256 + +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestSetInt64(t *testing.T) { + tests := []struct { + x int64 + want string + }{ + {0, "0"}, + {1, "1"}, + {-1, "-1"}, + {9223372036854775807, "9223372036854775807"}, + {-9223372036854775808, "-9223372036854775808"}, + } + + for _, tc := range tests { + var z Int + z.SetInt64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSetUint64(t *testing.T) { + tests := []struct { + x uint64 + want string + }{ + {0, "0"}, + {1, "1"}, + } + + for _, tc := range tests { + var z Int + z.SetUint64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetUint64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestUint64(t *testing.T) { + tests := []struct { + x string + want uint64 + }{ + {"0", 0}, + {"1", 1}, + {"9223372036854775807", 9223372036854775807}, + {"9223372036854775808", 9223372036854775808}, + {"18446744073709551615", 18446744073709551615}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + {"-1", 1}, + {"-18446744073709551615", 18446744073709551615}, + {"-18446744073709551616", 0}, + {"-18446744073709551617", 1}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Uint64() + if got != tc.want { + t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestInt64(t *testing.T) { + tests := []struct { + x string + want int64 + }{ + {"0", 0}, + {"1", 1}, + {"9223372036854775807", 9223372036854775807}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + {"-1", -1}, + {"-9223372036854775808", -9223372036854775808}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Int64() + if got != tc.want { + t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestNeg(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "-1"}, + {"-1", "1"}, + {"9223372036854775807", "-9223372036854775807"}, + {"-18446744073709551615", "18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Neg(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("Neg(%s) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSet(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "-1"}, + {"9223372036854775807", "9223372036854775807"}, + {"-18446744073709551615", "-18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Set(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("Set(%s) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSetUint256(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "1"}, + {"9223372036854775807", "9223372036854775807"}, + {"18446744073709551615", "18446744073709551615"}, + } + + for _, tc := range tests { + got := New() + + z := uint256.MustFromDecimal(tc.x) + got.SetUint256(z) + + if got.ToString() != tc.want { + t.Errorf("SetUint256(%s) = %s, want %s", tc.x, got.ToString(), tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/int256/gno.mod b/examples/gno.land/p/demo/int256/gno.mod new file mode 100644 index 00000000000..ef906c83c93 --- /dev/null +++ b/examples/gno.land/p/demo/int256/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/int256 + +require gno.land/p/demo/uint256 v0.0.0-latest diff --git a/examples/gno.land/p/demo/int256/int256.gno b/examples/gno.land/p/demo/int256/int256.gno new file mode 100644 index 00000000000..caccd17d531 --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256.gno @@ -0,0 +1,126 @@ +// This package provides a 256-bit signed integer type, Int, and associated functions. +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +var one = uint256.NewUint(1) + +type Int struct { + abs *uint256.Uint + neg bool +} + +// Zero returns a new Int set to 0. +func Zero() *Int { + return NewInt(0) +} + +// One returns a new Int set to 1. +func One() *Int { + return NewInt(1) +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +func (z *Int) Sign() int { + z.initiateAbs() + + if z.abs.IsZero() { + return 0 + } + if z.neg { + return -1 + } + return 1 +} + +// New returns a new Int set to 0. +func New() *Int { + return &Int{ + abs: new(uint256.Uint), + } +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return New().SetInt64(x) +} + +// FromDecimal returns a new Int from a decimal string. +// Returns a new Int and an error if the string is not a valid decimal. +func FromDecimal(s string) (*Int, error) { + return new(Int).SetString(s) +} + +// MustFromDecimal returns a new Int from a decimal string. +// Panics if the string is not a valid decimal. +func MustFromDecimal(s string) *Int { + z, err := FromDecimal(s) + if err != nil { + panic(err) + } + return z +} + +// SetString sets s to the value of z and returns z and a boolean indicating success. +func (z *Int) SetString(s string) (*Int, error) { + neg := false + // Remove max one leading + + if len(s) > 0 && s[0] == '+' { + neg = false + s = s[1:] + } + + if len(s) > 0 && s[0] == '-' { + neg = true + s = s[1:] + } + var ( + abs *uint256.Uint + err error + ) + abs, err = uint256.FromDecimal(s) + if err != nil { + return nil, err + } + + return &Int{ + abs, + neg, + }, nil +} + +// FromUint256 is a convenience-constructor from uint256.Uint. +// Returns a new Int and whether overflow occurred. +// OBS: If u is `nil`, this method returns `nil, false` +func FromUint256(x *uint256.Uint) *Int { + if x == nil { + return nil + } + z := Zero() + + z.SetUint256(x) + return z +} + +// OBS, differs from original mempooler int256 +// NilToZero sets z to 0 and return it if it's nil, otherwise it returns z +func (z *Int) NilToZero() *Int { + if z == nil { + return NewInt(0) + } + return z +} + +// initiateAbs sets default value for `z` or `z.abs` value if is nil +// OBS: differs from mempooler int256. It checks not only `z.abs` but also `z` +func (z *Int) initiateAbs() { + if z == nil || z.abs == nil { + z.abs = new(uint256.Uint) + } +} diff --git a/examples/gno.land/p/demo/int256/int256_test.gno b/examples/gno.land/p/demo/int256/int256_test.gno new file mode 100644 index 00000000000..974cc6ed87f --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256_test.gno @@ -0,0 +1,25 @@ +// ported from github.com/mempooler/int256 +package int256 + +import ( + "testing" +) + +func TestSign(t *testing.T) { + tests := []struct { + x string + want int + }{ + {"0", 0}, + {"1", 1}, + {"-1", -1}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + got := z.Sign() + if got != tc.want { + t.Errorf("Sign(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/json/LICENSE b/examples/gno.land/p/demo/json/LICENSE new file mode 100644 index 00000000000..954b7ef406d --- /dev/null +++ b/examples/gno.land/p/demo/json/LICENSE @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2019 Pyzhov Stepan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/gno.land/p/demo/json/README.md b/examples/gno.land/p/demo/json/README.md new file mode 100644 index 00000000000..86bc9928194 --- /dev/null +++ b/examples/gno.land/p/demo/json/README.md @@ -0,0 +1,170 @@ +# JSON Parser + +The JSON parser is a package that provides functionality for parsing and processing JSON strings. This package accepts JSON strings as byte slices. + +Currently, gno does not [support the `reflect` package](https://docs.gno.land/concepts/effective-gno#reflection-is-never-clear), so it cannot retrieve type information at runtime. Therefore, it is designed to infer and handle type information when parsing JSON strings using a state machine approach. + +After passing through the state machine, JSON strings are represented as the `Node` type. The `Node` type represents nodes for JSON data, including various types such as `ObjectNode`, `ArrayNode`, `StringNode`, `NumberNode`, `BoolNode`, and `NullNode`. + +This package provides methods for manipulating, searching, and extracting the Node type. + +## State Machine + +To parse JSON strings, a [finite state machine](https://en.wikipedia.org/wiki/Finite-state_machine) approach is used. The state machine transitions to the next state based on the current state and the input character while parsing the JSON string. Through this method, type information can be inferred and processed without reflect, and the amount of parser code can be significantly reduced. + +The image below shows the state transitions of the state machine according to the states and input characters. + +```mermaid +stateDiagram-v2 + [*] --> __: Start + __ --> ST: String + __ --> MI: Number + __ --> ZE: Zero + __ --> IN: Integer + __ --> T1: Boolean (true) + __ --> F1: Boolean (false) + __ --> N1: Null + __ --> ec: Empty Object End + __ --> cc: Object End + __ --> bc: Array End + __ --> co: Object Begin + __ --> bo: Array Begin + __ --> cm: Comma + __ --> cl: Colon + __ --> OK: Success/End + ST --> OK: String Complete + MI --> OK: Number Complete + ZE --> OK: Zero Complete + IN --> OK: Integer Complete + T1 --> OK: True Complete + F1 --> OK: False Complete + N1 --> OK: Null Complete + ec --> OK: Empty Object Complete + cc --> OK: Object Complete + bc --> OK: Array Complete + co --> OB: Inside Object + bo --> AR: Inside Array + cm --> KE: Expecting New Key + cm --> VA: Expecting New Value + cl --> VA: Expecting Value + OB --> ST: String in Object (Key) + OB --> ec: Empty Object + OB --> cc: End Object + AR --> ST: String in Array + AR --> bc: End Array + KE --> ST: String as Key + VA --> ST: String as Value + VA --> MI: Number as Value + VA --> T1: True as Value + VA --> F1: False as Value + VA --> N1: Null as Value + OK --> [*]: End +``` + +## Examples + +This package provides parsing functionality along with encoding and decoding functionality. The following examples demonstrate how to use this package. + +### Decoding + +Decoding (or Unmarshaling) is the functionality that converts an input byte slice JSON string into a `Node` type. + +The converted `Node` type allows you to modify the JSON data or search and extract data that meets specific conditions. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + node, err := json.Unmarshal([]byte(`{"foo": "var"}`)) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + ufmt.Sprintf("node: %v", node) +} +``` + +### Encoding + +Encoding (or Marshaling) is the functionality that converts JSON data represented as a Node type into a byte slice JSON string. + +> ⚠️ Caution: Converting a large `Node` type into a JSON string may _impact performance_. or might be cause _unexpected behavior_. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + node := ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + "baz": NumberNode("baz", 100500), + "qux": NullNode("qux"), + }) + + b, err := json.Marshal(node) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + ufmt.Sprintf("json: %s", string(b)) +} +``` + +### Searching + +Once the JSON data converted into a `Node` type, you can **search** and **extract** data that satisfy specific conditions. For example, you can find data with a specific type or data with a specific key. + +To use this functionality, you can use methods in the `GetXXX` prefixed methods. The `MustXXX` methods also provide the same functionality as the former methods, but they will **panic** if data doesn't satisfies the condition. + +Here is an example of finding data with a specific key. For more examples, please refer to the [node.gno](node.gno) file. + +```go +package main + +import ( + "fmt" + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" +) + +func main() { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`)) + if err != nil { + ufmt.Errorf("error: %v", err) + } + + value, err := root.GetKey("foo") + if err != nil { + ufmt.Errorf("error occurred while getting key, %s", err) + } + + if value.MustBool() != true { + ufmt.Errorf("value is not true") + } + + value, err = root.GetKey("bar") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + _, err = root.GetKey("baz") + if err == nil { + t.Errorf("key baz is not exist. must be failed") + } +} +``` + +## Contributing + +Please submit any issues or pull requests for this package through the GitHub repository at [gnolang/gno](). diff --git a/examples/gno.land/p/demo/json/buffer.gno b/examples/gno.land/p/demo/json/buffer.gno new file mode 100644 index 00000000000..23fb53fb0ea --- /dev/null +++ b/examples/gno.land/p/demo/json/buffer.gno @@ -0,0 +1,485 @@ +package json + +import ( + "errors" + "io" + "strings" + + "gno.land/p/demo/ufmt" +) + +type buffer struct { + data []byte + length int + index int + + last States + state States + class Classes +} + +// newBuffer creates a new buffer with the given data +func newBuffer(data []byte) *buffer { + return &buffer{ + data: data, + length: len(data), + last: GO, + state: GO, + } +} + +// first retrieves the first non-whitespace (or other escaped) character in the buffer. +func (b *buffer) first() (byte, error) { + for ; b.index < b.length; b.index++ { + c := b.data[b.index] + + if !(c == whiteSpace || c == carriageReturn || c == newLine || c == tab) { + return c, nil + } + } + + return 0, io.EOF +} + +// current returns the byte of the current index. +func (b *buffer) current() (byte, error) { + if b.index >= b.length { + return 0, io.EOF + } + + return b.data[b.index], nil +} + +// next moves to the next byte and returns it. +func (b *buffer) next() (byte, error) { + b.index++ + return b.current() +} + +// step just moves to the next position. +func (b *buffer) step() error { + _, err := b.next() + return err +} + +// move moves the index by the given position. +func (b *buffer) move(pos int) error { + newIndex := b.index + pos + + if newIndex > b.length { + return io.EOF + } + + b.index = newIndex + + return nil +} + +// slice returns the slice from the current index to the given position. +func (b *buffer) slice(pos int) ([]byte, error) { + end := b.index + pos + + if end > b.length { + return nil, io.EOF + } + + return b.data[b.index:end], nil +} + +// sliceFromIndices returns a slice of the buffer's data starting from 'start' up to (but not including) 'stop'. +func (b *buffer) sliceFromIndices(start, stop int) []byte { + if start > b.length { + start = b.length + } + + if stop > b.length { + stop = b.length + } + + return b.data[start:stop] +} + +// skip moves the index to skip the given byte. +func (b *buffer) skip(bs byte) error { + for b.index < b.length { + if b.data[b.index] == bs && !b.backslash() { + return nil + } + + b.index++ + } + + 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() + if err != nil { + return 0, err + } + + return b.index, nil +} + +// skipUntil moves the buffer index forward until it encounters a byte contained in the endTokens set. +func (b *buffer) skipUntil(endTokens map[byte]bool) (int, error) { + for b.index < b.length { + currentByte, err := b.current() + if err != nil { + return b.index, err + } + + // Check if the current byte is in the set of end tokens. + if _, exists := endTokens[currentByte]; exists { + return b.index, nil + } + + b.index++ + } + + return b.index, io.EOF +} + +// 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{ + dot: true, // access properties of an object + dollarSign: true, // root object + atSign: true, // current object + bracketOpen: true, // start of an array index or filter expression + bracketClose: true, // end of an array index or filter expression +} + +// filterTokens stores the filter expression tokens. +var filterTokens = map[byte]bool{ + aesterisk: true, // wildcard + andSign: true, + orSign: true, +} + +// skipToNextSignificantToken advances the buffer index to the next significant character. +// Significant characters are defined based on the JSON path syntax. +func (b *buffer) skipToNextSignificantToken() { + for b.index < b.length { + current := b.data[b.index] + + if _, ok := significantTokens[current]; ok { + break + } + + b.index++ + } +} + +// backslash checks to see if the number of backslashes before the current index is odd. +// +// This is used to check if the current character is escaped. However, unlike the "unescape" function, +// "backslash" only serves to check the number of backslashes. +func (b *buffer) backslash() bool { + if b.index == 0 { + return false + } + + count := 0 + for i := b.index - 1; ; i-- { + if i >= b.length || b.data[i] != backSlash { + break + } + + count++ + + if i == 0 { + break + } + } + + return count%2 != 0 +} + +// numIndex holds a map of valid numeric characters +var numIndex = map[byte]bool{ + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + '.': true, + 'e': true, + 'E': true, +} + +// pathToken checks if the current token is a valid JSON path token. +func (b *buffer) pathToken() error { + var stack []byte + + inToken := false + inNumber := false + first := b.index + + for b.index < b.length { + c := b.data[b.index] + + switch { + case c == doubleQuote || c == singleQuote: + inToken = true + if err := b.step(); err != nil { + return errors.New("error stepping through buffer") + } + + if err := b.skip(c); err != nil { + return errors.New("unmatched quote in path") + } + + if b.index >= b.length { + return errors.New("unmatched quote in path") + } + + case c == bracketOpen || c == parenOpen: + inToken = true + stack = append(stack, c) + + 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") + } + + stack = stack[:len(stack)-1] + + case pathStateContainsValidPathToken(c): + inToken = true + + case c == plus || c == minus: + if inNumber || (b.index > 0 && numIndex[b.data[b.index-1]]) { + inToken = true + } else if !inToken && (b.index+1 < b.length && numIndex[b.data[b.index+1]]) { + inToken = true + inNumber = true + } else if !inToken { + return errors.New("unexpected operator at start of token") + } + + default: + if len(stack) != 0 || inToken { + inToken = true + } else { + goto end + } + } + + b.index++ + } + +end: + if len(stack) != 0 { + return errors.New("unclosed bracket or parenthesis at end of path") + } + + if first == b.index { + return errors.New("no token found") + } + + if inNumber && !numIndex[b.data[b.index-1]] { + inNumber = false + } + + return nil +} + +func pathStateContainsValidPathToken(c byte) bool { + if _, ok := significantTokens[c]; ok { + return true + } + + if _, ok := filterTokens[c]; ok { + return true + } + + if _, ok := numIndex[c]; ok { + return true + } + + if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' { + return true + } + + return false +} + +func (b *buffer) numeric(token bool) error { + if token { + b.last = GO + } + + for ; b.index < b.length; b.index++ { + b.class = b.getClasses(doubleQuote) + if b.class == __ { + return errors.New("invalid token found while parsing path") + } + + b.state = StateTransitionTable[b.last][b.class] + if b.state == __ { + if token { + break + } + + return errors.New("invalid token found while parsing path") + } + + if b.state < __ { + return nil + } + + if b.state < MI || b.state > E3 { + return nil + } + + b.last = b.state + } + + if b.last != ZE && b.last != IN && b.last != FR && b.last != E3 { + return errors.New("invalid token found while parsing path") + } + + return nil +} + +func (b *buffer) getClasses(c byte) Classes { + if b.data[b.index] >= 128 { + return C_ETC + } + + if c == singleQuote { + return QuoteAsciiClasses[b.data[b.index]] + } + + return AsciiClasses[b.data[b.index]] +} + +func (b *buffer) getState() States { + b.last = b.state + + b.class = b.getClasses(doubleQuote) + if b.class == __ { + return __ + } + + b.state = StateTransitionTable[b.last][b.class] + + return b.state +} + +// string parses a string token from the buffer. +func (b *buffer) string(search byte, token bool) error { + if token { + b.last = GO + } + + for ; b.index < b.length; b.index++ { + b.class = b.getClasses(search) + + if b.class == __ { + return errors.New("invalid token found while parsing path") + } + + b.state = StateTransitionTable[b.last][b.class] + if b.state == __ { + return errors.New("invalid token found while parsing path") + } + + if b.state < __ { + break + } + + b.last = b.state + } + + return nil +} + +func (b *buffer) word(bs []byte) error { + var c byte + + max := len(bs) + index := 0 + + for ; b.index < b.length; b.index++ { + c = b.data[b.index] + + if c != bs[index] { + return errors.New("invalid token found while parsing path") + } + + index++ + if index >= max { + break + } + } + + if index != max { + return errors.New("invalid token found while parsing path") + } + + return nil +} + +func numberKind2f64(value interface{}) (result float64, err error) { + switch typed := value.(type) { + case float64: + result = typed + case float32: + result = float64(typed) + case int: + result = float64(typed) + case int8: + result = float64(typed) + case int16: + result = float64(typed) + case int32: + result = float64(typed) + case int64: + result = float64(typed) + case uint: + result = float64(typed) + case uint8: + result = float64(typed) + case uint16: + result = float64(typed) + case uint32: + result = float64(typed) + case uint64: + result = float64(typed) + default: + err = ufmt.Errorf("invalid number type: %T", value) + } + + return +} diff --git a/examples/gno.land/p/demo/json/buffer_test.gno b/examples/gno.land/p/demo/json/buffer_test.gno new file mode 100644 index 00000000000..a1acce4eba0 --- /dev/null +++ b/examples/gno.land/p/demo/json/buffer_test.gno @@ -0,0 +1,624 @@ +package json + +import ( + "testing" +) + +func TestBufferCurrent(t *testing.T) { + tests := []struct { + name string + buffer *buffer + expected byte + wantErr bool + }{ + { + name: "Valid current byte", + buffer: &buffer{ + data: []byte("test"), + length: 4, + index: 1, + }, + expected: 'e', + wantErr: false, + }, + { + name: "EOF", + buffer: &buffer{ + data: []byte("test"), + length: 4, + index: 4, + }, + expected: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.current() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.current() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.expected { + t.Errorf("buffer.current() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestBufferStep(t *testing.T) { + tests := []struct { + name string + buffer *buffer + wantErr bool + }{ + { + name: "Valid step", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.step() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.step() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestBufferNext(t *testing.T) { + tests := []struct { + name string + buffer *buffer + want byte + wantErr bool + }{ + { + name: "Valid next byte", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + want: 'e', + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + want: 0, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.next() + if (err != nil) != tt.wantErr { + t.Errorf("buffer.next() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("buffer.next() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestBufferSlice(t *testing.T) { + tests := []struct { + name string + buffer *buffer + pos int + want []byte + wantErr bool + }{ + { + name: "Valid slice -- 0 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 0, + want: nil, + wantErr: false, + }, + { + name: "Valid slice -- 1 character", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 1, + want: []byte("t"), + wantErr: false, + }, + { + name: "Valid slice -- 2 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 2, + want: []byte("es"), + wantErr: false, + }, + { + name: "Valid slice -- 3 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 3, + want: []byte("tes"), + wantErr: false, + }, + { + name: "Valid slice -- 4 characters", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + pos: 4, + want: []byte("test"), + wantErr: false, + }, + { + name: "EOF error", + buffer: &buffer{data: []byte("test"), length: 4, index: 3}, + pos: 2, + want: nil, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.buffer.slice(tt.pos) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.slice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if string(got) != string(tt.want) { + t.Errorf("buffer.slice() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestBufferMove(t *testing.T) { + tests := []struct { + name string + buffer *buffer + pos int + wantErr bool + wantIdx int + }{ + { + name: "Valid move", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 2, + wantErr: false, + wantIdx: 3, + }, + { + name: "Move beyond length", + buffer: &buffer{data: []byte("test"), length: 4, index: 1}, + pos: 4, + wantErr: true, + wantIdx: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.move(tt.pos) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.move() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.buffer.index != tt.wantIdx { + t.Errorf("buffer.move() index = %v, want %v", tt.buffer.index, tt.wantIdx) + } + }) + } +} + +func TestBufferSkip(t *testing.T) { + tests := []struct { + name string + buffer *buffer + b byte + wantErr bool + }{ + { + name: "Skip byte", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + b: 'e', + wantErr: false, + }, + { + name: "Skip to EOF", + buffer: &buffer{data: []byte("test"), length: 4, index: 0}, + b: 'x', + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.buffer.skip(tt.b) + if (err != nil) != tt.wantErr { + t.Errorf("buffer.skip() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +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 + input []byte + expected int + }{ + {"No significant chars", []byte("abc"), 3}, + {"One significant char at start", []byte(".abc"), 0}, + {"Significant char in middle", []byte("ab.c"), 2}, + {"Multiple significant chars", []byte("a$.c"), 1}, + {"Significant char at end", []byte("abc$"), 3}, + {"Only significant chars", []byte("$."), 0}, + {"Empty string", []byte(""), 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := newBuffer(tt.input) + b.skipToNextSignificantToken() + if b.index != tt.expected { + t.Errorf("after skipToNextSignificantToken(), got index = %v, want %v", b.index, tt.expected) + } + }) + } +} + +func mockBuffer(s string) *buffer { + return newBuffer([]byte(s)) +} + +func TestSkipAndReturnIndex(t *testing.T) { + tests := []struct { + name string + input string + expected int + }{ + {"StartOfString", "", 0}, + {"MiddleOfString", "abcdef", 1}, + {"EndOfString", "abc", 1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got, err := buf.skipAndReturnIndex() + if err != nil && tt.input != "" { // Expect no error unless input is empty + t.Errorf("skipAndReturnIndex() error = %v", err) + } + if got != tt.expected { + t.Errorf("skipAndReturnIndex() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestSkipUntil(t *testing.T) { + tests := []struct { + name string + input string + tokens map[byte]bool + expected int + }{ + {"SkipToToken", "abcdefg", map[byte]bool{'c': true}, 2}, + {"SkipToEnd", "abcdefg", map[byte]bool{'h': true}, 7}, + {"SkipNone", "abcdefg", map[byte]bool{'a': true}, 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got, err := buf.skipUntil(tt.tokens) + if err != nil && got != len(tt.input) { // Expect error only if reached end without finding token + t.Errorf("skipUntil() error = %v", err) + } + if got != tt.expected { + t.Errorf("skipUntil() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestSliceFromIndices(t *testing.T) { + tests := []struct { + name string + input string + start int + end int + expected string + }{ + {"FullString", "abcdefg", 0, 7, "abcdefg"}, + {"Substring", "abcdefg", 2, 5, "cde"}, + {"OutOfBounds", "abcdefg", 5, 10, "fg"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := mockBuffer(tt.input) + got := buf.sliceFromIndices(tt.start, tt.end) + if string(got) != tt.expected { + t.Errorf("sliceFromIndices() = %v, want %v", string(got), tt.expected) + } + }) + } +} + +func TestBufferToken(t *testing.T) { + tests := []struct { + name string + path string + index int + isErr bool + }{ + { + name: "Simple valid path", + path: "@.length", + index: 8, + isErr: false, + }, + { + name: "Path with array expr", + path: "@['foo'].0.bar", + index: 14, + isErr: false, + }, + { + name: "Path with array expr and simple fomula", + path: "@['foo'].[(@.length - 1)].*", + index: 27, + isErr: false, + }, + { + name: "Path with filter expr", + path: "@['foo'].[?(@.bar == 1 & @.baz < @.length)].*", + index: 45, + isErr: false, + }, + { + name: "addition of foo and bar", + path: "@.foo+@.bar", + index: 11, + isErr: false, + }, + { + name: "logical AND of foo and bar", + path: "@.foo && @.bar", + index: 14, + isErr: false, + }, + { + name: "logical OR of foo and bar", + path: "@.foo || @.bar", + index: 14, + isErr: false, + }, + { + name: "accessing third element of foo", + path: "@.foo,3", + index: 7, + isErr: false, + }, + { + name: "accessing last element of array", + path: "@.length-1", + index: 10, + isErr: false, + }, + { + name: "number 1", + path: "1", + index: 1, + isErr: false, + }, + { + name: "float", + path: "3.1e4", + index: 5, + isErr: false, + }, + { + name: "float with minus", + path: "3.1e-4", + index: 6, + isErr: false, + }, + { + name: "float with plus", + path: "3.1e+4", + index: 6, + isErr: false, + }, + { + name: "negative number", + path: "-12345", + index: 6, + isErr: false, + }, + { + name: "negative float", + path: "-3.1e4", + index: 6, + isErr: false, + }, + { + name: "negative float with minus", + path: "-3.1e-4", + index: 7, + isErr: false, + }, + { + name: "negative float with plus", + path: "-3.1e+4", + index: 7, + isErr: false, + }, + { + name: "string number", + path: "'12345'", + index: 7, + isErr: false, + }, + { + name: "string with backslash", + path: "'foo \\'bar '", + index: 12, + isErr: false, + }, + { + name: "string with inner double quotes", + path: "'foo \"bar \"'", + index: 12, + isErr: false, + }, + { + name: "parenthesis 1", + path: "(@abc)", + index: 6, + isErr: false, + }, + { + name: "parenthesis 2", + path: "[()]", + index: 4, + isErr: false, + }, + { + name: "parenthesis mismatch", + path: "[(])", + index: 2, + isErr: true, + }, + { + name: "parenthesis mismatch 2", + path: "(", + index: 1, + isErr: true, + }, + { + name: "parenthesis mismatch 3", + path: "())]", + index: 2, + isErr: true, + }, + { + name: "bracket mismatch", + path: "[()", + index: 3, + isErr: true, + }, + { + name: "bracket mismatch 2", + path: "()]", + index: 2, + isErr: true, + }, + { + name: "path does not close bracket", + path: "@.foo[)", + index: 6, + isErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := newBuffer([]byte(tt.path)) + + err := buf.pathToken() + if tt.isErr { + if err == nil { + t.Errorf("Expected an error for path `%s`, but got none", tt.path) + } + } + + if err == nil && tt.isErr { + t.Errorf("Expected an error for path `%s`, but got none", tt.path) + } + + if buf.index != tt.index { + t.Errorf("Expected final index %d, got %d (token: `%s`) for path `%s`", tt.index, buf.index, string(buf.data[buf.index]), tt.path) + } + }) + } +} + +func TestBufferFirst(t *testing.T) { + tests := []struct { + name string + data []byte + expected byte + }{ + { + name: "Valid first byte", + data: []byte("test"), + expected: 't', + }, + { + name: "Empty buffer", + data: []byte(""), + expected: 0, + }, + { + name: "Whitespace buffer", + data: []byte(" "), + expected: 0, + }, + { + name: "whitespace in middle", + data: []byte("hello world"), + expected: 'h', + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := newBuffer(tt.data) + + got, err := b.first() + if err != nil && tt.expected != 0 { + t.Errorf("Unexpected error: %v", err) + } + + if got != tt.expected { + t.Errorf("Expected first byte to be %q, got %q", tt.expected, got) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/decode.gno b/examples/gno.land/p/demo/json/decode.gno new file mode 100644 index 00000000000..8a6e2b99846 --- /dev/null +++ b/examples/gno.land/p/demo/json/decode.gno @@ -0,0 +1,370 @@ +// ref: https://github.com/spyzhov/ajson/blob/master/decode.go + +package json + +import ( + "errors" + "io" + + "gno.land/p/demo/ufmt" +) + +// This limits the max nesting depth to prevent stack overflow. +// This is permitted by https://tools.ietf.org/html/rfc7159#section-9 +const maxNestingDepth = 10000 + +// Unmarshal parses the JSON-encoded data and returns a Node. +// The data must be a valid JSON-encoded value. +// +// Usage: +// +// node, err := json.Unmarshal([]byte(`{"key": "value"}`)) +// if err != nil { +// ufmt.Println(err) +// } +// println(node) // {"key": "value"} +func Unmarshal(data []byte) (*Node, error) { + buf := newBuffer(data) + + var ( + state States + key *string + current *Node + nesting int + useKey = func() **string { + tmp := cptrs(key) + key = nil + return &tmp + } + err error + ) + + if _, err = buf.first(); err != nil { + return nil, io.EOF + } + + for { + state = buf.getState() + if state == __ { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + // region state machine + if state >= GO { + switch buf.state { + case ST: // string + if current != nil && current.IsObject() && key == nil { + // key detected + if key, err = getString(buf); err != nil { + return nil, err + } + + buf.state = CO + } else { + current, nesting, err = createNestedNode(current, buf, String, nesting, useKey()) + if err != nil { + return nil, err + } + + err = buf.string(doubleQuote, false) + if err != nil { + return nil, err + } + + current, nesting = updateNode(current, buf, nesting, true) + buf.state = OK + } + + case MI, ZE, IN: // number + current, err = processNumericNode(current, buf, useKey()) + if err != nil { + return nil, err + } + + case T1, F1: // boolean + literal := falseLiteral + if buf.state == T1 { + literal = trueLiteral + } + + current, nesting, err = processLiteralNode(current, buf, Boolean, literal, useKey(), nesting) + if err != nil { + return nil, err + } + + case N1: // null + current, nesting, err = processLiteralNode(current, buf, Null, nullLiteral, useKey(), nesting) + if err != nil { + return nil, err + } + } + } else { + // region action + switch state { + case ec, cc: // } + if key != nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + current, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Object) + if err != nil { + return nil, err + } + + case bc: // ] + current, nesting, err = updateNodeAndSetBufferState(current, buf, nesting, Array) + if err != nil { + return nil, err + } + + case co, bo: // { [ + valTyp, bState := Object, OB + if state == bo { + valTyp, bState = Array, AR + } + + current, nesting, err = createNestedNode(current, buf, valTyp, nesting, useKey()) + if err != nil { + return nil, err + } + + buf.state = bState + + case cm: // , + if current == nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + if !current.isContainer() { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + if current.IsObject() { + buf.state = KE // key expected + } else { + buf.state = VA // value expected + } + + case cl: // : + if current == nil || !current.IsObject() || key == nil { + return nil, unexpectedTokenError(buf.data, buf.index) + } + + buf.state = VA + + default: + return nil, unexpectedTokenError(buf.data, buf.index) + } + } + + if buf.step() != nil { + break + } + + if _, err = buf.first(); err != nil { + err = nil + break + } + } + + if current == nil || buf.state != OK { + return nil, io.EOF + } + + root := current.root() + if !root.ready() { + return nil, io.EOF + } + + return root, err +} + +// UnmarshalSafe parses the JSON-encoded data and returns a Node. +func UnmarshalSafe(data []byte) (*Node, error) { + var safe []byte + safe = append(safe, data...) + return Unmarshal(safe) +} + +// processNumericNode creates a new node, processes a numeric value, +// sets the node's borders, and moves to the previous node. +func processNumericNode(current *Node, buf *buffer, key **string) (*Node, error) { + var err error + current, err = createNode(current, buf, Number, key) + if err != nil { + return nil, err + } + + if err = buf.numeric(false); err != nil { + return nil, err + } + + current.borders[1] = buf.index + if current.prev != nil { + current = current.prev + } + + buf.index -= 1 + buf.state = OK + + return current, nil +} + +// processLiteralNode creates a new node, processes a literal value, +// sets the node's borders, and moves to the previous node. +func processLiteralNode( + current *Node, + buf *buffer, + literalType ValueType, + literalValue []byte, + useKey **string, + nesting int, +) (*Node, int, error) { + var err error + current, nesting, err = createLiteralNode(current, buf, literalType, literalValue, useKey, nesting) + if err != nil { + return nil, nesting, err + } + return current, nesting, nil +} + +// isValidContainerType checks if the current node is a valid container (object or array). +// The container must satisfy the following conditions: +// 1. The current node must not be nil. +// 2. The current node must be an object or array. +// 3. The current node must not be ready. +func isValidContainerType(current *Node, nodeType ValueType) bool { + switch nodeType { + case Object: + return current != nil && current.IsObject() && !current.ready() + case Array: + return current != nil && current.IsArray() && !current.ready() + default: + return false + } +} + +// getString extracts a string from the buffer and advances the buffer index past the string. +func getString(b *buffer) (*string, error) { + start := b.index + if err := b.string(doubleQuote, false); err != nil { + return nil, err + } + + value, ok := Unquote(b.data[start:b.index+1], doubleQuote) + if !ok { + return nil, unexpectedTokenError(b.data, start) + } + + return &value, nil +} + +// createNode creates a new node and sets the key if it is not nil. +func createNode( + current *Node, + buf *buffer, + nodeType ValueType, + key **string, +) (*Node, error) { + var err error + current, err = NewNode(current, buf, nodeType, key) + if err != nil { + return nil, err + } + + return current, nil +} + +// createNestedNode creates a new nested node (array or object) and sets the key if it is not nil. +func createNestedNode( + current *Node, + buf *buffer, + nodeType ValueType, + nesting int, + key **string, +) (*Node, int, error) { + var err error + if nesting, err = checkNestingDepth(nesting); err != nil { + return nil, nesting, err + } + + if current, err = createNode(current, buf, nodeType, key); err != nil { + return nil, nesting, err + } + + return current, nesting, nil +} + +// createLiteralNode creates a new literal node and sets the key if it is not nil. +// The literal is a byte slice that represents a boolean or null value. +func createLiteralNode( + current *Node, + buf *buffer, + literalType ValueType, + literal []byte, + useKey **string, + nesting int, +) (*Node, int, error) { + var err error + if current, err = createNode(current, buf, literalType, useKey); err != nil { + return nil, 0, err + } + + if err = buf.word(literal); err != nil { + return nil, 0, err + } + + current, nesting = updateNode(current, buf, nesting, false) + buf.state = OK + + return current, nesting, nil +} + +// updateNode updates the current node and returns the previous node. +func updateNode( + current *Node, buf *buffer, nesting int, decreaseLevel bool, +) (*Node, int) { + current.borders[1] = buf.index + 1 + + prev := current.prev + if prev == nil { + return current, nesting + } + + current = prev + if decreaseLevel { + nesting-- + } + + return current, nesting +} + +// updateNodeAndSetBufferState updates the current node and sets the buffer state to OK. +func updateNodeAndSetBufferState( + current *Node, + buf *buffer, + nesting int, + typ ValueType, +) (*Node, int, error) { + if !isValidContainerType(current, typ) { + return nil, nesting, unexpectedTokenError(buf.data, buf.index) + } + + current, nesting = updateNode(current, buf, nesting, true) + buf.state = OK + + return current, nesting, nil +} + +// checkNestingDepth checks if the nesting depth is within the maximum allowed depth. +func checkNestingDepth(nesting int) (int, error) { + if nesting >= maxNestingDepth { + return nesting, errors.New("maximum nesting depth exceeded") + } + + return nesting + 1, nil +} + +func unexpectedTokenError(data []byte, index int) error { + return ufmt.Errorf("unexpected token at index %d. data %b", index, data) +} diff --git a/examples/gno.land/p/demo/json/decode_test.gno b/examples/gno.land/p/demo/json/decode_test.gno new file mode 100644 index 00000000000..5a04846224e --- /dev/null +++ b/examples/gno.land/p/demo/json/decode_test.gno @@ -0,0 +1,555 @@ +package json + +import ( + "bytes" + "encoding/json" + "testing" +) + +type testNode struct { + name string + input []byte + _type ValueType + value []byte +} + +func simpleValid(test *testNode, t *testing.T) { + root, err := Unmarshal(test.input) + if err != nil { + t.Errorf("Error on Unmarshal(%s): %s", test.input, err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal(%s): root is nil", test.name) + } else if root.nodeType != test._type { + t.Errorf("Error on Unmarshal(%s): wrong type", test.name) + } else if !bytes.Equal(root.source(), test.value) { + t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value) + } +} + +func simpleInvalid(test *testNode, t *testing.T) { + root, err := Unmarshal(test.input) + if err == nil { + t.Errorf("Error on Unmarshal(%s): error expected, got '%s'", test.name, root.source()) + } else if root != nil { + t.Errorf("Error on Unmarshal(%s): root is not nil", test.name) + } +} + +func simpleCorrupted(name string) *testNode { + return &testNode{name: name, input: []byte(name)} +} + +func TestUnmarshal_StringSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "blank", input: []byte("\"\""), _type: String, value: []byte("\"\"")}, + {name: "char", input: []byte("\"c\""), _type: String, value: []byte("\"c\"")}, + {name: "word", input: []byte("\"cat\""), _type: String, value: []byte("\"cat\"")}, + {name: "spaces", input: []byte(" \"good cat or dog\"\r\n "), _type: String, value: []byte("\"good cat or dog\"")}, + {name: "backslash", input: []byte("\"good \\\"cat\\\"\""), _type: String, value: []byte("\"good \\\"cat\\\"\"")}, + {name: "backslash 2", input: []byte("\"good \\\\\\\"cat\\\"\""), _type: String, value: []byte("\"good \\\\\\\"cat\\\"\"")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_NumericSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "1", input: []byte("1"), _type: Number, value: []byte("1")}, + {name: "-1", input: []byte("-1"), _type: Number, value: []byte("-1")}, + + {name: "1234567890", input: []byte("1234567890"), _type: Number, value: []byte("1234567890")}, + {name: "-123", input: []byte("-123"), _type: Number, value: []byte("-123")}, + + {name: "123.456", input: []byte("123.456"), _type: Number, value: []byte("123.456")}, + {name: "-123.456", input: []byte("-123.456"), _type: Number, value: []byte("-123.456")}, + + {name: "1e3", input: []byte("1e3"), _type: Number, value: []byte("1e3")}, + {name: "1e+3", input: []byte("1e+3"), _type: Number, value: []byte("1e+3")}, + {name: "1e-3", input: []byte("1e-3"), _type: Number, value: []byte("1e-3")}, + {name: "-1e3", input: []byte("-1e3"), _type: Number, value: []byte("-1e3")}, + {name: "-1e-3", input: []byte("-1e-3"), _type: Number, value: []byte("-1e-3")}, + + {name: "1.123e3456", input: []byte("1.123e3456"), _type: Number, value: []byte("1.123e3456")}, + {name: "1.123e-3456", input: []byte("1.123e-3456"), _type: Number, value: []byte("1.123e-3456")}, + {name: "-1.123e3456", input: []byte("-1.123e3456"), _type: Number, value: []byte("-1.123e3456")}, + {name: "-1.123e-3456", input: []byte("-1.123e-3456"), _type: Number, value: []byte("-1.123e-3456")}, + + {name: "1E3", input: []byte("1E3"), _type: Number, value: []byte("1E3")}, + {name: "1E-3", input: []byte("1E-3"), _type: Number, value: []byte("1E-3")}, + {name: "-1E3", input: []byte("-1E3"), _type: Number, value: []byte("-1E3")}, + {name: "-1E-3", input: []byte("-1E-3"), _type: Number, value: []byte("-1E-3")}, + + {name: "1.123E3456", input: []byte("1.123E3456"), _type: Number, value: []byte("1.123E3456")}, + {name: "1.123E-3456", input: []byte("1.123E-3456"), _type: Number, value: []byte("1.123E-3456")}, + {name: "-1.123E3456", input: []byte("-1.123E3456"), _type: Number, value: []byte("-1.123E3456")}, + {name: "-1.123E-3456", input: []byte("-1.123E-3456"), _type: Number, value: []byte("-1.123E-3456")}, + + {name: "-1.123E-3456 with spaces", input: []byte(" \r -1.123E-3456 \t\n"), _type: Number, value: []byte("-1.123E-3456")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + root, err := Unmarshal(test.input) + if err != nil { + t.Errorf("Error on Unmarshal(%s): %s", test.name, err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal(%s): root is nil", test.name) + } else if root.nodeType != test._type { + t.Errorf("Error on Unmarshal(%s): wrong type", test.name) + } else if !bytes.Equal(root.source(), test.value) { + t.Errorf("Error on Unmarshal(%s): %s != %s", test.name, root.source(), test.value) + } + }) + } +} + +func TestUnmarshal_StringSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + {name: "white NL", input: []byte("\"foo\nbar\"")}, + {name: "white R", input: []byte("\"foo\rbar\"")}, + {name: "white Tab", input: []byte("\"foo\tbar\"")}, + {name: "wrong quotes", input: []byte("'cat'")}, + {name: "double string", input: []byte("\"Hello\" \"World\"")}, + {name: "quotes in quotes", input: []byte("\"good \"cat\"\"")}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_ObjectSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "{}", input: []byte("{}"), _type: Object, value: []byte("{}")}, + {name: `{ \r\n }`, input: []byte("{ \r\n }"), _type: Object, value: []byte("{ \r\n }")}, + {name: `{"key":1}`, input: []byte(`{"key":1}`), _type: Object, value: []byte(`{"key":1}`)}, + {name: `{"key":true}`, input: []byte(`{"key":true}`), _type: Object, value: []byte(`{"key":true}`)}, + {name: `{"key":"value"}`, input: []byte(`{"key":"value"}`), _type: Object, value: []byte(`{"key":"value"}`)}, + {name: `{"foo":"bar","baz":"foo"}`, input: []byte(`{"foo":"bar", "baz":"foo"}`), _type: Object, value: []byte(`{"foo":"bar", "baz":"foo"}`)}, + {name: "spaces", input: []byte(` { "foo" : "bar" , "baz" : "foo" } `), _type: Object, value: []byte(`{ "foo" : "bar" , "baz" : "foo" }`)}, + {name: "nested", input: []byte(`{"foo":{"bar":{"baz":{}}}}`), _type: Object, value: []byte(`{"foo":{"bar":{"baz":{}}}}`)}, + {name: "array", input: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`), _type: Object, value: []byte(`{"array":[{},{},{"foo":[{"bar":["baz"]}]}]}`)}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_ObjectSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("{{{\"key\": \"foo\"{{{{"), + simpleCorrupted("}"), + simpleCorrupted("{ }}}}}}}"), + simpleCorrupted(" }"), + simpleCorrupted("{,}"), + simpleCorrupted("{:}"), + simpleCorrupted("{100000}"), + simpleCorrupted("{1:1}"), + simpleCorrupted("{'1:2,3:4'}"), + simpleCorrupted(`{"d"}`), + simpleCorrupted(`{"foo"}`), + simpleCorrupted(`{"foo":}`), + simpleCorrupted(`{:"foo"}`), + simpleCorrupted(`{"foo":bar}`), + simpleCorrupted(`{"foo":"bar",}`), + simpleCorrupted(`{}{}`), + simpleCorrupted(`{},{}`), + simpleCorrupted(`{[},{]}`), + simpleCorrupted(`{[,]}`), + simpleCorrupted(`{[]}`), + simpleCorrupted(`{}1`), + simpleCorrupted(`1{}`), + simpleCorrupted(`{"x"::1}`), + simpleCorrupted(`{null:null}`), + simpleCorrupted(`{"foo:"bar"}`), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_NullSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + {name: "nul", input: []byte("nul")}, + {name: "nil", input: []byte("nil")}, + {name: "nill", input: []byte("nill")}, + {name: "NILL", input: []byte("NILL")}, + {name: "Null", input: []byte("Null")}, + {name: "NULL", input: []byte("NULL")}, + {name: "spaces", input: []byte("Nu ll")}, + {name: "null1", input: []byte("null1")}, + {name: "double", input: []byte("null null")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_BoolSimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "lower true", input: []byte("true"), _type: Boolean, value: []byte("true")}, + {name: "lower false", input: []byte("false"), _type: Boolean, value: []byte("false")}, + {name: "spaces true", input: []byte(" true\r\n "), _type: Boolean, value: []byte("true")}, + {name: "spaces false", input: []byte(" false\r\n "), _type: Boolean, value: []byte("false")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_BoolSimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("tru"), + simpleCorrupted("fals"), + simpleCorrupted("tre"), + simpleCorrupted("fal se"), + simpleCorrupted("true false"), + simpleCorrupted("True"), + simpleCorrupted("TRUE"), + simpleCorrupted("False"), + simpleCorrupted("FALSE"), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +func TestUnmarshal_ArraySimpleSuccess(t *testing.T) { + tests := []*testNode{ + {name: "[]", input: []byte("[]"), _type: Array, value: []byte("[]")}, + {name: "[1]", input: []byte("[1]"), _type: Array, value: []byte("[1]")}, + {name: "[1,2,3]", input: []byte("[1,2,3]"), _type: Array, value: []byte("[1,2,3]")}, + {name: "[1, 2, 3]", input: []byte("[1, 2, 3]"), _type: Array, value: []byte("[1, 2, 3]")}, + {name: "[1,[2],3]", input: []byte("[1,[2],3]"), _type: Array, value: []byte("[1,[2],3]")}, + {name: "[[],[],[]]", input: []byte("[[],[],[]]"), _type: Array, value: []byte("[[],[],[]]")}, + {name: "[[[[[]]]]]", input: []byte("[[[[[]]]]]"), _type: Array, value: []byte("[[[[[]]]]]")}, + {name: "[true,null,1,\"foo\",[]]", input: []byte("[true,null,1,\"foo\",[]]"), _type: Array, value: []byte("[true,null,1,\"foo\",[]]")}, + {name: "spaces", input: []byte("\n\r [\n1\n ]\r\n"), _type: Array, value: []byte("[\n1\n ]")}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleValid(test, t) + }) + } +} + +func TestUnmarshal_ArraySimpleCorrupted(t *testing.T) { + tests := []*testNode{ + simpleCorrupted("[,]"), + simpleCorrupted("[]\\"), + simpleCorrupted("[1,]"), + simpleCorrupted("[[]"), + simpleCorrupted("[]]"), + simpleCorrupted("1[]"), + simpleCorrupted("[]1"), + simpleCorrupted("[[]1]"), + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + simpleInvalid(test, t) + }) + } +} + +// Examples from https://json.org/example.html +func TestUnmarshal(t *testing.T) { + tests := []struct { + name string + value string + }{ + { + name: "glossary", + value: `{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } + }`, + }, + { + name: "menu", + value: `{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } + }}`, + }, + { + name: "widget", + value: `{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + }} `, + }, + { + name: "web-app", + value: `{"web-app": { + "servlet": [ + { + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500}}, + { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2"}}, + { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet"}, + + { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet"}, + { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true}}], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*"}, + + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}`, + }, + { + name: "SVG Viewer", + value: `{"menu": { + "header": "SVG Viewer", + "items": [ + {"id": "Open"}, + {"id": "OpenNew", "label": "Open New"}, + null, + {"id": "ZoomIn", "label": "Zoom In"}, + {"id": "ZoomOut", "label": "Zoom Out"}, + {"id": "OriginalView", "label": "Original View"}, + null, + {"id": "Quality"}, + {"id": "Pause"}, + {"id": "Mute"}, + null, + {"id": "Find", "label": "Find..."}, + {"id": "FindAgain", "label": "Find Again"}, + {"id": "Copy"}, + {"id": "CopyAgain", "label": "Copy Again"}, + {"id": "CopySVG", "label": "Copy SVG"}, + {"id": "ViewSVG", "label": "View SVG"}, + {"id": "ViewSource", "label": "View Source"}, + {"id": "SaveAs", "label": "Save As"}, + null, + {"id": "Help"}, + {"id": "About", "label": "About Adobe CVG Viewer..."} + ] + }}`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := Unmarshal([]byte(test.value)) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } + }) + } +} + +func TestUnmarshalSafe(t *testing.T) { + json := []byte(`{ "store": { + "book": [ + { "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + } + }`) + safe, err := UnmarshalSafe(json) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } else if safe == nil { + t.Errorf("Error on Unmarshal: safe is nil") + } else { + root, err := Unmarshal(json) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } else if root == nil { + t.Errorf("Error on Unmarshal: root is nil") + } else if !bytes.Equal(root.source(), safe.source()) { + t.Errorf("Error on UnmarshalSafe: values not same") + } + } +} + +// BenchmarkGoStdUnmarshal-8 61698 19350 ns/op 288 B/op 6 allocs/op +// BenchmarkUnmarshal-8 45620 26165 ns/op 21889 B/op 367 allocs/op +// +// type bench struct { +// Name string `json:"name"` +// Value int `json:"value"` +// } + +// func BenchmarkGoStdUnmarshal(b *testing.B) { +// data := []byte(webApp) +// for i := 0; i < b.N; i++ { +// err := json.Unmarshal(data, &bench{}) +// if err != nil { +// b.Fatal(err) +// } +// } +// } + +// func BenchmarkUnmarshal(b *testing.B) { +// data := []byte(webApp) +// for i := 0; i < b.N; i++ { +// _, err := Unmarshal(data) +// if err != nil { +// b.Fatal(err) +// } +// } +// } 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 new file mode 100644 index 00000000000..6a29f7f1350 --- /dev/null +++ b/examples/gno.land/p/demo/json/eisel_lemire/eisel_lemire.gno @@ -0,0 +1,839 @@ +// 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 new file mode 100644 index 00000000000..d6670de82e2 --- /dev/null +++ b/examples/gno.land/p/demo/json/eisel_lemire/gno.mod @@ -0,0 +1 @@ +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 new file mode 100644 index 00000000000..be90d7aa73d --- /dev/null +++ b/examples/gno.land/p/demo/json/encode.gno @@ -0,0 +1,128 @@ +package json + +import ( + "bytes" + "errors" + "math" + "strconv" + + "gno.land/p/demo/json/ryu" + "gno.land/p/demo/ufmt" +) + +// Marshal returns the JSON encoding of a Node. +func Marshal(node *Node) ([]byte, error) { + var ( + buf bytes.Buffer + sVal string + bVal bool + nVal float64 + oVal []byte + err error + ) + + if node == nil { + return nil, errors.New("node is nil") + } + + if !node.modified && !node.ready() { + return nil, errors.New("node is not ready") + } + + if !node.modified && node.ready() { + buf.Write(node.source()) + } + + if node.modified { + switch node.nodeType { + case Null: + buf.Write(nullLiteral) + + case Number: + nVal, err = node.GetNumeric() + if err != nil { + 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) + } + + case String: + sVal, err = node.GetString() + if err != nil { + return nil, err + } + + quoted := ufmt.Sprintf("%s", strconv.Quote(sVal)) + buf.WriteString(quoted) + + case Boolean: + bVal, err = node.GetBool() + if err != nil { + return nil, err + } + + bStr := ufmt.Sprintf("%t", bVal) + buf.WriteString(bStr) + + case Array: + buf.WriteByte(bracketOpen) + + for i := 0; i < len(node.next); i++ { + if i != 0 { + buf.WriteByte(comma) + } + + elem, ok := node.next[strconv.Itoa(i)] + if !ok { + return nil, ufmt.Errorf("array element %d is not found", i) + } + + oVal, err = Marshal(elem) + if err != nil { + return nil, err + } + + buf.Write(oVal) + } + + buf.WriteByte(bracketClose) + + case Object: + buf.WriteByte(curlyOpen) + + bVal = false + for k, v := range node.next { + if bVal { + buf.WriteByte(comma) + } else { + bVal = true + } + + key := ufmt.Sprintf("%s", strconv.Quote(k)) + buf.WriteString(key) + buf.WriteByte(colon) + + oVal, err = Marshal(v) + if err != nil { + return nil, err + } + + buf.Write(oVal) + } + + buf.WriteByte(curlyClose) + } + } + + return buf.Bytes(), nil +} diff --git a/examples/gno.land/p/demo/json/encode_test.gno b/examples/gno.land/p/demo/json/encode_test.gno new file mode 100644 index 00000000000..33a1fae3d4e --- /dev/null +++ b/examples/gno.land/p/demo/json/encode_test.gno @@ -0,0 +1,257 @@ +package json + +import ( + "testing" +) + +func TestMarshal_Primitive(t *testing.T) { + tests := []struct { + name string + node *Node + }{ + { + name: "null", + node: NullNode(""), + }, + { + name: "true", + node: BoolNode("", true), + }, + { + name: "false", + node: BoolNode("", false), + }, + { + name: `"string"`, + node: StringNode("", "string"), + }, + { + name: `"one \"encoded\" string"`, + node: StringNode("", `one "encoded" string`), + }, + { + name: `{"foo":"bar"}`, + node: ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + }), + }, + { + name: "42", + node: NumberNode("", 42), + }, + // TODO: fix output for not to use scientific notation + { + name: "1.005e+02", + node: NumberNode("", 100.5), + }, + { + name: `[1,2,3]`, + node: ArrayNode("", []*Node{ + NumberNode("0", 1), + NumberNode("2", 2), + NumberNode("3", 3), + }), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, err := Marshal(test.node) + if err != nil { + t.Errorf("unexpected error: %s", err) + } else if string(value) != test.name { + t.Errorf("wrong result: '%s', expected '%s'", value, test.name) + } + }) + } +} + +func TestMarshal_Object(t *testing.T) { + node := ObjectNode("", map[string]*Node{ + "foo": StringNode("foo", "bar"), + "baz": NumberNode("baz", 100500), + "qux": NullNode("qux"), + }) + + mustKey := []string{"foo", "baz", "qux"} + + value, err := Marshal(node) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // the order of keys in the map is not guaranteed + // so we need to unmarshal the result and check the keys + decoded, err := Unmarshal(value) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + for _, key := range mustKey { + if node, err := decoded.GetKey(key); err != nil { + t.Errorf("unexpected error: %s", err) + } else { + if node == nil { + t.Errorf("node is nil") + } else if node.key == nil { + t.Errorf("key is nil") + } else if *node.key != key { + t.Errorf("wrong key: '%s', expected '%s'", *node.key, key) + } + } + } +} + +func valueNode(prev *Node, key string, typ ValueType, val interface{}) *Node { + curr := &Node{ + prev: prev, + data: nil, + key: &key, + borders: [2]int{0, 0}, + value: val, + modified: true, + } + + if val != nil { + curr.nodeType = typ + } + + return curr +} + +func TestMarshal_Errors(t *testing.T) { + tests := []struct { + name string + node func() (node *Node) + }{ + { + name: "nil", + node: func() (node *Node) { + return + }, + }, + { + name: "broken", + node: func() (node *Node) { + node = Must(Unmarshal([]byte(`{}`))) + node.borders[1] = 0 + return + }, + }, + { + name: "Numeric", + node: func() (node *Node) { + return valueNode(nil, "", Number, false) + }, + }, + { + name: "String", + node: func() (node *Node) { + return valueNode(nil, "", String, false) + }, + }, + { + name: "Bool", + node: func() (node *Node) { + return valueNode(nil, "", Boolean, 1) + }, + }, + { + name: "Array_1", + node: func() (node *Node) { + node = ArrayNode("", nil) + node.next["1"] = NullNode("1") + return + }, + }, + { + name: "Array_2", + node: func() (node *Node) { + return ArrayNode("", []*Node{valueNode(nil, "", Boolean, 1)}) + }, + }, + { + name: "Object", + node: func() (node *Node) { + return ObjectNode("", map[string]*Node{"key": valueNode(nil, "key", Boolean, 1)}) + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, err := Marshal(test.node()) + if err == nil { + t.Errorf("expected error") + } else if len(value) != 0 { + t.Errorf("wrong result") + } + }) + } +} + +func TestMarshal_Nil(t *testing.T) { + _, err := Marshal(nil) + if err == nil { + t.Error("Expected error for nil node, but got nil") + } +} + +func TestMarshal_NotModified(t *testing.T) { + node := &Node{} + _, err := Marshal(node) + if err == nil { + t.Error("Expected error for not modified node, but got nil") + } +} + +func TestMarshalCycleReference(t *testing.T) { + node1 := &Node{ + key: stringPtr("node1"), + nodeType: String, + next: map[string]*Node{ + "next": nil, + }, + } + + node2 := &Node{ + key: stringPtr("node2"), + nodeType: String, + prev: node1, + } + + node1.next["next"] = node2 + + _, err := Marshal(node1) + if err == nil { + t.Error("Expected error for cycle reference, but got nil") + } +} + +func TestMarshalNoCycleReference(t *testing.T) { + node1 := &Node{ + key: stringPtr("node1"), + nodeType: String, + value: "value1", + modified: true, + } + + node2 := &Node{ + key: stringPtr("node2"), + nodeType: String, + value: "value2", + modified: true, + } + + _, err := Marshal(node1) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + _, err = Marshal(node2) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } +} + +func stringPtr(s string) *string { + return &s +} diff --git a/examples/gno.land/p/demo/json/escape.gno b/examples/gno.land/p/demo/json/escape.gno new file mode 100644 index 00000000000..5a834068127 --- /dev/null +++ b/examples/gno.land/p/demo/json/escape.gno @@ -0,0 +1,300 @@ +package json + +import ( + "bytes" + "errors" + "unicode/utf8" +) + +const ( + supplementalPlanesOffset = 0x10000 + highSurrogateOffset = 0xD800 + lowSurrogateOffset = 0xDC00 + surrogateEnd = 0xDFFF + basicMultilingualPlaneOffset = 0xFFFF + badHex = -1 +) + +var hexLookupTable = [256]int{ + '0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4, + '5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9, + 'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF, + 'a': 0xA, 'b': 0xB, 'c': 0xC, 'd': 0xD, 'e': 0xE, 'f': 0xF, + // Fill unspecified index-value pairs with key and value of -1 + 'G': -1, 'H': -1, 'I': -1, 'J': -1, + 'K': -1, 'L': -1, 'M': -1, 'N': -1, + 'O': -1, 'P': -1, 'Q': -1, 'R': -1, + 'S': -1, 'T': -1, 'U': -1, 'V': -1, + 'W': -1, 'X': -1, 'Y': -1, 'Z': -1, + 'g': -1, 'h': -1, 'i': -1, 'j': -1, + 'k': -1, 'l': -1, 'm': -1, 'n': -1, + 'o': -1, 'p': -1, 'q': -1, 'r': -1, + 's': -1, 't': -1, 'u': -1, 'v': -1, + 'w': -1, 'x': -1, 'y': -1, 'z': -1, +} + +func h2i(c byte) int { + return hexLookupTable[c] +} + +// Unescape takes an input byte slice, processes it to Unescape certain characters, +// and writes the result into an output byte slice. +// +// 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. + 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 + + // find the next backslash in the remaining input + nextBackslash := bytes.IndexByte(input, backSlash) + if nextBackslash == -1 { + copy(buf, input) + buf = buf[len(input):] + break + } + + copy(buf, input[:nextBackslash]) + + input = input[nextBackslash:] + buf = buf[nextBackslash:] + } + + return output[:len(output)-len(buf)], nil +} + +// isSurrogatePair returns true if the rune is a surrogate pair. +// +// A surrogate pairs are used in UTF-16 encoding to encode characters +// outside the Basic Multilingual Plane (BMP). +func isSurrogatePair(r rune) bool { + return highSurrogateOffset <= r && r <= surrogateEnd +} + +// combineSurrogates reconstruct the original unicode code points in the +// supplemental plane by combinin the high and low surrogate. +// +// The hight surrogate in the range from U+D800 to U+DBFF, +// and the low surrogate in the range from U+DC00 to U+DFFF. +// +// The formula to combine the surrogates is: +// (high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000 +func combineSurrogates(high, low rune) rune { + return ((high - highSurrogateOffset) << 10) + (low - lowSurrogateOffset) + supplementalPlanesOffset +} + +// deocdeSingleUnicodeEscape decodes a unicode escape sequence (e.g., \uXXXX) into a rune. +func decodeSingleUnicodeEscape(b []byte) (rune, bool) { + if len(b) < 6 { + return utf8.RuneError, false + } + + // convert hex to decimal + h1, h2, h3, h4 := h2i(b[2]), h2i(b[3]), h2i(b[4]), h2i(b[5]) + if h1 == badHex || h2 == badHex || h3 == badHex || h4 == badHex { + return utf8.RuneError, false + } + + return rune(h1<<12 + h2<<8 + h3<<4 + h4), true +} + +// decodeUnicodeEscape decodes a Unicode escape sequence from a byte slice. +func decodeUnicodeEscape(b []byte) (rune, int) { + r, ok := decodeSingleUnicodeEscape(b) + if !ok { + return utf8.RuneError, -1 + } + + // determine valid unicode escapes within the BMP + 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 { + return utf8.RuneError, -1 + } + + if r2 < lowSurrogateOffset { + return utf8.RuneError, -1 + } + + return combineSurrogates(r, r2), 12 +} + +var escapeByteSet = [256]byte{ + '"': doubleQuote, + '\\': backSlash, + '/': slash, + 'b': backSpace, + 'f': formFeed, + 'n': newLine, + 'r': carriageReturn, + 't': tab, +} + +// Unquote takes a byte slice and unquotes it by removing +// the surrounding quotes and unescaping the contents. +func Unquote(s []byte, border byte) (string, bool) { + s, ok := unquoteBytes(s, border) + return string(s), ok +} + +// 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 + } + + s = s[1 : len(s)-1] + + r := 0 + for r < len(s) { + c := s[r] + + if c == backSlash || c == border || c < 0x20 { + break + } + + if c < utf8.RuneSelf { + r++ + continue + } + + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + + r += size + } + + if r == len(s) { + return s, true + } + + utfDoubleMax := utf8.UTFMax * 2 + b := make([]byte, len(s)+utfDoubleMax) + w := copy(b, s[0:r]) + + for r < len(s) { + if w >= len(b)-utf8.UTFMax { + nb := make([]byte, utfDoubleMax+(2*len(b))) + copy(nb, b) + b = nb + } + + c := s[r] + if c == backSlash { + r++ + if r >= len(s) { + return nil, false + } + + if s[r] == 'u' { + rr, res := decodeUnicodeEscape(s[r-1:]) + if res < 0 { + return nil, false + } + + w += utf8.EncodeRune(b[w:], rr) + r += 5 + } else { + decode := escapeByteSet[s[r]] + if decode == 0 { + return nil, false + } + + if decode == doubleQuote || decode == backSlash || decode == slash { + decode = s[r] + } + + b[w] = decode + r++ + w++ + } + } else if c == border || c < 0x20 { + return nil, false + } else if c < utf8.RuneSelf { + b[w] = c + r++ + w++ + } else { + rr, size := utf8.DecodeRune(s[r:]) + + if rr == utf8.RuneError && size == 1 { + return nil, false + } + + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + + 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. +func processEscapedUTF8(in, out []byte) (int, int, error) { + if len(in) < 2 || in[0] != backSlash { + return -1, -1, errors.New("invalid escape sequence") + } + + escapeSeqLen := 2 + escapeChar := in[1] + + if escapeChar != 'u' { + val := escapeByteSet[escapeChar] + if val == 0 { + return -1, -1, errors.New("invalid escape sequence") + } + + out[0] = val + return escapeSeqLen, 1, nil + } + + r, size := decodeUnicodeEscape(in) + if size == -1 { + return -1, -1, errors.New("invalid escape sequence") + } + + outLen := utf8.EncodeRune(out, r) + + return size, outLen, nil +} diff --git a/examples/gno.land/p/demo/json/escape_test.gno b/examples/gno.land/p/demo/json/escape_test.gno new file mode 100644 index 00000000000..40c118d93ce --- /dev/null +++ b/examples/gno.land/p/demo/json/escape_test.gno @@ -0,0 +1,222 @@ +package json + +import ( + "bytes" + "testing" + "unicode/utf8" +) + +func TestHexToInt(t *testing.T) { + tests := []struct { + name string + c byte + want int + }{ + {"Digit 0", '0', 0}, + {"Digit 9", '9', 9}, + {"Uppercase A", 'A', 10}, + {"Uppercase F", 'F', 15}, + {"Lowercase a", 'a', 10}, + {"Lowercase f", 'f', 15}, + {"Invalid character1", 'g', badHex}, + {"Invalid character2", 'G', badHex}, + {"Invalid character3", 'z', badHex}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := h2i(tt.c); got != tt.want { + t.Errorf("h2i() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsSurrogatePair(t *testing.T) { + testCases := []struct { + name string + r rune + expected bool + }{ + {"high surrogate start", 0xD800, true}, + {"high surrogate end", 0xDBFF, true}, + {"low surrogate start", 0xDC00, true}, + {"low surrogate end", 0xDFFF, true}, + {"Non-surrogate", 0x0000, false}, + {"Non-surrogate 2", 0xE000, false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if got := isSurrogatePair(tc.r); got != tc.expected { + t.Errorf("isSurrogate() = %v, want %v", got, tc.expected) + } + }) + } +} + +func TestCombineSurrogates(t *testing.T) { + testCases := []struct { + high, low rune + expected rune + }{ + {0xD83D, 0xDC36, 0x1F436}, // 🐶 U+1F436 DOG FACE + {0xD83D, 0xDE00, 0x1F600}, // 😀 U+1F600 GRINNING FACE + {0xD83C, 0xDF03, 0x1F303}, // 🌃 U+1F303 NIGHT WITH STARS + } + + for _, tc := range testCases { + result := combineSurrogates(tc.high, tc.low) + if result != tc.expected { + t.Errorf("combineSurrogates(%U, %U) = %U; want %U", tc.high, tc.low, result, tc.expected) + } + } +} + +func TestDecodeSingleUnicodeEscape(t *testing.T) { + testCases := []struct { + input []byte + expected rune + isValid bool + }{ + // valid unicode escape sequences + {[]byte(`\u0041`), 'A', true}, + {[]byte(`\u03B1`), 'α', true}, + {[]byte(`\u00E9`), 'é', true}, // valid non-English character + {[]byte(`\u0021`), '!', true}, // valid special character + {[]byte(`\uFF11`), '1', true}, + {[]byte(`\uD83D`), 0xD83D, true}, + {[]byte(`\uDE03`), 0xDE03, true}, + + // invalid unicode escape sequences + {[]byte(`\u004`), utf8.RuneError, false}, // too short + {[]byte(`\uXYZW`), utf8.RuneError, false}, // invalid hex + {[]byte(`\u00G1`), utf8.RuneError, false}, // non-hex character + } + + for _, tc := range testCases { + result, isValid := decodeSingleUnicodeEscape(tc.input) + if result != tc.expected || isValid != tc.isValid { + t.Errorf("decodeSingleUnicodeEscape(%s) = (%U, %v); want (%U, %v)", tc.input, result, isValid, tc.expected, tc.isValid) + } + } +} + +func TestDecodeUnicodeEscape(t *testing.T) { + testCases := []struct { + input string + 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}, + } + + for _, tc := range testCases { + r, size := decodeUnicodeEscape([]byte(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) + } + } +} + +func TestUnescapeToUTF8(t *testing.T) { + testCases := []struct { + input []byte + expectedIn int + expectedOut int + isError bool + }{ + // valid escape sequences + {[]byte(`\n`), 2, 1, false}, + {[]byte(`\t`), 2, 1, false}, + {[]byte(`\u0041`), 6, 1, false}, + {[]byte(`\u03B1`), 6, 2, false}, + {[]byte(`\uD830\uDE03`), 12, 4, false}, + + // invalid escape sequences + {[]byte(`\`), -1, -1, true}, // incomplete escape sequence + {[]byte(`\x`), -1, -1, true}, // invalid escape character + {[]byte(`\u`), -1, -1, true}, // incomplete unicode escape sequence + {[]byte(`\u004`), -1, -1, true}, // invalid unicode escape sequence + {[]byte(`\uXYZW`), -1, -1, true}, // invalid unicode escape sequence + {[]byte(`\uD83D\u0041`), -1, -1, true}, // invalid unicode escape sequence + } + + for _, tc := range testCases { + input := make([]byte, len(tc.input)) + copy(input, tc.input) + output := make([]byte, utf8.UTFMax) + inLen, outLen, err := processEscapedUTF8(input, output) + if (err != nil) != tc.isError { + t.Errorf("processEscapedUTF8(%q) = %v; want %v", tc.input, err, tc.isError) + } + + if inLen != tc.expectedIn || outLen != tc.expectedOut { + t.Errorf("processEscapedUTF8(%q) = (%d, %d); want (%d, %d)", tc.input, inLen, outLen, tc.expectedIn, tc.expectedOut) + } + } +} + +func TestUnescape(t *testing.T) { + testCases := []struct { + name string + input []byte + expected []byte + }{ + {"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")}, + } + + for _, tc := range testCases { + 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) + } + }) + } +} + +func TestUnquoteBytes(t *testing.T) { + tests := []struct { + input []byte + border byte + expected []byte + ok bool + }{ + {[]byte("\"hello\""), '"', []byte("hello"), true}, + {[]byte("'hello'"), '\'', []byte("hello"), true}, + {[]byte("\"hello"), '"', nil, false}, + {[]byte("hello\""), '"', nil, false}, + {[]byte("\"he\\\"llo\""), '"', []byte("he\"llo"), true}, + {[]byte("\"he\\nllo\""), '"', []byte("he\nllo"), true}, + {[]byte("\"\""), '"', []byte(""), true}, + {[]byte("''"), '\'', []byte(""), true}, + {[]byte("\"\\u0041\""), '"', []byte("A"), true}, + {[]byte(`"Hello, 世界"`), '"', []byte("Hello, 世界"), true}, + {[]byte(`"Hello, \x80"`), '"', nil, false}, + } + + for _, tc := range tests { + result, pass := unquoteBytes(tc.input, tc.border) + + if pass != tc.ok { + t.Errorf("unquoteBytes(%q) = %v; want %v", tc.input, pass, tc.ok) + } + + if !bytes.Equal(result, tc.expected) { + t.Errorf("unquoteBytes(%q) = %q; want %q", tc.input, result, tc.expected) + } + } +} diff --git a/examples/gno.land/p/demo/json/gno.mod b/examples/gno.land/p/demo/json/gno.mod new file mode 100644 index 00000000000..8a380644acc --- /dev/null +++ b/examples/gno.land/p/demo/json/gno.mod @@ -0,0 +1,7 @@ +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 +) diff --git a/examples/gno.land/p/demo/json/indent.gno b/examples/gno.land/p/demo/json/indent.gno new file mode 100644 index 00000000000..cdcfd4524ee --- /dev/null +++ b/examples/gno.land/p/demo/json/indent.gno @@ -0,0 +1,144 @@ +package json + +import ( + "bytes" + "strings" +) + +// indentGrowthFactor specifies the growth factor of indenting JSON input. +// 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. +func Indent(data []byte, indent string) ([]byte, error) { + var ( + out bytes.Buffer + level int + inArray bool + arrayDepth int + ) + + for i := 0; i < len(data); i++ { + c := data[i] // current character + + switch c { + case bracketOpen: + arrayDepth++ + if arrayDepth > 1 { + level++ // increase the level if it's nested array + inArray = true + + if err := out.WriteByte(c); err != nil { + return nil, err + } + + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } else { + // case of the top-level array + inArray = true + if err := out.WriteByte(c); err != nil { + return nil, err + } + } + + case bracketClose: + if inArray && arrayDepth > 1 { // nested array + level-- + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } + + arrayDepth-- + if arrayDepth == 0 { + inArray = false + } + + if err := out.WriteByte(c); err != nil { + return nil, err + } + + case curlyOpen: + // check if the empty object or array + // we don't need to apply the indent when it's empty containers. + if i+1 < len(data) && data[i+1] == curlyClose { + if err := out.WriteByte(c); err != nil { + return nil, err + } + + i++ // skip next character + if err := out.WriteByte(data[i]); err != nil { + return nil, err + } + } else { + if err := out.WriteByte(c); err != nil { + return nil, err + } + + level++ + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } + + case curlyClose: + level-- + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + if err := out.WriteByte(c); err != nil { + return nil, err + } + + case comma, colon: + if err := out.WriteByte(c); err != nil { + return nil, err + } + if inArray && arrayDepth > 1 { // nested array + if err := writeNewlineAndIndent(&out, level, indent); err != nil { + return nil, err + } + } else if c == colon { + if err := out.WriteByte(' '); err != nil { + return nil, err + } + } + + default: + if err := out.WriteByte(c); err != nil { + return nil, err + } + } + } + + return out.Bytes(), nil +} + +func writeNewlineAndIndent(out *bytes.Buffer, level int, indent string) error { + if err := out.WriteByte('\n'); err != nil { + return err + } + + idt := strings.Repeat(indent, level*indentGrowthFactor) + if _, err := out.WriteString(idt); err != nil { + return err + } + + return nil +} diff --git a/examples/gno.land/p/demo/json/indent_test.gno b/examples/gno.land/p/demo/json/indent_test.gno new file mode 100644 index 00000000000..bc57449b12d --- /dev/null +++ b/examples/gno.land/p/demo/json/indent_test.gno @@ -0,0 +1,84 @@ +package json + +import ( + "bytes" + "testing" +) + +func TestIndentJSON(t *testing.T) { + tests := []struct { + name string + input []byte + indent string + expected []byte + }{ + { + name: "empty object", + input: []byte(`{}`), + indent: " ", + expected: []byte(`{}`), + }, + { + name: "empty array", + input: []byte(`[]`), + indent: " ", + expected: []byte(`[]`), + }, + { + name: "nested object", + input: []byte(`{{}}`), + indent: "\t", + expected: []byte("{\n\t\t{}\n}"), + }, + { + name: "nested array", + input: []byte(`[[[]]]`), + indent: "\t", + expected: []byte("[[\n\t\t[\n\t\t\t\t\n\t\t]\n]]"), + }, + { + name: "top-level array", + input: []byte(`["apple","banana","cherry"]`), + indent: "\t", + expected: []byte(`["apple","banana","cherry"]`), + }, + { + name: "array of arrays", + input: []byte(`["apple",["banana","cherry"],"date"]`), + indent: " ", + expected: []byte("[\"apple\",[\n \"banana\",\n \"cherry\"\n],\"date\"]"), + }, + + { + name: "nested array in object", + input: []byte(`{"fruits":["apple",["banana","cherry"],"date"]}`), + indent: " ", + expected: []byte("{\n \"fruits\": [\"apple\",[\n \"banana\",\n \"cherry\"\n ],\"date\"]\n}"), + }, + { + name: "complex nested structure", + input: []byte(`{"data":{"array":[1,2,3],"bool":true,"nestedArray":[["a","b"],"c"]}}`), + indent: " ", + expected: []byte("{\n \"data\": {\n \"array\": [1,2,3],\"bool\": true,\"nestedArray\": [[\n \"a\",\n \"b\"\n ],\"c\"]\n }\n}"), + }, + { + name: "custom ident character", + input: []byte(`{"fruits":["apple",["banana","cherry"],"date"]}`), + indent: "*", + expected: []byte("{\n**\"fruits\": [\"apple\",[\n****\"banana\",\n****\"cherry\"\n**],\"date\"]\n}"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := Indent(tt.input, tt.indent) + if err != nil { + t.Errorf("IndentJSON() error = %v", err) + return + } + if !bytes.Equal(actual, tt.expected) { + t.Errorf("IndentJSON() = %q, want %q", actual, tt.expected) + } + }) + } +} diff --git a/examples/gno.land/p/demo/json/internal.gno b/examples/gno.land/p/demo/json/internal.gno new file mode 100644 index 00000000000..cae7fbaba7a --- /dev/null +++ b/examples/gno.land/p/demo/json/internal.gno @@ -0,0 +1,198 @@ +package json + +// Reference: https://github.com/freddierice/php_source/blob/467ed5d6edff72219afd3e644516f131118ef48e/ext/json/JSON_parser.c +// Copyright (c) 2005 JSON.org + +// Go implementation is taken from: https://github.com/spyzhov/ajson/blob/master/internal/state.go + +type ( + States int8 // possible states of the parser + Classes int8 // JSON string character types +) + +const __ = -1 + +// enum classes +const ( + C_SPACE Classes = iota /* space */ + C_WHITE /* other whitespace */ + C_LCURB /* { */ + C_RCURB /* } */ + C_LSQRB /* [ */ + C_RSQRB /* ] */ + C_COLON /* : */ + C_COMMA /* , */ + C_QUOTE /* " */ + C_BACKS /* \ */ + C_SLASH /* / */ + C_PLUS /* + */ + C_MINUS /* - */ + C_POINT /* . */ + C_ZERO /* 0 */ + C_DIGIT /* 123456789 */ + C_LOW_A /* a */ + C_LOW_B /* b */ + C_LOW_C /* c */ + C_LOW_D /* d */ + C_LOW_E /* e */ + C_LOW_F /* f */ + C_LOW_L /* l */ + C_LOW_N /* n */ + C_LOW_R /* r */ + C_LOW_S /* s */ + C_LOW_T /* t */ + C_LOW_U /* u */ + C_ABCDF /* ABCDF */ + C_E /* E */ + C_ETC /* everything else */ +) + +// AsciiClasses array maps the 128 ASCII characters into character classes. +var AsciiClasses = [128]Classes{ + /* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. + */ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC, +} + +// QuoteAsciiClasses is a HACK for single quote from AsciiClasses +var QuoteAsciiClasses = [128]Classes{ + /* + This array maps the 128 ASCII characters into character classes. + The remaining Unicode characters should be mapped to C_ETC. + Non-whitespace control characters are errors. + */ + __, __, __, __, __, __, __, __, + __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + + C_SPACE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_QUOTE, + C_ETC, C_ETC, C_ETC, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + + C_ETC, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LSQRB, C_BACKS, C_RSQRB, C_ETC, C_ETC, + + C_ETC, C_LOW_A, C_LOW_B, C_LOW_C, C_LOW_D, C_LOW_E, C_LOW_F, C_ETC, + C_ETC, C_ETC, C_ETC, C_ETC, C_LOW_L, C_ETC, C_LOW_N, C_ETC, + C_ETC, C_ETC, C_LOW_R, C_LOW_S, C_LOW_T, C_LOW_U, C_ETC, C_ETC, + C_ETC, C_ETC, C_ETC, C_LCURB, C_ETC, C_RCURB, C_ETC, C_ETC, +} + +/* +The state codes. +*/ +const ( + GO States = iota /* start */ + OK /* ok */ + OB /* object */ + KE /* key */ + CO /* colon */ + VA /* value */ + AR /* array */ + ST /* string */ + ES /* escape */ + U1 /* u1 */ + U2 /* u2 */ + U3 /* u3 */ + U4 /* u4 */ + MI /* minus */ + ZE /* zero */ + IN /* integer */ + DT /* dot */ + FR /* fraction */ + E1 /* e */ + E2 /* ex */ + E3 /* exp */ + T1 /* tr */ + T2 /* tru */ + T3 /* true */ + F1 /* fa */ + F2 /* fal */ + F3 /* fals */ + F4 /* false */ + N1 /* nu */ + N2 /* nul */ + N3 /* null */ +) + +// List of action codes. +// these constants are defining an action that should be performed under certain conditions. +const ( + cl States = -2 /* colon */ + cm States = -3 /* comma */ + qt States = -4 /* quote */ + bo States = -5 /* bracket open */ + co States = -6 /* curly bracket open */ + bc States = -7 /* bracket close */ + cc States = -8 /* curly bracket close */ + ec States = -9 /* curly bracket empty */ +) + +// StateTransitionTable is the state transition table takes the current state and the current symbol, and returns either +// a new state or an action. An action is represented as a negative number. A JSON text is accepted if at the end of the +// text the state is OK and if the mode is DONE. +var StateTransitionTable = [31][31]States{ + /* + The state transition table takes the current state and the current symbol, + and returns either a new state or an action. An action is represented as a + negative number. A JSON text is accepted if at the end of the text the + state is OK and if the mode is DONE. + white 1-9 ABCDF etc + space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E |*/ + /*start GO*/ {GO, GO, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*ok OK*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*object OB*/ {OB, OB, __, ec, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*key KE*/ {KE, KE, __, __, __, __, __, __, ST, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*colon CO*/ {CO, CO, __, __, __, __, cl, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*value VA*/ {VA, VA, co, __, bo, __, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*array AR*/ {AR, AR, co, __, bo, bc, __, __, ST, __, __, __, MI, __, ZE, IN, __, __, __, __, __, F1, __, N1, __, __, T1, __, __, __, __}, + /*string ST*/ {ST, __, ST, ST, ST, ST, ST, ST, qt, ES, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST, ST}, + /*escape ES*/ {__, __, __, __, __, __, __, __, ST, ST, ST, __, __, __, __, __, __, ST, __, __, __, ST, __, ST, ST, __, ST, U1, __, __, __}, + /*u1 U1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U2, U2, U2, U2, U2, U2, U2, U2, __, __, __, __, __, __, U2, U2, __}, + /*u2 U2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U3, U3, U3, U3, U3, U3, U3, U3, __, __, __, __, __, __, U3, U3, __}, + /*u3 U3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, U4, U4, U4, U4, U4, U4, U4, U4, __, __, __, __, __, __, U4, U4, __}, + /*u4 U4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ST, ST, ST, ST, ST, ST, ST, ST, __, __, __, __, __, __, ST, ST, __}, + /*minus MI*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, ZE, IN, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*zero ZE*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, __, __, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*int IN*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, DT, IN, IN, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*dot DT*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, FR, FR, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*frac FR*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, FR, FR, __, __, __, __, E1, __, __, __, __, __, __, __, __, E1, __}, + /*e E1*/ {__, __, __, __, __, __, __, __, __, __, __, E2, E2, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*ex E2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*exp E3*/ {OK, OK, __, cc, __, bc, __, cm, __, __, __, __, __, __, E3, E3, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*tr T1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T2, __, __, __, __, __, __}, + /*tru T2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, T3, __, __, __}, + /*true T3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __}, + /*fa F1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F2, __, __, __, __, __, __, __, __, __, __, __, __, __, __}, + /*fal F2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F3, __, __, __, __, __, __, __, __}, + /*fals F3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, F4, __, __, __, __, __}, + /*false F4*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __, __, __}, + /*nu N1*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N2, __, __, __}, + /*nul N2*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, N3, __, __, __, __, __, __, __, __}, + /*null N3*/ {__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, OK, __, __, __, __, __, __, __, __}, +} diff --git a/examples/gno.land/p/demo/json/node.gno b/examples/gno.land/p/demo/json/node.gno new file mode 100644 index 00000000000..1e71a101e62 --- /dev/null +++ b/examples/gno.land/p/demo/json/node.gno @@ -0,0 +1,1083 @@ +package json + +import ( + "errors" + "strconv" + "strings" + + "gno.land/p/demo/ufmt" +) + +// Node represents a JSON node. +type Node struct { + prev *Node // prev is the parent node of the current node. + next map[string]*Node // next is the child nodes of the current node. + key *string // key holds the key of the current node in the parent node. + data []byte // byte slice of JSON data + value interface{} // value holds the value of the current node. + nodeType ValueType // NodeType holds the type of the current node. (Object, Array, String, Number, Boolean, Null) + index *int // index holds the index of the current node in the parent array node. + borders [2]int // borders stores the start and end index of the current node in the data. + modified bool // modified indicates the current node is changed or not. +} + +// NewNode creates a new node instance with the given parent node, buffer, type, and key. +func NewNode(prev *Node, b *buffer, typ ValueType, key **string) (*Node, error) { + curr := &Node{ + prev: prev, + data: b.data, + borders: [2]int{b.index, 0}, + key: *key, + nodeType: typ, + modified: false, + } + + if typ == Object || typ == Array { + curr.next = make(map[string]*Node) + } + + if prev != nil { + if prev.IsArray() { + size := len(prev.next) + curr.index = &size + + prev.next[strconv.Itoa(size)] = curr + } else if prev.IsObject() { + if key == nil { + return nil, errors.New("key is required for object") + } + + prev.next[**key] = curr + } else { + return nil, errors.New("invalid parent type") + } + } + + return curr, nil +} + +// load retrieves the value of the current node. +func (n *Node) load() interface{} { + return n.value +} + +// Changed checks the current node is changed or not. +func (n *Node) Changed() bool { + return n.modified +} + +// Key returns the key of the current node. +func (n *Node) Key() string { + if n == nil || n.key == nil { + return "" + } + + return *n.key +} + +// HasKey checks the current node has the given key or not. +func (n *Node) HasKey(key string) bool { + if n == nil { + return false + } + + _, ok := n.next[key] + return ok +} + +// 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") + } + + if n.Type() != Object { + return nil, ufmt.Errorf("target node is not object type. got: %s", n.Type().String()) + } + + value, ok := n.next[key] + if !ok { + return nil, ufmt.Errorf("key not found: %s", key) + } + + return value, nil +} + +// MustKey returns the value of the given key from the current object node. +func (n *Node) MustKey(key string) *Node { + val, err := n.GetKey(key) + if err != nil { + panic(err) + } + + return val +} + +// UniqueKeyLists traverses the current JSON nodes and collects all the unique keys. +func (n *Node) UniqueKeyLists() []string { + var collectKeys func(*Node) []string + collectKeys = func(node *Node) []string { + if node == nil || !node.IsObject() { + return nil + } + + result := make(map[string]bool) + for key, childNode := range node.next { + result[key] = true + childKeys := collectKeys(childNode) + for _, childKey := range childKeys { + result[childKey] = true + } + } + + keys := make([]string, 0, len(result)) + for key := range result { + keys = append(keys, key) + } + return keys + } + + return collectKeys(n) +} + +// Empty returns true if the current node is empty. +func (n *Node) Empty() bool { + if n == nil { + return false + } + + return len(n.next) == 0 +} + +// Type returns the type (ValueType) of the current node. +func (n *Node) Type() ValueType { + return n.nodeType +} + +// Value returns the value of the current node. +// +// Usage: +// +// root := Unmarshal([]byte(`{"key": "value"}`)) +// val, err := root.MustKey("key").Value() +// if err != nil { +// t.Errorf("Value returns error: %v", err) +// } +// +// result: "value" +func (n *Node) Value() (value interface{}, err error) { + value = n.load() + + if value == nil { + switch n.nodeType { + case Null: + return nil, nil + + case Number: + value, err = ParseFloatLiteral(n.source()) + if err != nil { + return nil, err + } + + n.value = value + + case String: + var ok bool + value, ok = Unquote(n.source(), doubleQuote) + if !ok { + return "", errors.New("invalid string value") + } + + n.value = value + + case Boolean: + if len(n.source()) == 0 { + return nil, errors.New("empty boolean value") + } + + b := n.source()[0] + value = b == 't' || b == 'T' + n.value = value + + case Array: + elems := make([]*Node, len(n.next)) + + for _, e := range n.next { + elems[*e.index] = e + } + + value = elems + n.value = value + + case Object: + obj := make(map[string]*Node, len(n.next)) + + for k, v := range n.next { + obj[k] = v + } + + value = obj + n.value = value + } + } + + return value, nil +} + +// Delete removes the current node from the parent node. +// +// Usage: +// +// root := Unmarshal([]byte(`{"key": "value"}`)) +// if err := root.MustKey("key").Delete(); err != nil { +// t.Errorf("Delete returns error: %v", err) +// } +// +// result: {} (empty object) +func (n *Node) Delete() error { + if n == nil { + return errors.New("can't delete nil node") + } + + if n.prev == nil { + return nil + } + + return n.prev.remove(n) +} + +// Size returns the size (length) of the current array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +// +// if root.Size() != 2 { +// t.Errorf("ArrayNode returns wrong size: %d", root.Size()) +// } +func (n *Node) Size() int { + if n == nil { + return 0 + } + + return len(n.next) +} + +// Index returns the index of the current node in the parent array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +// +// if root.MustIndex(1).Index() != 1 { +// t.Errorf("Index returns wrong index: %d", root.MustIndex(1).Index()) +// } +// +// We can also use the index to the byte slice of the JSON data directly. +// +// Example: +// +// root := Unmarshal([]byte(`["foo", 1]`)) +// if root == nil { +// t.Errorf("Unmarshal returns nil") +// } +// +// if string(root.MustIndex(1).source()) != "1" { +// t.Errorf("source returns wrong result: %s", root.MustIndex(1).source()) +// } +func (n *Node) Index() int { + if n == nil || n.index == nil { + return -1 + } + + return *n.index +} + +// MustIndex returns the array element at the given index. +// +// If the index is negative, it returns the index is from the end of the array. +// Also, it panics if the index is not found. +// +// check the Index method for detailed usage. +func (n *Node) MustIndex(expectIdx int) *Node { + val, err := n.GetIndex(expectIdx) + if err != nil { + panic(err) + } + + return val +} + +// GetIndex returns the array element at the given index. +// +// 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") + } + + if !n.IsArray() { + return nil, errors.New("node is not array") + } + + if idx > n.Size() { + return nil, errors.New("input index exceeds the array size") + } + + if idx < 0 { + idx += len(n.next) + } + + child, ok := n.next[strconv.Itoa(idx)] + if !ok { + return nil, errors.New("index not found") + } + + return child, nil +} + +// DeleteIndex removes the array element at the given index. +func (n *Node) DeleteIndex(idx int) error { + node, err := n.GetIndex(idx) + if err != nil { + return err + } + + return n.remove(node) +} + +// NullNode creates a new null type node. +// +// Usage: +// +// _ := NullNode("") +func NullNode(key string) *Node { + return &Node{ + key: &key, + value: nil, + nodeType: Null, + modified: true, + } +} + +// NumberNode creates a new number type node. +// +// Usage: +// +// root := NumberNode("", 1) +// if root == nil { +// t.Errorf("NumberNode returns nil") +// } +func NumberNode(key string, value float64) *Node { + return &Node{ + key: &key, + value: value, + nodeType: Number, + modified: true, + } +} + +// StringNode creates a new string type node. +// +// Usage: +// +// root := StringNode("", "foo") +// if root == nil { +// t.Errorf("StringNode returns nil") +// } +func StringNode(key string, value string) *Node { + return &Node{ + key: &key, + value: value, + nodeType: String, + modified: true, + } +} + +// BoolNode creates a new given boolean value node. +// +// Usage: +// +// root := BoolNode("", true) +// if root == nil { +// t.Errorf("BoolNode returns nil") +// } +func BoolNode(key string, value bool) *Node { + return &Node{ + key: &key, + value: value, + nodeType: Boolean, + modified: true, + } +} + +// ArrayNode creates a new array type node. +// +// If the given value is nil, it creates an empty array node. +// +// Usage: +// +// root := ArrayNode("", []*Node{StringNode("", "foo"), NumberNode("", 1)}) +// if root == nil { +// t.Errorf("ArrayNode returns nil") +// } +func ArrayNode(key string, value []*Node) *Node { + curr := &Node{ + key: &key, + nodeType: Array, + modified: true, + } + + curr.next = make(map[string]*Node, len(value)) + if value != nil { + curr.value = value + + for i, v := range value { + idx := i + curr.next[strconv.Itoa(i)] = v + + v.prev = curr + v.index = &idx + } + } + + return curr +} + +// ObjectNode creates a new object type node. +// +// If the given value is nil, it creates an empty object node. +// +// next is a map of key and value pairs of the object. +func ObjectNode(key string, value map[string]*Node) *Node { + curr := &Node{ + nodeType: Object, + key: &key, + next: value, + modified: true, + } + + if value != nil { + curr.value = value + + for key, val := range value { + vkey := key + val.prev = curr + val.key = &vkey + } + } else { + curr.next = make(map[string]*Node) + } + + return curr +} + +// IsArray returns true if the current node is array type. +func (n *Node) IsArray() bool { + return n.nodeType == Array +} + +// IsObject returns true if the current node is object type. +func (n *Node) IsObject() bool { + return n.nodeType == Object +} + +// IsNull returns true if the current node is null type. +func (n *Node) IsNull() bool { + return n.nodeType == Null +} + +// IsBool returns true if the current node is boolean type. +func (n *Node) IsBool() bool { + return n.nodeType == Boolean +} + +// IsString returns true if the current node is string type. +func (n *Node) IsString() bool { + return n.nodeType == String +} + +// IsNumber returns true if the current node is number type. +func (n *Node) IsNumber() bool { + return n.nodeType == Number +} + +// ready checks the current node is ready or not. +// +// the meaning of ready is the current node is parsed and has a valid value. +func (n *Node) ready() bool { + return n.borders[1] != 0 +} + +// source returns the source of the current node. +func (n *Node) source() []byte { + if n == nil { + return nil + } + + if n.ready() && !n.modified && n.data != nil { + return (n.data)[n.borders[0]:n.borders[1]] + } + + return nil +} + +// root returns the root node of the current node. +func (n *Node) root() *Node { + if n == nil { + return nil + } + + curr := n + for curr.prev != nil { + curr = curr.prev + } + + return curr +} + +// GetNull returns the null value if current node is null type. +// +// Usage: +// +// root := Unmarshal([]byte("null")) +// val, err := root.GetNull() +// if err != nil { +// t.Errorf("GetNull returns error: %v", err) +// } +// if val != nil { +// t.Errorf("GetNull returns wrong result: %v", val) +// } +func (n *Node) GetNull() (interface{}, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if !n.IsNull() { + return nil, errors.New("node is not null") + } + + return nil, nil +} + +// MustNull returns the null value if current node is null type. +// +// It panics if the current node is not null type. +func (n *Node) MustNull() interface{} { + v, err := n.GetNull() + if err != nil { + panic(err) + } + + return v +} + +// GetNumeric returns the numeric (int/float) value if current node is number type. +// +// Usage: +// +// root := Unmarshal([]byte("10.5")) +// val, err := root.GetNumeric() +// if err != nil { +// t.Errorf("GetNumeric returns error: %v", err) +// } +// println(val) // 10.5 +func (n *Node) GetNumeric() (float64, error) { + if n == nil { + return 0, errors.New("node is nil") + } + + if n.nodeType != Number { + return 0, errors.New("node is not number") + } + + val, err := n.Value() + if err != nil { + return 0, err + } + + v, ok := val.(float64) + if !ok { + return 0, errors.New("node is not number") + } + + return v, nil +} + +// MustNumeric returns the numeric (int/float) value if current node is number type. +// +// It panics if the current node is not number type. +func (n *Node) MustNumeric() float64 { + v, err := n.GetNumeric() + if err != nil { + panic(err) + } + + return v +} + +// GetString returns the string value if current node is string type. +// +// Usage: +// +// root, err := Unmarshal([]byte("foo")) +// if err != nil { +// t.Errorf("Error on Unmarshal(): %s", err) +// } +// +// str, err := root.GetString() +// if err != nil { +// t.Errorf("should retrieve string value: %s", err) +// } +// +// println(str) // "foo" +func (n *Node) GetString() (string, error) { + if n == nil { + return "", errors.New("string node is empty") + } + + if !n.IsString() { + return "", errors.New("node type is not string") + } + + val, err := n.Value() + if err != nil { + return "", err + } + + v, ok := val.(string) + if !ok { + return "", errors.New("node is not string") + } + + return v, nil +} + +// MustString returns the string value if current node is string type. +// +// It panics if the current node is not string type. +func (n *Node) MustString() string { + v, err := n.GetString() + if err != nil { + panic(err) + } + + return v +} + +// GetBool returns the boolean value if current node is boolean type. +// +// Usage: +// +// root := Unmarshal([]byte("true")) +// val, err := root.GetBool() +// if err != nil { +// t.Errorf("GetBool returns error: %v", err) +// } +// println(val) // true +func (n *Node) GetBool() (bool, error) { + if n == nil { + return false, errors.New("node is nil") + } + + if n.nodeType != Boolean { + return false, errors.New("node is not boolean") + } + + val, err := n.Value() + if err != nil { + return false, err + } + + v, ok := val.(bool) + if !ok { + return false, errors.New("node is not boolean") + } + + return v, nil +} + +// MustBool returns the boolean value if current node is boolean type. +// +// It panics if the current node is not boolean type. +func (n *Node) MustBool() bool { + v, err := n.GetBool() + if err != nil { + panic(err) + } + + return v +} + +// GetArray returns the array value if current node is array type. +// +// Usage: +// +// root := Must(Unmarshal([]byte(`["foo", 1]`))) +// arr, err := root.GetArray() +// if err != nil { +// t.Errorf("GetArray returns error: %v", err) +// } +// +// for _, val := range arr { +// println(val) +// } +// +// result: "foo", 1 +func (n *Node) GetArray() ([]*Node, error) { + if n == nil { + return nil, errors.New("node is nil") + } + + if n.nodeType != Array { + return nil, errors.New("node is not array") + } + + val, err := n.Value() + if err != nil { + return nil, err + } + + v, ok := val.([]*Node) + if !ok { + return nil, errors.New("node is not array") + } + + return v, nil +} + +// MustArray returns the array value if current node is array type. +// +// It panics if the current node is not array type. +func (n *Node) MustArray() []*Node { + v, err := n.GetArray() + if err != nil { + panic(err) + } + + return v +} + +// AppendArray appends the given values to the current array node. +// +// If the current node is not array type, it returns an error. +// +// Example 1: +// +// root := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))) +// if err := root.AppendArray(NullNode("")); err != nil { +// t.Errorf("should not return error: %s", err) +// } +// +// result: [{"foo":"bar"}, null] +// +// Example 2: +// +// root := Must(Unmarshal([]byte(`["bar", "baz"]`))) +// err := root.AppendArray(NumberNode("", 1), StringNode("", "foo")) +// if err != nil { +// t.Errorf("AppendArray returns error: %v", err) +// } +// +// 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") + } + + for _, val := range value { + if err := n.append(nil, val); err != nil { + return err + } + } + + n.mark() + return nil +} + +// ArrayEach executes the callback for each element in the JSON array. +// +// Usage: +// +// jsonArrayNode.ArrayEach(func(i int, valueNode *Node) { +// ufmt.Println(i, valueNode) +// }) +func (n *Node) ArrayEach(callback func(i int, target *Node)) { + if n == nil || !n.IsArray() { + return + } + + for idx := 0; idx < len(n.next); idx++ { + element, err := n.GetIndex(idx) + if err != nil { + continue + } + + callback(idx, element) + } +} + +// GetObject returns the object value if current node is object type. +// +// Usage: +// +// root := Must(Unmarshal([]byte(`{"key": "value"}`))) +// obj, err := root.GetObject() +// if err != nil { +// t.Errorf("GetObject returns error: %v", err) +// } +// +// 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") + } + + if !n.IsObject() { + return nil, errors.New("node is not object") + } + + val, err := n.Value() + if err != nil { + return nil, err + } + + v, ok := val.(map[string]*Node) + if !ok { + return nil, errors.New("node is not object") + } + + return v, nil +} + +// MustObject returns the object value if current node is object type. +// +// It panics if the current node is not object type. +func (n *Node) MustObject() map[string]*Node { + v, err := n.GetObject() + if err != nil { + panic(err) + } + + return v +} + +// AppendObject appends the given key and value to the current object 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") + } + + if err := n.append(&key, value); err != nil { + return err + } + + n.mark() + return nil +} + +// ObjectEach executes the callback for each key-value pair in the JSON object. +// +// Usage: +// +// jsonObjectNode.ObjectEach(func(key string, valueNode *Node) { +// ufmt.Println(key, valueNode) +// }) +func (n *Node) ObjectEach(callback func(key string, value *Node)) { + if n == nil || !n.IsObject() { + return + } + + for key, child := range n.next { + callback(key, child) + } +} + +// String converts the node to a string representation. +func (n *Node) String() string { + if n == nil { + return "" + } + + if n.ready() && !n.modified { + return string(n.source()) + } + + val, err := Marshal(n) + if err != nil { + return "error: " + err.Error() + } + + return string(val) +} + +// Path builds the path of the current node. +// +// For example: +// +// { "key": { "sub": [ "val1", "val2" ] }} +// +// The path of "val2" is: $.key.sub[1] +func (n *Node) Path() string { + if n == nil { + return "" + } + + var sb strings.Builder + + if n.prev == nil { + sb.WriteString("$") + } else { + sb.WriteString(n.prev.Path()) + + if n.key != nil { + sb.WriteString("['" + n.Key() + "']") + } else { + sb.WriteString("[" + strconv.Itoa(n.Index()) + "]") + } + } + + return sb.String() +} + +// mark marks the current node as modified. +func (n *Node) mark() { + node := n + for node != nil && !node.modified { + node.modified = true + node = node.prev + } +} + +// isContainer checks the current node type is array or object. +func (n *Node) isContainer() bool { + return n.IsArray() || n.IsObject() +} + +// remove removes the value from the current container type node. +func (n *Node) remove(v *Node) error { + if !n.isContainer() { + return ufmt.Errorf( + "can't remove value from non-array or non-object node. got=%s", + n.Type().String(), + ) + } + + if v.prev != n { + return errors.New("invalid parent node") + } + + n.mark() + if n.IsArray() { + delete(n.next, strconv.Itoa(*v.index)) + n.dropIndex(*v.index) + } else { + delete(n.next, *v.key) + } + + v.prev = nil + return nil +} + +// dropIndex rebase the index of current array node values. +func (n *Node) dropIndex(idx int) { + for i := idx + 1; i <= len(n.next); i++ { + prv := i - 1 + if curr, ok := n.next[strconv.Itoa(i)]; ok { + curr.index = &prv + n.next[strconv.Itoa(prv)] = curr + } + + delete(n.next, strconv.Itoa(i)) + } +} + +// 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") + } + + if val.prev != nil { + if err := val.prev.remove(val); err != nil { + return err + } + } + + val.prev = n + val.key = key + + if key == nil { + size := len(n.next) + val.index = &size + n.next[strconv.Itoa(size)] = val + } else { + if old, ok := n.next[*key]; ok { + if err := n.remove(old); err != nil { + return err + } + } + n.next[*key] = val + } + + return nil +} + +func (n *Node) isSameOrParentNode(nd *Node) bool { + return n == nd || n.isParentNode(nd) +} + +func (n *Node) isParentNode(nd *Node) bool { + if n == nil { + return false + } + + for curr := nd.prev; curr != nil; curr = curr.prev { + if curr == n { + return true + } + } + + return false +} + +// cptrs returns the pointer of the given string value. +func cptrs(cpy *string) *string { + if cpy == nil { + return nil + } + + val := *cpy + + return &val +} + +// cptri returns the pointer of the given integer value. +func cptri(i *int) *int { + if i == nil { + return nil + } + + val := *i + return &val +} + +// Must panics if the given node is not fulfilled the expectation. +// Usage: +// +// node := Must(Unmarshal([]byte(`{"key": "value"}`)) +func Must(root *Node, expect error) *Node { + if expect != nil { + panic(expect) + } + + return root +} diff --git a/examples/gno.land/p/demo/json/node_test.gno b/examples/gno.land/p/demo/json/node_test.gno new file mode 100644 index 00000000000..dbc82369f68 --- /dev/null +++ b/examples/gno.land/p/demo/json/node_test.gno @@ -0,0 +1,1392 @@ +package json + +import ( + "bytes" + "sort" + "strconv" + "strings" + "testing" + + "gno.land/p/demo/ufmt" +) + +var ( + nilKey *string + dummyKey = "key" +) + +type _args struct { + prev *Node + buf *buffer + typ ValueType + key **string +} + +type simpleNode struct { + name string + node *Node +} + +func TestNode_CreateNewNode(t *testing.T) { + rel := &dummyKey + + tests := []struct { + name string + args _args + expectCurr *Node + expectErr bool + expectPanic bool + }{ + { + name: "child for non container type", + args: _args{ + prev: BoolNode("", true), + buf: newBuffer(make([]byte, 10)), + typ: Boolean, + key: &rel, + }, + expectCurr: nil, + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + if tt.expectPanic { + return + } + t.Errorf("%s panic occurred when not expected: %v", tt.name, r) + } else if tt.expectPanic { + t.Errorf("%s expected panic but didn't occur", tt.name) + } + }() + + got, err := NewNode(tt.args.prev, tt.args.buf, tt.args.typ, tt.args.key) + if (err != nil) != tt.expectErr { + t.Errorf("%s error = %v, expect error %v", tt.name, err, tt.expectErr) + return + } + + if tt.expectErr { + return + } + + if !compareNodes(got, tt.expectCurr) { + t.Errorf("%s got = %v, want %v", tt.name, got, tt.expectCurr) + } + }) + } +} + +func TestNode_Value(t *testing.T) { + tests := []struct { + name string + data []byte + _type ValueType + expected interface{} + errExpected bool + }{ + {name: "null", data: []byte("null"), _type: Null, expected: nil}, + {name: "1", data: []byte("1"), _type: Number, expected: float64(1)}, + {name: ".1", data: []byte(".1"), _type: Number, expected: float64(.1)}, + {name: "-.1e1", data: []byte("-.1e1"), _type: Number, expected: float64(-1)}, + {name: "string", data: []byte("\"foo\""), _type: String, expected: "foo"}, + {name: "space", data: []byte("\"foo bar\""), _type: String, expected: "foo bar"}, + {name: "true", data: []byte("true"), _type: Boolean, expected: true}, + {name: "invalid true", data: []byte("tru"), _type: Unknown, errExpected: true}, + {name: "invalid false", data: []byte("fals"), _type: Unknown, errExpected: true}, + {name: "false", data: []byte("false"), _type: Boolean, expected: false}, + {name: "e1", data: []byte("e1"), _type: Unknown, errExpected: true}, + {name: "1a", data: []byte("1a"), _type: Unknown, errExpected: true}, + {name: "string error", data: []byte("\"foo\nbar\""), _type: String, errExpected: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + curr := &Node{ + data: tt.data, + nodeType: tt._type, + borders: [2]int{0, len(tt.data)}, + } + + got, err := curr.Value() + if err != nil { + if !tt.errExpected { + t.Errorf("%s error = %v, expect error %v", tt.name, err, tt.errExpected) + } + return + } + + if got != tt.expected { + t.Errorf("%s got = %v, want %v", tt.name, got, tt.expected) + } + }) + } +} + +func TestNode_Delete(t *testing.T) { + root := Must(Unmarshal([]byte(`{"foo":"bar"}`))) + if err := root.Delete(); err != nil { + t.Errorf("Delete returns error: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `{"foo":"bar"}` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + foo := root.MustKey("foo") + if err := foo.Delete(); err != nil { + t.Errorf("Delete returns error while handling foo: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `{}` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if value, err := Marshal(foo); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `"bar"` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if foo.prev != nil { + t.Errorf("foo.prev should be nil") + } +} + +func TestNode_ObjectNode(t *testing.T) { + objs := map[string]*Node{ + "key1": NullNode("null"), + "key2": NumberNode("answer", 42), + "key3": StringNode("string", "foobar"), + "key4": BoolNode("bool", true), + } + + node := ObjectNode("test", objs) + + if len(node.next) != len(objs) { + t.Errorf("ObjectNode: want %v got %v", len(objs), len(node.next)) + } + + for k, v := range objs { + if node.next[k] == nil { + t.Errorf("ObjectNode: want %v got %v", v, node.next[k]) + } + } +} + +func TestNode_AppendObject(t *testing.T) { + if err := Must(Unmarshal([]byte(`{"foo":"bar","baz":null}`))).AppendObject("biz", NullNode("")); err != nil { + t.Errorf("AppendArray should return error") + } + + root := Must(Unmarshal([]byte(`{"foo":"bar"}`))) + if err := root.AppendObject("baz", NullNode("")); err != nil { + t.Errorf("AppendObject should not return error: %s", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if isSameObject(string(value), `"{"foo":"bar","baz":null}"`) { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + // FIXME: this may fail if execute test in more than 3 times in a row. + if err := root.AppendObject("biz", NumberNode("", 42)); err != nil { + t.Errorf("AppendObject returns error: %v", err) + } + + val, err := Marshal(root) + if err != nil { + t.Errorf("Marshal returns error: %v", err) + } + + // FIXME: this may fail if execute test in more than 3 times in a row. + if isSameObject(string(val), `"{"foo":"bar","baz":null,"biz":42}"`) { + t.Errorf("Marshal returns wrong value: %s", string(val)) + } +} + +func TestNode_ArrayNode(t *testing.T) { + arr := []*Node{ + NullNode("nil"), + NumberNode("num", 42), + StringNode("str", "foobar"), + BoolNode("bool", true), + } + + node := ArrayNode("test", arr) + + if len(node.next) != len(arr) { + t.Errorf("ArrayNode: want %v got %v", len(arr), len(node.next)) + } + + for i, v := range arr { + if node.next[strconv.Itoa(i)] == nil { + t.Errorf("ArrayNode: want %v got %v", v, node.next[strconv.Itoa(i)]) + } + } +} + +func TestNode_AppendArray(t *testing.T) { + if err := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))).AppendArray(NullNode("")); err != nil { + t.Errorf("should return error") + } + + root := Must(Unmarshal([]byte(`[{"foo":"bar"}]`))) + if err := root.AppendArray(NullNode("")); err != nil { + t.Errorf("should not return error: %s", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `[{"foo":"bar"},null]` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } + + if err := root.AppendArray( + NumberNode("", 1), + StringNode("", "foo"), + Must(Unmarshal([]byte(`[0,1,null,true,"example"]`))), + Must(Unmarshal([]byte(`{"foo": true, "bar": null, "baz": 123}`))), + ); err != nil { + t.Errorf("AppendArray returns error: %v", err) + } + + if value, err := Marshal(root); err != nil { + t.Errorf("Marshal returns error: %v", err) + } else if string(value) != `[{"foo":"bar"},null,1,"foo",[0,1,null,true,"example"],{"foo": true, "bar": null, "baz": 123}]` { + t.Errorf("Marshal returns wrong value: %s", string(value)) + } +} + +/******** value getter ********/ + +func TestNode_GetBool(t *testing.T) { + root, err := Unmarshal([]byte(`true`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + value, err := root.GetBool() + if err != nil { + t.Errorf("Error on root.GetBool(): %s", err.Error()) + } + + if !value { + t.Errorf("root.GetBool() is corrupted") + } +} + +func TestNode_GetBool_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"literally null node", NullNode("")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetBool(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_IsBool(t *testing.T) { + tests := []simpleNode{ + {"true", BoolNode("", true)}, + {"false", BoolNode("", false)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.node.IsBool() { + t.Errorf("%s should be a bool", tt.name) + } + }) + } +} + +func TestNode_IsBool_With_Unmarshal(t *testing.T) { + tests := []struct { + name string + json []byte + want bool + }{ + {"true", []byte("true"), true}, + {"false", []byte("false"), true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal(tt.json) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + if root.IsBool() != tt.want { + t.Errorf("%s should be a bool", tt.name) + } + }) + } +} + +var nullJson = []byte(`null`) + +func TestNode_GetNull(t *testing.T) { + root, err := Unmarshal(nullJson) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + value, err := root.GetNull() + if err != nil { + t.Errorf("error occurred while getting null, %s", err) + } + + if value != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value) + } +} + +func TestNode_GetNull_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"number node is null", NumberNode("", 42)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetNull(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_MustNull(t *testing.T) { + root, err := Unmarshal(nullJson) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + value := root.MustNull() + if value != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value) + } +} + +func TestNode_GetNumeric_Float(t *testing.T) { + root, err := Unmarshal([]byte(`123.456`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(123.456) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123.456, got: %v", value)) + } +} + +func TestNode_GetNumeric_Scientific_Notation(t *testing.T) { + root, err := Unmarshal([]byte(`1e3`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(1000) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 1000, got: %v", value)) + } +} + +func TestNode_GetNumeric_With_Unmarshal(t *testing.T) { + root, err := Unmarshal([]byte(`123`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + value, err := root.GetNumeric() + if err != nil { + t.Errorf("Error on root.GetNumeric(): %s", err) + } + + if value != float64(123) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123, got: %v", value)) + } +} + +func TestNode_GetNumeric_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"string node", StringNode("", "123")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetNumeric(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_GetString(t *testing.T) { + root, err := Unmarshal([]byte(`"123foobar 3456"`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + } + + value, err := root.GetString() + if err != nil { + t.Errorf("Error on root.GetString(): %s", err) + } + + if value != "123foobar 3456" { + t.Errorf(ufmt.Sprintf("value is not matched. expected: 123, got: %s", value)) + } +} + +func TestNode_GetString_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"number node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetString(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_MustString(t *testing.T) { + tests := []struct { + name string + data []byte + }{ + {"foo", []byte(`"foo"`)}, + {"foo bar", []byte(`"foo bar"`)}, + {"", []byte(`""`)}, + {"안녕하세요", []byte(`"안녕하세요"`)}, + {"こんにちは", []byte(`"こんにちは"`)}, + {"你好", []byte(`"你好"`)}, + {"one \"encoded\" string", []byte(`"one \"encoded\" string"`)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal(tt.data) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + } + + value := root.MustString() + if value != tt.name { + t.Errorf("value is not matched. expected: %s, got: %s", tt.name, value) + } + }) + } +} + +func TestUnmarshal_Array(t *testing.T) { + root, err := Unmarshal([]byte(" [1,[\"1\",[1,[1,2,3]]]]\r\n")) + if err != nil { + t.Errorf("Error on Unmarshal: %s", err.Error()) + } + + if root == nil { + t.Errorf("Error on Unmarshal: root is nil") + } + + if root.Type() != Array { + t.Errorf("Error on Unmarshal: wrong type") + } + + array, err := root.GetArray() + if err != nil { + t.Errorf("error occurred while getting array, %s", err) + } else if len(array) != 2 { + t.Errorf("expected 2 elements, got %d", len(array)) + } else if val, err := array[0].GetNumeric(); err != nil { + t.Errorf("value of array[0] is not numeric. got: %v", array[0].value) + } else if val != 1 { + t.Errorf("Error on array[0].GetNumeric(): expected to be '1', got: %v", val) + } else if val, err := array[1].GetArray(); err != nil { + t.Errorf("error occurred while getting array, %s", err.Error()) + } else if len(val) != 2 { + t.Errorf("Error on array[1].GetArray(): expected 2 elements, got %d", len(val)) + } else if el, err := val[0].GetString(); err != nil { + t.Errorf("error occurred while getting string, %s", err.Error()) + } else if el != "1" { + t.Errorf("Error on val[0].GetString(): expected to be '1', got: %s", el) + } +} + +var sampleArr = []byte(`[-1, 2, 3, 4, 5, 6]`) + +func TestNode_GetArray(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + array, err := root.GetArray() + if err != nil { + t.Errorf("Error on root.GetArray(): %s", err) + } + + if len(array) != 6 { + t.Errorf(ufmt.Sprintf("length is not matched. expected: 3, got: %d", len(array))) + } + + for i, node := range array { + for j, val := range []int{-1, 2, 3, 4, 5, 6} { + if i == j { + if v, err := node.GetNumeric(); err != nil { + t.Errorf(ufmt.Sprintf("Error on node.GetNumeric(): %s", err)) + } else if v != float64(val) { + t.Errorf(ufmt.Sprintf("value is not matched. expected: %d, got: %v", val, v)) + } + } + } + } +} + +func TestNode_GetArray_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + {"number node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetArray(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_IsArray(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err) + return + } + + if root.Type() != Array { + t.Errorf(ufmt.Sprintf("Must be an array. got: %s", root.Type().String())) + } +} + +func TestNode_ArrayEach(t *testing.T) { + tests := []struct { + name string + json string + expected []int + }{ + { + name: "empty array", + json: `[]`, + expected: []int{}, + }, + { + name: "single element", + json: `[42]`, + expected: []int{42}, + }, + { + name: "multiple elements", + json: `[1, 2, 3, 4, 5]`, + expected: []int{1, 2, 3, 4, 5}, + }, + { + name: "multiple elements but all values are same", + json: `[1, 1, 1, 1, 1]`, + expected: []int{1, 1, 1, 1, 1}, + }, + { + name: "multiple elements with non-numeric values", + json: `["a", "b", "c", "d", "e"]`, + expected: []int{}, + }, + { + name: "non-array node", + json: `{"not": "an array"}`, + expected: []int{}, + }, + { + name: "array containing numeric and non-numeric elements", + json: `["1", 2, 3, "4", 5, "6"]`, + expected: []int{2, 3, 5}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tc.json)) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + var result []int // callback result + root.ArrayEach(func(index int, element *Node) { + if val, err := strconv.Atoi(element.String()); err == nil { + result = append(result, val) + } + }) + + if len(result) != len(tc.expected) { + t.Errorf("%s: expected %d elements, got %d", tc.name, len(tc.expected), len(result)) + return + } + + for i, val := range result { + if val != tc.expected[i] { + t.Errorf("%s: expected value at index %d to be %d, got %d", tc.name, i, tc.expected[i], val) + } + } + }) + } +} + +func TestNode_Key(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null, "baz": 123, "biz": [1,2,3]}`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + } + + obj := root.MustObject() + for key, node := range obj { + if key != node.Key() { + t.Errorf("Key() = %v, want %v", node.Key(), key) + } + } + + keys := []string{"foo", "bar", "baz", "biz"} + for _, key := range keys { + if obj[key].Key() != key { + t.Errorf("Key() = %v, want %v", obj[key].Key(), key) + } + } + + // TODO: resolve stack overflow + // if root.MustKey("foo").Clone().Key() != "" { + // t.Errorf("wrong key found for cloned key") + // } + + if (*Node)(nil).Key() != "" { + t.Errorf("wrong key found for nil node") + } +} + +func TestNode_Size(t *testing.T) { + root, err := Unmarshal(sampleArr) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + size := root.Size() + if size != 6 { + t.Errorf(ufmt.Sprintf("Size() must be 6. got: %v", size)) + } + + if (*Node)(nil).Size() != 0 { + t.Errorf(ufmt.Sprintf("Size() must be 0. got: %v", (*Node)(nil).Size())) + } +} + +func TestNode_Index(t *testing.T) { + root, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`)) + if err != nil { + t.Error("error occurred while unmarshal") + } + + arr := root.MustArray() + for i, node := range arr { + if i != node.Index() { + t.Errorf(ufmt.Sprintf("Index() must be nil. got: %v", i)) + } + } +} + +func TestNode_Index_Fail(t *testing.T) { + tests := []struct { + name string + node *Node + want int + }{ + {"nil node", (*Node)(nil), -1}, + {"null node", NullNode(""), -1}, + {"object node", ObjectNode("", nil), -1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Index(); got != tt.want { + t.Errorf("Index() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_GetIndex(t *testing.T) { + root := Must(Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`))) + expected := []int{1, 2, 3, 4, 5, 6} + + if len(expected) != root.Size() { + t.Errorf("length is not matched. expected: %d, got: %d", len(expected), root.Size()) + } + + // TODO: if length exceeds, stack overflow occurs. need to fix + for i, v := range expected { + val, err := root.GetIndex(i) + if err != nil { + t.Errorf("error occurred while getting index %d, %s", i, err) + } + + if val.MustNumeric() != float64(v) { + t.Errorf("value is not matched. expected: %d, got: %v", v, val.MustNumeric()) + } + } +} + +func TestNode_GetIndex_InputIndex_Exceed_Original_Node_Index(t *testing.T) { + root, err := Unmarshal([]byte(`[1, 2, 3, 4, 5, 6]`)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + _, err = root.GetIndex(10) + if err == nil { + t.Errorf("GetIndex should return error") + } +} + +func TestNode_DeleteIndex(t *testing.T) { + tests := []struct { + name string + expected string + index int + ok bool + }{ + {`null`, ``, 0, false}, + {`1`, ``, 0, false}, + {`{}`, ``, 0, false}, + {`{"foo":"bar"}`, ``, 0, false}, + {`true`, ``, 0, false}, + {`[]`, ``, 0, false}, + {`[]`, ``, -1, false}, + {`[1]`, `[]`, 0, true}, + {`[{}]`, `[]`, 0, true}, + {`[{}, [], 42]`, `[{}, []]`, -1, true}, + {`[{}, [], 42]`, `[[], 42]`, 0, true}, + {`[{}, [], 42]`, `[{}, 42]`, 1, true}, + {`[{}, [], 42]`, `[{}, []]`, 2, true}, + {`[{}, [], 42]`, ``, 10, false}, + {`[{}, [], 42]`, ``, -10, false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root := Must(Unmarshal([]byte(tt.name))) + err := root.DeleteIndex(tt.index) + if err != nil && tt.ok { + t.Errorf("DeleteIndex returns error: %v", err) + } + }) + } +} + +func TestNode_GetKey(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`)) + if err != nil { + t.Error("error occurred while unmarshal") + } + + value, err := root.GetKey("foo") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + if value.MustBool() != true { + t.Errorf("value is not matched. expected: true, got: %v", value.MustBool()) + } + + value, err = root.GetKey("bar") + if err != nil { + t.Errorf("error occurred while getting key, %s", err) + } + + _, err = root.GetKey("baz") + if err == nil { + t.Errorf("key baz is not exist. must be failed") + } + + if value.MustNull() != nil { + t.Errorf("value is not matched. expected: nil, got: %v", value.MustNull()) + } +} + +func TestNode_GetKey_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"null node", NullNode("")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetKey(""); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_GetUniqueKeyList(t *testing.T) { + tests := []struct { + name string + json string + expected []string + }{ + { + name: "simple foo/bar", + json: `{"foo": true, "bar": null}`, + expected: []string{"foo", "bar"}, + }, + { + name: "empty object", + json: `{}`, + expected: []string{}, + }, + { + name: "nested object", + json: `{ + "outer": { + "inner": { + "key": "value" + }, + "array": [1, 2, 3] + }, + "another": "item" + }`, + expected: []string{"outer", "inner", "key", "array", "another"}, + }, + { + name: "complex object", + json: `{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated": false, + "IDs": [116, 943, 234, 38793] + } + }`, + expected: []string{"Image", "Width", "Height", "Title", "Thumbnail", "Url", "Animated", "IDs"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tt.json)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + value := root.UniqueKeyLists() + if len(value) != len(tt.expected) { + t.Errorf("%s length must be %v. got: %v. retrieved keys: %s", tt.name, len(tt.expected), len(value), value) + } + + for _, key := range value { + if !contains(tt.expected, key) { + t.Errorf("EachKey() must be in %v. got: %v", tt.expected, key) + } + } + }) + } +} + +// TODO: resolve stack overflow +func TestNode_IsEmpty(t *testing.T) { + tests := []struct { + name string + node *Node + expected bool + }{ + {"nil node", (*Node)(nil), false}, // nil node is not empty. + // {"null node", NullNode(""), true}, + {"empty object", ObjectNode("", nil), true}, + {"empty array", ArrayNode("", nil), true}, + {"non-empty object", ObjectNode("", map[string]*Node{"foo": BoolNode("foo", true)}), false}, + {"non-empty array", ArrayNode("", []*Node{BoolNode("0", true)}), false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Empty(); got != tt.expected { + t.Errorf("%s = %v, want %v", tt.name, got, tt.expected) + } + }) + } +} + +func TestNode_Index_EmptyList(t *testing.T) { + root, err := Unmarshal([]byte(`[]`)) + if err != nil { + t.Errorf("error occurred while unmarshal") + } + + array := root.MustArray() + for i, node := range array { + if i != node.Index() { + t.Errorf(ufmt.Sprintf("Index() must be nil. got: %v", i)) + } + } +} + +func TestNode_GetObject(t *testing.T) { + root, err := Unmarshal([]byte(`{"foo": true,"bar": null}`)) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + value, err := root.GetObject() + if err != nil { + t.Errorf("Error on root.GetObject(): %s", err.Error()) + } + + if _, ok := value["foo"]; !ok { + t.Errorf("root.GetObject() is corrupted: foo") + } + + if _, ok := value["bar"]; !ok { + t.Errorf("root.GetObject() is corrupted: bar") + } +} + +func TestNode_GetObject_Fail(t *testing.T) { + tests := []simpleNode{ + {"nil node", (*Node)(nil)}, + {"get object from null node", NullNode("")}, + {"not object node", NumberNode("", 123)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := tt.node.GetObject(); err == nil { + t.Errorf("%s should be an error", tt.name) + } + }) + } +} + +func TestNode_ObjectEach(t *testing.T) { + tests := []struct { + name string + json string + expected map[string]int + }{ + { + name: "empty object", + json: `{}`, + expected: make(map[string]int), + }, + { + name: "single key-value pair", + json: `{"key": 42}`, + expected: map[string]int{"key": 42}, + }, + { + name: "multiple key-value pairs", + json: `{"one": 1, "two": 2, "three": 3}`, + expected: map[string]int{"one": 1, "two": 2, "three": 3}, + }, + { + name: "multiple key-value pairs with some non-numeric values", + json: `{"one": 1, "two": "2", "three": 3, "four": "4"}`, + expected: map[string]int{"one": 1, "three": 3}, + }, + { + name: "non-object node", + json: `["not", "an", "object"]`, + expected: make(map[string]int), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + root, err := Unmarshal([]byte(tc.json)) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + result := make(map[string]int) + root.ObjectEach(func(key string, value *Node) { + // extract integer values from the object + if val, err := strconv.Atoi(value.String()); err == nil { + result[key] = val + } + }) + + if len(result) != len(tc.expected) { + t.Errorf("%s: expected %d key-value pairs, got %d", tc.name, len(tc.expected), len(result)) + return + } + + for key, val := range tc.expected { + if result[key] != val { + t.Errorf("%s: expected value for key %s to be %d, got %d", tc.name, key, val, result[key]) + } + } + }) + } +} + +func TestNode_ExampleMust(t *testing.T) { + data := []byte(`{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + }`) + + root := Must(Unmarshal(data)) + if root.Size() != 1 { + t.Errorf("root.Size() must be 1. got: %v", root.Size()) + } + + ufmt.Sprintf("Object has %d inheritors inside", root.Size()) + // Output: + // Object has 1 inheritors inside +} + +// Calculate AVG price from different types of objects, JSON from: https://goessner.net/articles/JsonPath/index.html#e3 +func TestExampleUnmarshal(t *testing.T) { + data := []byte(`{ "store": { + "book": [ + { "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { "color": "red", + "price": 19.95 + }, + "tools": null + } +}`) + + root, err := Unmarshal(data) + if err != nil { + t.Errorf("error occurred when unmarshal") + } + + store := root.MustKey("store").MustObject() + + var prices float64 + size := 0 + for _, objects := range store { + if objects.IsArray() && objects.Size() > 0 { + size += objects.Size() + for _, object := range objects.MustArray() { + prices += object.MustKey("price").MustNumeric() + } + } else if objects.IsObject() && objects.HasKey("price") { + size++ + prices += objects.MustKey("price").MustNumeric() + } + } + + result := int(prices / float64(size)) + ufmt.Sprintf("AVG price: %d", result) +} + +func TestNode_ExampleMust_panic(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + data := []byte(`{]`) + root := Must(Unmarshal(data)) + ufmt.Sprintf("Object has %d inheritors inside", root.Size()) +} + +func TestNode_Path(t *testing.T) { + data := []byte(`{ + "Image": { + "Width": 800, + "Height": 600, + "Title": "View from 15th Floor", + "Thumbnail": { + "Url": "http://www.example.com/image/481989943", + "Height": 125, + "Width": 100 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793] + } + }`) + + root, err := Unmarshal(data) + if err != nil { + t.Errorf("Error on Unmarshal(): %s", err.Error()) + return + } + + if root.Path() != "$" { + t.Errorf("Wrong root.Path()") + } + + element := root.MustKey("Image").MustKey("Thumbnail").MustKey("Url") + if element.Path() != "$['Image']['Thumbnail']['Url']" { + t.Errorf("Wrong path found: %s", element.Path()) + } + + if (*Node)(nil).Path() != "" { + t.Errorf("Wrong (nil).Path()") + } +} + +func TestNode_Path2(t *testing.T) { + tests := []struct { + name string + node *Node + want string + }{ + { + name: "Node with key", + node: &Node{ + prev: &Node{}, + key: func() *string { s := "key"; return &s }(), + }, + want: "$['key']", + }, + { + name: "Node with index", + node: &Node{ + prev: &Node{}, + index: func() *int { i := 1; return &i }(), + }, + want: "$[1]", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.Path(); got != tt.want { + t.Errorf("Path() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_Root(t *testing.T) { + root := &Node{} + child := &Node{prev: root} + grandChild := &Node{prev: child} + + tests := []struct { + name string + node *Node + want *Node + }{ + { + name: "Root node", + node: root, + want: root, + }, + { + name: "Child node", + node: child, + want: root, + }, + { + name: "Grandchild node", + node: grandChild, + want: root, + }, + { + name: "Node is nil", + node: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.node.root(); got != tt.want { + t.Errorf("root() = %v, want %v", got, tt.want) + } + }) + } +} + +func contains(slice []string, item string) bool { + for _, a := range slice { + if a == item { + return true + } + } + + return false +} + +// ignore the sequence of keys by ordering them. +// need to avoid import encoding/json and reflect package. +// because gno does not support them for now. +// TODO: use encoding/json to compare the result after if possible in gno. +func isSameObject(a, b string) bool { + aPairs := strings.Split(strings.Trim(a, "{}"), ",") + bPairs := strings.Split(strings.Trim(b, "{}"), ",") + + aMap := make(map[string]string) + bMap := make(map[string]string) + for _, pair := range aPairs { + kv := strings.Split(pair, ":") + key := strings.Trim(kv[0], `"`) + value := strings.Trim(kv[1], `"`) + aMap[key] = value + } + for _, pair := range bPairs { + kv := strings.Split(pair, ":") + key := strings.Trim(kv[0], `"`) + value := strings.Trim(kv[1], `"`) + bMap[key] = value + } + + aKeys := make([]string, 0, len(aMap)) + bKeys := make([]string, 0, len(bMap)) + for k := range aMap { + aKeys = append(aKeys, k) + } + + for k := range bMap { + bKeys = append(bKeys, k) + } + + sort.Strings(aKeys) + sort.Strings(bKeys) + + if len(aKeys) != len(bKeys) { + return false + } + + for i := range aKeys { + if aKeys[i] != bKeys[i] { + return false + } + + if aMap[aKeys[i]] != bMap[bKeys[i]] { + return false + } + } + + return true +} + +func compareNodes(n1, n2 *Node) bool { + if n1 == nil || n2 == nil { + return n1 == n2 + } + + if n1.key != n2.key { + return false + } + + if !bytes.Equal(n1.data, n2.data) { + return false + } + + if n1.index != n2.index { + return false + } + + if n1.borders != n2.borders { + return false + } + + if n1.modified != n2.modified { + return false + } + + if n1.nodeType != n2.nodeType { + return false + } + + if !compareNodes(n1.prev, n2.prev) { + return false + } + + if len(n1.next) != len(n2.next) { + return false + } + + for k, v := range n1.next { + if !compareNodes(v, n2.next[k]) { + return false + } + } + + return true +} diff --git a/examples/gno.land/p/demo/json/parser.gno b/examples/gno.land/p/demo/json/parser.gno new file mode 100644 index 00000000000..9a2c3a8c817 --- /dev/null +++ b/examples/gno.land/p/demo/json/parser.gno @@ -0,0 +1,185 @@ +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 +) + +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 string(bf), nil +} + +// ParseBoolLiteral parses a boolean value from the given byte slice. +func ParseBoolLiteral(data []byte) (bool, error) { + switch { + case bytes.Equal(data, trueLiteral): + return true, nil + 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") + } + + 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 new file mode 100644 index 00000000000..44a2fee6404 --- /dev/null +++ b/examples/gno.land/p/demo/json/parser_test.gno @@ -0,0 +1,191 @@ +package json + +import ( + "strconv" + "testing" +) + +func TestParseStringLiteral(t *testing.T) { + tests := []struct { + input string + expected string + isError bool + }{ + {`"Hello, World!"`, "\"Hello, World!\"", false}, + {`\uFF11`, "\uFF11", false}, + {`\uFFFF`, "\uFFFF", false}, + {`true`, "true", false}, + {`false`, "false", false}, + {`\uDF00`, "", true}, + } + + for i, tt := range tests { + s, err := ParseStringLiteral([]byte(tt.input)) + + if !tt.isError && err != nil { + t.Errorf("%d. unexpected error: %s", i, err) + } + + if tt.isError && err == nil { + t.Errorf("%d. expected error, but not error", i) + } + + if s != tt.expected { + t.Errorf("%d. expected=%s, but actual=%s", i, tt.expected, s) + } + } +} + +func TestParseBoolLiteral(t *testing.T) { + tests := []struct { + input string + expected bool + isError bool + }{ + {`true`, true, false}, + {`false`, false, false}, + {`TRUE`, false, true}, + {`FALSE`, false, true}, + {`foo`, false, true}, + {`"true"`, false, true}, + {`"false"`, false, true}, + } + + for i, tt := range tests { + b, err := ParseBoolLiteral([]byte(tt.input)) + + if !tt.isError && err != nil { + t.Errorf("%d. unexpected error: %s", i, err) + } + + if tt.isError && err == nil { + t.Errorf("%d. expected error, but not error", i) + } + + if b != tt.expected { + t.Errorf("%d. expected=%t, but actual=%t", i, tt.expected, b) + } + } +} + +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/path.gno b/examples/gno.land/p/demo/json/path.gno new file mode 100644 index 00000000000..31f7e04633f --- /dev/null +++ b/examples/gno.land/p/demo/json/path.gno @@ -0,0 +1,78 @@ +package json + +import ( + "errors" +) + +// ParsePath takes a JSONPath string and returns a slice of strings representing the path segments. +func ParsePath(path string) ([]string, error) { + buf := newBuffer([]byte(path)) + result := make([]string, 0) + + for { + b, err := buf.current() + if err != nil { + break + } + + switch { + case b == dollarSign || b == atSign: + result = append(result, string(b)) + buf.step() + + case b == dot: + buf.step() + + if next, _ := buf.current(); next == dot { + buf.step() + result = append(result, "..") + + extractNextSegment(buf, &result) + } else { + extractNextSegment(buf, &result) + } + + case b == bracketOpen: + start := buf.index + buf.step() + + for { + if buf.index >= buf.length || buf.data[buf.index] == bracketClose { + break + } + + buf.step() + } + + if buf.index >= buf.length { + return nil, errors.New("unexpected end of path") + } + + segment := string(buf.sliceFromIndices(start+1, buf.index)) + result = append(result, segment) + + buf.step() + + default: + buf.step() + } + } + + return result, nil +} + +// extractNextSegment extracts the segment from the current index +// to the next significant character and adds it to the resulting slice. +func extractNextSegment(buf *buffer, result *[]string) { + start := buf.index + buf.skipToNextSignificantToken() + + if buf.index <= start { + return + } + + segment := string(buf.sliceFromIndices(start, buf.index)) + if segment != "" { + *result = append(*result, segment) + } +} diff --git a/examples/gno.land/p/demo/json/path_test.gno b/examples/gno.land/p/demo/json/path_test.gno new file mode 100644 index 00000000000..dd242849f03 --- /dev/null +++ b/examples/gno.land/p/demo/json/path_test.gno @@ -0,0 +1,64 @@ +package json + +import ( + "testing" +) + +func TestParseJSONPath(t *testing.T) { + tests := []struct { + name string + path string + expected []string + }{ + {name: "Empty string path", path: "", expected: []string{}}, + {name: "Root only path", path: "$", expected: []string{"$"}}, + {name: "Root with dot path", path: "$.", expected: []string{"$"}}, + {name: "All objects in path", path: "$..", expected: []string{"$", ".."}}, + {name: "Only children in path", path: "$.*", expected: []string{"$", "*"}}, + {name: "All objects' children in path", path: "$..*", expected: []string{"$", "..", "*"}}, + {name: "Simple dot notation path", path: "$.root.element", expected: []string{"$", "root", "element"}}, + {name: "Complex dot notation path with wildcard", path: "$.root.*.element", expected: []string{"$", "root", "*", "element"}}, + {name: "Path with array wildcard", path: "$.phoneNumbers[*].type", expected: []string{"$", "phoneNumbers", "*", "type"}}, + {name: "Path with filter expression", path: "$.store.book[?(@.price < 10)].title", expected: []string{"$", "store", "book", "?(@.price < 10)", "title"}}, + {name: "Path with formula", path: "$..phoneNumbers..('ty' + 'pe')", expected: []string{"$", "..", "phoneNumbers", "..", "('ty' + 'pe')"}}, + {name: "Simple bracket notation path", path: "$['root']['element']", expected: []string{"$", "'root'", "'element'"}}, + {name: "Complex bracket notation path with wildcard", path: "$['root'][*]['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Bracket notation path with integer index", path: "$['store']['book'][0]['title']", expected: []string{"$", "'store'", "'book'", "0", "'title'"}}, + {name: "Complex path with wildcard in bracket notation", path: "$['root'].*['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Mixed notation path with dot after bracket", path: "$.['root'].*.['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Mixed notation path with dot before bracket", path: "$['root'].*.['element']", expected: []string{"$", "'root'", "*", "'element'"}}, + {name: "Single character path with root", path: "$.a", expected: []string{"$", "a"}}, + {name: "Multiple characters path with root", path: "$.abc", expected: []string{"$", "abc"}}, + {name: "Multiple segments path with root", path: "$.a.b.c", expected: []string{"$", "a", "b", "c"}}, + {name: "Multiple segments path with wildcard and root", path: "$.a.*.c", expected: []string{"$", "a", "*", "c"}}, + {name: "Multiple segments path with filter and root", path: "$.a[?(@.b == 'c')].d", expected: []string{"$", "a", "?(@.b == 'c')", "d"}}, + {name: "Complex path with multiple filters", path: "$.a[?(@.b == 'c')].d[?(@.e == 'f')].g", expected: []string{"$", "a", "?(@.b == 'c')", "d", "?(@.e == 'f')", "g"}}, + {name: "Complex path with multiple filters and wildcards", path: "$.a[?(@.b == 'c')].*.d[?(@.e == 'f')].g", expected: []string{"$", "a", "?(@.b == 'c')", "*", "d", "?(@.e == 'f')", "g"}}, + {name: "Path with array index and root", path: "$.a[0].b", expected: []string{"$", "a", "0", "b"}}, + {name: "Path with multiple array indices and root", path: "$.a[0].b[1].c", expected: []string{"$", "a", "0", "b", "1", "c"}}, + {name: "Path with array index, wildcard and root", path: "$.a[0].*.c", expected: []string{"$", "a", "0", "*", "c"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reult, _ := ParsePath(tt.path) + if !isEqualSlice(reult, tt.expected) { + t.Errorf("ParsePath(%s) expected: %v, got: %v", tt.path, tt.expected, reult) + } + }) + } +} + +func isEqualSlice(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} diff --git a/examples/gno.land/p/demo/json/ryu/License b/examples/gno.land/p/demo/json/ryu/License new file mode 100644 index 00000000000..55beeadce54 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/License @@ -0,0 +1,21 @@ +# 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 new file mode 100644 index 00000000000..617141d2734 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/floatconv.gno @@ -0,0 +1,143 @@ +// 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 new file mode 100644 index 00000000000..7f01d4034f7 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/floatconv_test.gno @@ -0,0 +1,33 @@ +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 new file mode 100644 index 00000000000..86a1988b052 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/gno.mod @@ -0,0 +1 @@ +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 new file mode 100644 index 00000000000..249e3d0f526 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/ryu64.gno @@ -0,0 +1,344 @@ +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 new file mode 100644 index 00000000000..fe33ad90a57 --- /dev/null +++ b/examples/gno.land/p/demo/json/ryu/table.gno @@ -0,0 +1,678 @@ +// 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}, +} diff --git a/examples/gno.land/p/demo/json/token.gno b/examples/gno.land/p/demo/json/token.gno new file mode 100644 index 00000000000..4791850bf46 --- /dev/null +++ b/examples/gno.land/p/demo/json/token.gno @@ -0,0 +1,76 @@ +package json + +const ( + bracketOpen = '[' + bracketClose = ']' + parenOpen = '(' + parenClose = ')' + curlyOpen = '{' + curlyClose = '}' + comma = ',' + dot = '.' + colon = ':' + backTick = '`' + singleQuote = '\'' + doubleQuote = '"' + emptyString = "" + whiteSpace = ' ' + plus = '+' + minus = '-' + aesterisk = '*' + bang = '!' + question = '?' + newLine = '\n' + tab = '\t' + carriageReturn = '\r' + formFeed = '\f' + backSpace = '\b' + slash = '/' + backSlash = '\\' + underScore = '_' + dollarSign = '$' + atSign = '@' + andSign = '&' + orSign = '|' +) + +var ( + trueLiteral = []byte("true") + falseLiteral = []byte("false") + nullLiteral = []byte("null") +) + +type ValueType int + +const ( + NotExist ValueType = iota + String + Number + Float + Object + Array + Boolean + Null + Unknown +) + +func (v ValueType) String() string { + switch v { + case NotExist: + return "not-exist" + case String: + return "string" + case Number: + return "number" + case Object: + return "object" + case Array: + return "array" + case Boolean: + return "boolean" + case Null: + return "null" + default: + return "unknown" + } +} diff --git a/examples/gno.land/p/demo/memeland/memeland.gno b/examples/gno.land/p/demo/memeland/memeland.gno index 2296b82eb23..9c302ca365b 100644 --- a/examples/gno.land/p/demo/memeland/memeland.gno +++ b/examples/gno.land/p/demo/memeland/memeland.gno @@ -12,6 +12,11 @@ import ( "gno.land/p/demo/seqid" ) +const ( + DATE_CREATED = "DATE_CREATED" + UPVOTES = "UPVOTES" +) + type Post struct { ID string Data string @@ -78,7 +83,7 @@ func (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pag } if page < 1 { - panic("page count cannot be less than 1") + panic("page number cannot be less than 1") } // No empty pages @@ -91,6 +96,11 @@ func (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pag panic("page size cannot be larger than 10") } + // Need to pass in a sort parameter + if sortBy == "" { + panic("sort order cannot be empty") + } + var filteredPosts []*Post start := time.Unix(startTimestamp, 0) @@ -105,7 +115,7 @@ func (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pag switch sortBy { // Sort by upvote descending - case "UPVOTES": + case UPVOTES: dateSorter := PostSorter{ Posts: filteredPosts, LessF: func(i, j int) bool { @@ -113,7 +123,7 @@ func (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pag }, } sort.Sort(dateSorter) - default: + case DATE_CREATED: // Sort by timestamp, beginning with newest dateSorter := PostSorter{ Posts: filteredPosts, @@ -122,6 +132,8 @@ func (m *Memeland) GetPostsInRange(startTimestamp, endTimestamp int64, page, pag }, } sort.Sort(dateSorter) + default: + panic("sort order can only be \"UPVOTES\" or \"DATE_CREATED\"") } // Pagination diff --git a/examples/gno.land/p/demo/memeland/memeland_test.gno b/examples/gno.land/p/demo/memeland/memeland_test.gno index 0d3aea8de97..76c44327993 100644 --- a/examples/gno.land/p/demo/memeland/memeland_test.gno +++ b/examples/gno.land/p/demo/memeland/memeland_test.gno @@ -167,6 +167,56 @@ func TestGetPostsInRangeByUpvote(t *testing.T) { } } +func TestBadSortBy(t *testing.T) { + m := NewMemeland() + now := time.Now() + + numOfPosts := 5 + var memeData []string + for i := 1; i <= numOfPosts; i++ { + // Prepare meme data + nextTime := now.Add(time.Duration(i) * time.Minute) + data := ufmt.Sprintf("Meme #%d", i) + memeData = append(memeData, data) + + m.PostMeme(data, nextTime.Unix()) + } + + // Get timestamps + beforeEarliest := now.Add(-1 * time.Minute) + afterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute) + + tests := []struct { + name string + sortBy string + wantPanic string + }{ + { + name: "Empty sortBy", + sortBy: "", + wantPanic: "runtime error: index out of range", + }, + { + name: "Wrong sortBy", + sortBy: "random string", + wantPanic: "", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("code did not panic when it should have") + } + }() + + // Panics should be caught + _ = m.GetPostsInRange(beforeEarliest.Unix(), afterLatest.Unix(), 1, 1, tc.sortBy) + }) + } +} + func TestNoPosts(t *testing.T) { m := NewMemeland() @@ -252,7 +302,7 @@ func TestDeleteByNonAdmin(t *testing.T) { defer func() { if r := recover(); r == nil { - t.Errorf("code did not panic when in should have") + t.Errorf("code did not panic when it should have") } }() diff --git a/examples/gno.land/p/demo/ufmt/ufmt.gno b/examples/gno.land/p/demo/ufmt/ufmt.gno index 970fac2eccc..d075a1306ee 100644 --- a/examples/gno.land/p/demo/ufmt/ufmt.gno +++ b/examples/gno.land/p/demo/ufmt/ufmt.gno @@ -3,7 +3,38 @@ // (hence the name µfmt - micro fmt). package ufmt -import "strconv" +import ( + "strconv" + "strings" +) + +// Println formats using the default formats for its operands and writes to standard output. +// Println writes the given arguments to standard output with spaces between arguments +// and a newline at the end. +func Println(args ...interface{}) { + var strs []string + for _, arg := range args { + switch v := arg.(type) { + case string: + strs = append(strs, v) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + strs = append(strs, Sprintf("%d", v)) + case bool: + if v { + strs = append(strs, "true") + + continue + } + + strs = append(strs, "false") + default: + strs = append(strs, "(unhandled)") + } + } + + // TODO: remove println after gno supports os.Stdout + println(strings.Join(strs, " ")) +} // Sprintf offers similar functionality to Go's fmt.Sprintf, or the sprintf // equivalent available in many languages, including C/C++. diff --git a/examples/gno.land/p/demo/ufmt/ufmt_test.gno b/examples/gno.land/p/demo/ufmt/ufmt_test.gno index b9b02ec8af5..d4c1a2a4fc3 100644 --- a/examples/gno.land/p/demo/ufmt/ufmt_test.gno +++ b/examples/gno.land/p/demo/ufmt/ufmt_test.gno @@ -99,3 +99,46 @@ func TestErrorf(t *testing.T) { }) } } + +// NOTE: Currently, there is no way to get the output of Println without using os.Stdout, +// so we can only test that it doesn't panic and print arguments well. +func TestPrintln(t *testing.T) { + tests := []struct { + name string + args []interface{} + expected string + }{ + { + name: "Empty args", + args: []interface{}{}, + expected: "", + }, + { + name: "String args", + args: []interface{}{"Hello", "World"}, + expected: "Hello World", + }, + { + name: "Integer args", + args: []interface{}{1, 2, 3}, + expected: "1 2 3", + }, + { + name: "Mixed args", + args: []interface{}{"Hello", 42, true, false, "World"}, + expected: "Hello 42 true false World", + }, + { + name: "Unhandled type", + args: []interface{}{"Hello", 3.14, []int{1, 2, 3}}, + expected: "Hello (unhandled) (unhandled)", + }, + } + + // TODO: replace os.Stdout with a buffer to capture the output and test it. + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + Println(tt.args...) + }) + } +} diff --git a/examples/gno.land/p/demo/uint256/LICENSE b/examples/gno.land/p/demo/uint256/LICENSE new file mode 100644 index 00000000000..505e4324f88 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright 2020 uint256 Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/examples/gno.land/p/demo/uint256/README.md b/examples/gno.land/p/demo/uint256/README.md new file mode 100644 index 00000000000..b580cd33df6 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/README.md @@ -0,0 +1,5 @@ +# Fixed size 256-bit math library + +This is a library specialized at replacing the `big.Int` library for math based on 256-bit types. + +original repository: [uint256]() diff --git a/examples/gno.land/p/demo/uint256/arithmetic.gno b/examples/gno.land/p/demo/uint256/arithmetic.gno new file mode 100644 index 00000000000..c3e2ed83738 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/arithmetic.gno @@ -0,0 +1,472 @@ +// arithmetic provides arithmetic operations for Uint objects. +// This includes basic binary operations such as addition, subtraction, multiplication, division, and modulo operations +// as well as overflow checks, and negation. These functions are essential for numeric +// calculations using 256-bit unsigned integers. +package uint256 + +import ( + "math/bits" +) + +// Add sets z to the sum x+y +func (z *Uint) Add(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Add64(x.arr[3], y.arr[3], carry) + return z +} + +// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred +func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Add64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// Sub sets z to the difference x-y +func (z *Uint) Sub(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Sub64(x.arr[3], y.arr[3], carry) + return z +} + +// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed +func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Sub64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// Neg returns -x mod 2^256. +func (z *Uint) Neg(x *Uint) *Uint { + return z.Sub(new(Uint), x) +} + +// commented out for possible overflow +// Mul sets z to the product x*y +func (z *Uint) Mul(x, y *Uint) *Uint { + var ( + res Uint + carry uint64 + res1, res2, res3 uint64 + ) + + carry, res.arr[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + res3 = x.arr[3]*y.arr[0] + carry + + carry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + res3 = res3 + x.arr[2]*y.arr[1] + carry + + carry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2]) + res3 = res3 + x.arr[1]*y.arr[2] + carry + + res.arr[3] = res3 + x.arr[0]*y.arr[3] + + return z.Set(&res) +} + +// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred +func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) { + p := umul(x, y) + copy(z.arr[:], p[:4]) + return z, (p[4] | p[5] | p[6] | p[7]) != 0 +} + +// commented out for possible overflow +// Div sets z to the quotient x/y for returns z. +// If y == 0, z is set to 0 +func (z *Uint) Div(x, y *Uint) *Uint { + if y.IsZero() || y.Gt(x) { + return z.Clear() + } + if x.Eq(y) { + return z.SetOne() + } + // Shortcut some cases + if x.IsUint64() { + return z.SetUint64(x.Uint64() / y.Uint64()) + } + + // At this point, we know + // x/y ; x > y > 0 + + var quot Uint + udivrem(quot.arr[:], x.arr[:], y) + return z.Set(") +} + +// MulMod calculates the modulo-m multiplication of x and y and +// returns z. +// If m == 0, z is set to 0 (OBS: differs from the big.Int) +func (z *Uint) MulMod(x, y, m *Uint) *Uint { + if x.IsZero() || y.IsZero() || m.IsZero() { + return z.Clear() + } + p := umul(x, y) + + if m.arr[3] != 0 { + mu := Reciprocal(m) + r := reduce4(p, m, mu) + return z.Set(&r) + } + + var ( + pl Uint + ph Uint + ) + + pl = Uint{arr: [4]uint64{p[0], p[1], p[2], p[3]}} + ph = Uint{arr: [4]uint64{p[4], p[5], p[6], p[7]}} + + // If the multiplication is within 256 bits use Mod(). + if ph.IsZero() { + return z.Mod(&pl, m) + } + + var quot [8]uint64 + rem := udivrem(quot[:], p[:], m) + return z.Set(&rem) +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, z is set to 0 (OBS: differs from the big.Uint) +func (z *Uint) Mod(x, y *Uint) *Uint { + if x.IsZero() || y.IsZero() { + return z.Clear() + } + switch x.Cmp(y) { + case -1: + // x < y + copy(z.arr[:], x.arr[:]) + return z + case 0: + // x == y + return z.Clear() // They are equal + } + + // At this point: + // x != 0 + // y != 0 + // x > y + + // Shortcut trivial case + if x.IsUint64() { + return z.SetUint64(x.Uint64() % y.Uint64()) + } + + var quot Uint + *z = udivrem(quot.arr[:], x.arr[:], y) + return z +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y and returns the pair (z, m) for y != 0. +// If y == 0, both z and m are set to 0 (OBS: differs from the big.Int) +func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) { + if y.IsZero() { + return z.Clear(), m.Clear() + } + var quot Uint + *m = udivrem(quot.arr[:], x.arr[:], y) + *z = quot + return z, m +} + +// Exp sets z = base**exponent mod 2**256, and returns z. +func (z *Uint) Exp(base, exponent *Uint) *Uint { + res := Uint{arr: [4]uint64{1, 0, 0, 0}} + multiplier := *base + expBitLen := exponent.BitLen() + + curBit := 0 + word := exponent.arr[0] + for ; curBit < expBitLen && curBit < 64; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[1] + for ; curBit < expBitLen && curBit < 128; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[2] + for ; curBit < expBitLen && curBit < 192; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[3] + for ; curBit < expBitLen && curBit < 256; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + return z.Set(&res) +} + +func (z *Uint) squared() { + var ( + res Uint + carry0, carry1, carry2 uint64 + res1, res2 uint64 + ) + + carry0, res.arr[0] = bits.Mul64(z.arr[0], z.arr[0]) + carry0, res1 = umulHop(carry0, z.arr[0], z.arr[1]) + carry0, res2 = umulHop(carry0, z.arr[0], z.arr[2]) + + carry1, res.arr[1] = umulHop(res1, z.arr[0], z.arr[1]) + carry1, res2 = umulStep(res2, z.arr[1], z.arr[1], carry1) + + carry2, res.arr[2] = umulHop(res2, z.arr[0], z.arr[2]) + + res.arr[3] = 2*(z.arr[0]*z.arr[3]+z.arr[1]*z.arr[2]) + carry0 + carry1 + carry2 + + z.Set(&res) +} + +// udivrem divides u by d and produces both quotient and remainder. +// The quotient is stored in provided quot - len(u)-len(d)+1 words. +// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +func udivrem(quot, u []uint64, d *Uint) (rem Uint) { + var dLen int + for i := len(d.arr) - 1; i >= 0; i-- { + if d.arr[i] != 0 { + dLen = i + 1 + break + } + } + + shift := uint(bits.LeadingZeros64(d.arr[dLen-1])) + + var dnStorage Uint + dn := dnStorage.arr[:dLen] + for i := dLen - 1; i > 0; i-- { + dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) + } + dn[0] = d.arr[0] << shift + + var uLen int + for i := len(u) - 1; i >= 0; i-- { + if u[i] != 0 { + uLen = i + 1 + break + } + } + + if uLen < dLen { + copy(rem.arr[:], u) + return rem + } + + var unStorage [9]uint64 + un := unStorage[:uLen+1] + un[uLen] = u[uLen-1] >> (64 - shift) + for i := uLen - 1; i > 0; i-- { + un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) + } + un[0] = u[0] << shift + + // TODO: Skip the highest word of numerator if not significant. + + if dLen == 1 { + r := udivremBy1(quot, un, dn[0]) + rem.SetUint64(r >> shift) + return rem + } + + udivremKnuth(quot, un, dn) + + for i := 0; i < dLen-1; i++ { + rem.arr[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) + } + rem.arr[dLen-1] = un[dLen-1] >> shift + + return rem +} + +// umul computes full 256 x 256 -> 512 multiplication. +func umul(x, y *Uint) [8]uint64 { + var ( + res [8]uint64 + carry, carry4, carry5, carry6 uint64 + res1, res2, res3, res4, res5 uint64 + ) + + carry, res[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + carry4, res3 = umulHop(carry, x.arr[3], y.arr[0]) + + carry, res[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + carry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry) + carry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry) + + carry, res[2] = umulHop(res2, x.arr[0], y.arr[2]) + carry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry) + carry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry) + carry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry) + + carry, res[3] = umulHop(res3, x.arr[0], y.arr[3]) + carry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry) + carry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry) + res[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry) + + return res +} + +// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry. +func umulStep(z, x, y, carry uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry = bits.Add64(lo, carry, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// umulHop computes (hi * 2^64 + lo) = z + (x * y) +func umulHop(z, x, y uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry := bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// udivremBy1 divides u by single normalized word d and produces both quotient and remainder. +// The quotient is stored in provided quot. +func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { + reciprocal := reciprocal2by1(d) + rem = u[len(u)-1] // Set the top word as remainder. + for j := len(u) - 2; j >= 0; j-- { + quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) + } + return rem +} + +// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm. +// The quotient is stored in provided quot - len(u)-len(d) words. +// Updates u to contain the remainder - len(d) words. +func udivremKnuth(quot, u, d []uint64) { + dh := d[len(d)-1] + dl := d[len(d)-2] + reciprocal := reciprocal2by1(dh) + + for j := len(u) - len(d) - 1; j >= 0; j-- { + u2 := u[j+len(d)] + u1 := u[j+len(d)-1] + u0 := u[j+len(d)-2] + + var qhat, rhat uint64 + if u2 >= dh { // Division overflows. + qhat = ^uint64(0) + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } else { + qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) + ph, pl := bits.Mul64(qhat, dl) + if ph > rhat || (ph == rhat && pl > u0) { + qhat-- + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } + } + + // Multiply and subtract. + borrow := subMulTo(u[j:], d, qhat) + u[j+len(d)] = u2 - borrow + if u2 < borrow { // Too much subtracted, add back. + qhat-- + u[j+len(d)] += addTo(u[j:], d) + } + + quot[j] = qhat // Store quotient digit. + } +} + +// isBitSet returns true if bit n-th is set, where n = 0 is LSB. +// The n must be <= 255. +func (z *Uint) isBitSet(n uint) bool { + return (z.arr[n/64] & (1 << (n % 64))) != 0 +} + +// addTo computes x += y. +// Requires len(x) >= len(y). +func addTo(x, y []uint64) uint64 { + var carry uint64 + for i := 0; i < len(y); i++ { + x[i], carry = bits.Add64(x[i], y[i], carry) + } + return carry +} + +// subMulTo computes x -= y * multiplier. +// Requires len(x) >= len(y). +func subMulTo(x, y []uint64, multiplier uint64) uint64 { + var borrow uint64 + for i := 0; i < len(y); i++ { + s, carry1 := bits.Sub64(x[i], borrow, 0) + ph, pl := bits.Mul64(y[i], multiplier) + t, carry2 := bits.Sub64(s, pl, 0) + x[i] = t + borrow = ph + carry1 + carry2 + } + return borrow +} + +// reciprocal2by1 computes <^d, ^0> / d. +func reciprocal2by1(d uint64) uint64 { + reciprocal, _ := bits.Div64(^d, ^uint64(0), d) + return reciprocal +} + +// udivrem2by1 divides / d and produces both quotient and remainder. +// It uses the provided d's reciprocal. +// Implementation ported from https://github.com/chfast/intx and is based on +// "Improved division by invariant integers", Algorithm 4. +func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { + qh, ql := bits.Mul64(reciprocal, uh) + ql, carry := bits.Add64(ql, ul, 0) + qh, _ = bits.Add64(qh, uh, carry) + qh++ + + r := ul - qh*d + + if r > ql { + qh-- + r += d + } + + if r >= d { + qh++ + r -= d + } + + return qh, r +} diff --git a/examples/gno.land/p/demo/uint256/arithmetic_test.gno b/examples/gno.land/p/demo/uint256/arithmetic_test.gno new file mode 100644 index 00000000000..9f45a507754 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/arithmetic_test.gno @@ -0,0 +1,326 @@ +package uint256 + +import "testing" + +type binOp2Test struct { + x, y, want string +} + +func TestAdd(t *testing.T) { + tests := []binOp2Test{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "3", "4"}, + {"10", "10", "20"}, + {"18446744073709551615", "18446744073709551615", "36893488147419103230"}, // uint64 overflow + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Add(x, y) + + if got.Neq(want) { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestSub(t *testing.T) { + tests := []binOp2Test{ + {"1", "0", "1"}, + {"1", "1", "0"}, + {"10", "10", "0"}, + {"31337", "1337", "30000"}, + {"2", "3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, // underflow + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Sub(x, y) + + if got.Neq(want) { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMul(t *testing.T) { + tests := []binOp2Test{ + {"1", "0", "0"}, + {"1", "1", "1"}, + {"10", "10", "100"}, + {"18446744073709551615", "2", "36893488147419103230"}, // uint64 overflow + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Mul(x, y) + + if got.Neq(want) { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDiv(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "10445"}, + {"31337", "0", "0"}, + {"0", "31337", "0"}, + {"1", "1", "1"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Div(x, y) + + if got.Neq(want) { + t.Errorf("Div(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMod(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "2"}, + {"31337", "0", "0"}, + {"0", "31337", "0"}, + {"2", "31337", "2"}, + {"1", "1", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Mod(x, y) + + if got.Neq(want) { + t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDivMod(t *testing.T) { + tests := []struct { + x string + y string + wantDiv string + wantMod string + }{ + {"1", "1", "1", "0"}, + {"10", "10", "1", "0"}, + {"100", "10", "10", "0"}, + {"31337", "3", "10445", "2"}, + {"31337", "0", "0", "0"}, + {"0", "31337", "0", "0"}, + {"2", "31337", "0", "2"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + wantDiv, err := FromDecimal(tc.wantDiv) + if err != nil { + t.Error(err) + continue + } + + wantMod, err := FromDecimal(tc.wantMod) + if err != nil { + t.Error(err) + continue + } + + gotDiv := new(Uint) + gotMod := new(Uint) + gotDiv.DivMod(x, y, gotMod) + + for i := range gotDiv.arr { + if gotDiv.arr[i] != wantDiv.arr[i] { + t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tc.x, tc.y, gotDiv, wantDiv) + break + } + } + for i := range gotMod.arr { + if gotMod.arr[i] != wantMod.arr[i] { + t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tc.x, tc.y, gotMod, wantMod) + break + } + } + } +} + +func TestNeg(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"31337", "115792089237316195423570985008687907853269984665640564039457584007913129608599"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129608599", "31337"}, + {"0", "0"}, + {"2", "115792089237316195423570985008687907853269984665640564039457584007913129639934"}, + {"1", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Neg(x) + + if got.Neq(want) { + t.Errorf("Neg(%s) = %v, want %v", tc.x, got.ToString(), want.ToString()) + } + } +} + +func TestExp(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "30773171189753"}, + {"31337", "0", "1"}, + {"0", "31337", "0"}, + {"1", "1", "1"}, + {"2", "3", "8"}, + {"2", "64", "18446744073709551616"}, + {"2", "128", "340282366920938463463374607431768211456"}, + {"2", "255", "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"2", "256", "0"}, // overflow + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Exp(x, y) + + if got.Neq(want) { + t.Errorf("Exp(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/bits_table.gno b/examples/gno.land/p/demo/uint256/bits_table.gno new file mode 100644 index 00000000000..53dbea94827 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bits_table.gno @@ -0,0 +1,79 @@ +// Copyright 2017 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. + +// Code generated by go run make_tables.go. DO NOT EDIT. + +package uint256 + +const ntz8tab = "" + + "\x08\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x07\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + +const pop8tab = "" + + "\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08" + +const rev8tab = "" + + "\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10\x90\x50\xd0\x30\xb0\x70\xf0" + + "\x08\x88\x48\xc8\x28\xa8\x68\xe8\x18\x98\x58\xd8\x38\xb8\x78\xf8" + + "\x04\x84\x44\xc4\x24\xa4\x64\xe4\x14\x94\x54\xd4\x34\xb4\x74\xf4" + + "\x0c\x8c\x4c\xcc\x2c\xac\x6c\xec\x1c\x9c\x5c\xdc\x3c\xbc\x7c\xfc" + + "\x02\x82\x42\xc2\x22\xa2\x62\xe2\x12\x92\x52\xd2\x32\xb2\x72\xf2" + + "\x0a\x8a\x4a\xca\x2a\xaa\x6a\xea\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa" + + "\x06\x86\x46\xc6\x26\xa6\x66\xe6\x16\x96\x56\xd6\x36\xb6\x76\xf6" + + "\x0e\x8e\x4e\xce\x2e\xae\x6e\xee\x1e\x9e\x5e\xde\x3e\xbe\x7e\xfe" + + "\x01\x81\x41\xc1\x21\xa1\x61\xe1\x11\x91\x51\xd1\x31\xb1\x71\xf1" + + "\x09\x89\x49\xc9\x29\xa9\x69\xe9\x19\x99\x59\xd9\x39\xb9\x79\xf9" + + "\x05\x85\x45\xc5\x25\xa5\x65\xe5\x15\x95\x55\xd5\x35\xb5\x75\xf5" + + "\x0d\x8d\x4d\xcd\x2d\xad\x6d\xed\x1d\x9d\x5d\xdd\x3d\xbd\x7d\xfd" + + "\x03\x83\x43\xc3\x23\xa3\x63\xe3\x13\x93\x53\xd3\x33\xb3\x73\xf3" + + "\x0b\x8b\x4b\xcb\x2b\xab\x6b\xeb\x1b\x9b\x5b\xdb\x3b\xbb\x7b\xfb" + + "\x07\x87\x47\xc7\x27\xa7\x67\xe7\x17\x97\x57\xd7\x37\xb7\x77\xf7" + + "\x0f\x8f\x4f\xcf\x2f\xaf\x6f\xef\x1f\x9f\x5f\xdf\x3f\xbf\x7f\xff" + +const len8tab = "" + + "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04" + + "\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" diff --git a/examples/gno.land/p/demo/uint256/bitwise.gno b/examples/gno.land/p/demo/uint256/bitwise.gno new file mode 100644 index 00000000000..fc6e098dc91 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bitwise.gno @@ -0,0 +1,264 @@ +// bitwise contains bitwise operations for Uint instances. +// This file includes functions to perform bitwise AND, OR, XOR, and NOT operations, as well as bit shifting. +// These operations are crucial for manipulating individual bits within a 256-bit unsigned integer. +package uint256 + +// Or sets z = x | y and returns z. +func (z *Uint) Or(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] | y.arr[0] + z.arr[1] = x.arr[1] | y.arr[1] + z.arr[2] = x.arr[2] | y.arr[2] + z.arr[3] = x.arr[3] | y.arr[3] + return z +} + +// And sets z = x & y and returns z. +func (z *Uint) And(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] & y.arr[0] + z.arr[1] = x.arr[1] & y.arr[1] + z.arr[2] = x.arr[2] & y.arr[2] + z.arr[3] = x.arr[3] & y.arr[3] + return z +} + +// Not sets z = ^x and returns z. +func (z *Uint) Not(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] + return z +} + +// AndNot sets z = x &^ y and returns z. +func (z *Uint) AndNot(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] &^ y.arr[0] + z.arr[1] = x.arr[1] &^ y.arr[1] + z.arr[2] = x.arr[2] &^ y.arr[2] + z.arr[3] = x.arr[3] &^ y.arr[3] + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Uint) Xor(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] ^ y.arr[0] + z.arr[1] = x.arr[1] ^ y.arr[1] + z.arr[2] = x.arr[2] ^ y.arr[2] + z.arr[3] = x.arr[3] ^ y.arr[3] + return z +} + +// Lsh sets z = x << n and returns z. +func (z *Uint) Lsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.lsh64(x) + case 128: + return z.lsh128(x) + case 192: + return z.lsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.lsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.lsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.lsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[0] >> (64 - n) + z.arr[0] = z.arr[0] << n + +sh64: + b = z.arr[1] >> (64 - n) + z.arr[1] = (z.arr[1] << n) | a + +sh128: + a = z.arr[2] >> (64 - n) + z.arr[2] = (z.arr[2] << n) | b + +sh192: + z.arr[3] = (z.arr[3] << n) | a + + return z +} + +// Rsh sets z = x >> n and returns z. +func (z *Uint) Rsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.rsh64(x) + case 128: + return z.rsh128(x) + case 192: + return z.rsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.rsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.rsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.rsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[3] << (64 - n) + z.arr[3] = z.arr[3] >> n + +sh64: + b = z.arr[2] << (64 - n) + z.arr[2] = (z.arr[2] >> n) | a + +sh128: + a = z.arr[1] << (64 - n) + z.arr[1] = (z.arr[1] >> n) | b + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +// SRsh (Signed/Arithmetic right shift) +// considers z to be a signed integer, during right-shift +// and sets z = x >> n and returns z. +func (z *Uint) SRsh(x *Uint, n uint) *Uint { + // If the MSB is 0, SRsh is same as Rsh. + if !x.isBitSet(255) { + return z.Rsh(x, n) + } + if n%64 == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.srsh64(x) + case 128: + return z.srsh128(x) + case 192: + return z.srsh192(x) + default: + return z.SetAllOne() + } + } + var a uint64 = MaxUint64 << (64 - n%64) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.SetAllOne() + } + z.srsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.srsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.srsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + z.arr[3], a = (z.arr[3]>>n)|a, z.arr[3]<<(64-n) + +sh64: + z.arr[2], a = (z.arr[2]>>n)|a, z.arr[2]<<(64-n) + +sh128: + z.arr[1], a = (z.arr[1]>>n)|a, z.arr[1]<<(64-n) + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +func (z *Uint) lsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 + return z +} + +func (z *Uint) lsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 + return z +} + +func (z *Uint) lsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 + return z +} + +func (z *Uint) rsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) rsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) rsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] + return z +} + +func (z *Uint) srsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) srsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) srsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] + return z +} diff --git a/examples/gno.land/p/demo/uint256/bitwise_test.gno b/examples/gno.land/p/demo/uint256/bitwise_test.gno new file mode 100644 index 00000000000..4437e7d5c50 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bitwise_test.gno @@ -0,0 +1,346 @@ +package uint256 + +import ( + "testing" +) + +type logicOpTest struct { + name string + x Uint + y Uint + want Uint +} + +func TestOr(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Or(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("Or(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestAnd(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).And(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("And(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestNot(t *testing.T) { + tests := []struct { + name string + x Uint + want Uint + }{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Not(&tc.x) + if *res != tc.want { + t.Errorf("Not(%s) = %s, want %s", tc.x.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestAndNot(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).AndNot(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("AndNot(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestXor(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Xor(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("Xor(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestLsh(t *testing.T) { + tests := []struct { + x string + y uint + want string + }{ + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 64, "0"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 64, "18446744073709551616"}, + {"1", 128, "340282366920938463463374607431768211456"}, + {"1", 192, "6277101735386680763835789423207666416102355444464034512896"}, + {"1", 255, "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"1", 256, "0"}, + {"31337", 0, "31337"}, + {"31337", 1, "62674"}, + {"31337", 64, "578065619037836218990592"}, + {"31337", 128, "10663428532201448629551770073089320442396672"}, + {"31337", 192, "196705537081812415096322133155058642481399512563169449530621952"}, + {"31337", 193, "393411074163624830192644266310117284962799025126338899061243904"}, + {"31337", 255, "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"31337", 256, "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Lsh(x, tc.y) + + if got.Neq(want) { + t.Errorf("Lsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestRsh(t *testing.T) { + tests := []struct { + x string + y uint + want string + }{ + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 64, "0"}, + {"1", 0, "1"}, + {"1", 1, "0"}, + {"1", 64, "0"}, + {"1", 128, "0"}, + {"1", 192, "0"}, + {"1", 255, "0"}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", 255, "1"}, + {"6277101735386680763835789423207666416102355444464034512896", 192, "1"}, + {"340282366920938463463374607431768211456", 128, "1"}, + {"18446744073709551616", 64, "1"}, + {"393411074163624830192644266310117284962799025126338899061243904", 193, "31337"}, + {"196705537081812415096322133155058642481399512563169449530621952", 192, "31337"}, + {"10663428532201448629551770073089320442396672", 128, "31337"}, + {"578065619037836218990592", 64, "31337"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Rsh(x, tc.y) + + if got.Neq(want) { + t.Errorf("Rsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/cmp.gno b/examples/gno.land/p/demo/uint256/cmp.gno new file mode 100644 index 00000000000..a43a31e5ff7 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/cmp.gno @@ -0,0 +1,125 @@ +// cmp (or, comparisons) includes methods for comparing Uint instances. +// These comparison functions cover a range of operations including equality checks, less than/greater than +// evaluations, and specialized comparisons such as signed greater than. These are fundamental for logical +// decision making based on Uint values. +package uint256 + +import ( + "math/bits" +) + +// Cmp compares z and x and returns: +// +// -1 if z < x +// 0 if z == x +// +1 if z > x +func (z *Uint) Cmp(x *Uint) (r int) { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + d0, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + d1, carry := bits.Sub64(z.arr[1], x.arr[1], carry) + d2, carry := bits.Sub64(z.arr[2], x.arr[2], carry) + d3, carry := bits.Sub64(z.arr[3], x.arr[3], carry) + if carry == 1 { + return -1 + } + if d0|d1|d2|d3 == 0 { + return 0 + } + return 1 +} + +// IsZero returns true if z == 0 +func (z *Uint) IsZero() bool { + return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// Sign returns: +// +// -1 if z < 0 +// 0 if z == 0 +// +1 if z > 0 +// +// Where z is interpreted as a two's complement signed number +func (z *Uint) Sign() int { + if z.IsZero() { + return 0 + } + if z.arr[3] < 0x8000000000000000 { + return 1 + } + return -1 +} + +// LtUint64 returns true if z is smaller than n +func (z *Uint) LtUint64(n uint64) bool { + return z.arr[0] < n && (z.arr[1]|z.arr[2]|z.arr[3]) == 0 +} + +// GtUint64 returns true if z is larger than n +func (z *Uint) GtUint64(n uint64) bool { + return z.arr[0] > n || (z.arr[1]|z.arr[2]|z.arr[3]) != 0 +} + +// Lt returns true if z < x +func (z *Uint) Lt(x *Uint) bool { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + _, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + _, carry = bits.Sub64(z.arr[1], x.arr[1], carry) + _, carry = bits.Sub64(z.arr[2], x.arr[2], carry) + _, carry = bits.Sub64(z.arr[3], x.arr[3], carry) + + return carry != 0 +} + +// Gt returns true if z > x +func (z *Uint) Gt(x *Uint) bool { + return x.Lt(z) +} + +// Lte returns true if z <= x +func (z *Uint) Lte(x *Uint) bool { + cond1 := z.Lt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Gte returns true if z >= x +func (z *Uint) Gte(x *Uint) bool { + cond1 := z.Gt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Eq returns true if z == x +func (z *Uint) Eq(x *Uint) bool { + return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) +} + +// Neq returns true if z != x +func (z *Uint) Neq(x *Uint) bool { + return !z.Eq(x) +} + +// Sgt interprets z and x as signed integers, and returns +// true if z > x +func (z *Uint) Sgt(x *Uint) bool { + zSign := z.Sign() + xSign := x.Sign() + + switch { + case zSign >= 0 && xSign < 0: + return true + case zSign < 0 && xSign >= 0: + return false + default: + return z.Gt(x) + } +} diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno new file mode 100644 index 00000000000..930079f70f0 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -0,0 +1,163 @@ +package uint256 + +import ( + "strings" + "testing" +) + +func TestCmp(t *testing.T) { + tests := []struct { + x, y string + want int + }{ + {"0", "0", 0}, + {"0", "1", -1}, + {"1", "0", 1}, + {"1", "1", 0}, + {"10", "10", 0}, + {"10", "11", -1}, + {"11", "10", 1}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Cmp(y) + if got != tc.want { + t.Errorf("Cmp(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestIsZero(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsZero() + if got != tc.want { + t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestLtUint64(t *testing.T) { + tests := []struct { + x string + y uint64 + want bool + }{ + {"0", 1, true}, + {"1", 0, false}, + {"10", 10, false}, + {"0xffffffffffffffff", 0, false}, + {"0x10000000000000000", 10000000000000000, false}, + } + + for _, tc := range tests { + var x *Uint + var err error + + if strings.HasPrefix(tc.x, "0x") { + x, err = FromHex(tc.x) + if err != nil { + t.Error(err) + continue + } + } else { + x, err = FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + } + + got := x.LtUint64(tc.y) + + if got != tc.want { + t.Errorf("LtUint64(%s, %d) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestSGT(t *testing.T) { + x := MustFromHex("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") + y := MustFromHex("0x0") + actual := x.Sgt(y) + if actual { + t.Fatalf("Expected %v false", actual) + } + + x = MustFromHex("0x0") + y = MustFromHex("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") + actual = x.Sgt(y) + if !actual { + t.Fatalf("Expected %v true", actual) + } +} + +func TestEq(t *testing.T) { + tests := []struct { + x string + y string + want bool + }{ + {"0xffffffffffffffff", "18446744073709551615", true}, + {"0x10000000000000000", "18446744073709551616", true}, + {"0", "0", true}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for i, tc := range tests { + var x *Uint + var err error + + if strings.HasPrefix(tc.x, "0x") { + x, err = FromHex(tc.x) + if err != nil { + t.Error(err) + continue + } + } else { + x, err = FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Eq(y) + + if got != tc.want { + t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno new file mode 100644 index 00000000000..4ef90602ab3 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -0,0 +1,570 @@ +// conversions contains methods for converting Uint instances to other types and vice versa. +// This includes conversions to and from basic types such as uint64 and int32, as well as string representations +// and byte slices. Additionally, it covers marshaling and unmarshaling for JSON and other text formats. +package uint256 + +import ( + "encoding/binary" + "errors" + "strconv" + "strings" +) + +// Uint64 returns the lower 64-bits of z +func (z *Uint) Uint64() uint64 { + return z.arr[0] +} + +// Uint64WithOverflow returns the lower 64-bits of z and bool whether overflow occurred +func (z *Uint) Uint64WithOverflow() (uint64, bool) { + return z.arr[0], (z.arr[1] | z.arr[2] | z.arr[3]) != 0 +} + +// SetUint64 sets z to the value x +func (z *Uint) SetUint64(x uint64) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x + return z +} + +// IsUint64 reports whether z can be represented as a uint64. +func (z *Uint) IsUint64() bool { + return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// Dec returns the decimal representation of z. +func (z *Uint) Dec() string { + if z.IsZero() { + return "0" + } + if z.IsUint64() { + return strconv.FormatUint(z.Uint64(), 10) + } + + // The max uint64 value being 18446744073709551615, the largest + // power-of-ten below that is 10000000000000000000. + // When we do a DivMod using that number, the remainder that we + // get back is the lower part of the output. + // + // The ascii-output of remainder will never exceed 19 bytes (since it will be + // below 10000000000000000000). + // + // Algorithm example using 100 as divisor + // + // 12345 % 100 = 45 (rem) + // 12345 / 100 = 123 (quo) + // -> output '45', continue iterate on 123 + var ( + // out is 98 bytes long: 78 (max size of a string without leading zeroes, + // plus slack so we can copy 19 bytes every iteration). + // We init it with zeroes, because when strconv appends the ascii representations, + // it will omit leading zeroes. + out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + divisor = NewUint(10000000000000000000) // 20 digits + y = new(Uint).Set(z) // copy to avoid modifying z + pos = len(out) // position to write to + buf = make([]byte, 0, 19) // buffer to write uint64:s to + ) + for { + // Obtain Q and R for divisor + var quot Uint + rem := udivrem(quot.arr[:], y.arr[:], divisor) + y.Set(") // Set Q for next loop + // Convert the R to ascii representation + buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + // Copy in the ascii digits + copy(out[pos-len(buf):], buf) + if y.IsZero() { + break + } + // Move 19 digits left + pos -= 19 + } + // skip leading zeroes by only using the 'used size' of buf + return string(out[pos-len(buf):]) +} + +func (z *Uint) Scan(src interface{}) error { + if src == nil { + z.Clear() + return nil + } + + switch src := src.(type) { + case string: + return z.scanScientificFromString(src) + case []byte: + return z.scanScientificFromString(string(src)) + } + return errors.New("default // unsupported type: can't convert to uint256.Uint") +} + +func (z *Uint) scanScientificFromString(src string) error { + if len(src) == 0 { + z.Clear() + return nil + } + + idx := strings.IndexByte(src, 'e') + if idx == -1 { + return z.SetFromDecimal(src) + } + if err := z.SetFromDecimal(src[:idx]); err != nil { + return err + } + if src[(idx+1):] == "0" { + return nil + } + exp := new(Uint) + if err := exp.SetFromDecimal(src[(idx + 1):]); err != nil { + return err + } + if exp.GtUint64(77) { // 10**78 is larger than 2**256 + return ErrBig256Range + } + exp.Exp(NewUint(10), exp) + if _, overflow := z.MulOverflow(z, exp); overflow { + return ErrBig256Range + } + return nil +} + +// ToString returns the decimal string representation of z. It returns an empty string if z is nil. +// OBS: doesn't exist from holiman's uint256 +func (z *Uint) ToString() string { + if z == nil { + return "" + } + + return z.Dec() +} + +// MarshalJSON implements json.Marshaler. +// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible +// with big.Uint: big.Uint marshals into JSON 'native' numeric format. +// +// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large +// integer space. Thus, U256 uses string-format, which is not compatible with +// big.int (big.Uint refuses to unmarshal a string representation). +func (z *Uint) MarshalJSON() ([]byte, error) { + return []byte(`"` + z.Dec() + `"`), nil +} + +// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either +// - Quoted string: either hexadecimal OR decimal +// - Not quoted string: only decimal +func (z *Uint) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + // if not quoted, it must be decimal + return z.fromDecimal(string(input)) + } + return z.UnmarshalText(input[1 : len(input)-1]) +} + +// MarshalText implements encoding.TextMarshaler +// MarshalText marshals using the decimal representation (compatible with big.Uint) +func (z *Uint) MarshalText() ([]byte, error) { + return []byte(z.Dec()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This method +// can unmarshal either hexadecimal or decimal. +// - For hexadecimal, the input _must_ be prefixed with 0x or 0X +func (z *Uint) UnmarshalText(input []byte) error { + if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + return z.fromHex(string(input)) + } + return z.fromDecimal(string(input)) +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +// If buf is larger than 32 bytes, the last 32 bytes is used. +func (z *Uint) SetBytes(buf []byte) *Uint { + switch l := len(buf); l { + case 0: + z.Clear() + case 1: + z.SetBytes1(buf) + case 2: + z.SetBytes2(buf) + case 3: + z.SetBytes3(buf) + case 4: + z.SetBytes4(buf) + case 5: + z.SetBytes5(buf) + case 6: + z.SetBytes6(buf) + case 7: + z.SetBytes7(buf) + case 8: + z.SetBytes8(buf) + case 9: + z.SetBytes9(buf) + case 10: + z.SetBytes10(buf) + case 11: + z.SetBytes11(buf) + case 12: + z.SetBytes12(buf) + case 13: + z.SetBytes13(buf) + case 14: + z.SetBytes14(buf) + case 15: + z.SetBytes15(buf) + case 16: + z.SetBytes16(buf) + case 17: + z.SetBytes17(buf) + case 18: + z.SetBytes18(buf) + case 19: + z.SetBytes19(buf) + case 20: + z.SetBytes20(buf) + case 21: + z.SetBytes21(buf) + case 22: + z.SetBytes22(buf) + case 23: + z.SetBytes23(buf) + case 24: + z.SetBytes24(buf) + case 25: + z.SetBytes25(buf) + case 26: + z.SetBytes26(buf) + case 27: + z.SetBytes27(buf) + case 28: + z.SetBytes28(buf) + case 29: + z.SetBytes29(buf) + case 30: + z.SetBytes30(buf) + case 31: + z.SetBytes31(buf) + default: + z.SetBytes32(buf[l-32:]) + } + return z +} + +// SetBytes1 is identical to SetBytes(in[:1]), but panics is input is too short +func (z *Uint) SetBytes1(in []byte) *Uint { + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(in[0]) + return z +} + +// SetBytes2 is identical to SetBytes(in[:2]), but panics is input is too short +func (z *Uint) SetBytes2(in []byte) *Uint { + _ = in[1] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint16(in[0:2])) + return z +} + +// SetBytes3 is identical to SetBytes(in[:3]), but panics is input is too short +func (z *Uint) SetBytes3(in []byte) *Uint { + _ = in[2] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + return z +} + +// SetBytes4 is identical to SetBytes(in[:4]), but panics is input is too short +func (z *Uint) SetBytes4(in []byte) *Uint { + _ = in[3] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint32(in[0:4])) + return z +} + +// SetBytes5 is identical to SetBytes(in[:5]), but panics is input is too short +func (z *Uint) SetBytes5(in []byte) *Uint { + _ = in[4] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint40(in[0:5]) + return z +} + +// SetBytes6 is identical to SetBytes(in[:6]), but panics is input is too short +func (z *Uint) SetBytes6(in []byte) *Uint { + _ = in[5] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint48(in[0:6]) + return z +} + +// SetBytes7 is identical to SetBytes(in[:7]), but panics is input is too short +func (z *Uint) SetBytes7(in []byte) *Uint { + _ = in[6] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint56(in[0:7]) + return z +} + +// SetBytes8 is identical to SetBytes(in[:8]), but panics is input is too short +func (z *Uint) SetBytes8(in []byte) *Uint { + _ = in[7] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = binary.BigEndian.Uint64(in[0:8]) + return z +} + +// SetBytes9 is identical to SetBytes(in[:9]), but panics is input is too short +func (z *Uint) SetBytes9(in []byte) *Uint { + _ = in[8] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(in[0]) + z.arr[0] = binary.BigEndian.Uint64(in[1:9]) + return z +} + +// SetBytes10 is identical to SetBytes(in[:10]), but panics is input is too short +func (z *Uint) SetBytes10(in []byte) *Uint { + _ = in[9] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[0] = binary.BigEndian.Uint64(in[2:10]) + return z +} + +// SetBytes11 is identical to SetBytes(in[:11]), but panics is input is too short +func (z *Uint) SetBytes11(in []byte) *Uint { + _ = in[10] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[0] = binary.BigEndian.Uint64(in[3:11]) + return z +} + +// SetBytes12 is identical to SetBytes(in[:12]), but panics is input is too short +func (z *Uint) SetBytes12(in []byte) *Uint { + _ = in[11] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[0] = binary.BigEndian.Uint64(in[4:12]) + return z +} + +// SetBytes13 is identical to SetBytes(in[:13]), but panics is input is too short +func (z *Uint) SetBytes13(in []byte) *Uint { + _ = in[12] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint40(in[0:5]) + z.arr[0] = binary.BigEndian.Uint64(in[5:13]) + return z +} + +// SetBytes14 is identical to SetBytes(in[:14]), but panics is input is too short +func (z *Uint) SetBytes14(in []byte) *Uint { + _ = in[13] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint48(in[0:6]) + z.arr[0] = binary.BigEndian.Uint64(in[6:14]) + return z +} + +// SetBytes15 is identical to SetBytes(in[:15]), but panics is input is too short +func (z *Uint) SetBytes15(in []byte) *Uint { + _ = in[14] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint56(in[0:7]) + z.arr[0] = binary.BigEndian.Uint64(in[7:15]) + return z +} + +// SetBytes16 is identical to SetBytes(in[:16]), but panics is input is too short +func (z *Uint) SetBytes16(in []byte) *Uint { + _ = in[15] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = binary.BigEndian.Uint64(in[0:8]) + z.arr[0] = binary.BigEndian.Uint64(in[8:16]) + return z +} + +// SetBytes17 is identical to SetBytes(in[:17]), but panics is input is too short +func (z *Uint) SetBytes17(in []byte) *Uint { + _ = in[16] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(in[0]) + z.arr[1] = binary.BigEndian.Uint64(in[1:9]) + z.arr[0] = binary.BigEndian.Uint64(in[9:17]) + return z +} + +// SetBytes18 is identical to SetBytes(in[:18]), but panics is input is too short +func (z *Uint) SetBytes18(in []byte) *Uint { + _ = in[17] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[1] = binary.BigEndian.Uint64(in[2:10]) + z.arr[0] = binary.BigEndian.Uint64(in[10:18]) + return z +} + +// SetBytes19 is identical to SetBytes(in[:19]), but panics is input is too short +func (z *Uint) SetBytes19(in []byte) *Uint { + _ = in[18] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[1] = binary.BigEndian.Uint64(in[3:11]) + z.arr[0] = binary.BigEndian.Uint64(in[11:19]) + return z +} + +// SetBytes20 is identical to SetBytes(in[:20]), but panics is input is too short +func (z *Uint) SetBytes20(in []byte) *Uint { + _ = in[19] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[1] = binary.BigEndian.Uint64(in[4:12]) + z.arr[0] = binary.BigEndian.Uint64(in[12:20]) + return z +} + +// SetBytes21 is identical to SetBytes(in[:21]), but panics is input is too short +func (z *Uint) SetBytes21(in []byte) *Uint { + _ = in[20] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint40(in[0:5]) + z.arr[1] = binary.BigEndian.Uint64(in[5:13]) + z.arr[0] = binary.BigEndian.Uint64(in[13:21]) + return z +} + +// SetBytes22 is identical to SetBytes(in[:22]), but panics is input is too short +func (z *Uint) SetBytes22(in []byte) *Uint { + _ = in[21] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint48(in[0:6]) + z.arr[1] = binary.BigEndian.Uint64(in[6:14]) + z.arr[0] = binary.BigEndian.Uint64(in[14:22]) + return z +} + +// SetBytes23 is identical to SetBytes(in[:23]), but panics is input is too short +func (z *Uint) SetBytes23(in []byte) *Uint { + _ = in[22] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint56(in[0:7]) + z.arr[1] = binary.BigEndian.Uint64(in[7:15]) + z.arr[0] = binary.BigEndian.Uint64(in[15:23]) + return z +} + +// SetBytes24 is identical to SetBytes(in[:24]), but panics is input is too short +func (z *Uint) SetBytes24(in []byte) *Uint { + _ = in[23] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = binary.BigEndian.Uint64(in[0:8]) + z.arr[1] = binary.BigEndian.Uint64(in[8:16]) + z.arr[0] = binary.BigEndian.Uint64(in[16:24]) + return z +} + +// SetBytes25 is identical to SetBytes(in[:25]), but panics is input is too short +func (z *Uint) SetBytes25(in []byte) *Uint { + _ = in[24] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(in[0]) + z.arr[2] = binary.BigEndian.Uint64(in[1:9]) + z.arr[1] = binary.BigEndian.Uint64(in[9:17]) + z.arr[0] = binary.BigEndian.Uint64(in[17:25]) + return z +} + +// SetBytes26 is identical to SetBytes(in[:26]), but panics is input is too short +func (z *Uint) SetBytes26(in []byte) *Uint { + _ = in[25] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[2] = binary.BigEndian.Uint64(in[2:10]) + z.arr[1] = binary.BigEndian.Uint64(in[10:18]) + z.arr[0] = binary.BigEndian.Uint64(in[18:26]) + return z +} + +// SetBytes27 is identical to SetBytes(in[:27]), but panics is input is too short +func (z *Uint) SetBytes27(in []byte) *Uint { + _ = in[26] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[2] = binary.BigEndian.Uint64(in[3:11]) + z.arr[1] = binary.BigEndian.Uint64(in[11:19]) + z.arr[0] = binary.BigEndian.Uint64(in[19:27]) + return z +} + +// SetBytes28 is identical to SetBytes(in[:28]), but panics is input is too short +func (z *Uint) SetBytes28(in []byte) *Uint { + _ = in[27] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[2] = binary.BigEndian.Uint64(in[4:12]) + z.arr[1] = binary.BigEndian.Uint64(in[12:20]) + z.arr[0] = binary.BigEndian.Uint64(in[20:28]) + return z +} + +// SetBytes29 is identical to SetBytes(in[:29]), but panics is input is too short +func (z *Uint) SetBytes29(in []byte) *Uint { + _ = in[23] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint40(in[0:5]) + z.arr[2] = binary.BigEndian.Uint64(in[5:13]) + z.arr[1] = binary.BigEndian.Uint64(in[13:21]) + z.arr[0] = binary.BigEndian.Uint64(in[21:29]) + return z +} + +// SetBytes30 is identical to SetBytes(in[:30]), but panics is input is too short +func (z *Uint) SetBytes30(in []byte) *Uint { + _ = in[29] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint48(in[0:6]) + z.arr[2] = binary.BigEndian.Uint64(in[6:14]) + z.arr[1] = binary.BigEndian.Uint64(in[14:22]) + z.arr[0] = binary.BigEndian.Uint64(in[22:30]) + return z +} + +// SetBytes31 is identical to SetBytes(in[:31]), but panics is input is too short +func (z *Uint) SetBytes31(in []byte) *Uint { + _ = in[30] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint56(in[0:7]) + z.arr[2] = binary.BigEndian.Uint64(in[7:15]) + z.arr[1] = binary.BigEndian.Uint64(in[15:23]) + z.arr[0] = binary.BigEndian.Uint64(in[23:31]) + return z +} + +// SetBytes32 sets z to the value of the big-endian 256-bit unsigned integer in. +func (z *Uint) SetBytes32(in []byte) *Uint { + _ = in[31] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = binary.BigEndian.Uint64(in[0:8]) + z.arr[2] = binary.BigEndian.Uint64(in[8:16]) + z.arr[1] = binary.BigEndian.Uint64(in[16:24]) + z.arr[0] = binary.BigEndian.Uint64(in[24:32]) + return z +} + +// Utility methods that are "missing" among the bigEndian.UintXX methods. + +// bigEndianUint40 returns the uint64 value represented by the 5 bytes in big-endian order. +func bigEndianUint40(b []byte) uint64 { + _ = b[4] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[4]) | uint64(b[3])<<8 | uint64(b[2])<<16 | uint64(b[1])<<24 | + uint64(b[0])<<32 +} + +// bigEndianUint56 returns the uint64 value represented by the 7 bytes in big-endian order. +func bigEndianUint56(b []byte) uint64 { + _ = b[6] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[6]) | uint64(b[5])<<8 | uint64(b[4])<<16 | uint64(b[3])<<24 | + uint64(b[2])<<32 | uint64(b[1])<<40 | uint64(b[0])<<48 +} + +// bigEndianUint48 returns the uint64 value represented by the 6 bytes in big-endian order. +func bigEndianUint48(b []byte) uint64 { + _ = b[5] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[5]) | uint64(b[4])<<8 | uint64(b[3])<<16 | uint64(b[2])<<24 | + uint64(b[1])<<32 | uint64(b[0])<<40 +} diff --git a/examples/gno.land/p/demo/uint256/conversion_test.gno b/examples/gno.land/p/demo/uint256/conversion_test.gno new file mode 100644 index 00000000000..12ae99cc0e9 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/conversion_test.gno @@ -0,0 +1,60 @@ +package uint256 + +import ( + "testing" +) + +func TestIsUint64(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0x0", true}, + {"0x1", true}, + {"0x10", true}, + {"0xffffffffffffffff", true}, + {"0x10000000000000000", false}, + } + + for _, tc := range tests { + x := MustFromHex(tc.x) + got := x.IsUint64() + + if got != tc.want { + t.Errorf("IsUint64(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestDec(t *testing.T) { + testCases := []struct { + name string + z Uint + want string + }{ + { + name: "zero", + z: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: "0", + }, + { + name: "less than 20 digits", + z: Uint{arr: [4]uint64{1234567890, 0, 0, 0}}, + want: "1234567890", + }, + { + name: "max possible value", + z: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: "115792089237316195423570985008687907853269984665640564039457584007913129639935", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.z.Dec() + if result != tc.want { + t.Errorf("Dec(%v) = %s, want %s", tc.z, result, tc.want) + } + }) + } +} diff --git a/examples/gno.land/p/demo/uint256/error.gno b/examples/gno.land/p/demo/uint256/error.gno new file mode 100644 index 00000000000..d200bb9cc8f --- /dev/null +++ b/examples/gno.land/p/demo/uint256/error.gno @@ -0,0 +1,73 @@ +package uint256 + +import ( + "errors" +) + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrSyntax = errors.New("invalid hex string") + ErrRange = errors.New("number out of range") + ErrMissingPrefix = errors.New("hex string without 0x prefix") + ErrEmptyNumber = errors.New("hex string \"0x\"") + ErrLeadingZero = errors.New("hex number with leading zero digits") + ErrBig256Range = errors.New("hex number > 256 bits") + ErrBadBufferLength = errors.New("bad ssz buffer length") + ErrBadEncodedLength = errors.New("bad ssz encoded length") + ErrInvalidBase = errors.New("invalid base") + ErrInvalidBitSize = errors.New("invalid bit size") +) + +type u256Error struct { + fn string // function name + input string + err error +} + +func (e *u256Error) Error() string { + return e.fn + ": " + e.input + ": " + e.err.Error() +} + +func (e *u256Error) Unwrap() error { + return e.err +} + +func errEmptyString(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrEmptyString} +} + +func errSyntax(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrSyntax} +} + +func errMissingPrefix(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrMissingPrefix} +} + +func errEmptyNumber(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrEmptyNumber} +} + +func errLeadingZero(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrLeadingZero} +} + +func errRange(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrRange} +} + +func errBig256Range(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrBig256Range} +} + +func errBadBufferLength(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrBadBufferLength} +} + +func errInvalidBase(fn string, base int) error { + return &u256Error{fn: fn, input: string(base), err: ErrInvalidBase} +} + +func errInvalidBitSize(fn string, bitSize int) error { + return &u256Error{fn: fn, input: string(bitSize), err: ErrInvalidBitSize} +} diff --git a/examples/gno.land/p/demo/uint256/gno.mod b/examples/gno.land/p/demo/uint256/gno.mod new file mode 100644 index 00000000000..71e5050c831 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/uint256 diff --git a/examples/gno.land/p/demo/uint256/mod.gno b/examples/gno.land/p/demo/uint256/mod.gno new file mode 100644 index 00000000000..f6ff0967e44 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/mod.gno @@ -0,0 +1,605 @@ +package uint256 + +import ( + "math/bits" +) + +// Some utility functions + +// Reciprocal computes a 320-bit value representing 1/m +// +// Notes: +// - specialized for m.arr[3] != 0, hence limited to 2^192 <= m < 2^256 +// - returns zero if m.arr[3] == 0 +// - starts with a 32-bit division, refines with newton-raphson iterations +func Reciprocal(m *Uint) (mu [5]uint64) { + if m.arr[3] == 0 { + return mu + } + + s := bits.LeadingZeros64(m.arr[3]) // Replace with leadingZeros(m) for general case + p := 255 - s // floor(log_2(m)), m>0 + + // 0 or a power of 2? + + // Check if at least one bit is set in m.arr[2], m.arr[1] or m.arr[0], + // or at least two bits in m.arr[3] + + if m.arr[0]|m.arr[1]|m.arr[2]|(m.arr[3]&(m.arr[3]-1)) == 0 { + + mu[4] = ^uint64(0) >> uint(p&63) + mu[3] = ^uint64(0) + mu[2] = ^uint64(0) + mu[1] = ^uint64(0) + mu[0] = ^uint64(0) + + return mu + } + + // Maximise division precision by left-aligning divisor + + var ( + y Uint // left-aligned copy of m + r0 uint32 // estimate of 2^31/y + ) + + y.Lsh(m, uint(s)) // 1/2 < y < 1 + + // Extract most significant 32 bits + + yh := uint32(y.arr[3] >> 32) + + if yh == 0x80000000 { // Avoid overflow in division + r0 = 0xffffffff + } else { + r0, _ = bits.Div32(0x80000000, 0, yh) + } + + // First iteration: 32 -> 64 + + t1 := uint64(r0) // 2^31/y + t1 *= t1 // 2^62/y^2 + t1, _ = bits.Mul64(t1, y.arr[3]) // 2^62/y^2 * 2^64/y / 2^64 = 2^62/y + + r1 := uint64(r0) << 32 // 2^63/y + r1 -= t1 // 2^63/y - 2^62/y = 2^62/y + r1 *= 2 // 2^63/y + + if (r1 | (y.arr[3] << 1)) == 0 { + r1 = ^uint64(0) + } + + // Second iteration: 64 -> 128 + + // square: 2^126/y^2 + a2h, a2l := bits.Mul64(r1, r1) + + // multiply by y: e2h:e2l:b2h = 2^126/y^2 * 2^128/y / 2^128 = 2^126/y + b2h, _ := bits.Mul64(a2l, y.arr[2]) + c2h, c2l := bits.Mul64(a2l, y.arr[3]) + d2h, d2l := bits.Mul64(a2h, y.arr[2]) + e2h, e2l := bits.Mul64(a2h, y.arr[3]) + + b2h, c := bits.Add64(b2h, c2l, 0) + e2l, c = bits.Add64(e2l, c2h, c) + e2h, _ = bits.Add64(e2h, 0, c) + + _, c = bits.Add64(b2h, d2l, 0) + e2l, c = bits.Add64(e2l, d2h, c) + e2h, _ = bits.Add64(e2h, 0, c) + + // subtract: t2h:t2l = 2^127/y - 2^126/y = 2^126/y + t2l, b := bits.Sub64(0, e2l, 0) + t2h, _ := bits.Sub64(r1, e2h, b) + + // double: r2h:r2l = 2^127/y + r2l, c := bits.Add64(t2l, t2l, 0) + r2h, _ := bits.Add64(t2h, t2h, c) + + if (r2h | r2l | (y.arr[3] << 1)) == 0 { + r2h = ^uint64(0) + r2l = ^uint64(0) + } + + // Third iteration: 128 -> 192 + + // square r2 (keep 256 bits): 2^190/y^2 + a3h, a3l := bits.Mul64(r2l, r2l) + b3h, b3l := bits.Mul64(r2l, r2h) + c3h, c3l := bits.Mul64(r2h, r2h) + + a3h, c = bits.Add64(a3h, b3l, 0) + c3l, c = bits.Add64(c3l, b3h, c) + c3h, _ = bits.Add64(c3h, 0, c) + + a3h, c = bits.Add64(a3h, b3l, 0) + c3l, c = bits.Add64(c3l, b3h, c) + c3h, _ = bits.Add64(c3h, 0, c) + + // multiply by y: q = 2^190/y^2 * 2^192/y / 2^192 = 2^190/y + + x0 := a3l + x1 := a3h + x2 := c3l + x3 := c3h + + var q0, q1, q2, q3, q4, t0 uint64 + + q0, _ = bits.Mul64(x2, y.arr[0]) + q1, t0 = bits.Mul64(x3, y.arr[0]) + q0, c = bits.Add64(q0, t0, 0) + q1, _ = bits.Add64(q1, 0, c) + + t1, _ = bits.Mul64(x1, y.arr[1]) + q0, c = bits.Add64(q0, t1, 0) + q2, t0 = bits.Mul64(x3, y.arr[1]) + q1, c = bits.Add64(q1, t0, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x2, y.arr[1]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[2]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q3, t0 = bits.Mul64(x3, y.arr[2]) + q2, c = bits.Add64(q2, t0, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x0, y.arr[2]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x2, y.arr[2]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[3]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + q4, t0 = bits.Mul64(x3, y.arr[3]) + q3, c = bits.Add64(q3, t0, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[3]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[3]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q4, _ = bits.Add64(q4, 0, c) + + // subtract: t3 = 2^191/y - 2^190/y = 2^190/y + _, b = bits.Sub64(0, q0, 0) + _, b = bits.Sub64(0, q1, b) + t3l, b := bits.Sub64(0, q2, b) + t3m, b := bits.Sub64(r2l, q3, b) + t3h, _ := bits.Sub64(r2h, q4, b) + + // double: r3 = 2^191/y + r3l, c := bits.Add64(t3l, t3l, 0) + r3m, c := bits.Add64(t3m, t3m, c) + r3h, _ := bits.Add64(t3h, t3h, c) + + // Fourth iteration: 192 -> 320 + + // square r3 + + a4h, a4l := bits.Mul64(r3l, r3l) + b4h, b4l := bits.Mul64(r3l, r3m) + c4h, c4l := bits.Mul64(r3l, r3h) + d4h, d4l := bits.Mul64(r3m, r3m) + e4h, e4l := bits.Mul64(r3m, r3h) + f4h, f4l := bits.Mul64(r3h, r3h) + + b4h, c = bits.Add64(b4h, c4l, 0) + e4l, c = bits.Add64(e4l, c4h, c) + e4h, _ = bits.Add64(e4h, 0, c) + + a4h, c = bits.Add64(a4h, b4l, 0) + d4l, c = bits.Add64(d4l, b4h, c) + d4h, c = bits.Add64(d4h, e4l, c) + f4l, c = bits.Add64(f4l, e4h, c) + f4h, _ = bits.Add64(f4h, 0, c) + + a4h, c = bits.Add64(a4h, b4l, 0) + d4l, c = bits.Add64(d4l, b4h, c) + d4h, c = bits.Add64(d4h, e4l, c) + f4l, c = bits.Add64(f4l, e4h, c) + f4h, _ = bits.Add64(f4h, 0, c) + + // multiply by y + + x1, x0 = bits.Mul64(d4h, y.arr[0]) + x3, x2 = bits.Mul64(f4h, y.arr[0]) + t1, t0 = bits.Mul64(f4l, y.arr[0]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + x3, _ = bits.Add64(x3, 0, c) + + t1, t0 = bits.Mul64(d4h, y.arr[1]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + x4, t0 := bits.Mul64(f4h, y.arr[1]) + x3, c = bits.Add64(x3, t0, c) + x4, _ = bits.Add64(x4, 0, c) + t1, t0 = bits.Mul64(d4l, y.arr[1]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[1]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + x4, _ = bits.Add64(x4, 0, c) + + t1, t0 = bits.Mul64(a4h, y.arr[2]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(d4h, y.arr[2]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + x5, t0 := bits.Mul64(f4h, y.arr[2]) + x4, c = bits.Add64(x4, t0, c) + x5, _ = bits.Add64(x5, 0, c) + t1, t0 = bits.Mul64(d4l, y.arr[2]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[2]) + x3, c = bits.Add64(x3, t0, c) + x4, c = bits.Add64(x4, t1, c) + x5, _ = bits.Add64(x5, 0, c) + + t1, t0 = bits.Mul64(a4h, y.arr[3]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + t1, t0 = bits.Mul64(d4h, y.arr[3]) + x3, c = bits.Add64(x3, t0, c) + x4, c = bits.Add64(x4, t1, c) + x6, t0 := bits.Mul64(f4h, y.arr[3]) + x5, c = bits.Add64(x5, t0, c) + x6, _ = bits.Add64(x6, 0, c) + t1, t0 = bits.Mul64(a4l, y.arr[3]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(d4l, y.arr[3]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[3]) + x4, c = bits.Add64(x4, t0, c) + x5, c = bits.Add64(x5, t1, c) + x6, _ = bits.Add64(x6, 0, c) + + // subtract + _, b = bits.Sub64(0, x0, 0) + _, b = bits.Sub64(0, x1, b) + r4l, b := bits.Sub64(0, x2, b) + r4k, b := bits.Sub64(0, x3, b) + r4j, b := bits.Sub64(r3l, x4, b) + r4i, b := bits.Sub64(r3m, x5, b) + r4h, _ := bits.Sub64(r3h, x6, b) + + // Multiply candidate for 1/4y by y, with full precision + + x0 = r4l + x1 = r4k + x2 = r4j + x3 = r4i + x4 = r4h + + q1, q0 = bits.Mul64(x0, y.arr[0]) + q3, q2 = bits.Mul64(x2, y.arr[0]) + q5, q4 := bits.Mul64(x4, y.arr[0]) + + t1, t0 = bits.Mul64(x1, y.arr[0]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[0]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q5, _ = bits.Add64(q5, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[1]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[1]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q6, t0 := bits.Mul64(x4, y.arr[1]) + q5, c = bits.Add64(q5, t0, c) + q6, _ = bits.Add64(q6, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[1]) + q2, c = bits.Add64(q2, t0, 0) + q3, c = bits.Add64(q3, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[1]) + q4, c = bits.Add64(q4, t0, c) + q5, c = bits.Add64(q5, t1, c) + q6, _ = bits.Add64(q6, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[2]) + q2, c = bits.Add64(q2, t0, 0) + q3, c = bits.Add64(q3, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[2]) + q4, c = bits.Add64(q4, t0, c) + q5, c = bits.Add64(q5, t1, c) + q7, t0 := bits.Mul64(x4, y.arr[2]) + q6, c = bits.Add64(q6, t0, c) + q7, _ = bits.Add64(q7, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[2]) + q3, c = bits.Add64(q3, t0, 0) + q4, c = bits.Add64(q4, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[2]) + q5, c = bits.Add64(q5, t0, c) + q6, c = bits.Add64(q6, t1, c) + q7, _ = bits.Add64(q7, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[3]) + q3, c = bits.Add64(q3, t0, 0) + q4, c = bits.Add64(q4, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[3]) + q5, c = bits.Add64(q5, t0, c) + q6, c = bits.Add64(q6, t1, c) + q8, t0 := bits.Mul64(x4, y.arr[3]) + q7, c = bits.Add64(q7, t0, c) + q8, _ = bits.Add64(q8, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[3]) + q4, c = bits.Add64(q4, t0, 0) + q5, c = bits.Add64(q5, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[3]) + q6, c = bits.Add64(q6, t0, c) + q7, c = bits.Add64(q7, t1, c) + q8, _ = bits.Add64(q8, 0, c) + + // Final adjustment + + // subtract q from 1/4 + _, b = bits.Sub64(0, q0, 0) + _, b = bits.Sub64(0, q1, b) + _, b = bits.Sub64(0, q2, b) + _, b = bits.Sub64(0, q3, b) + _, b = bits.Sub64(0, q4, b) + _, b = bits.Sub64(0, q5, b) + _, b = bits.Sub64(0, q6, b) + _, b = bits.Sub64(0, q7, b) + _, b = bits.Sub64(uint64(1)<<62, q8, b) + + // decrement the result + x0, t := bits.Sub64(r4l, 1, 0) + x1, t = bits.Sub64(r4k, 0, t) + x2, t = bits.Sub64(r4j, 0, t) + x3, t = bits.Sub64(r4i, 0, t) + x4, _ = bits.Sub64(r4h, 0, t) + + // commit the decrement if the subtraction underflowed (reciprocal was too large) + if b != 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + } + + // Shift to correct bit alignment, truncating excess bits + + p = (p & 63) - 1 + + x0, c = bits.Add64(r4l, r4l, 0) + x1, c = bits.Add64(r4k, r4k, c) + x2, c = bits.Add64(r4j, r4j, c) + x3, c = bits.Add64(r4i, r4i, c) + x4, _ = bits.Add64(r4h, r4h, c) + + if p < 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + p = 0 // avoid negative shift below + } + + { + r := uint(p) // right shift + l := uint(64 - r) // left shift + + x0 = (r4l >> r) | (r4k << l) + x1 = (r4k >> r) | (r4j << l) + x2 = (r4j >> r) | (r4i << l) + x3 = (r4i >> r) | (r4h << l) + x4 = (r4h >> r) + } + + if p > 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + } + + mu[0] = r4l + mu[1] = r4k + mu[2] = r4j + mu[3] = r4i + mu[4] = r4h + + return mu +} + +// reduce4 computes the least non-negative residue of x modulo m +// +// requires a four-word modulus (m.arr[3] > 1) and its inverse (mu) +func reduce4(x [8]uint64, m *Uint, mu [5]uint64) (z Uint) { + // NB: Most variable names in the comments match the pseudocode for + // Barrett reduction in the Handbook of Applied Cryptography. + + // q1 = x/2^192 + + x0 := x[3] + x1 := x[4] + x2 := x[5] + x3 := x[6] + x4 := x[7] + + // q2 = q1 * mu; q3 = q2 / 2^320 + + var q0, q1, q2, q3, q4, q5, t0, t1, c uint64 + + q0, _ = bits.Mul64(x3, mu[0]) + q1, t0 = bits.Mul64(x4, mu[0]) + q0, c = bits.Add64(q0, t0, 0) + q1, _ = bits.Add64(q1, 0, c) + + t1, _ = bits.Mul64(x2, mu[1]) + q0, c = bits.Add64(q0, t1, 0) + q2, t0 = bits.Mul64(x4, mu[1]) + q1, c = bits.Add64(q1, t0, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x3, mu[1]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x2, mu[2]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q3, t0 = bits.Mul64(x4, mu[2]) + q2, c = bits.Add64(q2, t0, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x1, mu[2]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x3, mu[2]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x0, mu[3]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x2, mu[3]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q4, t0 = bits.Mul64(x4, mu[3]) + q3, c = bits.Add64(q3, t0, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x1, mu[3]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x3, mu[3]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x0, mu[4]) + _, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x2, mu[4]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q5, t0 = bits.Mul64(x4, mu[4]) + q4, c = bits.Add64(q4, t0, c) + q5, _ = bits.Add64(q5, 0, c) + + t1, t0 = bits.Mul64(x1, mu[4]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x3, mu[4]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q5, _ = bits.Add64(q5, 0, c) + + // Drop the fractional part of q3 + + q0 = q1 + q1 = q2 + q2 = q3 + q3 = q4 + q4 = q5 + + // r1 = x mod 2^320 + + x0 = x[0] + x1 = x[1] + x2 = x[2] + x3 = x[3] + x4 = x[4] + + // r2 = q3 * m mod 2^320 + + var r0, r1, r2, r3, r4 uint64 + + r4, r3 = bits.Mul64(q0, m.arr[3]) + _, t0 = bits.Mul64(q1, m.arr[3]) + r4, _ = bits.Add64(r4, t0, 0) + + t1, r2 = bits.Mul64(q0, m.arr[2]) + r3, c = bits.Add64(r3, t1, 0) + _, t0 = bits.Mul64(q2, m.arr[2]) + r4, _ = bits.Add64(r4, t0, c) + + t1, t0 = bits.Mul64(q1, m.arr[2]) + r3, c = bits.Add64(r3, t0, 0) + r4, _ = bits.Add64(r4, t1, c) + + t1, r1 = bits.Mul64(q0, m.arr[1]) + r2, c = bits.Add64(r2, t1, 0) + t1, t0 = bits.Mul64(q2, m.arr[1]) + r3, c = bits.Add64(r3, t0, c) + r4, _ = bits.Add64(r4, t1, c) + + t1, t0 = bits.Mul64(q1, m.arr[1]) + r2, c = bits.Add64(r2, t0, 0) + r3, c = bits.Add64(r3, t1, c) + _, t0 = bits.Mul64(q3, m.arr[1]) + r4, _ = bits.Add64(r4, t0, c) + + t1, r0 = bits.Mul64(q0, m.arr[0]) + r1, c = bits.Add64(r1, t1, 0) + t1, t0 = bits.Mul64(q2, m.arr[0]) + r2, c = bits.Add64(r2, t0, c) + r3, c = bits.Add64(r3, t1, c) + _, t0 = bits.Mul64(q4, m.arr[0]) + r4, _ = bits.Add64(r4, t0, c) + + t1, t0 = bits.Mul64(q1, m.arr[0]) + r1, c = bits.Add64(r1, t0, 0) + r2, c = bits.Add64(r2, t1, c) + t1, t0 = bits.Mul64(q3, m.arr[0]) + r3, c = bits.Add64(r3, t0, c) + r4, _ = bits.Add64(r4, t1, c) + + // r = r1 - r2 + + var b uint64 + + r0, b = bits.Sub64(x0, r0, 0) + r1, b = bits.Sub64(x1, r1, b) + r2, b = bits.Sub64(x2, r2, b) + r3, b = bits.Sub64(x3, r3, b) + r4, b = bits.Sub64(x4, r4, b) + + // if r<0 then r+=m + + if b != 0 { + r0, c = bits.Add64(r0, m.arr[0], 0) + r1, c = bits.Add64(r1, m.arr[1], c) + r2, c = bits.Add64(r2, m.arr[2], c) + r3, c = bits.Add64(r3, m.arr[3], c) + r4, _ = bits.Add64(r4, 0, c) + } + + // while (r>=m) r-=m + + for { + // q = r - m + q0, b = bits.Sub64(r0, m.arr[0], 0) + q1, b = bits.Sub64(r1, m.arr[1], b) + q2, b = bits.Sub64(r2, m.arr[2], b) + q3, b = bits.Sub64(r3, m.arr[3], b) + q4, b = bits.Sub64(r4, 0, b) + + // if borrow break + if b != 0 { + break + } + + // r = q + r4, r3, r2, r1, r0 = q4, q3, q2, q1, q0 + } + + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = r3, r2, r1, r0 + + return z +} diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno new file mode 100644 index 00000000000..80da0ba882b --- /dev/null +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -0,0 +1,291 @@ +// Ported from https://github.com/holiman/uint256 +// This package provides a 256-bit unsigned integer type, Uint256, and associated functions. +package uint256 + +import ( + "errors" + "math/bits" +) + +const ( + MaxUint64 = 1<<64 - 1 + uintSize = 32 << (^uint(0) >> 63) +) + +// Uint is represented as an array of 4 uint64, in little-endian order, +// so that Uint[3] is the most significant, and Uint[0] is the least significant +type Uint struct { + arr [4]uint64 +} + +// NewUint returns a new initialized Uint. +func NewUint(val uint64) *Uint { + z := &Uint{arr: [4]uint64{val, 0, 0, 0}} + return z +} + +// Zero returns a new Uint initialized to zero. +func Zero() *Uint { + return NewUint(0) +} + +// One returns a new Uint initialized to one. +func One() *Uint { + return NewUint(1) +} + +// SetAllOne sets all the bits of z to 1 +func (z *Uint) SetAllOne() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 + return z +} + +// Set sets z to x and returns z. +func (z *Uint) Set(x *Uint) *Uint { + *z = *x + + return z +} + +// SetOne sets z to 1 +func (z *Uint) SetOne() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 1 + return z +} + +const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + +// SetFromDecimal sets z from the given string, interpreted as a decimal number. +// OBS! This method is _not_ strictly identical to the (*big.Uint).SetString(..., 10) method. +// Notable differences: +// - This method does not accept underscore input, e.g. "100_000", +// - This method does not accept negative zero as valid, e.g "-0", +// - (this method does not accept any negative input as valid)) +func (z *Uint) SetFromDecimal(s string) (err error) { + // Remove max one leading + + if len(s) > 0 && s[0] == '+' { + s = s[1:] + } + // Remove any number of leading zeroes + if len(s) > 0 && s[0] == '0' { + var i int + var c rune + for i, c = range s { + if c != '0' { + break + } + } + s = s[i:] + } + if len(s) < len(twoPow256Sub1) { + return z.fromDecimal(s) + } + if len(s) == len(twoPow256Sub1) { + if s > twoPow256Sub1 { + return ErrBig256Range + } + return z.fromDecimal(s) + } + return ErrBig256Range +} + +// FromDecimal is a convenience-constructor to create an Uint from a +// decimal (base 10) string. Numbers larger than 256 bits are not accepted. +func FromDecimal(decimal string) (*Uint, error) { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + return nil, err + } + return &z, nil +} + +// MustFromDecimal is a convenience-constructor to create an Uint from a +// decimal (base 10) string. +// Returns a new Uint and panics if any error occurred. +func MustFromDecimal(decimal string) *Uint { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + panic(err) + } + return &z +} + +// multipliers holds the values that are needed for fromDecimal +var multipliers = [5]*Uint{ + nil, // represents first round, no multiplication needed + {[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 + {[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 + {[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 + {[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 +} + +// fromDecimal is a helper function to only ever be called via SetFromDecimal +// this function takes a string and chunks it up, calling ParseUint on it up to 5 times +// these chunks are then multiplied by the proper power of 10, then added together. +func (z *Uint) fromDecimal(bs string) error { + // first clear the input + z.Clear() + // the maximum value of uint64 is 18446744073709551615, which is 20 characters + // one less means that a string of 19 9's is always within the uint64 limit + var ( + num uint64 + err error + remaining = len(bs) + ) + if remaining == 0 { + return errors.New("EOF") + } + // We proceed in steps of 19 characters (nibbles), from least significant to most significant. + // This means that the first (up to) 19 characters do not need to be multiplied. + // In the second iteration, our slice of 19 characters needs to be multipleied + // by a factor of 10^19. Et cetera. + for i, mult := range multipliers { + if remaining <= 0 { + return nil // Done + } else if remaining > 19 { + num, err = parseUint(bs[remaining-19:remaining], 10, 64) + } else { + // Final round + num, err = parseUint(bs, 10, 64) + } + if err != nil { + return err + } + // add that number to our running total + if i == 0 { + z.SetUint64(num) + } else { + base := NewUint(num) + z.Add(z, base.Mul(base, mult)) + } + // Chop off another 19 characters + if remaining > 19 { + bs = bs[0 : remaining-19] + } + remaining -= 19 + } + return nil +} + +// Byte sets z to the value of the byte at position n, +// with 'z' considered as a big-endian 32-byte integer +// if 'n' > 32, f is set to 0 +// Example: f = '5', n=31 => 5 +func (z *Uint) Byte(n *Uint) *Uint { + // in z, z.arr[0] is the least significant + if number, overflow := n.Uint64WithOverflow(); !overflow { + if number < 32 { + number := z.arr[4-1-number/8] + offset := (n.arr[0] & 0x7) << 3 // 8*(n.d % 8) + z.arr[0] = (number & (0xff00000000000000 >> offset)) >> (56 - offset) + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + return z + } + } + + return z.Clear() +} + +// BitLen returns the number of bits required to represent z +func (z *Uint) BitLen() int { + switch { + case z.arr[3] != 0: + return 192 + bits.Len64(z.arr[3]) + case z.arr[2] != 0: + return 128 + bits.Len64(z.arr[2]) + case z.arr[1] != 0: + return 64 + bits.Len64(z.arr[1]) + default: + return bits.Len64(z.arr[0]) + } +} + +// ByteLen returns the number of bytes required to represent z +func (z *Uint) ByteLen() int { + return (z.BitLen() + 7) / 8 +} + +// Clear sets z to 0 +func (z *Uint) Clear() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 + return z +} + +const ( + // hextable = "0123456789abcdef" + bintable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\a\b\t\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + badNibble = 0xff +) + +// SetFromHex sets z from the given string, interpreted as a hexadecimal number. +// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 16) method. +// Notable differences: +// - This method _require_ "0x" or "0X" prefix. +// - This method does not accept zero-prefixed hex, e.g. "0x0001" +// - This method does not accept underscore input, e.g. "100_000", +// - This method does not accept negative zero as valid, e.g "-0x0", +// - (this method does not accept any negative input as valid) +func (z *Uint) SetFromHex(hex string) error { + return z.fromHex(hex) +} + +// fromHex is the internal implementation of parsing a hex-string. +func (z *Uint) fromHex(hex string) error { + if err := checkNumberS(hex); err != nil { + return err + } + if len(hex) > 66 { + return ErrBig256Range + } + z.Clear() + end := len(hex) + for i := 0; i < 4; i++ { + start := end - 16 + if start < 2 { + start = 2 + } + for ri := start; ri < end; ri++ { + nib := bintable[hex[ri]] + if nib == badNibble { + return ErrSyntax + } + z.arr[i] = z.arr[i] << 4 + z.arr[i] += uint64(nib) + } + end = start + } + return nil +} + +// FromHex is a convenience-constructor to create an Uint from +// a hexadecimal string. The string is required to be '0x'-prefixed +// Numbers larger than 256 bits are not accepted. +func FromHex(hex string) (*Uint, error) { + var z Uint + if err := z.fromHex(hex); err != nil { + return nil, err + } + return &z, nil +} + +// MustFromHex is a convenience-constructor to create an Uint from +// a hexadecimal string. +// Returns a new Uint and panics if any error occurred. +func MustFromHex(hex string) *Uint { + var z Uint + if err := z.fromHex(hex); err != nil { + panic(err) + } + return &z +} + +// Clone creates a new Uint identical to z +func (z *Uint) Clone() *Uint { + var x Uint + x.arr[0] = z.arr[0] + x.arr[1] = z.arr[1] + x.arr[2] = z.arr[2] + x.arr[3] = z.arr[3] + + return &x +} diff --git a/examples/gno.land/p/demo/uint256/utils.gno b/examples/gno.land/p/demo/uint256/utils.gno new file mode 100644 index 00000000000..969728f3369 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/utils.gno @@ -0,0 +1,180 @@ +package uint256 + +// lower(c) is a lower-case letter if and only if +// c is either that lower-case letter or the equivalent upper-case letter. +// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. +// Note that lower of non-letters can produce other non-letters. +func lower(c byte) byte { + return c | ('x' - 'X') +} + +// underscoreOK reports whether the underscores in s are allowed. +// Checking them in this one function lets all the parsers skip over them simply. +// Underscore must appear only between digits or between a base prefix and a digit. +func underscoreOK(s string) bool { + // saw tracks the last character (class) we saw: + // ^ for beginning of number, + // 0 for a digit or base prefix, + // _ for an underscore, + // ! for none of the above. + saw := '^' + i := 0 + + // Optional sign. + if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { + s = s[1:] + } + + // Optional base prefix. + hex := false + if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { + i = 2 + saw = '0' // base prefix counts as a digit for "underscore as digit separator" + hex = lower(s[1]) == 'x' + } + + // Number proper. + for ; i < len(s); i++ { + // Digits are always okay. + if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { + saw = '0' + continue + } + // Underscore must follow digit. + if s[i] == '_' { + if saw != '0' { + return false + } + saw = '_' + continue + } + // Underscore must also be followed by digit. + if saw == '_' { + return false + } + // Saw non-digit, non-underscore. + saw = '!' + } + return saw != '_' +} + +func checkNumberS(input string) error { + const fn = "UnmarshalText" + l := len(input) + if l == 0 { + return errEmptyString(fn, input) + } + if l < 2 || input[0] != '0' || + (input[1] != 'x' && input[1] != 'X') { + return errMissingPrefix(fn, input) + } + if l == 2 { + return errEmptyNumber(fn, input) + } + if len(input) > 3 && input[2] == '0' { + return errLeadingZero(fn, input) + } + return nil +} + +// ParseUint is like ParseUint but for unsigned numbers. +// +// A sign prefix is not permitted. +func parseUint(s string, base int, bitSize int) (uint64, error) { + const fnParseUint = "ParseUint" + + if s == "" { + return 0, errSyntax(fnParseUint, s) + } + + base0 := base == 0 + + s0 := s + switch { + case 2 <= base && base <= 36: + // valid base; nothing to do + + case base == 0: + // Look for octal, hex prefix. + base = 10 + if s[0] == '0' { + switch { + case len(s) >= 3 && lower(s[1]) == 'b': + base = 2 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'o': + base = 8 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'x': + base = 16 + s = s[2:] + default: + base = 8 + s = s[1:] + } + } + + default: + return 0, errInvalidBase(fnParseUint, base) + } + + if bitSize == 0 { + bitSize = uintSize + } else if bitSize < 0 || bitSize > 64 { + return 0, errInvalidBitSize(fnParseUint, bitSize) + } + + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // Use compile-time constants for common cases. + var cutoff uint64 + switch base { + case 10: + cutoff = MaxUint64/10 + 1 + case 16: + cutoff = MaxUint64/16 + 1 + default: + cutoff = MaxUint64/uint64(base) + 1 + } + + maxVal := uint64(1)<= byte(base) { + return 0, errSyntax(fnParseUint, s0) + } + + if n >= cutoff { + // n*base overflows + return maxVal, errRange(fnParseUint, s0) + } + n *= uint64(base) + + n1 := n + uint64(d) + if n1 < n || n1 > maxVal { + // n+d overflows + return maxVal, errRange(fnParseUint, s0) + } + n = n1 + } + + if underscores && !underscoreOK(s0) { + return 0, errSyntax(fnParseUint, s0) + } + + return n, nil +} diff --git a/examples/gno.land/r/demo/foo20/foo20_test.gno b/examples/gno.land/r/demo/foo20/foo20_test.gno index 9db0d5f7a6e..38bdcb333c1 100644 --- a/examples/gno.land/r/demo/foo20/foo20_test.gno +++ b/examples/gno.land/r/demo/foo20/foo20_test.gno @@ -4,13 +4,14 @@ import ( "std" "testing" - "gno.land/p/demo/users" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/users" ) func TestReadOnlyPublicMethods(t *testing.T) { - admin := users.AddressOrName("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") - manfred := users.AddressOrName("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") - unknown := std.Address("g1u0000000000000000000000000000000000000") + admin := pusers.AddressOrName("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + manfred := pusers.AddressOrName("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") + unknown := pusers.AddressOrName("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") // valid but never used. type test struct { name string @@ -25,7 +26,7 @@ func TestReadOnlyPublicMethods(t *testing.T) { {"BalanceOf(admin)", 10000000000, func() uint64 { return BalanceOf(admin) }}, {"BalanceOf(manfred)", 100000000, func() uint64 { return BalanceOf(manfred) }}, {"Allowance(admin, manfred)", 0, func() uint64 { return Allowance(admin, manfred) }}, - {"BalanceOf(unknown)", 0, func() uint64 { return BalanceOf(users.AddressOrName(unknown)) }}, + {"BalanceOf(unknown)", 0, func() uint64 { return BalanceOf(unknown) }}, } for _, tc := range tests { if tc.fn() != tc.balance { @@ -35,7 +36,7 @@ func TestReadOnlyPublicMethods(t *testing.T) { } // unknown uses the faucet. - std.TestSetOrigCaller(unknown) + std.TestSetOrigCaller(users.Resolve(unknown)) Faucet() // check balances #2. @@ -45,7 +46,7 @@ func TestReadOnlyPublicMethods(t *testing.T) { {"BalanceOf(admin)", 10000000000, func() uint64 { return BalanceOf(admin) }}, {"BalanceOf(manfred)", 100000000, func() uint64 { return BalanceOf(manfred) }}, {"Allowance(admin, manfred)", 0, func() uint64 { return Allowance(admin, manfred) }}, - {"BalanceOf(unknown)", 10000000, func() uint64 { return BalanceOf(users.AddressOrName(unknown)) }}, + {"BalanceOf(unknown)", 10000000, func() uint64 { return BalanceOf(unknown) }}, } for _, tc := range tests { if tc.fn() != tc.balance { @@ -68,8 +69,8 @@ func TestErrConditions(t *testing.T) { std.TestSetOrigCaller(admin) { tests := []test{ - {"Transfer(admin, 1)", "cannot send transfer to self", func() { Transfer(users.AddressOrName(admin), 1) }}, - {"Approve(empty, 1))", "invalid address", func() { Approve(users.AddressOrName(empty), 1) }}, + {"Transfer(admin, 1)", "cannot send transfer to self", func() { Transfer(pusers.AddressOrName(admin), 1) }}, + {"Approve(empty, 1))", "invalid address", func() { Approve(pusers.AddressOrName(empty), 1) }}, } for _, tc := range tests { shouldPanicWithMsg(t, tc.fn, tc.msg) diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go index 9565c4fe08f..acef192e0c6 100644 --- a/gno.land/cmd/gnoland/root.go +++ b/gno.land/cmd/gnoland/root.go @@ -33,6 +33,7 @@ func newRootCmd(io commands.IO) *commands.Command { cmd.AddSubCommands( newStartCmd(io), + newSecretsCmd(io), newConfigCmd(io), ) diff --git a/gno.land/cmd/gnoland/secrets.go b/gno.land/cmd/gnoland/secrets.go new file mode 100644 index 00000000000..36113a3e207 --- /dev/null +++ b/gno.land/cmd/gnoland/secrets.go @@ -0,0 +1,64 @@ +package main + +import ( + "errors" + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +var ( + errInvalidDataDir = errors.New("invalid data directory provided") + errInvalidSecretsKey = errors.New("invalid number of secret key arguments") +) + +const ( + defaultSecretsDir = "./secrets" + defaultValidatorKeyName = "priv_validator_key.json" + defaultNodeKeyName = "node_key.json" + defaultValidatorStateName = "priv_validator_state.json" +) + +const ( + nodeKeyKey = "NodeKey" + validatorPrivateKeyKey = "ValidatorPrivateKey" + validatorStateKey = "ValidatorState" +) + +// newSecretsCmd creates the secrets root command +func newSecretsCmd(io commands.IO) *commands.Command { + cmd := commands.NewCommand( + commands.Metadata{ + Name: "secrets", + ShortUsage: "secrets [flags] [...]", + ShortHelp: "gno secrets manipulation suite", + LongHelp: "gno secrets manipulation suite, for managing the validator key, p2p key and validator state", + }, + commands.NewEmptyConfig(), + commands.HelpExec, + ) + + cmd.AddSubCommands( + newSecretsInitCmd(io), + newSecretsVerifyCmd(io), + newSecretsGetCmd(io), + ) + + return cmd +} + +// commonAllCfg is the common +// configuration for secrets commands +// that require a bundled secrets dir +type commonAllCfg struct { + dataDir string +} + +func (c *commonAllCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.dataDir, + "data-dir", + defaultSecretsDir, + "the secrets output directory", + ) +} diff --git a/gno.land/cmd/gnoland/secrets_common.go b/gno.land/cmd/gnoland/secrets_common.go new file mode 100644 index 00000000000..588307b9b8e --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_common.go @@ -0,0 +1,193 @@ +package main + +import ( + "errors" + "fmt" + "os" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/p2p" +) + +var ( + errInvalidPrivateKey = errors.New("invalid validator private key") + errPublicKeyMismatch = errors.New("public key does not match private key derivation") + errAddressMismatch = errors.New("address does not match public key") + + errInvalidSignStateStep = errors.New("invalid sign state step value") + errInvalidSignStateHeight = errors.New("invalid sign state height value") + errInvalidSignStateRound = errors.New("invalid sign state round value") + + errSignatureMismatch = errors.New("signature does not match signature bytes") + errSignatureValuesMissing = errors.New("missing signature value") + + errInvalidNodeKey = errors.New("invalid node p2p key") +) + +// saveSecretData saves the given data as Amino JSON to the path +func saveSecretData(data any, path string) error { + // Get Amino JSON + marshalledData, err := amino.MarshalJSONIndent(data, "", "\t") + if err != nil { + return fmt.Errorf("unable to marshal data into JSON, %w", err) + } + + // Save the data to disk + if err := os.WriteFile(path, marshalledData, 0o644); err != nil { + return fmt.Errorf("unable to save data to path, %w", err) + } + + return nil +} + +// isValidDirectory verifies the directory at the given path exists +func isValidDirectory(dirPath string) bool { + fileInfo, err := os.Stat(dirPath) + if err != nil { + return false + } + + // Check if the path is indeed a directory + return fileInfo.IsDir() +} + +type secretData interface { + privval.FilePVKey | privval.FilePVLastSignState | p2p.NodeKey +} + +// readSecretData reads the secret data from the given path +func readSecretData[T secretData]( + path string, +) (*T, error) { + dataRaw, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("unable to read data, %w", err) + } + + var data T + if err := amino.UnmarshalJSON(dataRaw, &data); err != nil { + return nil, fmt.Errorf("unable to unmarshal data, %w", err) + } + + return &data, nil +} + +// validateValidatorKey validates the validator's private key +func validateValidatorKey(key *privval.FilePVKey) error { + // Make sure the private key is set + if key.PrivKey == nil { + return errInvalidPrivateKey + } + + // Make sure the public key is derived + // from the private one + if !key.PrivKey.PubKey().Equals(key.PubKey) { + return errPublicKeyMismatch + } + + // Make sure the address is derived + // from the public key + if key.PubKey.Address().Compare(key.Address) != 0 { + return errAddressMismatch + } + + return nil +} + +// validateValidatorState validates the validator's last sign state +func validateValidatorState(state *privval.FilePVLastSignState) error { + // Make sure the sign step is valid + if state.Step < 0 { + return errInvalidSignStateStep + } + + // Make sure the height is valid + if state.Height < 0 { + return errInvalidSignStateHeight + } + + // Make sure the round is valid + if state.Round < 0 { + return errInvalidSignStateRound + } + + return nil +} + +// validateValidatorStateSignature validates the signature section +// of the last sign validator state +func validateValidatorStateSignature( + state *privval.FilePVLastSignState, + key crypto.PubKey, +) error { + // Make sure the signature and signature bytes are valid + signBytesPresent := state.SignBytes != nil + signaturePresent := state.Signature != nil + + if signBytesPresent && !signaturePresent || + !signBytesPresent && signaturePresent { + return errSignatureValuesMissing + } + + if !signaturePresent { + // No need to verify further + return nil + } + + // Make sure the signature bytes match the signature + if !key.VerifyBytes(state.SignBytes, state.Signature) { + return errSignatureMismatch + } + + return nil +} + +// validateNodeKey validates the node's p2p key +func validateNodeKey(key *p2p.NodeKey) error { + if key.PrivKey == nil { + return errInvalidNodeKey + } + + return nil +} + +// verifySecretsKey verifies the secrets key value from the passed in arguments +func verifySecretsKey(args []string) error { + // Check if any key is set + if len(args) == 0 { + return nil + } + + // Check if more than 1 key is set + if len(args) > 1 { + return errInvalidSecretsKey + } + + // Verify the set key + key := args[0] + + if key != nodeKeyKey && + key != validatorPrivateKeyKey && + key != validatorStateKey { + return fmt.Errorf( + "invalid secrets key value [%s, %s, %s]", + validatorPrivateKeyKey, + validatorStateKey, + nodeKeyKey, + ) + } + + return nil +} + +// getAvailableSecretsKeys formats and returns the available secret keys (constants) +func getAvailableSecretsKeys() string { + return fmt.Sprintf( + "[%s, %s, %s]", + validatorPrivateKeyKey, + nodeKeyKey, + validatorStateKey, + ) +} diff --git a/gno.land/cmd/gnoland/secrets_common_test.go b/gno.land/cmd/gnoland/secrets_common_test.go new file mode 100644 index 00000000000..34592c3bd8f --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_common_test.go @@ -0,0 +1,259 @@ +package main + +import ( + "path/filepath" + "testing" + + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCommon_SaveReadData(t *testing.T) { + t.Parallel() + + t.Run("invalid data save path", func(t *testing.T) { + t.Parallel() + + assert.ErrorContains( + t, + saveSecretData(nil, ""), + "unable to save data to path", + ) + }) + + t.Run("invalid data read path", func(t *testing.T) { + t.Parallel() + + readData, err := readSecretData[p2p.NodeKey]("") + assert.Nil(t, readData) + + assert.ErrorContains( + t, + err, + "unable to read data", + ) + }) + + t.Run("invalid data read", func(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + path := filepath.Join(dir, "key.json") + + require.NoError(t, saveSecretData("totally valid key", path)) + + readData, err := readSecretData[p2p.NodeKey](path) + require.Nil(t, readData) + + assert.ErrorContains(t, err, "unable to unmarshal data") + }) + + t.Run("valid data save and read", func(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + path := filepath.Join(dir, "key.json") + key := generateNodeKey() + + require.NoError(t, saveSecretData(key, path)) + + readKey, err := readSecretData[p2p.NodeKey](path) + require.NoError(t, err) + + assert.Equal(t, key, readKey) + }) +} + +func TestCommon_ValidateValidatorKey(t *testing.T) { + t.Parallel() + + t.Run("valid validator key", func(t *testing.T) { + t.Parallel() + + key := generateValidatorPrivateKey() + + assert.NoError(t, validateValidatorKey(key)) + }) + + t.Run("invalid private key", func(t *testing.T) { + t.Parallel() + + key := generateValidatorPrivateKey() + key.PrivKey = nil + + assert.ErrorIs(t, validateValidatorKey(key), errInvalidPrivateKey) + }) + + t.Run("public key mismatch", func(t *testing.T) { + t.Parallel() + + key := generateValidatorPrivateKey() + key.PubKey = nil + + assert.ErrorIs(t, validateValidatorKey(key), errPublicKeyMismatch) + }) + + t.Run("address mismatch", func(t *testing.T) { + t.Parallel() + + key := generateValidatorPrivateKey() + key.Address = crypto.Address{} // zero address + + assert.ErrorIs(t, validateValidatorKey(key), errAddressMismatch) + }) +} + +func TestCommon_ValidateValidatorState(t *testing.T) { + t.Parallel() + + t.Run("valid validator state", func(t *testing.T) { + t.Parallel() + + state := generateLastSignValidatorState() + + assert.NoError(t, validateValidatorState(state)) + }) + + t.Run("invalid step", func(t *testing.T) { + t.Parallel() + + state := generateLastSignValidatorState() + state.Step = -1 + + assert.ErrorIs(t, validateValidatorState(state), errInvalidSignStateStep) + }) + + t.Run("invalid height", func(t *testing.T) { + t.Parallel() + + state := generateLastSignValidatorState() + state.Height = -1 + + assert.ErrorIs(t, validateValidatorState(state), errInvalidSignStateHeight) + }) + + t.Run("invalid round", func(t *testing.T) { + t.Parallel() + + state := generateLastSignValidatorState() + state.Round = -1 + + assert.ErrorIs(t, validateValidatorState(state), errInvalidSignStateRound) + }) +} + +func TestCommon_ValidateStateSignature(t *testing.T) { + t.Parallel() + + t.Run("valid state signature", func(t *testing.T) { + t.Parallel() + + var ( + key = generateValidatorPrivateKey() + state = generateLastSignValidatorState() + + signData = []byte("random data") + ) + + // Prepare the signature + signature, err := key.PrivKey.Sign(signData) + require.NoError(t, err) + + state.Signature = signature + state.SignBytes = signData + + assert.NoError(t, validateValidatorStateSignature(state, key.PubKey)) + }) + + t.Run("no state signature", func(t *testing.T) { + t.Parallel() + + var ( + key = generateValidatorPrivateKey() + state = generateLastSignValidatorState() + ) + + assert.NoError(t, validateValidatorStateSignature(state, key.PubKey)) + }) + + t.Run("signature values missing, sign bytes", func(t *testing.T) { + t.Parallel() + + var ( + key = generateValidatorPrivateKey() + state = generateLastSignValidatorState() + ) + + state.Signature = []byte("signature") + + assert.ErrorIs( + t, + validateValidatorStateSignature(state, key.PubKey), + errSignatureValuesMissing, + ) + }) + + t.Run("signature values missing, signature", func(t *testing.T) { + t.Parallel() + + var ( + key = generateValidatorPrivateKey() + state = generateLastSignValidatorState() + ) + + state.SignBytes = []byte("signature") + + assert.ErrorIs( + t, + validateValidatorStateSignature(state, key.PubKey), + errSignatureValuesMissing, + ) + }) + + t.Run("signature mismatch", func(t *testing.T) { + t.Parallel() + + var ( + key = generateValidatorPrivateKey() + state = generateLastSignValidatorState() + + signData = []byte("random data") + ) + + // Prepare the signature + signature, err := key.PrivKey.Sign(signData) + require.NoError(t, err) + + state.Signature = signature + state.SignBytes = []byte("something different") + + assert.ErrorIs( + t, + validateValidatorStateSignature(state, key.PubKey), + errSignatureMismatch, + ) + }) +} + +func TestCommon_ValidateNodeKey(t *testing.T) { + t.Parallel() + + t.Run("valid node key", func(t *testing.T) { + t.Parallel() + + key := generateNodeKey() + + assert.NoError(t, validateNodeKey(key)) + }) + + t.Run("invalid node key", func(t *testing.T) { + t.Parallel() + + key := generateNodeKey() + key.PrivKey = nil + + assert.ErrorIs(t, validateNodeKey(key), errInvalidNodeKey) + }) +} diff --git a/gno.land/cmd/gnoland/secrets_get.go b/gno.land/cmd/gnoland/secrets_get.go new file mode 100644 index 00000000000..699450702b4 --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_get.go @@ -0,0 +1,203 @@ +package main + +import ( + "context" + "flag" + "fmt" + "path/filepath" + "text/tabwriter" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/p2p" +) + +type secretsGetCfg struct { + commonAllCfg +} + +// newSecretsGetCmd creates the secrets get command +func newSecretsGetCmd(io commands.IO) *commands.Command { + cfg := &secretsGetCfg{} + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "get", + ShortUsage: "secrets get [flags] []", + ShortHelp: "shows all Gno secrets present in a common directory", + LongHelp: fmt.Sprintf( + "shows the validator private key, the node p2p key and the validator's last sign state. "+ + "If a key is provided, it shows the specified key value. Available keys: %s", + getAvailableSecretsKeys(), + ), + }, + cfg, + func(_ context.Context, args []string) error { + return execSecretsGet(cfg, args, io) + }, + ) + + return cmd +} + +func (c *secretsGetCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonAllCfg.RegisterFlags(fs) +} + +func execSecretsGet(cfg *secretsGetCfg, args []string, io commands.IO) error { + // Make sure the directory is there + if cfg.dataDir == "" || !isValidDirectory(cfg.dataDir) { + return errInvalidDataDir + } + + // Verify the secrets key + if err := verifySecretsKey(args); err != nil { + return err + } + + var key string + + if len(args) > 0 { + key = args[0] + } + + // Construct the paths + var ( + validatorKeyPath = filepath.Join(cfg.dataDir, defaultValidatorKeyName) + validatorStatePath = filepath.Join(cfg.dataDir, defaultValidatorStateName) + nodeKeyPath = filepath.Join(cfg.dataDir, defaultNodeKeyName) + ) + + switch key { + case validatorPrivateKeyKey: + // Show the validator's key info + return readAndShowValidatorKey(validatorKeyPath, io) + case validatorStateKey: + // Show the validator's last sign state + return readAndShowValidatorState(validatorStatePath, io) + case nodeKeyKey: + // Show the node's p2p info + return readAndShowNodeKey(nodeKeyPath, io) + default: + // Show the node's p2p info + if err := readAndShowNodeKey(nodeKeyPath, io); err != nil { + return err + } + + // Show the validator's key info + if err := readAndShowValidatorKey(validatorKeyPath, io); err != nil { + return err + } + + // Show the validator's last sign state + return readAndShowValidatorState(validatorStatePath, io) + } +} + +// readAndShowValidatorKey reads and shows the validator key from the given path +func readAndShowValidatorKey(path string, io commands.IO) error { + validatorKey, err := readSecretData[privval.FilePVKey](path) + if err != nil { + return fmt.Errorf("unable to read validator key, %w", err) + } + + w := tabwriter.NewWriter(io.Out(), 0, 0, 2, ' ', 0) + + if _, err := fmt.Fprintf(w, "[Validator Key Info]\n\n"); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, "Address:\t%s\n", validatorKey.Address.String()); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, "Public Key:\t%s\n", validatorKey.PubKey.String()); err != nil { + return err + } + + return w.Flush() +} + +// readAndShowValidatorState reads and shows the validator state from the given path +func readAndShowValidatorState(path string, io commands.IO) error { + validatorState, err := readSecretData[privval.FilePVLastSignState](path) + if err != nil { + return fmt.Errorf("unable to read validator state, %w", err) + } + + w := tabwriter.NewWriter(io.Out(), 0, 0, 2, ' ', 0) + + if _, err := fmt.Fprintf(w, "[Last Validator Sign State Info]\n\n"); err != nil { + return err + } + + if _, err := fmt.Fprintf( + w, + "Height:\t%d\n", + validatorState.Height, + ); err != nil { + return err + } + + if _, err := fmt.Fprintf( + w, + "Round:\t%d\n", + validatorState.Round, + ); err != nil { + return err + } + + if _, err := fmt.Fprintf( + w, + "Step:\t%d\n", + validatorState.Step, + ); err != nil { + return err + } + + if validatorState.Signature != nil { + if _, err := fmt.Fprintf( + w, + "Signature:\t%X\n", + validatorState.Signature, + ); err != nil { + return err + } + } + + if validatorState.SignBytes != nil { + if _, err := fmt.Fprintf( + w, + "Sign Bytes:\t%X\n", + validatorState.SignBytes, + ); err != nil { + return err + } + } + + return w.Flush() +} + +// readAndShowNodeKey reads and shows the node p2p key from the given path +func readAndShowNodeKey(path string, io commands.IO) error { + nodeKey, err := readSecretData[p2p.NodeKey](path) + if err != nil { + return fmt.Errorf("unable to read node key, %w", err) + } + + w := tabwriter.NewWriter(io.Out(), 0, 0, 2, ' ', 0) + + if _, err := fmt.Fprintf(w, "[Node P2P Info]\n\n"); err != nil { + return err + } + + if _, err := fmt.Fprintf( + w, + "Node ID:\t%s\n", + nodeKey.ID(), + ); err != nil { + return err + } + + return w.Flush() +} diff --git a/gno.land/cmd/gnoland/secrets_get_test.go b/gno.land/cmd/gnoland/secrets_get_test.go new file mode 100644 index 00000000000..20f1eb2ef35 --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_get_test.go @@ -0,0 +1,276 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "path/filepath" + "strconv" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSecrets_Get_All(t *testing.T) { + t.Parallel() + + t.Run("invalid data directory", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "get", + "--data-dir", + "", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidDataDir.Error()) + }) + + t.Run("all secrets shown", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + + // Run the init command + initArgs := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the init command + require.NoError(t, cmd.ParseAndRun(context.Background(), initArgs)) + + mockOutput := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(mockOutput)) + + cmd = newRootCmd(io) + + // Get the node key + nodeKeyPath := filepath.Join(tempDir, defaultNodeKeyName) + nodeKey, err := readSecretData[p2p.NodeKey](nodeKeyPath) + require.NoError(t, err) + + // Get the validator private key + validatorKeyPath := filepath.Join(tempDir, defaultValidatorKeyName) + validatorKey, err := readSecretData[privval.FilePVKey](validatorKeyPath) + require.NoError(t, err) + + // Get the validator state + validatorStatePath := filepath.Join(tempDir, defaultValidatorStateName) + state, err := readSecretData[privval.FilePVLastSignState](validatorStatePath) + require.NoError(t, err) + + // Run the show command + showArgs := []string{ + "secrets", + "get", + "--data-dir", + tempDir, + } + + require.NoError(t, cmd.ParseAndRun(context.Background(), showArgs)) + + output := mockOutput.String() + + // Make sure the node p2p key is displayed + assert.Contains( + t, + output, + nodeKey.ID().String(), + ) + + // Make sure the private key info is displayed + assert.Contains( + t, + output, + validatorKey.Address.String(), + ) + + assert.Contains( + t, + output, + validatorKey.PubKey.String(), + ) + + // Make sure the private key info is displayed + assert.Contains( + t, + output, + validatorKey.Address.String(), + ) + + assert.Contains( + t, + output, + validatorKey.PubKey.String(), + ) + + // Make sure the state info is displayed + assert.Contains( + t, + output, + fmt.Sprintf("%d", state.Step), + ) + + assert.Contains( + t, + output, + fmt.Sprintf("%d", state.Height), + ) + + assert.Contains( + t, + output, + strconv.Itoa(state.Round), + ) + }) +} + +func TestSecrets_Get_Single(t *testing.T) { + t.Parallel() + + t.Run("validator key shown", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + keyPath := filepath.Join(dirPath, defaultValidatorKeyName) + + validKey := generateValidatorPrivateKey() + + require.NoError(t, saveSecretData(validKey, keyPath)) + + mockOutput := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(mockOutput)) + + // Create the command + cmd := newRootCmd(io) + args := []string{ + "secrets", + "get", + "--data-dir", + dirPath, + validatorPrivateKeyKey, + } + + // Run the command + require.NoError(t, cmd.ParseAndRun(context.Background(), args)) + + output := mockOutput.String() + + // Make sure the private key info is displayed + assert.Contains( + t, + output, + validKey.Address.String(), + ) + + assert.Contains( + t, + output, + validKey.PubKey.String(), + ) + }) + + t.Run("validator state shown", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + statePath := filepath.Join(dirPath, defaultValidatorStateName) + + validState := generateLastSignValidatorState() + + require.NoError(t, saveSecretData(validState, statePath)) + + mockOutput := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(mockOutput)) + + // Create the command + cmd := newRootCmd(io) + args := []string{ + "secrets", + "get", + "--data-dir", + dirPath, + validatorStateKey, + } + + // Run the command + require.NoError(t, cmd.ParseAndRun(context.Background(), args)) + + output := mockOutput.String() + + // Make sure the state info is displayed + assert.Contains( + t, + output, + fmt.Sprintf("%d", validState.Step), + ) + + assert.Contains( + t, + output, + fmt.Sprintf("%d", validState.Height), + ) + + assert.Contains( + t, + output, + strconv.Itoa(validState.Round), + ) + }) + + t.Run("node key shown", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + nodeKeyPath := filepath.Join(dirPath, defaultNodeKeyName) + + validNodeKey := generateNodeKey() + + require.NoError(t, saveSecretData(validNodeKey, nodeKeyPath)) + + mockOutput := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(mockOutput)) + + // Create the command + cmd := newRootCmd(io) + args := []string{ + "secrets", + "get", + "--data-dir", + dirPath, + nodeKeyKey, + } + + // Run the command + require.NoError(t, cmd.ParseAndRun(context.Background(), args)) + + output := mockOutput.String() + + // Make sure the node p2p key is displayed + assert.Contains( + t, + output, + validNodeKey.ID().String(), + ) + }) +} diff --git a/gno.land/cmd/gnoland/secrets_init.go b/gno.land/cmd/gnoland/secrets_init.go new file mode 100644 index 00000000000..55be73c22fc --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_init.go @@ -0,0 +1,210 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/p2p" +) + +var errOverwriteNotEnabled = errors.New("overwrite not enabled") + +type secretsInitCfg struct { + commonAllCfg + + forceOverwrite bool +} + +// newSecretsInitCmd creates the secrets init command +func newSecretsInitCmd(io commands.IO) *commands.Command { + cfg := &secretsInitCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "init", + ShortUsage: "secrets init [flags] []", + ShortHelp: "initializes required Gno secrets in a common directory", + LongHelp: fmt.Sprintf( + "initializes the validator private key, the node p2p key and the validator's last sign state. "+ + "If a key is provided, it initializes the specified key. Available keys: %s", + getAvailableSecretsKeys(), + ), + }, + cfg, + func(_ context.Context, args []string) error { + return execSecretsInit(cfg, args, io) + }, + ) +} + +func (c *secretsInitCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonAllCfg.RegisterFlags(fs) + + fs.BoolVar( + &c.forceOverwrite, + "force", + false, + "overwrite existing secrets, if any", + ) +} + +func execSecretsInit(cfg *secretsInitCfg, args []string, io commands.IO) error { + // Check the data output directory path + if cfg.dataDir == "" { + return errInvalidDataDir + } + + // Verify the secrets key + if err := verifySecretsKey(args); err != nil { + return err + } + + var key string + + if len(args) > 0 { + key = args[0] + } + + // Make sure the directory is there + if err := os.MkdirAll(cfg.dataDir, 0o755); err != nil { + return fmt.Errorf("unable to create secrets dir, %w", err) + } + + // Construct the paths + var ( + validatorKeyPath = filepath.Join(cfg.dataDir, defaultValidatorKeyName) + validatorStatePath = filepath.Join(cfg.dataDir, defaultValidatorStateName) + nodeKeyPath = filepath.Join(cfg.dataDir, defaultNodeKeyName) + ) + + switch key { + case validatorPrivateKeyKey: + if osm.FileExists(validatorKeyPath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // Initialize and save the validator's private key + return initAndSaveValidatorKey(validatorKeyPath, io) + case nodeKeyKey: + if osm.FileExists(nodeKeyPath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // Initialize and save the node's p2p key + return initAndSaveNodeKey(nodeKeyPath, io) + case validatorStateKey: + if osm.FileExists(validatorStatePath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // Initialize and save the validator's last sign state + return initAndSaveValidatorState(validatorStatePath, io) + default: + // Check if the validator key should be overwritten + if osm.FileExists(validatorKeyPath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // Check if the validator state should be overwritten + if osm.FileExists(validatorStatePath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // Check if the node key should be overwritten + if osm.FileExists(nodeKeyPath) && !cfg.forceOverwrite { + return errOverwriteNotEnabled + } + + // No key provided, initialize everything + // Initialize and save the validator's private key + if err := initAndSaveValidatorKey(validatorKeyPath, io); err != nil { + return err + } + + // Initialize and save the validator's last sign state + if err := initAndSaveValidatorState(validatorStatePath, io); err != nil { + return err + } + + // Initialize and save the node's p2p key + return initAndSaveNodeKey(nodeKeyPath, io) + } +} + +// initAndSaveValidatorKey generates a validator private key and saves it to the given path +func initAndSaveValidatorKey(path string, io commands.IO) error { + // Initialize the validator's private key + privateKey := generateValidatorPrivateKey() + + // Save the key + if err := saveSecretData(privateKey, path); err != nil { + return fmt.Errorf("unable to save validator key, %w", err) + } + + io.Printfln("Validator private key saved at %s", path) + + return nil +} + +// initAndSaveValidatorState generates an empty last validator sign state and saves it to the given path +func initAndSaveValidatorState(path string, io commands.IO) error { + // Initialize the validator's last sign state + validatorState := generateLastSignValidatorState() + + // Save the last sign state + if err := saveSecretData(validatorState, path); err != nil { + return fmt.Errorf("unable to save last validator sign state, %w", err) + } + + io.Printfln("Validator last sign state saved at %s", path) + + return nil +} + +// initAndSaveNodeKey generates a node p2p key and saves it to the given path +func initAndSaveNodeKey(path string, io commands.IO) error { + // Initialize the node's p2p key + nodeKey := generateNodeKey() + + // Save the node key + if err := saveSecretData(nodeKey, path); err != nil { + return fmt.Errorf("unable to save node p2p key, %w", err) + } + + io.Printfln("Node key saved at %s", path) + + return nil +} + +// generateValidatorPrivateKey generates the validator's private key +func generateValidatorPrivateKey() *privval.FilePVKey { + privKey := ed25519.GenPrivKey() + + return &privval.FilePVKey{ + Address: privKey.PubKey().Address(), + PubKey: privKey.PubKey(), + PrivKey: privKey, + } +} + +// generateLastSignValidatorState generates the empty last sign state +func generateLastSignValidatorState() *privval.FilePVLastSignState { + return &privval.FilePVLastSignState{} // Empty last sign state +} + +// generateNodeKey generates the p2p node key +func generateNodeKey() *p2p.NodeKey { + privKey := ed25519.GenPrivKey() + + return &p2p.NodeKey{ + PrivKey: privKey, + } +} diff --git a/gno.land/cmd/gnoland/secrets_init_test.go b/gno.land/cmd/gnoland/secrets_init_test.go new file mode 100644 index 00000000000..4a707778cc6 --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_init_test.go @@ -0,0 +1,242 @@ +package main + +import ( + "context" + "path/filepath" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func verifyValidatorKey(t *testing.T, path string) { + t.Helper() + + validatorKey, err := readSecretData[privval.FilePVKey](path) + require.NoError(t, err) + + assert.NoError(t, validateValidatorKey(validatorKey)) +} + +func verifyValidatorState(t *testing.T, path string) { + t.Helper() + + validatorState, err := readSecretData[privval.FilePVLastSignState](path) + require.NoError(t, err) + + assert.Zero(t, validatorState.Height) + assert.Zero(t, validatorState.Round) + assert.Zero(t, validatorState.Step) + assert.Nil(t, validatorState.Signature) + assert.Nil(t, validatorState.SignBytes) +} + +func verifyNodeKey(t *testing.T, path string) { + t.Helper() + + nodeKey, err := readSecretData[p2p.NodeKey](path) + require.NoError(t, err) + + assert.NoError(t, validateNodeKey(nodeKey)) +} + +func TestSecrets_Init_All(t *testing.T) { + t.Parallel() + + t.Run("invalid data directory", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "init", + "--data-dir", + "", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidDataDir.Error()) + }) + + t.Run("all secrets initialized", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Verify the validator key is saved + verifyValidatorKey(t, filepath.Join(tempDir, defaultValidatorKeyName)) + + // Verify the last sign validator state is saved + verifyValidatorState(t, filepath.Join(tempDir, defaultValidatorStateName)) + + // Verify the node p2p key is saved + verifyNodeKey(t, filepath.Join(tempDir, defaultNodeKeyName)) + }) + + t.Run("no secrets overwritten", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Verify the validator key is saved + verifyValidatorKey(t, filepath.Join(tempDir, defaultValidatorKeyName)) + + // Verify the last sign validator state is saved + verifyValidatorState(t, filepath.Join(tempDir, defaultValidatorStateName)) + + // Verify the node p2p key is saved + verifyNodeKey(t, filepath.Join(tempDir, defaultNodeKeyName)) + + // Attempt to reinitialize the secrets, without the overwrite permission + cmdErr = cmd.ParseAndRun(context.Background(), args) + require.ErrorIs(t, cmdErr, errOverwriteNotEnabled) + }) +} + +func TestSecrets_Init_Single(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + keyValue string + expectedFile string + verifyFn func(*testing.T, string) + }{ + { + "validator key initialized", + validatorPrivateKeyKey, + defaultValidatorKeyName, + verifyValidatorKey, + }, + { + "validator state initialized", + validatorStateKey, + defaultValidatorStateName, + verifyValidatorState, + }, + { + "node p2p key initialized", + nodeKeyKey, + defaultNodeKeyName, + verifyNodeKey, + }, + } + + for _, testCase := range testTable { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + var ( + tempDir = t.TempDir() + + expectedPath = filepath.Join(tempDir, testCase.expectedFile) + ) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + testCase.keyValue, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Verify the validator key is saved + testCase.verifyFn(t, expectedPath) + }) + } +} + +func TestSecrets_Init_Single_Overwrite(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + keyValue string + expectedFile string + }{ + { + "validator key not overwritten", + validatorPrivateKeyKey, + defaultValidatorKeyName, + }, + { + "validator state not overwritten", + validatorStateKey, + defaultValidatorStateName, + }, + { + "node p2p key not overwritten", + nodeKeyKey, + defaultNodeKeyName, + }, + } + + for _, testCase := range testTable { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + testCase.keyValue, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Attempt to reinitialize the secret, without the overwrite permission + cmdErr = cmd.ParseAndRun(context.Background(), args) + require.ErrorIs(t, cmdErr, errOverwriteNotEnabled) + }) + } +} diff --git a/gno.land/cmd/gnoland/secrets_verify.go b/gno.land/cmd/gnoland/secrets_verify.go new file mode 100644 index 00000000000..7e6c154d1ac --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_verify.go @@ -0,0 +1,161 @@ +package main + +import ( + "context" + "flag" + "fmt" + "path/filepath" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/p2p" +) + +type secretsVerifyCfg struct { + commonAllCfg +} + +// newSecretsVerifyCmd creates the secrets verify command +func newSecretsVerifyCmd(io commands.IO) *commands.Command { + cfg := &secretsVerifyCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "verify", + ShortUsage: "secrets verify [flags] []", + ShortHelp: "verifies all Gno secrets in a common directory", + LongHelp: fmt.Sprintf( + "verifies the validator private key, the node p2p key and the validator's last sign state. "+ + "If a key is provided, it verifies the specified key value. Available keys: %s", + getAvailableSecretsKeys(), + ), + }, + cfg, + func(_ context.Context, args []string) error { + return execSecretsVerify(cfg, args, io) + }, + ) +} + +func (c *secretsVerifyCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonAllCfg.RegisterFlags(fs) +} + +func execSecretsVerify(cfg *secretsVerifyCfg, args []string, io commands.IO) error { + // Make sure the directory is there + if cfg.dataDir == "" || !isValidDirectory(cfg.dataDir) { + return errInvalidDataDir + } + + // Verify the secrets key + if err := verifySecretsKey(args); err != nil { + return err + } + + var key string + + if len(args) > 0 { + key = args[0] + } + + // Construct the paths + var ( + validatorKeyPath = filepath.Join(cfg.dataDir, defaultValidatorKeyName) + validatorStatePath = filepath.Join(cfg.dataDir, defaultValidatorStateName) + nodeKeyPath = filepath.Join(cfg.dataDir, defaultNodeKeyName) + ) + + switch key { + case validatorPrivateKeyKey: + // Validate the validator's private key + _, err := readAndVerifyValidatorKey(validatorKeyPath, io) + + return err + case validatorStateKey: + // Validate the validator's last sign state + validatorState, err := readAndVerifyValidatorState(validatorStatePath, io) + if err != nil { + return err + } + + // Attempt to read the validator key + if validatorKey, err := readAndVerifyValidatorKey(validatorKeyPath, io); validatorKey != nil && err == nil { + // Validate the signature bytes + return validateValidatorStateSignature(validatorState, validatorKey.PubKey) + } else { + io.Println("WARN: Skipped verification of validator state, as validator key is not present") + } + + return nil + case nodeKeyKey: + return readAndVerifyNodeKey(nodeKeyPath, io) + default: + // Validate the validator's private key + validatorKey, err := readAndVerifyValidatorKey(validatorKeyPath, io) + if err != nil { + return err + } + + // Validate the validator's last sign state + validatorState, err := readAndVerifyValidatorState(validatorStatePath, io) + if err != nil { + return err + } + + // Validate the signature bytes + if err = validateValidatorStateSignature(validatorState, validatorKey.PubKey); err != nil { + return err + } + + // Validate the node's p2p key + return readAndVerifyNodeKey(nodeKeyPath, io) + } +} + +// readAndVerifyValidatorKey reads the validator key from the given path and verifies it +func readAndVerifyValidatorKey(path string, io commands.IO) (*privval.FilePVKey, error) { + validatorKey, err := readSecretData[privval.FilePVKey](path) + if err != nil { + return nil, fmt.Errorf("unable to read validator key, %w", err) + } + + if err := validateValidatorKey(validatorKey); err != nil { + return nil, err + } + + io.Printfln("Validator Private Key at %s is valid", path) + + return validatorKey, nil +} + +// readAndVerifyValidatorState reads the validator state from the given path and verifies it +func readAndVerifyValidatorState(path string, io commands.IO) (*privval.FilePVLastSignState, error) { + validatorState, err := readSecretData[privval.FilePVLastSignState](path) + if err != nil { + return nil, fmt.Errorf("unable to read last validator sign state, %w", err) + } + + if err := validateValidatorState(validatorState); err != nil { + return nil, err + } + + io.Printfln("Last Validator Sign state at %s is valid", path) + + return validatorState, nil +} + +// readAndVerifyNodeKey reads the node p2p key from the given path and verifies it +func readAndVerifyNodeKey(path string, io commands.IO) error { + nodeKey, err := readSecretData[p2p.NodeKey](path) + if err != nil { + return fmt.Errorf("unable to read node p2p key, %w", err) + } + + if err := validateNodeKey(nodeKey); err != nil { + return err + } + + io.Printfln("Node P2P key at %s is valid", path) + + return nil +} diff --git a/gno.land/cmd/gnoland/secrets_verify_test.go b/gno.land/cmd/gnoland/secrets_verify_test.go new file mode 100644 index 00000000000..77ab8523d40 --- /dev/null +++ b/gno.land/cmd/gnoland/secrets_verify_test.go @@ -0,0 +1,370 @@ +package main + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSecrets_Verify_All(t *testing.T) { + t.Parallel() + + t.Run("invalid data directory", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + "", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidDataDir.Error()) + }) + + t.Run("invalid data directory", func(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + path := filepath.Join(dir, "example.json") + + require.NoError( + t, + os.WriteFile( + path, + []byte("hello"), + 0o644, + ), + ) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + path, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidDataDir.Error()) + }) + + t.Run("signature mismatch", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + + // Run the init command + initArgs := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the init command + require.NoError(t, cmd.ParseAndRun(context.Background(), initArgs)) + + // Modify the signature + statePath := filepath.Join(tempDir, defaultValidatorStateName) + state, err := readSecretData[privval.FilePVLastSignState](statePath) + require.NoError(t, err) + + state.SignBytes = []byte("something totally random") + state.Signature = []byte("signature") + + require.NoError(t, saveSecretData(state, statePath)) + + cmd = newRootCmd(commands.NewTestIO()) + + // Run the verify command + verifyArgs := []string{ + "secrets", + "verify", + "--data-dir", + tempDir, + } + + assert.ErrorContains( + t, + cmd.ParseAndRun(context.Background(), verifyArgs), + errSignatureMismatch.Error(), + ) + }) + + t.Run("all secrets valid", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + + // Run the init command + initArgs := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the init command + require.NoError(t, cmd.ParseAndRun(context.Background(), initArgs)) + + cmd = newRootCmd(commands.NewTestIO()) + + // Run the verify command + verifyArgs := []string{ + "secrets", + "verify", + "--data-dir", + tempDir, + } + + assert.NoError(t, cmd.ParseAndRun(context.Background(), verifyArgs)) + }) +} + +func TestSecrets_Verify_All_Missing(t *testing.T) { + t.Parallel() + + testTable := []struct { + name string + fileName string + expectedErrorMessage string + }{ + { + "invalid validator key path", + defaultValidatorKeyName, + "unable to read validator key", + }, + { + "invalid validator state path", + defaultValidatorStateName, + "unable to read last validator sign state", + }, + { + "invalid node p2p key path", + defaultNodeKeyName, + "unable to read node p2p key", + }, + } + + for _, testCase := range testTable { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + + // Run the init command + initArgs := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the init command + require.NoError(t, cmd.ParseAndRun(context.Background(), initArgs)) + + // Delete the validator key + require.NoError(t, os.Remove(filepath.Join(tempDir, testCase.fileName))) + + cmd = newRootCmd(commands.NewTestIO()) + + // Run the verify command + verifyArgs := []string{ + "secrets", + "verify", + "--data-dir", + tempDir, + } + + assert.ErrorContains( + t, + cmd.ParseAndRun(context.Background(), verifyArgs), + testCase.expectedErrorMessage, + ) + }) + } + + t.Run("invalid validator key path", func(t *testing.T) { + t.Parallel() + + // Create a temporary directory + tempDir := t.TempDir() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + + // Run the init command + initArgs := []string{ + "secrets", + "init", + "--data-dir", + tempDir, + } + + // Run the init command + require.NoError(t, cmd.ParseAndRun(context.Background(), initArgs)) + + // Delete the validator key + require.NoError(t, os.Remove(filepath.Join(tempDir, defaultValidatorKeyName))) + + cmd = newRootCmd(commands.NewTestIO()) + + // Run the verify command + verifyArgs := []string{ + "secrets", + "verify", + "--data-dir", + tempDir, + } + + assert.ErrorContains( + t, + cmd.ParseAndRun(context.Background(), verifyArgs), + "unable to read validator key", + ) + }) +} + +func TestSecrets_Verify_Single(t *testing.T) { + t.Parallel() + + t.Run("invalid validator key", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + path := filepath.Join(dirPath, defaultValidatorKeyName) + + invalidKey := &privval.FilePVKey{ + PrivKey: nil, // invalid + } + + require.NoError(t, saveSecretData(invalidKey, path)) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + dirPath, + validatorPrivateKeyKey, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorIs(t, cmdErr, errInvalidPrivateKey) + }) + + t.Run("invalid validator state", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + path := filepath.Join(dirPath, defaultValidatorStateName) + + invalidState := &privval.FilePVLastSignState{ + Height: -1, // invalid + } + + require.NoError(t, saveSecretData(invalidState, path)) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + dirPath, + validatorStateKey, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorIs(t, cmdErr, errInvalidSignStateHeight) + }) + + t.Run("invalid validator state signature", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + keyPath := filepath.Join(dirPath, defaultValidatorKeyName) + statePath := filepath.Join(dirPath, defaultValidatorStateName) + + validKey := generateValidatorPrivateKey() + validState := generateLastSignValidatorState() + + // Save an invalid signature + validState.Signature = []byte("totally valid signature") + + require.NoError(t, saveSecretData(validKey, keyPath)) + require.NoError(t, saveSecretData(validState, statePath)) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + dirPath, + validatorStateKey, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorIs(t, cmdErr, errSignatureValuesMissing) + }) + + t.Run("invalid node key", func(t *testing.T) { + t.Parallel() + + dirPath := t.TempDir() + path := filepath.Join(dirPath, defaultNodeKeyName) + + invalidNodeKey := &p2p.NodeKey{ + PrivKey: nil, // invalid + } + + require.NoError(t, saveSecretData(invalidNodeKey, path)) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "secrets", + "verify", + "--data-dir", + dirPath, + nodeKeyKey, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorIs(t, cmdErr, errInvalidNodeKey) + }) +} diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 2b1757706f8..7fa7de32e5c 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -36,7 +36,6 @@ type startCfg struct { chainID string genesisRemote string dataDir string - genesisMaxVMCycles int64 config string txEventStoreType string @@ -125,13 +124,6 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { "replacement for '%%REMOTE%%' in genesis", ) - fs.Int64Var( - &c.genesisMaxVMCycles, - "genesis-max-vm-cycles", - 10_000_000, - "set maximum allowed vm cycles per operation. Zero means no limit.", - ) - fs.StringVar( &c.config, flagConfigFlag, @@ -254,7 +246,7 @@ func execStart(c *startCfg, io commands.IO) error { cfg.TxEventStore = txEventStoreCfg // Create application and node. - gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) + gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger) if err != nil { return fmt.Errorf("error in creating new app: %w", err) } diff --git a/gno.land/cmd/gnoland/testdata/grc20-invalid-address.txtar b/gno.land/cmd/gnoland/testdata/grc20-invalid-address.txtar new file mode 100644 index 00000000000..da903315333 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/grc20-invalid-address.txtar @@ -0,0 +1,12 @@ +# Test for https://github.com/gnolang/gno/pull/1799 +loadpkg gno.land/r/demo/foo20 + +gnoland start + +# execute Faucet +gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Faucet -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +# execute Transfer for invalid address +! gnokey maketx call -pkgpath gno.land/r/demo/foo20 -func Transfer -args g1ubwj0apf60hd90txhnh855fkac34rxlsvua0aa -args 1 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stderr '"gnokey" error: --= Error =--\nData: invalid address' \ No newline at end of file diff --git a/gno.land/genesis/genesis_balances.txt b/gno.land/genesis/genesis_balances.txt index 09d20a3eec3..fa3232149c1 100644 --- a/gno.land/genesis/genesis_balances.txt +++ b/gno.land/genesis/genesis_balances.txt @@ -13,6 +13,7 @@ g1f4v282mwyhu29afke4vq5r2xzcm6z3ftnugcnv=1000000000000ugnot # faucet0 (jae) g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa=1000000000000ugnot # faucet1 (moul) g1q6jrp203fq0239pv38sdq3y3urvd6vt5azacpv=1000000000000ugnot # faucet2 (devx) g13d7jc32adhc39erm5me38w5v7ej7lpvlnqjk73=1000000000000ugnot # faucet3 (devx) +g18l9us6trqaljw39j94wzf5ftxmd9qqkvrxghd2=1000000000000ugnot # faucet4 (adena) # Contributors premine & GitHub requests (closed). g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=10000000000ugnot # @moul diff --git a/gno.land/pkg/gnoclient/client.go b/gno.land/pkg/gnoclient/client.go index 0a6918999a6..af57440d61e 100644 --- a/gno.land/pkg/gnoclient/client.go +++ b/gno.land/pkg/gnoclient/client.go @@ -11,7 +11,7 @@ type Client struct { } // validateSigner checks that the signer is correctly configured. -func (c Client) validateSigner() error { +func (c *Client) validateSigner() error { if c.Signer == nil { return ErrMissingSigner } @@ -19,7 +19,7 @@ func (c Client) validateSigner() error { } // validateRPCClient checks that the RPCClient is correctly configured. -func (c Client) validateRPCClient() error { +func (c *Client) validateRPCClient() error { if c.RPCClient == nil { return ErrMissingRPCClient } diff --git a/gno.land/pkg/gnoclient/client_queries.go b/gno.land/pkg/gnoclient/client_queries.go index ba63c0d543e..8ceb8352e34 100644 --- a/gno.land/pkg/gnoclient/client_queries.go +++ b/gno.land/pkg/gnoclient/client_queries.go @@ -11,7 +11,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -// QueryCfg contains configuration options for performing queries. +// QueryCfg contains configuration options for performing ABCI queries. type QueryCfg struct { Path string // Query path Data []byte // Query data @@ -19,7 +19,7 @@ type QueryCfg struct { } // Query performs a generic query on the blockchain. -func (c Client) Query(cfg QueryCfg) (*ctypes.ResultABCIQuery, error) { +func (c *Client) Query(cfg QueryCfg) (*ctypes.ResultABCIQuery, error) { if err := c.validateRPCClient(); err != nil { return nil, err } @@ -36,7 +36,7 @@ func (c Client) Query(cfg QueryCfg) (*ctypes.ResultABCIQuery, error) { } // QueryAccount retrieves account information for a given address. -func (c Client) QueryAccount(addr crypto.Address) (*std.BaseAccount, *ctypes.ResultABCIQuery, error) { +func (c *Client) QueryAccount(addr crypto.Address) (*std.BaseAccount, *ctypes.ResultABCIQuery, error) { if err := c.validateRPCClient(); err != nil { return nil, nil, err } @@ -61,7 +61,8 @@ func (c Client) QueryAccount(addr crypto.Address) (*std.BaseAccount, *ctypes.Res return &qret.BaseAccount, qres, nil } -func (c Client) QueryAppVersion() (string, *ctypes.ResultABCIQuery, error) { +// QueryAppVersion retrieves information about the app version +func (c *Client) QueryAppVersion() (string, *ctypes.ResultABCIQuery, error) { if err := c.validateRPCClient(); err != nil { return "", nil, err } @@ -81,7 +82,7 @@ func (c Client) QueryAppVersion() (string, *ctypes.ResultABCIQuery, error) { // Render calls the Render function for pkgPath with optional args. The pkgPath should // include the prefix like "gno.land/". This is similar to using a browser URL // /: where doesn't have the prefix like "gno.land/". -func (c Client) Render(pkgPath string, args string) (string, *ctypes.ResultABCIQuery, error) { +func (c *Client) Render(pkgPath string, args string) (string, *ctypes.ResultABCIQuery, error) { if err := c.validateRPCClient(); err != nil { return "", nil, err } @@ -104,7 +105,7 @@ func (c Client) Render(pkgPath string, args string) (string, *ctypes.ResultABCIQ // include the prefix like "gno.land/". The expression is usually a function call like // "GetBoardIDFromName(\"testboard\")". The return value is a typed expression like // "(1 gno.land/r/demo/boards.BoardID)\n(true bool)". -func (c Client) QEval(pkgPath string, expression string) (string, *ctypes.ResultABCIQuery, error) { +func (c *Client) QEval(pkgPath string, expression string) (string, *ctypes.ResultABCIQuery, error) { if err := c.validateRPCClient(); err != nil { return "", nil, err } diff --git a/gno.land/pkg/gnoclient/client_txs.go b/gno.land/pkg/gnoclient/client_txs.go index 2a83eef1b79..eda34c82b07 100644 --- a/gno.land/pkg/gnoclient/client_txs.go +++ b/gno.land/pkg/gnoclient/client_txs.go @@ -23,6 +23,7 @@ var ( ErrInvalidSendAmount = errors.New("invalid send amount") ) +// BaseTxCfg defines the base transaction configuration, shared by all message types. type BaseTxCfg struct { GasFee string // Gas fee GasWanted int64 // Gas wanted @@ -31,7 +32,7 @@ type BaseTxCfg struct { Memo string // Memo } -// MsgCall - syntax sugar for vm.MsgCall +// MsgCall - syntax sugar for vm.MsgCall. type MsgCall struct { PkgPath string // Package path FuncName string // Function name @@ -39,13 +40,13 @@ type MsgCall struct { Send string // Send amount } -// MsgSend - syntax sugar for bank.MsgSend minus fields in BaseTxCfg +// MsgSend - syntax sugar for bank.MsgSend. type MsgSend struct { ToAddress crypto.Address // Send to address Send string // Send amount } -// MsgRun - syntax sugar for vm.MsgRun +// MsgRun - syntax sugar for vm.MsgRun. type MsgRun struct { Package *std.MemPackage // Package to run Send string // Send amount @@ -171,7 +172,7 @@ func (c *Client) Run(cfg BaseTxCfg, msgs ...MsgRun) (*ctypes.ResultBroadcastTxCo return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.SequenceNumber) } -// Send currency to an account on the blockchain. +// Send executes one or more MsgSend calls on the blockchain. func (c *Client) Send(cfg BaseTxCfg, msgs ...MsgSend) (*ctypes.ResultBroadcastTxCommit, error) { // Validate required client fields. if err := c.validateSigner(); err != nil { @@ -226,7 +227,7 @@ func (c *Client) Send(cfg BaseTxCfg, msgs ...MsgSend) (*ctypes.ResultBroadcastTx } // signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result. -func (c Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) { +func (c *Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) { caller := c.Signer.Info().GetAddress() if sequenceNumber == 0 || accountNumber == 0 { diff --git a/gno.land/pkg/gnoclient/example_test.go b/gno.land/pkg/gnoclient/example_test.go index 0a5589bc776..08c3bf19066 100644 --- a/gno.land/pkg/gnoclient/example_test.go +++ b/gno.land/pkg/gnoclient/example_test.go @@ -1,8 +1,6 @@ package gnoclient_test import ( - "fmt" - "github.com/gnolang/gno/gno.land/pkg/gnoclient" rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" "github.com/gnolang/gno/tm2/pkg/crypto/keys" @@ -44,9 +42,6 @@ func Example_withInMemCrypto() { RPCClient: rpcClient, } _ = client - fmt.Println("Hello") - // Output: - // Hello } // Example_readOnly demonstrates how to initialize a read-only gnoclient, which can only query. @@ -58,7 +53,4 @@ func Example_readOnly() { RPCClient: rpcClient, } _ = client - fmt.Println("Hello") - // Output: - // Hello } diff --git a/gno.land/pkg/gnoclient/signer.go b/gno.land/pkg/gnoclient/signer.go index ba2aea4d287..4d40680037e 100644 --- a/gno.land/pkg/gnoclient/signer.go +++ b/gno.land/pkg/gnoclient/signer.go @@ -24,6 +24,7 @@ type SignerFromKeybase struct { ChainID string // Chain ID for transaction signing } +// Validate checks if the signer is properly configured. func (s SignerFromKeybase) Validate() error { if s.ChainID == "" { return errors.New("missing ChainID") @@ -51,6 +52,7 @@ func (s SignerFromKeybase) Validate() error { return nil } +// Info gets keypair information. func (s SignerFromKeybase) Info() keys.Info { info, err := s.Keybase.GetByNameOrAddress(s.Account) if err != nil { @@ -59,13 +61,15 @@ func (s SignerFromKeybase) Info() keys.Info { return info } -// Sign implements the Signer interface for SignerFromKeybase. +// SignCfg provides the signing configuration, containing: +// unsigned transaction data, account number, and account sequence. type SignCfg struct { UnsignedTX std.Tx SequenceNumber uint64 AccountNumber uint64 } +// Sign implements the Signer interface for SignerFromKeybase. func (s SignerFromKeybase) Sign(cfg SignCfg) (*std.Tx, error) { tx := cfg.UnsignedTX chainID := s.ChainID @@ -120,10 +124,10 @@ func (s SignerFromKeybase) Sign(cfg SignCfg) (*std.Tx, error) { // Ensure SignerFromKeybase implements the Signer interface. var _ Signer = (*SignerFromKeybase)(nil) -// SignerFromBip39 creates an in-memory keybase with a single default account. +// SignerFromBip39 creates a signer from an in-memory keybase with a single default account, derived from the given mnemonic. // This can be useful in scenarios where storing private keys in the filesystem isn't feasible. // -// Warning: Using keys.NewKeyBaseFromDir is recommended where possible, as it is more secure. +// Warning: Using keys.NewKeyBaseFromDir to get a keypair from local storage is recommended where possible, as it is more secure. func SignerFromBip39(mnemonic string, chainID string, passphrase string, account uint32, index uint32) (Signer, error) { kb := keys.NewInMemory() name := "default" diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index cc15f74134e..86fb6321386 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -31,7 +31,6 @@ type AppOptions struct { GnoRootDir string SkipFailingGenesisTxs bool Logger *slog.Logger - MaxCycles int64 } func NewAppOptions() *AppOptions { @@ -78,7 +77,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { // XXX: Embed this ? stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir) // Set InitChainer baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) @@ -123,7 +122,7 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { } // NewApp creates the GnoLand application. -func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger, maxCycles int64) (abci.Application, error) { +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger) (abci.Application, error) { var err error cfg := NewAppOptions() diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go index 89f222738d0..d8dc3caa485 100644 --- a/gno.land/pkg/gnoland/node_inmemory.go +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -25,7 +25,6 @@ type InMemoryNodeConfig struct { Genesis *bft.GenesisDoc TMConfig *tmcfg.Config SkipFailingGenesisTxs bool - GenesisMaxVMCycles int64 } // NewMockedPrivValidator generate a new key @@ -79,10 +78,9 @@ func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { } return &InMemoryNodeConfig{ - PrivValidator: pv, - TMConfig: tm, - Genesis: genesis, - GenesisMaxVMCycles: 10_000_000, + PrivValidator: pv, + TMConfig: tm, + Genesis: genesis, } } @@ -115,7 +113,6 @@ func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, Logger: logger, GnoRootDir: cfg.TMConfig.RootDir, SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, - MaxCycles: cfg.GenesisMaxVMCycles, DB: memdb.NewMemDB(), }) if err != nil { diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index b65757da403..ec14ae8515b 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -39,7 +39,7 @@ func setupTestEnv() testEnv { acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) stdlibsDir := filepath.Join("..", "..", "..", "..", "gnovm", "stdlibs") - vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir, 10_000_000) + vmk := NewVMKeeper(baseCapKey, iavlCapKey, acck, bank, stdlibsDir) vmk.Initialize(ms.MultiCacheWrap()) diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 67710be620c..af794fb9b1f 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -22,6 +22,11 @@ import ( const ( maxAllocTx = 500 * 1000 * 1000 maxAllocQuery = 1500 * 1000 * 1000 // higher limit for queries + + // maxVMCycles is the maximum number of cycles allowed while executing a single VM + // message. Ideally this should not be needed, as execution should halt when out of + // gas. The worst case scenario is that this value is used as a fallback. + maxVMCycles = 10_000_000 ) // vm.VMKeeperI defines a module interface that supports Gno @@ -55,7 +60,6 @@ func NewVMKeeper( acck auth.AccountKeeper, bank bank.BankKeeper, stdlibsDir string, - maxCycles int64, ) *VMKeeper { // TODO: create an Options struct to avoid too many constructor parameters vmk := &VMKeeper{ @@ -64,7 +68,7 @@ func NewVMKeeper( acck: acck, bank: bank, stdlibsDir: stdlibsDir, - maxCycles: maxCycles, + maxCycles: maxVMCycles, } return vmk } diff --git a/gnovm/cmd/gno/testdata/gno_test/recover.txtar b/gnovm/cmd/gno/testdata/gno_test/recover.txtar index 9960ee74a1d..418545f97c9 100644 --- a/gnovm/cmd/gno/testdata/gno_test/recover.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/recover.txtar @@ -32,20 +32,30 @@ package recov import "testing" +type RecoverySetter struct { + value interface{} +} + +func (s *RecoverySetter) Set(v interface{}) { + s.value = v +} + func TestRecover(t *testing.T) { + var setter RecoverySetter defer func() { - err := testing.Recover() - t.Log("recovered", err) + t.Log("recovered", setter.value) }() + defer testing.Recover(&setter) panic("bad panic!") } func TestRecoverSkip(t *testing.T) { + var setter RecoverySetter defer func() { - err := testing.Recover() - t.Log("recovered", err) + t.Log("recovered", setter.value) }() + defer testing.Recover(&setter) t.Skip("skipped") panic("bad panic!") diff --git a/gnovm/pkg/gnolang/frame.go b/gnovm/pkg/gnolang/frame.go index 7f87fa26097..c808fc111b0 100644 --- a/gnovm/pkg/gnolang/frame.go +++ b/gnovm/pkg/gnolang/frame.go @@ -26,6 +26,8 @@ type Frame struct { Defers []Defer // deferred calls LastPackage *PackageValue // previous package context LastRealm *Realm // previous realm context + + Popped bool // true if frame has been popped } func (fr Frame) String() string { @@ -84,4 +86,9 @@ type Defer struct { Args []TypedValue // arguments Source *DeferStmt // source Parent *Block + + // PanicScope is set to the value of the Machine's PanicScope when the + // defer is created. The PanicScope of the Machine is incremented each time + // a panic occurs and is decremented each time a panic is recovered. + PanicScope uint } diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index e9e8eba8adc..ea7be1d1f22 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -16,6 +16,19 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) +// Exception represents a panic that originates from a gno program. +type Exception struct { + // Value is the value passed to panic. + Value TypedValue + // Frame is used to reference the frame a panic occurred in so that recover() knows if the + // currently executing deferred function is able to recover from the panic. + Frame *Frame +} + +func (e Exception) Sprint(m *Machine) string { + return e.Value.Sprint(m) +} + //---------------------------------------- // Machine @@ -28,13 +41,13 @@ type Machine struct { Exprs []Expr // pending expressions Stmts []Stmt // pending statements Blocks []*Block // block (scope) stack - Frames []Frame // func call stack + Frames []*Frame // func call stack Package *PackageValue // active package Realm *Realm // active realm Alloc *Allocator // memory allocations - Exceptions []*TypedValue // if panic'd unless recovered - NumResults int // number of results returned - Cycles int64 // number of "cpu" cycles + Exceptions []Exception + NumResults int // number of results returned + Cycles int64 // number of "cpu" cycles // Configuration CheckTypes bool // not yet used @@ -44,6 +57,14 @@ type Machine struct { Output io.Writer Store Store Context interface{} + + // PanicScope is incremented each time a panic occurs and is reset to + // zero when it is recovered. + PanicScope uint + // DeferPanicScope is set to the value of the defer's panic scope before + // it is executed. It is reset to zero after the defer functions in the current + // scope have finished executing. + DeferPanicScope uint } // NewMachine initializes a new gno virtual machine, acting as a shorthand @@ -1425,6 +1446,7 @@ func (m *Machine) PushOp(op Op) { copy(newOps, m.Ops) m.Ops = newOps } + m.Ops[m.NumOps] = op m.NumOps++ } @@ -1642,7 +1664,7 @@ func (m *Machine) LastBlock() *Block { // Pushes a frame with one less statement. func (m *Machine) PushFrameBasic(s Stmt) { label := s.GetLabel() - fr := Frame{ + fr := &Frame{ Label: label, Source: s, NumOps: m.NumOps, @@ -1661,7 +1683,7 @@ func (m *Machine) PushFrameBasic(s Stmt) { // ensure the counts are consistent, otherwise we mask // bugs with frame pops. func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) { - fr := Frame{ + fr := &Frame{ Source: cx, NumOps: m.NumOps, NumValues: m.NumValues - cx.NumArgs - 1, @@ -1698,7 +1720,7 @@ func (m *Machine) PushFrameCall(cx *CallExpr, fv *FuncValue, recv TypedValue) { } func (m *Machine) PushFrameGoNative(cx *CallExpr, fv *NativeValue) { - fr := Frame{ + fr := &Frame{ Source: cx, NumOps: m.NumOps, NumValues: m.NumValues - cx.NumArgs - 1, @@ -1724,15 +1746,17 @@ func (m *Machine) PushFrameGoNative(cx *CallExpr, fv *NativeValue) { func (m *Machine) PopFrame() Frame { numFrames := len(m.Frames) f := m.Frames[numFrames-1] + f.Popped = true if debug { m.Printf("-F %#v\n", f) } m.Frames = m.Frames[:numFrames-1] - return f + return *f } func (m *Machine) PopFrameAndReset() { fr := m.PopFrame() + fr.Popped = true m.NumOps = fr.NumOps m.NumValues = fr.NumValues m.Exprs = m.Exprs[:fr.NumExprs] @@ -1744,6 +1768,7 @@ func (m *Machine) PopFrameAndReset() { // TODO: optimize by passing in last frame. func (m *Machine) PopFrameAndReturn() { fr := m.PopFrame() + fr.Popped = true if debug { // TODO: optimize with fr.IsCall if fr.Func == nil && fr.GoFunc == nil { @@ -1799,18 +1824,29 @@ func (m *Machine) NumFrames() int { } func (m *Machine) LastFrame() *Frame { - return &m.Frames[len(m.Frames)-1] + return m.Frames[len(m.Frames)-1] +} + +// MustLastCallFrame returns the last call frame with an offset of n. It panics if the frame is not found. +func (m *Machine) MustLastCallFrame(n int) *Frame { + return m.lastCallFrame(n, true) +} + +// LastCallFrame behaves the same as MustLastCallFrame, but rather than panicking, +// returns nil if the frame is not found. +func (m *Machine) LastCallFrame(n int) *Frame { + return m.lastCallFrame(n, false) } // TODO: this function and PopUntilLastCallFrame() is used in conjunction // spanning two disjoint operations upon return. Optimize. // If n is 1, returns the immediately last call frame. -func (m *Machine) LastCallFrame(n int) *Frame { +func (m *Machine) lastCallFrame(n int, mustBeFound bool) *Frame { if n == 0 { panic("n must be positive") } for i := len(m.Frames) - 1; i >= 0; i-- { - fr := &m.Frames[i] + fr := m.Frames[i] if fr.Func != nil || fr.GoFunc != nil { // TODO: optimize with fr.IsCall if n == 1 { @@ -1820,20 +1856,34 @@ func (m *Machine) LastCallFrame(n int) *Frame { } } } - panic("frame not found") + + if mustBeFound { + panic("frame not found") + } + + return nil } // pops the last non-call (loop) frames // and returns the last call frame (which is left on stack). func (m *Machine) PopUntilLastCallFrame() *Frame { for i := len(m.Frames) - 1; i >= 0; i-- { - fr := &m.Frames[i] + fr := m.Frames[i] if fr.Func != nil || fr.GoFunc != nil { // TODO: optimize with fr.IsCall m.Frames = m.Frames[:i+1] return fr } + + fr.Popped = true } + + // No frames are popped, so revert all the frames' popped flag. + // This is expected to happen infrequently. + for _, frame := range m.Frames { + frame.Popped = false + } + return nil } @@ -1926,7 +1976,15 @@ func (m *Machine) CheckEmpty() error { } func (m *Machine) Panic(ex TypedValue) { - m.Exceptions = append(m.Exceptions, &ex) + m.Exceptions = append( + m.Exceptions, + Exception{ + Value: ex, + Frame: m.MustLastCallFrame(1), + }, + ) + + m.PanicScope++ m.PopUntilLastCallFrame() m.PushOp(OpPanic2) m.PushOp(OpReturnCallDefers) diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index c7274dd73af..5479ee6d5ae 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -247,7 +247,7 @@ func (m *Machine) doOpReturnFromBlock() { // deferred statements can refer to results with name // expressions. func (m *Machine) doOpReturnToBlock() { - cfr := m.LastCallFrame(1) + cfr := m.MustLastCallFrame(1) ft := cfr.Func.GetType(m.Store) numParams := len(ft.Params) numResults := len(ft.Results) @@ -260,10 +260,11 @@ func (m *Machine) doOpReturnToBlock() { } func (m *Machine) doOpReturnCallDefers() { - cfr := m.LastCallFrame(1) + cfr := m.MustLastCallFrame(1) dfr, ok := cfr.PopDefer() if !ok { // Done with defers. + m.DeferPanicScope = 0 m.ForcePopOp() if len(m.Exceptions) > 0 { // In a state of panic (not return). @@ -272,6 +273,9 @@ func (m *Machine) doOpReturnCallDefers() { } return } + + m.DeferPanicScope = dfr.PanicScope + // Call last deferred call. // NOTE: the following logic is largely duplicated in doOpCall(). // Convert if variadic argument. @@ -347,7 +351,7 @@ func (m *Machine) doOpReturnCallDefers() { func (m *Machine) doOpDefer() { lb := m.LastBlock() - cfr := m.LastCallFrame(1) + cfr := m.MustLastCallFrame(1) ds := m.PopStmt().(*DeferStmt) // Pop arguments numArgs := len(ds.Call.Args) @@ -361,10 +365,11 @@ func (m *Machine) doOpDefer() { case *FuncValue: // TODO what if value is NativeValue? cfr.PushDefer(Defer{ - Func: cv, - Args: args, - Source: ds, - Parent: lb, + Func: cv, + Args: args, + Source: ds, + Parent: lb, + PanicScope: m.PanicScope, }) case *BoundMethodValue: if debug { @@ -381,17 +386,19 @@ func (m *Machine) doOpDefer() { args2[0] = cv.Receiver copy(args2[1:], args) cfr.PushDefer(Defer{ - Func: cv.Func, - Args: args2, - Source: ds, - Parent: lb, + Func: cv.Func, + Args: args2, + Source: ds, + Parent: lb, + PanicScope: m.PanicScope, }) case *NativeValue: cfr.PushDefer(Defer{ - GoFunc: cv, - Args: args, - Source: ds, - Parent: lb, + GoFunc: cv, + Args: args, + Source: ds, + Parent: lb, + PanicScope: m.PanicScope, }) default: panic("should not happen") @@ -410,6 +417,7 @@ func (m *Machine) doOpPanic2() { // Recovered from panic m.PushOp(OpReturnFromBlock) m.PushOp(OpReturnCallDefers) + m.PanicScope = 0 } else { // Keep panicking last := m.PopUntilLastCallFrame() diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index 300303135ad..12e0f9e26e3 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -541,7 +541,7 @@ EXEC_SWITCH: m.PushForPointer(cs.X) case *ReturnStmt: m.PopStmt() - fr := m.LastCallFrame(1) + fr := m.MustLastCallFrame(1) ft := fr.Func.GetType(m.Store) hasDefers := 0 < len(fr.Defers) hasResults := 0 < len(ft.Results) diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go index a0e9913eaad..9f836c789fe 100644 --- a/gnovm/pkg/gnolang/uverse.go +++ b/gnovm/pkg/gnolang/uverse.go @@ -972,9 +972,29 @@ func UverseNode() *PackageNode { m.PushValue(TypedValue{}) return } - // Just like in go, only the last exception is returned to recover. - m.PushValue(*m.Exceptions[len(m.Exceptions)-1]) - // The remaining exceptions are removed + + // If the exception is out of scope, this recover can't help; return nil. + if m.PanicScope <= m.DeferPanicScope { + m.PushValue(TypedValue{}) + return + } + + exception := &m.Exceptions[len(m.Exceptions)-1] + + // If the frame the exception occurred in is not popped, it's possible that + // the exception is still in scope and can be recovered. + if !exception.Frame.Popped { + // If the frame is not the current frame, the exception is not in scope; return nil. + // This retrieves the second most recent call frame because the first most recent + // is the call to recover itself. + if frame := m.LastCallFrame(2); frame == nil || (frame != nil && frame != exception.Frame) { + m.PushValue(TypedValue{}) + return + } + } + + m.PushValue(exception.Value) + // Recover complete; remove exceptions. m.Exceptions = nil }, ) diff --git a/gnovm/pkg/transpiler/transpiler.go b/gnovm/pkg/transpiler/transpiler.go index a5e1e068ba5..a87a336125e 100644 --- a/gnovm/pkg/transpiler/transpiler.go +++ b/gnovm/pkg/transpiler/transpiler.go @@ -70,6 +70,7 @@ var stdlibWhitelist = []string{ "time", "unicode", "unicode/utf8", + "unicode/utf16", // gno "std", diff --git a/gnovm/stdlibs/std/crypto.gno b/gnovm/stdlibs/std/crypto.gno index 8d005dccf5c..402a6af3e22 100644 --- a/gnovm/stdlibs/std/crypto.gno +++ b/gnovm/stdlibs/std/crypto.gno @@ -6,9 +6,10 @@ func (a Address) String() string { return string(a) } -// IsValid checks if the address is of specific length. Doesn't check prefix or checksum for the address +// IsValid checks if the address is valid bech32 encoded string func (a Address) IsValid() bool { - return len(a) == RawAddressSize*2 // hex length + _, _, ok := DecodeBech32(a) + return ok } const RawAddressSize = 20 diff --git a/gnovm/stdlibs/std/crypto_test.gno b/gnovm/stdlibs/std/crypto_test.gno index 293f3e06945..75283c03523 100644 --- a/gnovm/stdlibs/std/crypto_test.gno +++ b/gnovm/stdlibs/std/crypto_test.gno @@ -15,6 +15,17 @@ func TestValid(t *testing.T) { {inputAddress: "g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa", expected: true}, {inputAddress: "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", expected: true}, {inputAddress: "g14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa", expected: true}, + + // Bech32 doesn't allow '1', 'b', 'i', 'o' for data part + // + // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32 + {inputAddress: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", expected: true}, + {inputAddress: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf1", expected: false}, + {inputAddress: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqfb", expected: false}, + {inputAddress: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqfi", expected: false}, + {inputAddress: "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqfo", expected: false}, + + {inputAddress: "g1u0000000000000000000000000000000000000", expected: false}, {inputAddress: "", expected: false}, {inputAddress: "000000000000", expected: false}, {inputAddress: "0000000000000000000000000000000000000000000000000000000000000000000000", expected: false}, diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go index 0965ef74277..26bfe433858 100644 --- a/gnovm/stdlibs/std/native.go +++ b/gnovm/stdlibs/std/native.go @@ -64,7 +64,7 @@ func X_callerAt(m *gno.Machine, n int) string { ctx := m.Context.(ExecContext) return string(ctx.OrigCaller) } - return string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) + return string(m.MustLastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) } func X_getRealm(m *gno.Machine, height int) (address string, pkgPath string) { diff --git a/gnovm/stdlibs/stdshim/crypto.gno b/gnovm/stdlibs/stdshim/crypto.gno index 8d005dccf5c..402a6af3e22 100644 --- a/gnovm/stdlibs/stdshim/crypto.gno +++ b/gnovm/stdlibs/stdshim/crypto.gno @@ -6,9 +6,10 @@ func (a Address) String() string { return string(a) } -// IsValid checks if the address is of specific length. Doesn't check prefix or checksum for the address +// IsValid checks if the address is valid bech32 encoded string func (a Address) IsValid() bool { - return len(a) == RawAddressSize*2 // hex length + _, _, ok := DecodeBech32(a) + return ok } const RawAddressSize = 20 diff --git a/gnovm/stdlibs/testing/fuzz.gno b/gnovm/stdlibs/testing/fuzz.gno new file mode 100644 index 00000000000..62a36800b2b --- /dev/null +++ b/gnovm/stdlibs/testing/fuzz.gno @@ -0,0 +1,292 @@ +package testing + +import ( + "math" + "strings" + "time" +) + +type Fuzzer interface { + InsertDeleteMutate(p float64) Fuzzer + Mutate() Fuzzer + String() string +} + +type StringFuzzer struct { + Value string + f *F +} + +func NewStringFuzzer(value string) *StringFuzzer { + return &StringFuzzer{Value: value} +} + +// Mutate changes a StringFuzzer's value by replacing a random character +// with a random ASCII character. +func (sf *StringFuzzer) Mutate() Fuzzer { + runes := []rune(sf.Value) + if len(runes) == 0 { + return sf + } + + index := randRange(0, len(runes)-1) + runes[index] = randomASCIIChar() + + return NewStringFuzzer(string(runes)) +} + +func (sf *StringFuzzer) InsertDeleteMutate(p float64) Fuzzer { + value := InsertDelete(sf.Value, p) + return NewStringFuzzer(value) +} + +func (sf *StringFuzzer) Fuzz() string { + if GenerateRandomBool(0.2) { + return InsertDelete(sf.Value, 0.1) + } + + rs := []rune(sf.Value) + lrs := len(rs) + + if lrs == 0 { + return sf.Value + } + + index := randRange(0, lrs-1) + rs[index] = randomASCIIChar() + + return string(rs) +} + +func (sf *StringFuzzer) String() string { + return sf.Value +} + +func randomASCIIChar() rune { + r := int(randRange(32, 126)) + + return rune(r) +} + +// Individual represents a single individual in the population. +type Individual struct { + Fuzzer Fuzzer + Fitness int +} + +func NewIndividual(fuzzer Fuzzer) *Individual { + return &Individual{Fuzzer: fuzzer} +} + +func (ind *Individual) calculateFitness() { + ind.Fitness = len(ind.Fuzzer.String()) +} + +// Selection selects individuals from the population based on their fitness. +// +// Use roulette wheel selection to select individuals from the population. +// ref: https://en.wikipedia.org/wiki/Fitness_proportionate_selection +func Selection(population []*Individual) []*Individual { + totalFitness := calculateTotalFitness(population) + selected := make([]*Individual, len(population)) + + for i := range selected { + selected[i] = selectIndividual(population, totalFitness) + } + + return selected +} + +func calculateTotalFitness(population []*Individual) int { + totalFitness := 0 + + for _, ind := range population { + totalFitness += ind.Fitness + } + + return totalFitness +} + +func selectIndividual(population []*Individual, totalFitness int) *Individual { + pick := randRange(0, totalFitness-1) + sum := 0 + + for _, ind := range population { + sum += ind.Fitness + if uint64(sum) > uint64(pick) { + return ind + } + } + + return nil +} + +// Crossover takes two parents and creates two children by combining their genetic material. +// +// The pivot point is chosen randomly from the length of the shortest parent. after the pivot point selected, +// the genetic material of the two parents is swapped to create the two children. +func Crossover(parent1, parent2 *Individual) (*Individual, *Individual) { + p1Runes := []rune(parent1.Fuzzer.String()) + p2Runes := []rune(parent2.Fuzzer.String()) + + p1Len := len(p1Runes) + p2Len := len(p2Runes) + + point := 0 + if p1Len >= p2Len { + point = int(randRange(0, p2Len-1)) + } else { + point = int(randRange(0, p1Len-1)) + } + + child1 := append(append([]rune{}, p1Runes[:point]...), p2Runes[point:]...) + child2 := append(append([]rune{}, p2Runes[:point]...), p1Runes[point:]...) + + updatedIdv1 := NewIndividual(NewStringFuzzer(string(child1))) + updatedIdv2 := NewIndividual(NewStringFuzzer(string(child2))) + + return updatedIdv1, updatedIdv2 +} + +func (ind *Individual) Mutate() { + ind.Fuzzer = ind.Fuzzer.Mutate() +} + +// InsertDelete randomly inserts or deletes a character from a string. +func InsertDelete(s string, p float64) string { + rr := []rune(s) + l := len(rr) + + // Insert + if GenerateRandomBool(p) { + pos := randRange(0, l-1) + rr = append(rr, 0) + + copy(rr[pos+1:], rr[pos:]) + + char := randomASCIIChar() + rr[pos] = char + } else { + if l == 0 { + return s + } + + pos := randRange(0, l-1) + rr = append(rr[:pos], rr[pos+1:]...) + } + + return string(rr) +} + +type F struct { + corpus []string + failed bool // Indicates whether the fuzzing has encountered a failure. + msgs []string // Stores log messages for reporting. + iters int // Number of iterations to run the fuzzing process. TODO: CLI flag to set this. +} + +// Runner is a type for the target function to fuzz. +type Runner func(*T, ...interface{}) + +// Fuzz applies the fuzzing process to the target function. +func (f *F) Fuzz(run Runner, iter int) { + f.evolve(iter) + + for _, input := range f.corpus { + args := make([]interface{}, len(f.corpus)) + for i := range args { + args[i] = input + } + + run(nil, args...) + } +} + +// Add adds test values to initialize the corpus. +func (f *F) Add(values ...interface{}) []Fuzzer { + fuzzers := make([]Fuzzer, len(values)) + + for i, v := range values { + str, ok := v.(string) + if !ok { + continue + } + f.corpus = append(f.corpus, str) + fuzzers[i] = &StringFuzzer{Value: str} + } + + return fuzzers +} + +func (f *F) evolve(generations int) { + population := make([]*Individual, len(f.corpus)) + for i, c := range f.corpus { + population[i] = &Individual{Fuzzer: &StringFuzzer{Value: c, f: f}} + } + + for _, ind := range population { + ind.calculateFitness() + } + + for gen := 0; gen < generations; gen++ { + population = Selection(population) + newPopulation := make([]*Individual, 0, len(population)) + + for i := 0; i < len(population); i += 2 { + if i+1 < len(population) { + child1, child2 := Crossover(population[i], population[i+1]) + newPopulation = append(newPopulation, child1, child2) + continue + } + + newPopulation = append(newPopulation, population[i]) + } + + var ( + bestFitness int + bestIndividual string + ) + + for _, ind := range newPopulation { + if GenerateRandomBool(0.2) { + ind.Mutate() + } + + if GenerateRandomBool(0.1) { + ind.Fuzzer = ind.Fuzzer.InsertDeleteMutate(0.3) + } + + ind.calculateFitness() + + if ind.Fitness > bestFitness { + bestFitness = ind.Fitness + bestIndividual = ind.Fuzzer.String() + } + } + + population = newPopulation + } + + f.corpus = make([]string, len(population)) + for i, ind := range population { + f.corpus[i] = ind.Fuzzer.String() + } +} + +// Fail marks the function as having failed bur continue execution. +func (f *F) Fail() { + f.failed = true +} + +// Fatal is equivalent to Log followed by FailNow. +// It logs the message and marks the fuzzing as failed. +func (f *F) Fatal(args ...interface{}) { + var sb strings.Builder + + for _, arg := range args { + sb.WriteString(arg.(string)) + } + + f.msgs = append(f.msgs, sb.String()) + f.Fail() +} diff --git a/gnovm/stdlibs/testing/fuzz_test.gno b/gnovm/stdlibs/testing/fuzz_test.gno new file mode 100644 index 00000000000..454c1cccd6e --- /dev/null +++ b/gnovm/stdlibs/testing/fuzz_test.gno @@ -0,0 +1,172 @@ +package testing + +import ( + "encoding/binary" + "strings" + "time" +) + +func TestMutate(t *T) { + originalValue := "Hello" + fuzzer := StringFuzzer{Value: originalValue} + + newFuzzer := fuzzer.Mutate().(*StringFuzzer) + + if newFuzzer.Value == originalValue { + t.Errorf("Mutate did not change the string: got %v, want different from %v", newFuzzer.Value, originalValue) + } + + if len(newFuzzer.Value) != len(originalValue) { + t.Errorf("Mutated string has different length: got %s (len=%v), want %s (len=%v)", newFuzzer.Value, len(newFuzzer.Value), originalValue, len(originalValue)) + } +} + +func TestSelection(t *T) { + tests := []struct { + name string + population []*Individual + }{ + { + name: "Empty population", + population: []*Individual{}, + }, + { + name: "Uniform fitness", + population: []*Individual{ + {Fitness: 10}, + {Fitness: 10}, + {Fitness: 10}, + }, + }, + { + name: "Different fitness", + population: []*Individual{ + {Fitness: 5}, + {Fitness: 15}, + {Fitness: 10}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *T) { + selected := Selection(tc.population) + if len(selected) != len(tc.population) { + t.Errorf("Expected selected length to be %d, got %d", len(tc.population), len(selected)) + } + }) + } +} + +func TestCrossover(t *T) { + parent1 := NewIndividual(&StringFuzzer{Value: "foobar"}) + parent2 := NewIndividual(&StringFuzzer{Value: "bazbiz"}) + + var child1, child2 *Individual + for i := 0; i < 100; i++ { + child1, child2 = Crossover(parent1, parent2) + } + + if child1.Fuzzer.String() == "foobar" || child2.Fuzzer.String() == "bazbiz" { + t.Errorf("Crossover did not modify children correctly, got %s and %s", child1.Fuzzer.String(), child2.Fuzzer.String()) + } +} + +func Test_StringManipulation(t *T) { + f := &F{ + corpus: []string{"hello", "world", "foo", "bar"}, + } + + f.evolve(30) + + if len(f.corpus) != 4 { + t.Fatalf("corpus length is %d, want 4", len(f.corpus)) + } + + for i, c := range f.corpus { + if c == "" { + t.Fatalf("corpus[%d] is empty", i) + } + + if len(c) < 3 { + t.Fatalf("corpus[%d] is too short: %s", i, c) + } + + if f.corpus[0] == "hello" { + t.Fatalf("corpus[0] is still the same: %s", f.corpus[0]) + } + + if f.corpus[1] == "world" { + t.Fatalf("corpus[1] is still the same: %s", f.corpus[1]) + } + + if f.corpus[2] == "foo" { + t.Fatalf("corpus[2] is still the same: %s", f.corpus[2]) + } + + if f.corpus[3] == "bar" { + t.Fatalf("corpus[3] is still the same: %s", f.corpus[3]) + } + + } +} + +func TestFuzz(t *T) { + f := F{} + f.Add("hello", "world", "foo") + f.Fuzz(func(t *T, inputs ...interface{}) { + for _, input := range inputs { + strInput, ok := input.(string) + if !ok { + t.Errorf("Type mismatch, expected a string but got %T", input) + continue + } + + words := strings.Fields(strInput) + if len(words) == 0 { + t.Errorf("Expected non-empty input") + } + } + }, 15) + + if len(f.corpus) == 0 { + t.Fatalf("Fuzzing corpus is empty after testing") + } + + if len(f.corpus) > 3 { + t.Fatalf("Fuzzing corpus has more than 3 elements: %v", f.corpus) + } + + for _, c := range f.corpus { + if c == "hello" || c == "world" || c == "foo" { + t.Fatalf("Fuzzing corpus still contains the original elements: %v", f.corpus) + } + } +} + +func TestF_Fail(t *T) { + f := F{} + f.Fail() + + if !f.failed { + t.Errorf("Fail did not set the failed flag.") + } +} + +func TestF_Fatal(t *T) { + f := F{} + testMessage := "test failure message" + f.Fatal(testMessage) + + if !f.failed { + t.Errorf("Fatal did not set the failed flag.") + } + + if len(f.msgs) != 1 { + t.Fatalf("Fatal did not set the message correctly: got %v, want %v", f.msgs, testMessage) + } + + if !strings.Contains(f.msgs[0], testMessage) { + t.Errorf("Fatal did not set the message correctly: got %v, want %v", f.msgs[0], testMessage) + } +} diff --git a/gnovm/stdlibs/testing/random.gno b/gnovm/stdlibs/testing/random.gno new file mode 100644 index 00000000000..b7ee6ca93ef --- /dev/null +++ b/gnovm/stdlibs/testing/random.gno @@ -0,0 +1,75 @@ +package testing + +import ( + "math" + "time" +) + +// Internal state for the random number generator. +var x uint64 = 42 + +// UniformRand generates a uniformly distributed random number. +// It uses the linear congrential generator method to produce the random number. +// and the result is in the range from 0 to m-1. here, m is 32768. +// To produce random number in [0, m-1], repeat this method as many times as needed. +// [1] https://en.wikipedia.org/wiki/Linear_congruential_generator +func UniformRand() uint64 { + var a uint64 = 950213 + var c uint64 = 12345 + var m uint64 = 32768 + x = x*a + c + return (x >> 16) % m +} + +// _srand function sets the seed for the random number generator. +// This function provides an initial starting point for the sequence of random numbers. +func _srand(seed int64) { + x = uint64(seed) +} + +// nrand function generates a number approximating a normal distribution[1]. +// It uses the Central Limit Theorem[2] by summing multiple uniformly distributed random numbers +// to approximate a normal distribution. +// +// y = Sum(k=1, K) (x_k - K/2) / sqrt(K/12) +// +// Here, K is some integer ans x_k are uniformly distributed numbers, +// even for K as small as 10, the approximation is quite good. +// [1] https://en.wikipedia.org/wiki/Normal_distribution +// [2] https://en.wikipedia.org/wiki/Central_limit_theorem +func nrand() float64 { + var i, K uint64 = 0, 10 + var m uint64 = 32768 + var y float64 = 0 + + for i = 0; i < K; i++ { + y += float64(UniformRand()) / float64(m) + } + y = (y - float64(K)/2) / math.Sqrt(float64(K)/12) + return y +} + +// randRange generates a random integer between min and max (inclusive). +// This function leverages the UniformRand function to generate a random number in a specified range. +// Note: max should be greater than min. +func randRange(min, max int) uint64 { + _min := uint64(min) + _max := uint64(max) + if _min >= _max { + return _min + } + + rangeSize := _max - _min + 1 + // adjust UniformRand to fit into our range. + return _min + (UniformRand() % rangeSize) +} + +func GenerateRandomBool(bias float64) bool { + // Modify to use fuzz's random function for generating boolean with bias + if bias < 0 || bias > 1 { + panic("bias should be in the range [0, 1]") + } + // Convert fuzz's normalized range random float [-1, 1] to [0, 1] + res := (nrand() + 1) / 2 + return res > bias +} diff --git a/gnovm/stdlibs/testing/random_test.gno b/gnovm/stdlibs/testing/random_test.gno new file mode 100644 index 00000000000..8c1c741b2b8 --- /dev/null +++ b/gnovm/stdlibs/testing/random_test.gno @@ -0,0 +1,99 @@ +package testing + +import ( + "math" + "strconv" + "time" +) + +func updateSeed() { + seed := time.Now().UnixNano() + _srand(seed) +} + +func Test_UniformRand(t *T) { + valueMap := make(map[uint64]int) + maxIter := 1000 + + for i := 0; i < maxIter; i++ { + result := UniformRand() + + if result < 0 || result >= 32768 { + t.Errorf("rand() = %v, want in range [0, 32767]", result) + } + + if _, ok := valueMap[result]; ok { + valueMap[result]++ + } else { + valueMap[result] = 1 + } + } + + lvm := len(valueMap) + if lvm > maxIter || lvm == 0 { + t.Errorf("len(valueMap) = %v, want <= %v", lvm, maxIter) + } +} + +func Test_nrand(t *T) { + sum := 0.0 + for i := 0; i < 1000; i++ { + result := nrand() + sum += result + } + avg := sum / float64(1000) + + // The value returned by nrand() should be close to 0 + // on average for large sample sizes. + // The expectation of the standard deviation should be + // close to 1 for large sample sizes. + if math.Abs(avg) > 0.1 { // can be adjusted based on sample size + t.Errorf("nrand() = %v, want in range [-0.1, 0.1]", avg) + } +} + +func Test_GenerateRandomBool(t *T) { + updateSeed() + + for _, bias := range []float64{0, 0.5, 1} { + trueCount, falseCount := 0, 0 + sampleSize := 1000 + + for i := 0; i < sampleSize; i++ { + result := GenerateRandomBool(bias) + if result { + trueCount++ + } else { + falseCount++ + } + } + + if trueCount == 0 || falseCount == 0 { + t.Errorf("Bias = %v, trueCount = %v, falseCount = %v, want both > 0", bias, trueCount, falseCount) + } + + if bias < 0 || bias > 1 { + t.Errorf("Bias = %v, want in range [0, 1]", bias) + } + } +} + +func TestRandRange(t *T) { + nums := make(map[uint64]int) + for i := 0; i < 1000; i++ { + res := randRange(0, 10) + if res < 0 || res > 10 { + t.Errorf("gerandRangenerateRange() = %v, want in range [0, 9]", res) + } + + if _, ok := nums[res]; ok { + nums[res]++ + } else { + nums[res] = 1 + } + } + + if len(nums) != 11 { + t.Errorf("len(nums) = %v, want in range [0, 10]", len(nums)) + } +} diff --git a/gnovm/stdlibs/testing/testing.gno b/gnovm/stdlibs/testing/testing.gno index 36e8e7a6955..fb13c0f39cd 100644 --- a/gnovm/stdlibs/testing/testing.gno +++ b/gnovm/stdlibs/testing/testing.gno @@ -30,12 +30,18 @@ func (s skipErr) Error() string { // (and Skip* functions). // // NOTE: Recover() is likely to be removed. -func Recover() interface{} { +func Recover(result Setter) { r := recover() - if _, ok := r.(skipErr); ok { - panic(r) + if _, ok := r.(skipErr); !ok { + result.Set(r) + return } - return r + + panic(r) +} + +type Setter interface { + Set(v interface{}) } func Short() bool { diff --git a/gnovm/stdlibs/unicode/utf16/utf16.gno b/gnovm/stdlibs/unicode/utf16/utf16.gno new file mode 100644 index 00000000000..38d8be60602 --- /dev/null +++ b/gnovm/stdlibs/unicode/utf16/utf16.gno @@ -0,0 +1,125 @@ +// Copyright 2010 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 utf16 implements encoding and decoding of UTF-16 sequences. +package utf16 + +// The conditions replacementChar==unicode.ReplacementChar and +// maxRune==unicode.MaxRune are verified in the tests. +// Defining them locally avoids this package depending on package unicode. + +const ( + replacementChar = '\uFFFD' // Unicode replacement character + maxRune = '\U0010FFFF' // Maximum valid Unicode code point. +) + +const ( + // 0xd800-0xdc00 encodes the high 10 bits of a pair. + // 0xdc00-0xe000 encodes the low 10 bits of a pair. + // the value is those 20 bits plus 0x10000. + surr1 = 0xd800 + surr2 = 0xdc00 + surr3 = 0xe000 + + surrSelf = 0x10000 +) + +// IsSurrogate reports whether the specified Unicode code point +// can appear in a surrogate pair. +func IsSurrogate(r rune) bool { + return surr1 <= r && r < surr3 +} + +// DecodeRune returns the UTF-16 decoding of a surrogate pair. +// If the pair is not a valid UTF-16 surrogate pair, DecodeRune returns +// the Unicode replacement code point U+FFFD. +func DecodeRune(r1, r2 rune) rune { + if surr1 <= r1 && r1 < surr2 && surr2 <= r2 && r2 < surr3 { + return (r1-surr1)<<10 | (r2 - surr2) + surrSelf + } + return replacementChar +} + +// EncodeRune returns the UTF-16 surrogate pair r1, r2 for the given rune. +// If the rune is not a valid Unicode code point or does not need encoding, +// EncodeRune returns U+FFFD, U+FFFD. +func EncodeRune(r rune) (r1, r2 rune) { + if r < surrSelf || r > maxRune { + return replacementChar, replacementChar + } + r -= surrSelf + return surr1 + (r>>10)&0x3ff, surr2 + r&0x3ff +} + +// Encode returns the UTF-16 encoding of the Unicode code point sequence s. +func Encode(s []rune) []uint16 { + n := len(s) + for _, v := range s { + if v >= surrSelf { + n++ + } + } + + a := make([]uint16, n) + n = 0 + for _, v := range s { + switch { + case 0 <= v && v < surr1, surr3 <= v && v < surrSelf: + // normal rune + a[n] = uint16(v) + n++ + case surrSelf <= v && v <= maxRune: + // needs surrogate sequence + r1, r2 := EncodeRune(v) + a[n] = uint16(r1) + a[n+1] = uint16(r2) + n += 2 + default: + a[n] = uint16(replacementChar) + n++ + } + } + return a[:n] +} + +// AppendRune appends the UTF-16 encoding of the Unicode code point r +// to the end of p and returns the extended buffer. If the rune is not +// a valid Unicode code point, it appends the encoding of U+FFFD. +func AppendRune(a []uint16, r rune) []uint16 { + // This function is inlineable for fast handling of ASCII. + switch { + case 0 <= r && r < surr1, surr3 <= r && r < surrSelf: + // normal rune + return append(a, uint16(r)) + case surrSelf <= r && r <= maxRune: + // needs surrogate sequence + r1, r2 := EncodeRune(r) + return append(a, uint16(r1), uint16(r2)) + } + return append(a, replacementChar) +} + +// Decode returns the Unicode code point sequence represented +// by the UTF-16 encoding s. +func Decode(s []uint16) []rune { + a := make([]rune, len(s)) + n := 0 + for i := 0; i < len(s); i++ { + switch r := s[i]; { + case r < surr1, surr3 <= r: + // normal rune + a[n] = rune(r) + case surr1 <= r && r < surr2 && i+1 < len(s) && + surr2 <= s[i+1] && s[i+1] < surr3: + // valid surrogate sequence + a[n] = DecodeRune(rune(r), rune(s[i+1])) + i++ + default: + // invalid surrogate sequence + a[n] = replacementChar + } + n++ + } + return a[:n] +} diff --git a/gnovm/stdlibs/unicode/utf16/utf16_test.gno b/gnovm/stdlibs/unicode/utf16/utf16_test.gno new file mode 100644 index 00000000000..b56fd81494d --- /dev/null +++ b/gnovm/stdlibs/unicode/utf16/utf16_test.gno @@ -0,0 +1,221 @@ +// Copyright 2010 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 utf16 + +import ( + "testing" + "unicode" + "unicode/utf16" +) + +type encodeTest struct { + in []rune + out []uint16 +} + +var encodeTests = []encodeTest{ + {[]rune{1, 2, 3, 4}, []uint16{1, 2, 3, 4}}, + { + []rune{0xffff, 0x10000, 0x10001, 0x12345, 0x10ffff}, + []uint16{0xffff, 0xd800, 0xdc00, 0xd800, 0xdc01, 0xd808, 0xdf45, 0xdbff, 0xdfff}, + }, + { + []rune{'a', 'b', 0xd7ff, 0xd800, 0xdfff, 0xe000, 0x110000, -1}, + []uint16{'a', 'b', 0xd7ff, 0xfffd, 0xfffd, 0xe000, 0xfffd, 0xfffd}, + }, +} + +func slicesEqual(a, b []uint16) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func TestEncode(t *testing.T) { + for _, tt := range encodeTests { + out := Encode(tt.in) + if !slicesEqual(out, tt.out) { + t.Errorf("Encode(%x) = %x; want %x", tt.in, out, tt.out) + } + } +} + +func TestEncodeRune(t *testing.T) { + for i, tt := range encodeTests { + j := 0 + for _, r := range tt.in { + r1, r2 := EncodeRune(r) + if r < 0x10000 || r > unicode.MaxRune { + if j >= len(tt.out) { + t.Errorf("#%d: ran out of tt.out", i) + break + } + if r1 != unicode.ReplacementChar || r2 != unicode.ReplacementChar { + t.Errorf("EncodeRune(%#x) = %#x, %#x; want 0xfffd, 0xfffd", r, r1, r2) + } + j++ + } else { + if j+1 >= len(tt.out) { + t.Errorf("#%d: ran out of tt.out", i) + break + } + if r1 != rune(tt.out[j]) || r2 != rune(tt.out[j+1]) { + t.Errorf("EncodeRune(%#x) = %#x, %#x; want %#x, %#x", r, r1, r2, tt.out[j], tt.out[j+1]) + } + j += 2 + dec := DecodeRune(r1, r2) + if dec != r { + t.Errorf("DecodeRune(%#x, %#x) = %#x; want %#x", r1, r2, dec, r) + } + } + } + if j != len(tt.out) { + t.Errorf("#%d: EncodeRune didn't generate enough output", i) + } + } +} + +type decodeTest struct { + in []uint16 + out []rune +} + +var decodeTests = []decodeTest{ + {[]uint16{1, 2, 3, 4}, []rune{1, 2, 3, 4}}, + { + []uint16{0xffff, 0xd800, 0xdc00, 0xd800, 0xdc01, 0xd808, 0xdf45, 0xdbff, 0xdfff}, + []rune{0xffff, 0x10000, 0x10001, 0x12345, 0x10ffff}, + }, + {[]uint16{0xd800, 'a'}, []rune{0xfffd, 'a'}}, + {[]uint16{0xdfff}, []rune{0xfffd}}, +} + +func TestDecode(t *testing.T) { + for _, tt := range decodeTests { + out := Decode(tt.in) + if !runesEqual(out, tt.out) { + t.Errorf("Decode(%x) = %x; want %x", tt.in, out, tt.out) + } + } +} + +func runesEqual(a, b []rune) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +var decodeRuneTests = []struct { + r1, r2 rune + want rune +}{ + {0xd800, 0xdc00, 0x10000}, + {0xd800, 0xdc01, 0x10001}, + {0xd808, 0xdf45, 0x12345}, + {0xdbff, 0xdfff, 0x10ffff}, + {0xd800, 'a', 0xfffd}, // illegal, replacement rune substituted +} + +func TestDecodeRune(t *testing.T) { + for i, tt := range decodeRuneTests { + got := DecodeRune(tt.r1, tt.r2) + if got != tt.want { + t.Errorf("%d: DecodeRune(%q, %q) = %v; want %v", i, tt.r1, tt.r2, got, tt.want) + } + } +} + +var surrogateTests = []struct { + r rune + want bool +}{ + // from https://en.wikipedia.org/wiki/UTF-16 + {'\u007A', false}, // LATIN SMALL LETTER Z + {'\u6C34', false}, // CJK UNIFIED IDEOGRAPH-6C34 (water) + {'\uFEFF', false}, // Byte Order Mark + {'\U00010000', false}, // LINEAR B SYLLABLE B008 A (first non-BMP code point) + {'\U0001D11E', false}, // MUSICAL SYMBOL G CLEF + {'\U0010FFFD', false}, // PRIVATE USE CHARACTER-10FFFD (last Unicode code point) + + {rune(0xd7ff), false}, // surr1-1 + {rune(0xd800), true}, // surr1 + {rune(0xdc00), true}, // surr2 + {rune(0xe000), false}, // surr3 + {rune(0xdfff), true}, // surr3-1 +} + +func TestIsSurrogate(t *testing.T) { + for i, tt := range surrogateTests { + got := IsSurrogate(tt.r) + if got != tt.want { + t.Errorf("%d: IsSurrogate(%q) = %v; want %v", i, tt.r, got, tt.want) + } + } +} + +func BenchmarkDecodeValidASCII(b *testing.B) { + // "hello world" + data := []uint16{104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} + for i := 0; i < b.N; i++ { + Decode(data) + } +} + +func BenchmarkDecodeValidJapaneseChars(b *testing.B) { + // "日本語日本語日本語" + data := []uint16{26085, 26412, 35486, 26085, 26412, 35486, 26085, 26412, 35486} + for i := 0; i < b.N; i++ { + Decode(data) + } +} + +func BenchmarkDecodeRune(b *testing.B) { + rs := make([]rune, 10) + // U+1D4D0 to U+1D4D4: MATHEMATICAL BOLD SCRIPT CAPITAL LETTERS + for i, u := range []rune{'𝓐', '𝓑', '𝓒', '𝓓', '𝓔'} { + rs[2*i], rs[2*i+1] = EncodeRune(u) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < 5; j++ { + DecodeRune(rs[2*j], rs[2*j+1]) + } + } +} + +func BenchmarkEncodeValidASCII(b *testing.B) { + data := []rune{'h', 'e', 'l', 'l', 'o'} + for i := 0; i < b.N; i++ { + Encode(data) + } +} + +func BenchmarkEncodeValidJapaneseChars(b *testing.B) { + data := []rune{'日', '本', '語'} + for i := 0; i < b.N; i++ { + Encode(data) + } +} + +func BenchmarkEncodeRune(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, u := range []rune{'𝓐', '𝓑', '𝓒', '𝓓', '𝓔'} { + EncodeRune(u) + } + } +} diff --git a/gnovm/tests/files/recover10.gno b/gnovm/tests/files/recover10.gno new file mode 100644 index 00000000000..16dff4d4fed --- /dev/null +++ b/gnovm/tests/files/recover10.gno @@ -0,0 +1,10 @@ +package main + +func main() { + defer recover() + + panic("ahhhhh") +} + +// Error: +// ahhhhh diff --git a/gnovm/tests/files/recover10a.gno b/gnovm/tests/files/recover10a.gno new file mode 100644 index 00000000000..4e9aa9301e7 --- /dev/null +++ b/gnovm/tests/files/recover10a.gno @@ -0,0 +1,12 @@ +package main + +func main() { + defer func() { + println(recover()) + }() + + panic("ahhhhh") +} + +// Output: +// ahhhhh diff --git a/gnovm/tests/files/recover11.gno b/gnovm/tests/files/recover11.gno new file mode 100644 index 00000000000..a8adaff2c34 --- /dev/null +++ b/gnovm/tests/files/recover11.gno @@ -0,0 +1,38 @@ +package main + +func level3() { + panic("level3") + defer func() { + if r := recover(); r != nil { + println("recover level3 in level3") + } + }() +} + +func level2() { + defer func() { + if r := recover(); r != nil { + println("recover level3 in level2") + } + }() + defer level3() + panic("level2") +} + +func level1() { + defer func() { + if r := recover(); r != nil { + println("recover level3 in level1") + } + }() + level2() + panic("level1") +} + +func main() { + level1() +} + +// Output: +// recover level3 in level2 +// recover level3 in level1 diff --git a/gnovm/tests/files/recover12a.gno b/gnovm/tests/files/recover12a.gno new file mode 100644 index 00000000000..c8b82511ad0 --- /dev/null +++ b/gnovm/tests/files/recover12a.gno @@ -0,0 +1,26 @@ +package main + +func anotherRecover() { + if r := recover(); r != nil { + println(r) + } +} + +func main() { + defer func() { + if r := recover(); r != nil { + println(r) + } + }() + defer anotherRecover() + defer func() { + if r := recover(); r != nil { + panic("panic in defer func") + } + }() + + panic("panic in main") +} + +// Output: +// panic in defer func diff --git a/gnovm/tests/files/recover12b.gno b/gnovm/tests/files/recover12b.gno new file mode 100644 index 00000000000..4cc0a48b9b0 --- /dev/null +++ b/gnovm/tests/files/recover12b.gno @@ -0,0 +1,28 @@ +package main + +func anotherRecover() { + if r := recover(); r != nil { + println(r) + } +} + +func main() { + defer func() { + if r := recover(); r != nil { + println(r.(string) + ": not another recover") + } + }() + defer func() { + anotherRecover() + }() + defer func() { + if r := recover(); r != nil { + panic("panic in defer func") + } + }() + + panic("panic in main") +} + +// Output: +// panic in defer func: not another recover diff --git a/gnovm/tests/challenges/recover5b.gno b/gnovm/tests/files/recover5b.gno similarity index 98% rename from gnovm/tests/challenges/recover5b.gno rename to gnovm/tests/files/recover5b.gno index 285bc386093..f94c7442efe 100644 --- a/gnovm/tests/challenges/recover5b.gno +++ b/gnovm/tests/files/recover5b.gno @@ -24,4 +24,3 @@ func g() { // Output: // g recover undefined // f recover wtf -// false diff --git a/gnovm/tests/files/recover8.gno b/gnovm/tests/files/recover8.gno new file mode 100644 index 00000000000..53b31f05468 --- /dev/null +++ b/gnovm/tests/files/recover8.gno @@ -0,0 +1,24 @@ +package main + +func doSomething() { + defer func() { + doSomethingElse() + if r := recover(); r != nil { + panic("do something panic") + } + }() + panic("first panic") +} + +func doSomethingElse() { + if r := recover(); r != nil { + panic("do something else panic") + } +} + +func main() { + doSomething() +} + +// Error: +// do something panic diff --git a/gnovm/tests/files/recover9.gno b/gnovm/tests/files/recover9.gno new file mode 100644 index 00000000000..d6c944aeb06 --- /dev/null +++ b/gnovm/tests/files/recover9.gno @@ -0,0 +1,14 @@ +package main + +func rec() { + println(recover()) +} + +func main() { + defer rec() + + panic("ahhhhh") +} + +// Output: +// ahhhhh diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go index 0e15c8d0241..f7bc001043e 100644 --- a/gnovm/tests/stdlibs/std/std.go +++ b/gnovm/tests/stdlibs/std/std.go @@ -85,7 +85,7 @@ func X_callerAt(m *gno.Machine, n int) string { ctx := m.Context.(std.ExecContext) return string(ctx.OrigCaller) } - return string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) + return string(m.MustLastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) } func X_testSetOrigCaller(m *gno.Machine, addr string) { diff --git a/go.mod b/go.mod index 08acbc12a74..c6983d10b4f 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,16 @@ go 1.21 require ( dario.cat/mergo v1.0.0 - github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/btcsuite/btcd/btcutil v1.1.5 github.com/cockroachdb/apd/v3 v3.2.1 github.com/cosmos/ledger-cosmos-go v0.13.3 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/fortytw2/leaktest v1.3.0 - github.com/gdamore/tcell/v2 v2.6.0 + github.com/gdamore/tcell/v2 v2.7.4 github.com/gnolang/faucet v0.1.3 - github.com/gnolang/goleveldb v0.0.9 github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/google/gofuzz v1.2.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.1 @@ -27,25 +26,25 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/peterbourgon/ff/v3 v3.4.0 github.com/pmezard/go-difflib v1.0.0 - github.com/rogpeppe/go-internal v1.11.0 + github.com/rogpeppe/go-internal v1.12.0 github.com/rs/cors v1.10.1 - github.com/stretchr/testify v1.8.4 - go.etcd.io/bbolt v1.3.8 - go.uber.org/multierr v1.10.0 - go.uber.org/zap v1.26.0 + github.com/stretchr/testify v1.9.0 + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + go.etcd.io/bbolt v1.3.9 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 go.uber.org/zap/exp v0.2.0 - golang.org/x/crypto v0.19.0 + golang.org/x/crypto v0.21.0 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/mod v0.15.0 - golang.org/x/net v0.21.0 - golang.org/x/term v0.17.0 - golang.org/x/tools v0.18.0 - google.golang.org/protobuf v1.31.0 + golang.org/x/mod v0.16.0 + golang.org/x/net v0.22.0 + golang.org/x/term v0.18.0 + golang.org/x/time v0.5.0 + golang.org/x/tools v0.19.0 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/time v0.5.0 - require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/gdamore/encoding v1.0.0 // indirect diff --git a/go.sum b/go.sum index 854e4531bdd..b71747f5d32 100644 --- a/go.sum +++ b/go.sum @@ -3,19 +3,20 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -54,12 +55,10 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= -github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= +github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= +github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/gnolang/faucet v0.1.3 h1:eg3S4rGkW6LYWo7nhc4AhWrPmHsaEFy6R8fyef/KgK4= github.com/gnolang/faucet v0.1.3/go.mod h1:+91pqgE+pyX8FO9eoe2MGiwgTpYY0bITYsHO/4Zg+CY= -github.com/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= -github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= @@ -71,15 +70,13 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -92,6 +89,7 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= @@ -119,7 +117,6 @@ github.com/linxGnu/grocksdb v1.6.20 h1:C0SNv12/OBr/zOdGw6reXS+mKpIdQGb/AkZWjHYnO github.com/linxGnu/grocksdb v1.6.20/go.mod h1:IbTMGpmWg/1pg2hcG9LlxkqyqiJymdCweaUrzsLRFmg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -146,43 +143,45 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= -go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -191,11 +190,13 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -213,13 +214,15 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -232,8 +235,9 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= @@ -248,10 +252,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/misc/docusaurus/sidebars.js b/misc/docusaurus/sidebars.js index 7d26b6bf342..cab3af1ce25 100644 --- a/misc/docusaurus/sidebars.js +++ b/misc/docusaurus/sidebars.js @@ -35,6 +35,7 @@ const sidebars = { 'how-to-guides/write-simple-dapp', 'how-to-guides/creating-grc20', 'how-to-guides/creating-grc721', + 'how-to-guides/connect-from-go', 'how-to-guides/connect-wallet-dapp', ], }, @@ -114,8 +115,8 @@ const sidebars = { { type: 'category', label: 'tm2-js-client', + link: {type: 'doc', id: 'reference/tm2-js-client/tm2-js-client'}, items: [ - 'reference/tm2-js-client/tm2-js-getting-started', 'reference/tm2-js-client/tm2-js-wallet', { type: 'category', @@ -141,12 +142,21 @@ const sidebars = { { type: 'category', label: 'gno-js-client', + link: {type: 'doc', id: 'reference/gno-js-client/gno-js-client'}, items: [ - 'reference/gno-js-client/gno-js-getting-started', 'reference/gno-js-client/gno-js-provider', 'reference/gno-js-client/gno-js-wallet', ] }, + { + type: 'category', + label: 'gnoclient', + link: {type: 'doc', id: 'reference/gnoclient/gnoclient'}, + items: [ + 'reference/gnoclient/signer', + 'reference/gnoclient/client' + ] + }, ], }, ], diff --git a/tm2/pkg/bft/config/config.go b/tm2/pkg/bft/config/config.go index 1fb0fc87197..85db5ecc3ae 100644 --- a/tm2/pkg/bft/config/config.go +++ b/tm2/pkg/bft/config/config.go @@ -239,7 +239,7 @@ type BaseConfig struct { FastSyncMode bool `toml:"fast_sync" comment:"If this node is many blocks behind the tip of the chain, FastSync\n allows them to catchup quickly by downloading blocks in parallel\n and verifying their commits"` // Database backend: goleveldb | cleveldb | boltdb - // * goleveldb (github.com/gnolang/goleveldb - most popular implementation) + // * goleveldb (github.com/syndtr/goleveldb - most popular implementation) // - pure go // - stable // * cleveldb (uses levigo wrapper) @@ -250,7 +250,7 @@ type BaseConfig struct { // - EXPERIMENTAL // - may be faster is some use-cases (random reads - indexer) // - use boltdb build tag (go build -tags boltdb) - DBBackend string `toml:"db_backend" comment:"Database backend: goleveldb | cleveldb | boltdb\n * goleveldb (github.com/gnolang/goleveldb - most popular implementation)\n - pure go\n - stable\n * cleveldb (uses levigo wrapper)\n - fast\n - requires gcc\n - use cleveldb build tag (go build -tags cleveldb)\n * boltdb (uses etcd's fork of bolt - go.etcd.io/bbolt)\n - EXPERIMENTAL\n - may be faster is some use-cases (random reads - indexer)\n - use boltdb build tag (go build -tags boltdb)"` + DBBackend string `toml:"db_backend" comment:"Database backend: goleveldb | cleveldb | boltdb\n * goleveldb (github.com/syndtr/goleveldb - most popular implementation)\n - pure go\n - stable\n * cleveldb (uses levigo wrapper)\n - fast\n - requires gcc\n - use cleveldb build tag (go build -tags cleveldb)\n * boltdb (uses etcd's fork of bolt - go.etcd.io/bbolt)\n - EXPERIMENTAL\n - may be faster is some use-cases (random reads - indexer)\n - use boltdb build tag (go build -tags boltdb)"` // Database directory DBPath string `toml:"db_dir" comment:"Database directory"` diff --git a/tm2/pkg/bft/privval/file.go b/tm2/pkg/bft/privval/file.go index 4af1e9e24a6..b1bac8416f7 100644 --- a/tm2/pkg/bft/privval/file.go +++ b/tm2/pkg/bft/privval/file.go @@ -35,7 +35,7 @@ func voteToStep(vote *types.Vote) int8 { } } -//------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- // FilePVKey stores the immutable part of PrivValidator. type FilePVKey struct { @@ -63,7 +63,7 @@ func (pvKey FilePVKey) Save() { } } -//------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- // FilePVLastSignState stores the mutable part of PrivValidator. type FilePVLastSignState struct { @@ -126,7 +126,7 @@ func (lss *FilePVLastSignState) Save() { } } -//------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- // FilePV implements PrivValidator using data persisted to disk // to prevent double signing. @@ -273,7 +273,7 @@ func (pv *FilePV) String() string { return fmt.Sprintf("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastSignState.Height, pv.LastSignState.Round, pv.LastSignState.Step) } -//------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------ // signVote checks if the vote is good to sign and sets the vote signature. // It may need to set the timestamp as well if the vote is otherwise the same as @@ -371,7 +371,7 @@ func (pv *FilePV) saveSigned(height int64, round int, step int8, pv.LastSignState.Save() } -//----------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------- // returns the timestamp from the lastSignBytes. // returns true if the only difference in the votes is their timestamp. diff --git a/tm2/pkg/db/db.go b/tm2/pkg/db/db.go index 3da7d53bc23..13a9ad6e77f 100644 --- a/tm2/pkg/db/db.go +++ b/tm2/pkg/db/db.go @@ -35,7 +35,7 @@ func (b BackendType) String() string { // db.NewDB("mydb", db.BackendType(userProvidedBackend), "./data") // } const ( - // GoLevelDBBackend represents goleveldb (github.com/gnolang/goleveldb - most + // GoLevelDBBackend represents goleveldb (github.com/syndtr/goleveldb - most // popular implementation) // - stable GoLevelDBBackend BackendType = "goleveldb" diff --git a/tm2/pkg/db/goleveldb/go_level_db.go b/tm2/pkg/db/goleveldb/go_level_db.go index b4528d3fafa..27db61bb680 100644 --- a/tm2/pkg/db/goleveldb/go_level_db.go +++ b/tm2/pkg/db/goleveldb/go_level_db.go @@ -8,10 +8,10 @@ import ( "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/db/internal" - "github.com/gnolang/goleveldb/leveldb" - "github.com/gnolang/goleveldb/leveldb/errors" - "github.com/gnolang/goleveldb/leveldb/iterator" - "github.com/gnolang/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" ) func init() { @@ -291,7 +291,7 @@ func (itr *goLevelDBIterator) Valid() bool { // Implements Iterator. func (itr *goLevelDBIterator) Key() []byte { // Key returns a copy of the current key. - // See https://github.com/gnolang/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 + // See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 itr.assertNoError() itr.assertIsValid() return append([]byte{}, itr.source.Key()...) @@ -300,7 +300,7 @@ func (itr *goLevelDBIterator) Key() []byte { // Implements Iterator. func (itr *goLevelDBIterator) Value() []byte { // Value returns a copy of the current value. - // See https://github.com/gnolang/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 + // See https://github.com/syndtr/goleveldb/blob/52c212e6c196a1404ea59592d3f1c227c9f034b2/leveldb/iterator/iter.go#L88 itr.assertNoError() itr.assertIsValid() return append([]byte{}, itr.source.Value()...) diff --git a/tm2/pkg/db/goleveldb/go_level_db_test.go b/tm2/pkg/db/goleveldb/go_level_db_test.go index a83c5dd2280..68ea70c8459 100644 --- a/tm2/pkg/db/goleveldb/go_level_db_test.go +++ b/tm2/pkg/db/goleveldb/go_level_db_test.go @@ -6,9 +6,9 @@ import ( "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/db/internal" - "github.com/gnolang/goleveldb/leveldb/opt" "github.com/jaekwon/testify/assert" "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb/opt" ) func TestGoLevelDBNewGoLevelDB(t *testing.T) {