diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..58d7c0e
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,31 @@
+# This workflow will build a golang project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
+
+name: Go
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.19
+
+ - name: Build
+ run: make ast
+
+ - name: Test
+ run: go test -v ./...
+
+ - name: Benchmark
+ run: go test -bench ./...
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..766688e
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,52 @@
+name: Release
+
+# Uncomment the following to let goreleaser automatically
+# create a GitHub release when a tag is pushed.
+# permissions:
+# contents: write
+
+on:
+ push:
+ branches-ignore:
+ - '**'
+ tags:
+ - 'v*.*.*'
+ # to be used by fork patch-releases ^^
+ - 'v*.*.*-*'
+ workflow_dispatch:
+
+jobs:
+ goreleaser:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: dockerhub-login
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB }}
+ password: ${{ secrets.DOCKERHUB_KEY }}
+
+ - name: Prepare
+ id: prepare
+ run: |
+ TAG=${GITHUB_REF#refs/tags/}
+ echo ::set-output name=tag_name::${TAG}
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+
+ - run: echo ${{ steps.prepare.outputs.tag_name }}
+
+ - name: Run GoReleaser
+ run: |
+ make release
+ docker images
+ env:
+ GITHUB_TOKEN: ${{ secrets.TOKEN }}
+ VERSION: ${{ steps.prepare.outputs.tag_name }}
+ DOCKER_USERNAME: ${{ secrets.DOCKERHUB }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_KEY }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..701c594
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+#
+build/
+.idea
+
+#vscode
+.vscode/
+
+# utests
+modules/rawdb/mdbx.db/
+
+# mobile outputs
+mobile/
+
+.DS_Store
+ast
+
+genesis*.json
+
+./deployments/
\ No newline at end of file
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100644
index 0000000..1d57f04
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,99 @@
+project_name: ast
+
+release:
+ disable: false
+ draft: true
+ prerelease: auto
+
+builds:
+ - id: darwin-amd64
+ main: ./cmd/ast
+ binary: astranet
+ goos: [ darwin ]
+ goarch: [ amd64 ]
+ env:
+ - CC=o64-clang
+ - CXX=o64-clang++
+ tags: [ nosqlite, noboltdb ]
+ ldflags: -s -w
+
+ - id: darwin-arm64
+ main: ./cmd/ast
+ binary: astranet
+ goos: [ darwin ]
+ goarch: [ arm64 ]
+ env:
+ - CC=oa64-clang
+ - CXX=oa64-clang++
+ tags: [ nosqlite, noboltdb ]
+ ldflags: -s -w
+
+ - id: linux-amd64
+ main: ./cmd/ast
+ binary: astranet
+ goos: [ linux ]
+ goarch: [ amd64 ]
+ env:
+ - CC=x86_64-linux-gnu-gcc
+ - CXX=x86_64-linux-gnu-g++
+ tags: [ nosqlite, noboltdb ]
+ ldflags: -s -w -extldflags "-static" # We need to build a static binary because we are building in a glibc based system and running in a musl container
+
+ - id: linux-arm64
+ main: ./cmd/ast
+ binary: astranet
+ goos: [ linux ]
+ goarch: [ arm64 ]
+ env:
+ - CC=aarch64-linux-gnu-gcc
+ - CXX=aarch64-linux-gnu-g++
+ tags: [ nosqlite, noboltdb ]
+ ldflags: -s -w -extldflags "-static" # We need to build a static binary because we are building in a glibc based system and running in a musl container
+
+ - id: windows-amd64
+ main: ./cmd/ast
+ binary: astranet
+ goos: [ windows ]
+ goarch: [ amd64 ]
+ env:
+ - CC=x86_64-w64-mingw32-gcc
+ - CXX=x86_64-w64-mingw32-g++
+ tags: [ nosqlite, noboltdb ]
+ ldflags: -s -w
+
+
+dockers:
+ - image_templates:
+ - astranet/{{ .ProjectName }}:{{ .Version }}-amd64
+ dockerfile: Dockerfile.release
+ use: buildx
+ skip_push: true
+ goarch: amd64
+ ids:
+ - linux-amd64
+ build_flag_templates:
+ - --platform=linux/amd64
+
+ - image_templates:
+ - astranet/{{ .ProjectName }}:{{ .Version }}-arm64
+ dockerfile: Dockerfile.release
+ skip_push: true
+ use: buildx
+ goarch: arm64
+ ids:
+ - linux-arm64
+ build_flag_templates:
+ - --platform=linux/arm64/v8
+
+docker_manifests:
+ - name_template: astranet/{{ .ProjectName }}:{{ .Version }}
+ skip_push: true
+ image_templates:
+ - astranet/{{ .ProjectName }}:{{ .Version }}-amd64
+ - astranet/{{ .ProjectName }}:{{ .Version }}-arm64
+
+ - name_template: astranet/{{ .ProjectName }}:latest
+ skip_push: true
+ image_templates:
+ - astranet/{{ .ProjectName }}:{{ .Version }}-amd64
+ - astranet/{{ .ProjectName }}:{{ .Version }}-arm64
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..16653ae
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,39 @@
+# Build
+FROM golang:1.19-alpine3.15 AS builder
+
+# RUN apk add --no-cache gcc musl-dev linux-headers git make
+RUN apk add --no-cache build-base linux-headers git bash ca-certificates libstdc++
+
+WORKDIR /ast
+ADD . .
+ENV GO111MODULE="on"
+RUN go mod tidy && go build -o ./build/bin/astranet ./cmd/ast
+
+
+FROM alpine:3.15
+#libstdc++
+RUN apk add --no-cache ca-certificates curl tzdata
+# copy compiled artifacts from builder
+COPY --from=builder /ast/build/bin/* /usr/local/bin/
+
+# Setup user and group
+#
+# from the perspective of the container, uid=1000, gid=1000 is a sensible choice, but if caller creates a .env
+# (example in repo root), these defaults will get overridden when make calls docker-compose
+ARG UID=1000
+ARG GID=1000
+RUN adduser -D -u $UID -g $GID ast
+
+
+ENV astDATA /home/ast/data
+# this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)
+RUN mkdir -p "$astDATA" && chown -R ast:ast "$astDATA" && chmod 777 "$astDATA"
+VOLUME /home/ast/data
+
+USER ast
+WORKDIR /home/ast
+
+RUN echo $UID
+
+EXPOSE 20012 20013 20014 61015/udp 61016 6060
+ENTRYPOINT ["astranet"]
diff --git a/Dockerfile.release b/Dockerfile.release
new file mode 100644
index 0000000..b17f3af
--- /dev/null
+++ b/Dockerfile.release
@@ -0,0 +1,21 @@
+FROM alpine:3.15
+
+RUN apk add --no-cache ca-certificates curl tzdata
+
+COPY astranet /usr/local/bin/
+
+ARG UID=1000
+ARG GID=1000
+RUN adduser -D -u $UID -g $GID ast
+
+
+ENV astDATA /home/ast/data
+# this 777 will be replaced by 700 at runtime (allows semi-arbitrary "--user" values)
+RUN mkdir -p "$astDATA" && chown -R ast:ast "$astDATA" && chmod 777 "$astDATA"
+VOLUME /home/ast/data
+
+USER ast
+WORKDIR /home/ast
+
+EXPOSE 20012 20013 20014 61015/udp 61016 6060
+ENTRYPOINT ["astranet"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0657503
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,147 @@
+BUILD_TIME := $(shell date +"%Y-%m-%d %H:%M:%S")
+#GIT_COMMIT := $(shell git show -s --pretty=format:%h)
+GO_VERSION := $(shell go version)
+BUILD_PATH := ./build/bin/
+APP_NAME := astranet
+APP_PATH := ./cmd/ast
+SHELL := /bin/bash
+GO = go
+#LDFLAGS := -ldflags "-w -s -X github.com/astranetworld/ast/version.BuildNumber=${GIT_COMMIT} -X 'github.com/astranetworld/ast/version.BuildTime=${BUILD_TIME}' -X 'github.com/astranetworld/ast/version.GoVersion=${GO_VERSION}'"
+
+
+# Variables below for building on host OS, and are ignored for docker
+#
+# Pipe error below to /dev/null since Makefile structure kind of expects
+# Go to be available, but with docker it's not strictly necessary
+CGO_CFLAGS := $(shell $(GO) env CGO_CFLAGS 2>/dev/null) # don't lose default
+CGO_CFLAGS += -DMDBX_FORCE_ASSERTIONS=0 # Enable MDBX's asserts by default in 'devel' branch and disable in releases
+#CGO_CFLAGS += -DMDBX_DISABLE_VALIDATION=1 # This feature is not ready yet
+#CGO_CFLAGS += -DMDBX_ENABLE_PROFGC=0 # Disabled by default, but may be useful for performance debugging
+#CGO_CFLAGS += -DMDBX_ENABLE_PGOP_STAT=0 # Disabled by default, but may be useful for performance debugging
+#CGO_CFLAGS += -DMDBX_ENV_CHECKPID=0 # Erigon doesn't do fork() syscall
+CGO_CFLAGS += -O
+CGO_CFLAGS += -D__BLST_PORTABLE__
+CGO_CFLAGS += -Wno-unknown-warning-option -Wno-enum-int-mismatch -Wno-strict-prototypes
+#CGO_CFLAGS += -Wno-error=strict-prototypes
+
+GIT_COMMIT ?= $(shell git rev-list -1 HEAD)
+GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
+GIT_TAG ?= $(shell git describe --tags '--match=v*' --dirty)
+PACKAGE = github.com/astranetworld/ast
+
+BUILD_TAGS = nosqlite,noboltdb
+GO_FLAGS += -trimpath -tags $(BUILD_TAGS) -buildvcs=false
+GO_FLAGS += -ldflags "-X ${PACKAGE}/params.GitCommit=${GIT_COMMIT} -X ${PACKAGE}/params.GitBranch=${GIT_BRANCH} -X ${PACKAGE}/params.GitTag=${GIT_TAG}"
+GOBUILD = CGO_CFLAGS="$(CGO_CFLAGS)" go build -v $(GO_FLAGS)
+
+
+# if using volume-mounting data dir, then must exist on host OS
+DOCKER_UID ?= $(shell id -u)
+DOCKER_GID ?= $(shell id -g)
+
+
+# == mobiles
+#OSFLAG=$(shell uname -sm)
+
+ANDROID_SDK=$(ANDROID_HOME)
+NDK_VERSION=25.2.9519653
+NDK_HOME=$(ANDROID_SDK)/ndk/$(NDK_VERSION)
+#ANDROID_SDK=/Users/mac/Library/Android/sdk
+MOBILE_GO_FLAGS = -ldflags "-X ${PACKAGE}/cmd/evmsdk/common.VERSION=${GIT_COMMIT}"
+MOBILE_PACKAGE= $(shell pwd)/cmd/evmsdk
+BUILD_MOBILE_PATH = ./build/mobile/
+
+
+# --build-arg UID=${DOCKER_UID} --build-arg GID=${DOCKER_GID}
+
+## go-version: print and verify go version
+go-version:
+ @if [ $(shell go version | cut -c 16-17) -lt 18 ]; then \
+ echo "minimum required Golang version is 1.18"; \
+ exit 1 ;\
+ fi
+gen:
+ @echo "Generate go code ..."
+ go generate ./...
+ @echo "Generate done!"
+deps: go-version
+ @echo "setup go deps..."
+ go mod tidy
+ @echo "deps done!"
+
+ast: deps
+ @echo "start build $(APP_NAME)..."
+ #go build -v ${LDFLAGS} -o $(BUILD_PATH)$(APP_NAME) ${APP_PATH}
+ $(GOBUILD) -o $(BUILD_PATH)$(APP_NAME) ${APP_PATH}
+ @echo "Compile done!"
+
+images:
+ @echo "docker images build ..."
+ DOCKER_BUILDKIT=1 docker build -t astranet/ast:local .
+ @echo "Compile done!"
+
+up:
+ @echo "docker compose up $(APP_NAME) ..."
+ docker-compose --project-name $(APP_NAME) up -d
+ docker-compose --project-name $(APP_NAME) logs -f
+down:
+ @echo "docker compose down $(APP_NAME) ..."
+ docker-compose --project-name $(APP_NAME) down
+ docker volume ls -q | grep 'astranet' | xargs -I % docker volume rm %
+ @echo "done!"
+stop:
+ @echo "docker compose stop $(APP_NAME) ..."
+ docker-compose --project-name $(APP_NAME) stop
+ @echo "done!"
+start:
+ @echo "docker compose stop $(APP_NAME) ..."
+ docker-compose --project-name $(APP_NAME) start
+ docker-compose --project-name $(APP_NAME) logs -f
+clean:
+ go clean
+ @rm -rf build
+
+devtools:
+ env GOBIN= go install github.com/fjl/gencodec@latest
+ env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest
+ env GOBIN= go install github.com/prysmaticlabs/fastssz/sszgen@latest
+ env GOBIN= go install github.com/prysmaticlabs/protoc-gen-go-cast@latest
+
+
+PACKAGE_NAME := github.com/WeAreAmaze/ast
+GOLANG_CROSS_VERSION ?= v1.20.7
+
+.PHONY: release
+release:
+ @docker run \
+ --rm \
+ --privileged \
+ -e CGO_ENABLED=1 \
+ -e GITHUB_TOKEN \
+ -e DOCKER_USERNAME \
+ -e DOCKER_PASSWORD \
+ -v /var/run/docker.sock:/var/run/docker.sock \
+ -v `pwd`:/go/src/$(PACKAGE_NAME) \
+ -w /go/src/$(PACKAGE_NAME) \
+ ghcr.io/goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \
+ --clean --skip-validate
+
+ @docker image push --all-tags astranet/ast
+
+
+#== mobiles start
+mobile: clean mobile-dir android ios
+
+mobile-dir:
+ #go get golang.org/x/mobile/bind/objc
+ mkdir -p $(BUILD_MOBILE_PATH)/android
+ios:
+ GOOS=ios CGO_ENABLED=1 GOARCH=arm64 gomobile bind ${MOBILE_GO_FLAGS} -o $(BUILD_MOBILE_PATH)/evmsdk.xcframework -target=ios/arm64 $(MOBILE_PACKAGE)
+android:
+ CGO_ENABLED=1 ANDROID_HOME=$(ANDROID_SDK) ANDROID_NDK_HOME=$(NDK_HOME) gomobile bind -x ${MOBILE_GO_FLAGS} -androidapi 23 -o $(BUILD_MOBILE_PATH)/android/evmsdk.aar -target=android/arm -v $(MOBILE_PACKAGE)
+# ANDROID_NDK_CC=$(NDK_HOME)/bin/arm-linux-androideabi-gcc NDK_CC=$(NDK_HOME)/bin/arm-linux-androideabi-gcc
+# GOARCH=arm GOOS=android CGO_ENABLED=1
+open-output:
+ open ./mobile
+
+#== mobiles end
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..50cf740
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+# astranet
+astranet(ast) is an implementation of public blockchain (execution client), on the efficiency frontier, written in Go.
+
+**Disclaimer: this software is currently a tech preview. We will do our best to keep it stable and make no breaking changes, but we don't guarantee anything. Things can and will break.**
+
+## System Requirements
+
+* For an Full node : >=200GB storage space.
+
+SSD or NVMe. Do not recommend HDD.
+
+RAM: >=16GB, 64-bit architecture, [Golang version >= 1.19](https://golang.org/doc/install)
+
+
+## Build from source code
+For building the latest alpha release (this will be suitable for most users just wanting to run a node):
+
+### Most Linux systems and macOS
+
+ast is written in Go, so building from source code requires the most recent version of Go to be installed.
+Instructions for installing Go are available at the [Go installation page](https://golang.org/doc/install) and necessary bundles can be downloaded from the [Go download page](https://golang.org/dl/).
+And the repository should be cloned to a local repository. Then, the command make ast configures everything for a temporary build and cleans up afterwards. This method of building only works on UNIX-like operating systems
+```sh
+git clone https://github.com/astranetworld/ast.git
+cd ast
+make ast
+./build/bin/ast
+```
+### Windows
+
+Windows users may run ast in 3 possible ways:
+
+* Build executable binaries natively for Windows using [Chocolatey package manager](https://chocolatey.org/)
+* Use Docker : see [docker-compose.yml](./docker-compose.yml)
+* Use WSL (Windows Subsystem for Linux) **strictly on version 2**. Under this option you can build ast just as you would on a regular Linux distribution. You can point your data also to any of the mounted Windows partitions (eg. `/mnt/c/[...]`, `/mnt/d/[...]` etc) but in such case be advised performance is impacted: this is due to the fact those mount points use `DrvFS` which is a [network file system](#blocks-execution-is-slow-on-cloud-network-drives) and, additionally, MDBX locks the db for exclusive access which implies only one process at a time can access data. This has consequences on the running of `rpcdaemon` which has to be configured as [Remote DB](#for-remote-db) even if it is executed on the very same computer. If instead your data is hosted on the native Linux filesystem non limitations apply. **Please also note the default WSL2 environment has its own IP address which does not match the one of the network interface of Windows host: take this into account when configuring NAT for port 30303 on your router.**
+
+
+### Docker container
+Docker allows for building and running ast via containers. This alleviates the need for installing build dependencies onto the host OS.
+see [docker-compose.yml](./docker-compose.yml) [dockerfile](./Dockerfile).
+For convenience we provide the following commands:
+```sh
+make images # build docker images than contain executable ast binaries
+make up # alias for docker-compose up -d && docker-compose logs -f
+make down # alias for docker-compose down && clean docker data
+make start # alias for docker-compose start && docker-compose logs -f
+make stop # alias for docker-compose stop
+```
+
+## Executables
+
+The astranet project comes with one wrappers/executables found in the `cmd`
+directory.
+
+| Command | Description |
+| :-----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **`astranet`** | Our main astranet CLI client. It can be used by other processes as a gateway into the astranet network via JSON RPC endpoints exposed on top of HTTP transports. `astranet --help` for command line options. |
+
+
+## ast ports
+
+| Port | Protocol | Purpose | Expose |
+|:-----:|:--------:|:------------------------:|:------------------:|
+| 61015 | UDP | The port used by discv5. | Public |
+| 61016 | TCP | The port used by libp2p. | Public |
+| 20012 | TCP | Json rpc/HTTP | Public |
+| 20013 | TCP | Json rpc/Websocket | Public |
+| 20014 | TCP | Json rpc/HTTP/Websocket | JWT Authentication |
+| 4000 | TCP | BlockChain Explorer | Public |
+| 6060 | TCP | Metrics | Private |
+| 6060 | TCP | Pprof | Private |
+
+## License
+The astranet library is licensed under the
+[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..1a7688c
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,37 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported |
+|---------|--------------------|
+| 5.1.x | :white_check_mark: |
+| 5.0.x | :x: |
+| 4.0.x | :white_check_mark: |
+| < 4.0 | :x: |
+
+## Audit reports
+
+Audit reports are published in the `docs` folder
+
+| Scope | Date | Report Link |
+|-------|------|-------------|
+
+## Reporting a Vulnerability
+
+**Please do not file a public ticket** mentioning the vulnerability.
+
+To find out how to disclose a vulnerability in astranet email support@astranet.com. Please read the [disclosure page] for more information about publicly disclosed security vulnerabilities.
+
+Use the built-in `ast version-check` feature to check whether the software is affected by any known vulnerability. This command will fetch the latest [`vulnerabilities.json`] file which contains known security vulnerabilities concerning `ast`, and cross-check the data against its own version number.
+
+The following key may be used to communicate sensitive information to developers.
+
+Fingerprint:
+
+```
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: SKS 1.1.6
+Comment: Hostname: pgp.mit.edu
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
new file mode 100644
index 0000000..1e15b51
--- /dev/null
+++ b/accounts/abi/abi.go
@@ -0,0 +1,254 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "io"
+)
+
+// The ABI holds information about a contract's context and available
+// invokable methods. It will allow you to type check function calls and
+// packs data accordingly.
+type ABI struct {
+ Constructor Method
+ Methods map[string]Method
+ Events map[string]Event
+ Errors map[string]Error
+
+ // Additional "special" functions introduced in solidity v0.6.0.
+ // It's separated from the original default fallback. Each contract
+ // can only define one fallback and receive function.
+ Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
+ Receive Method
+}
+
+// JSON returns a parsed ABI interface and error if it failed.
+func JSON(reader io.Reader) (ABI, error) {
+ dec := json.NewDecoder(reader)
+
+ var abi ABI
+ if err := dec.Decode(&abi); err != nil {
+ return ABI{}, err
+ }
+ return abi, nil
+}
+
+// Pack the given method name to conform the ABI. Method call's data
+// will consist of method_id, args0, arg1, ... argN. Method id consists
+// of 4 bytes and arguments are all 32 bytes.
+// Method ids are created from the first 4 bytes of the hash of the
+// methods string signature. (signature = baz(uint32,string32))
+func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
+ // Fetch the ABI of the requested method
+ if name == "" {
+ // constructor
+ arguments, err := abi.Constructor.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ return arguments, nil
+ }
+ method, exist := abi.Methods[name]
+ if !exist {
+ return nil, fmt.Errorf("method '%s' not found", name)
+ }
+ arguments, err := method.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ // Pack up the method ID too if not a constructor and return
+ return append(method.ID, arguments...), nil
+}
+
+func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ var args Arguments
+ if method, ok := abi.Methods[name]; ok {
+ if len(data)%32 != 0 {
+ return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
+ }
+ args = method.Outputs
+ }
+ if event, ok := abi.Events[name]; ok {
+ args = event.Inputs
+ }
+ if args == nil {
+ return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
+ }
+ return args, nil
+}
+
+// Unpack unpacks the output according to the abi specification.
+func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return nil, err
+ }
+ return args.Unpack(data)
+}
+
+// UnpackIntoInterface unpacks the output in v according to the abi specification.
+// It performs an additional copy. Please only use, if you want to unpack into a
+// structure that does not strictly conform to the abi structure (e.g. has additional arguments)
+func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return err
+ }
+ unpacked, err := args.Unpack(data)
+ if err != nil {
+ return err
+ }
+ return args.Copy(v, unpacked)
+}
+
+// UnpackIntoMap unpacks a log into the provided map[string]interface{}.
+func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return err
+ }
+ return args.UnpackIntoMap(v, data)
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (abi *ABI) UnmarshalJSON(data []byte) error {
+ var fields []struct {
+ Type string
+ Name string
+ Inputs []Argument
+ Outputs []Argument
+
+ // Status indicator which can be: "pure", "view",
+ // "nonpayable" or "payable".
+ StateMutability string
+
+ // Deprecated Status indicators, but removed in v0.6.0.
+ Constant bool // True if function is either pure or view
+ Payable bool // True if function is payable
+
+ // Event relevant indicator represents the event is
+ // declared as anonymous.
+ Anonymous bool
+ }
+ if err := json.Unmarshal(data, &fields); err != nil {
+ return err
+ }
+ abi.Methods = make(map[string]Method)
+ abi.Events = make(map[string]Event)
+ abi.Errors = make(map[string]Error)
+ for _, field := range fields {
+ switch field.Type {
+ case "constructor":
+ abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
+ case "function":
+ name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
+ abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
+ case "fallback":
+ // New introduced function type in v0.6.0, check more detail
+ // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
+ if abi.HasFallback() {
+ return errors.New("only single fallback is allowed")
+ }
+ abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
+ case "receive":
+ // New introduced function type in v0.6.0, check more detail
+ // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
+ if abi.HasReceive() {
+ return errors.New("only single receive is allowed")
+ }
+ if field.StateMutability != "payable" {
+ return errors.New("the statemutability of receive can only be payable")
+ }
+ abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
+ case "event":
+ name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
+ abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
+ case "error":
+ // Errors cannot be overloaded or overridden but are inherited,
+ // no need to resolve the name conflict here.
+ abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
+ default:
+ return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
+ }
+ }
+ return nil
+}
+
+// MethodById looks up a method by the 4-byte id,
+// returns nil if none found.
+func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
+ if len(sigdata) < 4 {
+ return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
+ }
+ for _, method := range abi.Methods {
+ if bytes.Equal(method.ID, sigdata[:4]) {
+ return &method, nil
+ }
+ }
+ return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
+}
+
+// EventByID looks an event up by its topic hash in the
+// ABI and returns nil if none found.
+func (abi *ABI) EventByID(topic types.Hash) (*Event, error) {
+ for _, event := range abi.Events {
+ if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
+ return &event, nil
+ }
+ }
+ return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
+}
+
+// HasFallback returns an indicator whether a fallback function is included.
+func (abi *ABI) HasFallback() bool {
+ return abi.Fallback.Type == Fallback
+}
+
+// HasReceive returns an indicator whether a receive function is included.
+func (abi *ABI) HasReceive() bool {
+ return abi.Receive.Type == Receive
+}
+
+// revertSelector is a special function selector for revert reason unpacking.
+var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
+
+// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
+// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
+// the provided revert reason is abi-encoded as if it were a call to a function
+// `Error(string)`. So it's a special tool for it.
+func UnpackRevert(data []byte) (string, error) {
+ if len(data) < 4 {
+ return "", errors.New("invalid data for unpacking")
+ }
+ if !bytes.Equal(data[:4], revertSelector) {
+ return "", errors.New("invalid data for unpacking")
+ }
+ typ, _ := NewType("string", "", nil)
+ unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
+ if err != nil {
+ return "", err
+ }
+ return unpacked[0].(string), nil
+}
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
new file mode 100644
index 0000000..a437e41
--- /dev/null
+++ b/accounts/abi/argument.go
@@ -0,0 +1,273 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Argument holds the name of the argument and the corresponding type.
+// Types are used when packing and testing arguments.
+type Argument struct {
+ Name string
+ Type Type
+ Indexed bool // indexed is only used by events
+}
+
+type Arguments []Argument
+
+type ArgumentMarshaling struct {
+ Name string
+ Type string
+ InternalType string
+ Components []ArgumentMarshaling
+ Indexed bool
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (argument *Argument) UnmarshalJSON(data []byte) error {
+ var arg ArgumentMarshaling
+ err := json.Unmarshal(data, &arg)
+ if err != nil {
+ return fmt.Errorf("argument json err: %v", err)
+ }
+
+ argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
+ if err != nil {
+ return err
+ }
+ argument.Name = arg.Name
+ argument.Indexed = arg.Indexed
+
+ return nil
+}
+
+// NonIndexed returns the arguments with indexed arguments filtered out.
+func (arguments Arguments) NonIndexed() Arguments {
+ var ret []Argument
+ for _, arg := range arguments {
+ if !arg.Indexed {
+ ret = append(ret, arg)
+ }
+ }
+ return ret
+}
+
+// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[].
+func (arguments Arguments) isTuple() bool {
+ return len(arguments) > 1
+}
+
+// Unpack performs the operation hexdata -> Go format.
+func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
+ if len(data) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
+ }
+ return make([]interface{}, 0), nil
+ }
+ return arguments.UnpackValues(data)
+}
+
+// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value.
+func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
+ // Make sure map is not nil
+ if v == nil {
+ return errors.New("abi: cannot unpack into a nil map")
+ }
+ if len(data) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
+ }
+ return nil // Nothing to unmarshal, return
+ }
+ marshalledValues, err := arguments.UnpackValues(data)
+ if err != nil {
+ return err
+ }
+ for i, arg := range arguments.NonIndexed() {
+ v[arg.Name] = marshalledValues[i]
+ }
+ return nil
+}
+
+// Copy performs the operation go format -> provided struct.
+func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
+ // make sure the passed value is arguments pointer
+ if reflect.Ptr != reflect.ValueOf(v).Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+ if len(values) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return errors.New("abi: attempting to copy no values while arguments are expected")
+ }
+ return nil // Nothing to copy, return
+ }
+ if arguments.isTuple() {
+ return arguments.copyTuple(v, values)
+ }
+ return arguments.copyAtomic(v, values[0])
+}
+
+// unpackAtomic unpacks ( hexdata -> go ) a single value
+func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
+ dst := reflect.ValueOf(v).Elem()
+ src := reflect.ValueOf(marshalledValues)
+
+ if dst.Kind() == reflect.Struct {
+ return set(dst.Field(0), src)
+ }
+ return set(dst, src)
+}
+
+// copyTuple copies a batch of values from marshalledValues to v.
+func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error {
+ value := reflect.ValueOf(v).Elem()
+ nonIndexedArgs := arguments.NonIndexed()
+
+ switch value.Kind() {
+ case reflect.Struct:
+ argNames := make([]string, len(nonIndexedArgs))
+ for i, arg := range nonIndexedArgs {
+ argNames[i] = arg.Name
+ }
+ var err error
+ abi2struct, err := mapArgNamesToStructFields(argNames, value)
+ if err != nil {
+ return err
+ }
+ for i, arg := range nonIndexedArgs {
+ field := value.FieldByName(abi2struct[arg.Name])
+ if !field.IsValid() {
+ return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
+ }
+ if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
+ return err
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ if value.Len() < len(marshalledValues) {
+ return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
+ }
+ for i := range nonIndexedArgs {
+ if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
+ return err
+ }
+ }
+ default:
+ return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
+ }
+ return nil
+}
+
+// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
+// without supplying a struct to unpack into. Instead, this method returns a list containing the
+// values. An atomic argument will be a list with one element.
+func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
+ nonIndexedArgs := arguments.NonIndexed()
+ retval := make([]interface{}, 0, len(nonIndexedArgs))
+ virtualArgs := 0
+ for index, arg := range nonIndexedArgs {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
+ if err != nil {
+ return nil, err
+ }
+ if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ } else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ }
+ retval = append(retval, marshalledValue)
+ }
+ return retval, nil
+}
+
+// PackValues performs the operation Go format -> Hexdata.
+// It is the semantic opposite of UnpackValues.
+func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
+ return arguments.Pack(args...)
+}
+
+// Pack performs the operation Go format -> Hexdata.
+func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
+ // Make sure arguments match up and pack them
+ abiArgs := arguments
+ if len(args) != len(abiArgs) {
+ return nil, fmt.Errorf("argument count mismatch: got %d for %d", len(args), len(abiArgs))
+ }
+ // variable input is the output appended at the end of packed
+ // output. This is used for strings and bytes types input.
+ var variableInput []byte
+
+ // input offset is the bytes offset for packed output
+ inputOffset := 0
+ for _, abiArg := range abiArgs {
+ inputOffset += getTypeSize(abiArg.Type)
+ }
+ var ret []byte
+ for i, a := range args {
+ input := abiArgs[i]
+ // pack the input
+ packed, err := input.Type.pack(reflect.ValueOf(a))
+ if err != nil {
+ return nil, err
+ }
+ // check for dynamic types
+ if isDynamicType(input.Type) {
+ // set the offset
+ ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
+ // calculate next offset
+ inputOffset += len(packed)
+ // append to variable input
+ variableInput = append(variableInput, packed...)
+ } else {
+ // append the packed value to the input
+ ret = append(ret, packed...)
+ }
+ }
+ // append the variable input at the end of the packed input
+ ret = append(ret, variableInput...)
+
+ return ret, nil
+}
+
+// ToCamelCase converts an under-score string to a camel-case string
+func ToCamelCase(input string) string {
+ parts := strings.Split(input, "_")
+ for i, s := range parts {
+ if len(s) > 0 {
+ parts[i] = strings.ToUpper(s[:1]) + s[1:]
+ }
+ }
+ return strings.Join(parts, "")
+}
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
new file mode 100644
index 0000000..475d2fe
--- /dev/null
+++ b/accounts/abi/bind/auth.go
@@ -0,0 +1,162 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "errors"
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/accounts/keystore"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "io"
+ "math/big"
+)
+
+// ErrNoChainID is returned whenever the user failed to specify a chain id.
+var ErrNoChainID = errors.New("no chain id specified")
+
+// ErrNotAuthorized is returned when an account is not properly unlocked.
+var ErrNotAuthorized = errors.New("not authorized to sign this account")
+
+// NewTransactor is a utility method to easily create a transaction signer from
+// an encrypted json key stream and the associated passphrase.
+//
+// Deprecated: Use NewTransactorWithChainID instead.
+func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
+ log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
+ json, err := io.ReadAll(keyin)
+ if err != nil {
+ return nil, err
+ }
+ key, err := keystore.DecryptKey(json, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return NewKeyedTransactor(key.PrivateKey), nil
+}
+
+// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
+// an decrypted key from a keystore.
+//
+// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
+func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
+ log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
+ signer := transaction.HomesteadSigner{}
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(address types.Address, tx *transaction.Transaction) (*transaction.Transaction, error) {
+ if address != account.Address {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
+
+// NewKeyedTransactor is a utility method to easily create a transaction signer
+// from a single private key.
+//
+// Deprecated: Use NewKeyedTransactorWithChainID instead.
+func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
+ log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
+ keyAddr := crypto.PubkeyToAddress(key.PublicKey)
+ signer := transaction.HomesteadSigner{}
+ return &TransactOpts{
+ From: keyAddr,
+ Signer: func(address types.Address, tx *transaction.Transaction) (*transaction.Transaction, error) {
+ if address != keyAddr {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }
+}
+
+// NewTransactorWithChainID is a utility method to easily create a transaction signer from
+// an encrypted json key stream and the associated passphrase.
+func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
+ json, err := io.ReadAll(keyin)
+ if err != nil {
+ return nil, err
+ }
+ key, err := keystore.DecryptKey(json, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
+}
+
+// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from
+// an decrypted key from a keystore.
+func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) {
+ if chainID == nil {
+ return nil, ErrNoChainID
+ }
+ signer := transaction.LatestSignerForChainID(chainID)
+ return &TransactOpts{
+ From: account.Address,
+ Signer: func(address types.Address, tx *transaction.Transaction) (*transaction.Transaction, error) {
+ if address != account.Address {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
+
+// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer
+// from a single private key.
+func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
+ keyAddr := crypto.PubkeyToAddress(key.PublicKey)
+ if chainID == nil {
+ return nil, ErrNoChainID
+ }
+ signer := transaction.LatestSignerForChainID(chainID)
+ return &TransactOpts{
+ From: keyAddr,
+ Signer: func(address types.Address, tx *transaction.Transaction) (*transaction.Transaction, error) {
+ if address != keyAddr {
+ return nil, ErrNotAuthorized
+ }
+ signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(signer, signature)
+ },
+ Context: context.Background(),
+ }, nil
+}
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
new file mode 100644
index 0000000..285a60d
--- /dev/null
+++ b/accounts/abi/bind/backend.go
@@ -0,0 +1,127 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ astranet "github.com/astranetworld/ast"
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+ "math/big"
+)
+
+var (
+ // ErrNoCode is returned by call and transact operations for which the requested
+ // recipient contract to operate on does not exist in the state db or does not
+ // have any code associated with it (i.e. suicided).
+ ErrNoCode = errors.New("no contract code at given address")
+
+ // ErrNoPendingState is raised when attempting to perform a pending state action
+ // on a backend that doesn't implement PendingContractCaller.
+ ErrNoPendingState = errors.New("backend does not support pending state")
+
+ // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
+ // an empty contract behind.
+ ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
+)
+
+// ContractCaller defines the methods needed to allow operating with a contract on a read
+// only basis.
+type ContractCaller interface {
+ // CodeAt returns the code of the given account. This is needed to differentiate
+ // between contract internal errors and the local chain being out of sync.
+ CodeAt(ctx context.Context, contract types.Address, blockNumber *uint256.Int) ([]byte, error)
+
+ // CallContract executes an Ethereum contract call with the specified data as the
+ // input.
+ CallContract(ctx context.Context, call astranet.CallMsg, blockNumber *uint256.Int) ([]byte, error)
+}
+
+// PendingContractCaller defines methods to perform contract calls on the pending state.
+// Call will try to discover this interface when access to the pending state is requested.
+// If the backend does not support the pending state, Call returns ErrNoPendingState.
+type PendingContractCaller interface {
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, contract types.Address) ([]byte, error)
+
+ // PendingCallContract executes an Ethereum contract call against the pending state.
+ PendingCallContract(ctx context.Context, call astranet.CallMsg) ([]byte, error)
+}
+
+// ContractTransactor defines the methods needed to allow operating with a contract
+// on a write only basis. Besides the transacting method, the remainder are helpers
+// used when the user does not provide some needed values, but rather leaves it up
+// to the transactor to decide.
+type ContractTransactor interface {
+ // HeaderByNumber returns a block header from the current canonical chain. If
+ // number is nil, the latest known header is returned.
+ HeaderByNumber(ctx context.Context, number *uint256.Int) (*block.Header, error)
+
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, account types.Address) ([]byte, error)
+
+ // PendingNonceAt retrieves the current pending nonce associated with an account.
+ PendingNonceAt(ctx context.Context, account types.Address) (uint64, error)
+
+ // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+ // execution of a transaction.
+ SuggestGasPrice(ctx context.Context) (*uint256.Int, error)
+
+ // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
+ // a timely execution of a transaction.
+ SuggestGasTipCap(ctx context.Context) (*uint256.Int, error)
+
+ // EstimateGas tries to estimate the gas needed to execute a specific
+ // transaction based on the current pending state of the backend blockchain.
+ // There is no guarantee that this is the true gas limit requirement as other
+ // transactions may be added or removed by miners, but it should provide a basis
+ // for setting a reasonable default.
+ EstimateGas(ctx context.Context, call astranet.CallMsg) (gas uint64, err error)
+
+ // SendTransaction injects the transaction into the pending pool for execution.
+ SendTransaction(ctx context.Context, tx *transaction.Transaction) error
+}
+
+// ContractFilterer defines the methods needed to access log events using one-off
+// queries or continuous event subscriptions.
+type ContractFilterer interface {
+ // FilterLogs executes a log filter operation, blocking during execution and
+ // returning all the results in one batch.
+ //
+ // TODO(karalabe): Deprecate when the subscription one can return past data too.
+ FilterLogs(ctx context.Context, query astranet.FilterQuery) ([]block.Log, error)
+
+ // SubscribeFilterLogs creates a background log filtering operation, returning
+ // a subscription immediately, which can be used to stream the found events.
+ SubscribeFilterLogs(ctx context.Context, query astranet.FilterQuery, ch chan<- block.Log) (astranet.Subscription, error)
+}
+
+// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
+type DeployBackend interface {
+ TransactionReceipt(ctx context.Context, txHash types.Hash) (*block.Receipt, error)
+ CodeAt(ctx context.Context, account types.Address, blockNumber *big.Int) ([]byte, error)
+}
+
+// ContractBackend defines the methods needed to work with contracts on a read-write basis.
+type ContractBackend interface {
+ ContractCaller
+ ContractTransactor
+ ContractFilterer
+}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
new file mode 100644
index 0000000..467b81f
--- /dev/null
+++ b/accounts/abi/bind/base.go
@@ -0,0 +1,532 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ astranet "github.com/astranetworld/ast"
+ "github.com/astranetworld/ast/accounts/abi"
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ event "github.com/astranetworld/ast/modules/event/v2"
+ "github.com/holiman/uint256"
+ "strings"
+ "sync"
+)
+
+const basefeeWiggleMultiplier = 2
+
+// SignerFn is a signer function callback when a contract requires a method to
+// sign the transaction before submission.
+type SignerFn func(types.Address, *transaction.Transaction) (*transaction.Transaction, error)
+
+// CallOpts is the collection of options to fine tune a contract call request.
+type CallOpts struct {
+ Pending bool // Whether to operate on the pending state or the last known one
+ From types.Address // Optional the sender address, otherwise the first account is used
+ BlockNumber *uint256.Int // Optional the block number on which the call should be performed
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// TransactOpts is the collection of authorization data required to create a
+// valid Ethereum transaction.
+type TransactOpts struct {
+ From types.Address // Ethereum account to send the transaction from
+ Nonce *uint64 // Nonce to use for the transaction execution (nil = use pending state)
+ Signer SignerFn // Method to use for signing the transaction (mandatory)
+
+ Value *uint256.Int // Funds to transfer along the transaction (nil = 0 = no funds)
+ GasPrice *uint256.Int // Gas price to use for the transaction execution (nil = gas price oracle)
+ GasFeeCap *uint256.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasTipCap *uint256.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+
+ NoSend bool // Do all transact steps but do not send the transaction
+}
+
+// FilterOpts is the collection of options to fine tune filtering for events
+// within a bound contract.
+type FilterOpts struct {
+ Start uint64 // Start of the queried range
+ End *uint64 // End of the range (nil = latest)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// WatchOpts is the collection of options to fine tune subscribing for events
+// within a bound contract.
+type WatchOpts struct {
+ Start *uint64 // Start of the queried range (nil = latest)
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// MetaData collects all metadata for a bound contract.
+type MetaData struct {
+ mu sync.Mutex
+ Sigs map[string]string
+ Bin string
+ ABI string
+ ab *abi.ABI
+}
+
+func (m *MetaData) GetAbi() (*abi.ABI, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.ab != nil {
+ return m.ab, nil
+ }
+ if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
+ return nil, err
+ } else {
+ m.ab = &parsed
+ }
+ return m.ab, nil
+}
+
+// BoundContract is the base wrapper object that reflects a contract on the
+// Ethereum network. It contains a collection of methods that are used by the
+// higher level contract bindings to operate.
+type BoundContract struct {
+ address types.Address // Deployment address of the contract on the Ethereum blockchain
+ abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
+ caller ContractCaller // Read interface to interact with the blockchain
+ transactor ContractTransactor // Write interface to interact with the blockchain
+ filterer ContractFilterer // Event filtering to interact with the blockchain
+}
+
+// NewBoundContract creates a low level contract interface through which calls
+// and transactions may be made through.
+func NewBoundContract(address types.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
+ return &BoundContract{
+ address: address,
+ abi: abi,
+ caller: caller,
+ transactor: transactor,
+ filterer: filterer,
+ }
+}
+
+// DeployContract deploys a contract onto the Ethereum blockchain and binds the
+// deployment address with a Go wrapper.
+func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (types.Address, *transaction.Transaction, *BoundContract, error) {
+ // Otherwise try to deploy the contract
+ c := NewBoundContract(types.Address{}, abi, backend, backend, backend)
+
+ input, err := c.abi.Pack("", params...)
+ if err != nil {
+ return types.Address{}, nil, nil, err
+ }
+ tx, err := c.transact(opts, nil, append(bytecode, input...))
+ if err != nil {
+ return types.Address{}, nil, nil, err
+ }
+ c.address = crypto.CreateAddress(opts.From, tx.Nonce())
+ return c.address, tx, c, nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(CallOpts)
+ }
+ if results == nil {
+ results = new([]interface{})
+ }
+ // Pack the input, call and unpack the results
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return err
+ }
+ var (
+ msg = astranet.CallMsg{From: opts.From, To: &c.address, Data: input}
+ ctx = ensureContext(opts.Context)
+ code []byte
+ output []byte
+ )
+ if opts.Pending {
+ pb, ok := c.caller.(PendingContractCaller)
+ if !ok {
+ return ErrNoPendingState
+ }
+ output, err = pb.PendingCallContract(ctx, msg)
+ if err != nil {
+ return err
+ }
+ if len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ } else {
+ output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
+ if err != nil {
+ return err
+ }
+ if len(output) == 0 {
+ // Make sure we have a contract to operate on, and bail out otherwise.
+ if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
+ return err
+ } else if len(code) == 0 {
+ return ErrNoCode
+ }
+ }
+ }
+
+ if len(*results) == 0 {
+ res, err := c.abi.Unpack(method, output)
+ *results = res
+ return err
+ }
+ res := *results
+ return c.abi.UnpackIntoInterface(res[0], method, output)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*transaction.Transaction, error) {
+ // Otherwise pack up the parameters and invoke the contract
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return nil, err
+ }
+ // todo(rjl493456442) check the method is payable or not,
+ // reject invalid transaction at the first place
+ return c.transact(opts, &c.address, input)
+}
+
+// RawTransact initiates a transaction with the given raw calldata as the input.
+// It's usually used to initiate transactions for invoking **Fallback** function.
+func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*transaction.Transaction, error) {
+ // todo(rjl493456442) check the method is payable or not,
+ // reject invalid transaction at the first place
+ return c.transact(opts, &c.address, calldata)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (c *BoundContract) Transfer(opts *TransactOpts) (*transaction.Transaction, error) {
+ // todo(rjl493456442) check the payable fallback or receive is defined
+ // or not, reject invalid transaction at the first place
+ return c.transact(opts, &c.address, nil)
+}
+
+func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *types.Address, input []byte, head *block.Header) (*transaction.Transaction, error) {
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = uint256.NewInt(0)
+ }
+ // Estimate TipCap
+ gasTipCap := opts.GasTipCap
+ if gasTipCap == nil {
+ tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
+ if err != nil {
+ return nil, err
+ }
+ gasTipCap = tip
+ }
+ // Estimate FeeCap
+ gasFeeCap := opts.GasFeeCap
+ if gasFeeCap == nil {
+ gasFeeCap = gasTipCap.Add(gasTipCap, head.BaseFee.Mod(head.BaseFee, uint256.NewInt(basefeeWiggleMultiplier)))
+ }
+ if gasFeeCap.Cmp(gasTipCap) < 0 {
+ return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, uint256.NewInt(0), gasTipCap, gasFeeCap, value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
+ }
+ baseTx := &transaction.DynamicFeeTx{
+ To: contract,
+ Nonce: nonce,
+ GasFeeCap: gasFeeCap,
+ GasTipCap: gasTipCap,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return transaction.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *types.Address, input []byte) (*transaction.Transaction, error) {
+ if opts.GasFeeCap != nil || opts.GasTipCap != nil {
+ return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
+ }
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = uint256.NewInt(0)
+ }
+ // Estimate GasPrice
+ gasPrice := opts.GasPrice
+ if gasPrice == nil {
+ price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
+ if err != nil {
+ return nil, err
+ }
+ gasPrice = price
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, uint256.NewInt(0), uint256.NewInt(0), value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
+ }
+ baseTx := &transaction.LegacyTx{
+ To: contract,
+ Nonce: nonce,
+ GasPrice: gasPrice,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return transaction.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *types.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *uint256.Int) (uint64, error) {
+ if contract != nil {
+ // Gas estimation cannot succeed without code for method invocations.
+ if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
+ return 0, err
+ } else if len(code) == 0 {
+ return 0, ErrNoCode
+ }
+ }
+ msg := astranet.CallMsg{
+ From: opts.From,
+ To: contract,
+ GasPrice: gasPrice,
+ GasTipCap: gasTipCap,
+ GasFeeCap: gasFeeCap,
+ Value: value,
+ Data: input,
+ }
+ return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+}
+
+func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
+ if opts.Nonce == nil {
+ return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+ } else {
+ return *opts.Nonce, nil
+ }
+}
+
+// transact executes an actual transaction invocation, first deriving any missing
+// authorization fields, and then scheduling the transaction for execution.
+func (c *BoundContract) transact(opts *TransactOpts, contract *types.Address, input []byte) (*transaction.Transaction, error) {
+ if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
+ return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ }
+ // Create the transaction
+ var (
+ rawTx *transaction.Transaction
+ err error
+ )
+ if opts.GasPrice != nil {
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ } else if opts.GasFeeCap != nil && opts.GasTipCap != nil {
+ rawTx, err = c.createDynamicTx(opts, contract, input, nil)
+ } else {
+
+ // Only query for basefee if gasPrice not specified
+ if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), uint256.NewInt(0) /* latest */); errHead != nil {
+ return nil, errHead
+ } else if head.BaseFee != nil {
+ rawTx, err = c.createDynamicTx(opts, contract, input, head)
+ } else {
+ // Chain is not London ready -> use legacy transaction
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ // Sign the transaction and schedule it for execution
+ if opts.Signer == nil {
+ return nil, errors.New("no signer to authorize the transaction with")
+ }
+ signedTx, err := opts.Signer(opts.From, rawTx)
+ if err != nil {
+ return nil, err
+ }
+ if opts.NoSend {
+ return signedTx, nil
+ }
+ if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
+ return nil, err
+ }
+ return signedTx, nil
+}
+
+// FilterLogs filters contract logs for past blocks, returning the necessary
+// channels to construct a strongly typed bound iterator on top of them.
+func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan block.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(FilterOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+ topics, err := abi.MakeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan block.Log, 128)
+
+ config := astranet.FilterQuery{
+ Addresses: []types.Address{c.address},
+ Topics: topics,
+ FromBlock: uint256.NewInt(opts.Start),
+ }
+ if opts.End != nil {
+ config.ToBlock = uint256.NewInt(*opts.End)
+ }
+ /* TODO(karalabe): Replace the rest of the method below with this when supported
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ */
+ buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
+ if err != nil {
+ return nil, nil, err
+ }
+ sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
+ for _, log := range buff {
+ select {
+ case logs <- log:
+ case <-quit:
+ return nil
+ }
+ }
+ return nil
+ }), nil
+
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// WatchLogs filters subscribes to contract logs for future blocks, returning a
+// subscription object that can be used to tear down the watcher.
+func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan block.Log, event.Subscription, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(WatchOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+ topics, err := abi.MakeTopics(query...)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Start the background filtering
+ logs := make(chan block.Log, 128)
+
+ config := astranet.FilterQuery{
+ Addresses: []types.Address{c.address},
+ Topics: topics,
+ }
+ if opts.Start != nil {
+ config.FromBlock = uint256.NewInt(*opts.Start)
+ }
+ sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+ if err != nil {
+ return nil, nil, err
+ }
+ return logs, sub, nil
+}
+
+// UnpackLog unpacks a retrieved log into the provided output structure.
+func (c *BoundContract) UnpackLog(out interface{}, event string, log block.Log) error {
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return fmt.Errorf("event signature mismatch")
+ }
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return abi.ParseTopics(out, indexed, log.Topics[1:])
+}
+
+// UnpackLogIntoMap unpacks a retrieved log into the provided map.
+func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log block.Log) error {
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return fmt.Errorf("event signature mismatch")
+ }
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
+}
+
+// ensureContext is a helper method to ensure a context is not nil, even if the
+// user specified it as such.
+func ensureContext(ctx context.Context) context.Context {
+ if ctx == nil {
+ return context.Background()
+ }
+ return ctx
+}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
new file mode 100644
index 0000000..5228c54
--- /dev/null
+++ b/accounts/abi/bind/bind.go
@@ -0,0 +1,648 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package bind generates Ethereum contract Go bindings.
+//
+// Detailed usage document and tutorial available on the go-ethereum Wiki page:
+// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
+package bind
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/accounts/abi"
+ "github.com/astranetworld/ast/log"
+ "go/format"
+ "regexp"
+ "strings"
+ "text/template"
+ "unicode"
+)
+
+// Lang is a target programming language selector to generate bindings for.
+type Lang int
+
+const (
+ LangGo Lang = iota
+ LangJava
+ LangObjC
+)
+
+func isKeyWord(arg string) bool {
+ switch arg {
+ case "break":
+ case "case":
+ case "chan":
+ case "const":
+ case "continue":
+ case "default":
+ case "defer":
+ case "else":
+ case "fallthrough":
+ case "for":
+ case "func":
+ case "go":
+ case "goto":
+ case "if":
+ case "import":
+ case "interface":
+ case "iota":
+ case "map":
+ case "make":
+ case "new":
+ case "package":
+ case "range":
+ case "return":
+ case "select":
+ case "struct":
+ case "switch":
+ case "type":
+ case "var":
+ default:
+ return false
+ }
+
+ return true
+}
+
+// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
+// to be used as is in client code, but rather as an intermediate struct which
+// enforces compile time type safety and naming convention opposed to having to
+// manually maintain hard coded strings that break on runtime.
+func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
+ var (
+ // contracts is the map of each individual contract requested binding
+ contracts = make(map[string]*tmplContract)
+
+ // structs is the map of all redeclared structs shared by passed contracts.
+ structs = make(map[string]*tmplStruct)
+
+ // isLib is the map used to flag each encountered library as such
+ isLib = make(map[string]struct{})
+ )
+ for i := 0; i < len(types); i++ {
+ // Parse the actual ABI to generate the binding for
+ evmABI, err := abi.JSON(strings.NewReader(abis[i]))
+ if err != nil {
+ return "", err
+ }
+ // Strip any whitespace from the JSON ABI
+ strippedABI := strings.Map(func(r rune) rune {
+ if unicode.IsSpace(r) {
+ return -1
+ }
+ return r
+ }, abis[i])
+
+ // Extract the call and transact methods; events, struct definitions; and sort them alphabetically
+ var (
+ calls = make(map[string]*tmplMethod)
+ transacts = make(map[string]*tmplMethod)
+ events = make(map[string]*tmplEvent)
+ fallback *tmplMethod
+ receive *tmplMethod
+
+ // identifiers are used to detect duplicated identifiers of functions
+ // and events. For all calls, transacts and events, abigen will generate
+ // corresponding bindings. However we have to ensure there is no
+ // identifier collisions in the bindings of these categories.
+ callIdentifiers = make(map[string]bool)
+ transactIdentifiers = make(map[string]bool)
+ eventIdentifiers = make(map[string]bool)
+ )
+
+ for _, input := range evmABI.Constructor.Inputs {
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+
+ for _, original := range evmABI.Methods {
+ // Normalize the method for capital cases and non-anonymous inputs/outputs
+ normalized := original
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+
+ // Ensure there is no duplicated identifier
+ var identifiers = callIdentifiers
+ if !original.IsConstant() {
+ identifiers = transactIdentifiers
+ }
+ if identifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ identifiers[normalizedName] = true
+
+ normalized.Name = normalizedName
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" || isKeyWord(input.Name) {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ normalized.Outputs = make([]abi.Argument, len(original.Outputs))
+ copy(normalized.Outputs, original.Outputs)
+ for j, output := range normalized.Outputs {
+ if output.Name != "" {
+ normalized.Outputs[j].Name = capitalise(output.Name)
+ }
+ if hasStruct(output.Type) {
+ bindStructType[lang](output.Type, structs)
+ }
+ }
+ // Append the methods to the call or transact lists
+ if original.IsConstant() {
+ calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ } else {
+ transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ }
+ }
+ for _, original := range evmABI.Events {
+ // Skip anonymous events as they don't support explicit filtering
+ if original.Anonymous {
+ continue
+ }
+ // Normalize the event for capital cases and non-anonymous outputs
+ normalized := original
+
+ // Ensure there is no duplicated identifier
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+ if eventIdentifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ eventIdentifiers[normalizedName] = true
+ normalized.Name = normalizedName
+
+ used := make(map[string]bool)
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" || isKeyWord(input.Name) {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ // Event is a bit special, we need to define event struct in binding,
+ // ensure there is no camel-case-style name conflict.
+ for index := 0; ; index++ {
+ if !used[capitalise(normalized.Inputs[j].Name)] {
+ used[capitalise(normalized.Inputs[j].Name)] = true
+ break
+ }
+ normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ // Append the event to the accumulator list
+ events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
+ }
+ // Add two special fallback functions if they exist
+ if evmABI.HasFallback() {
+ fallback = &tmplMethod{Original: evmABI.Fallback}
+ }
+ if evmABI.HasReceive() {
+ receive = &tmplMethod{Original: evmABI.Receive}
+ }
+ // There is no easy way to pass arbitrary java objects to the Go side.
+ if len(structs) > 0 && lang == LangJava {
+ return "", errors.New("java binding for tuple arguments is not supported yet")
+ }
+
+ contracts[types[i]] = &tmplContract{
+ Type: capitalise(types[i]),
+ InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
+ InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
+ Constructor: evmABI.Constructor,
+ Calls: calls,
+ Transacts: transacts,
+ Fallback: fallback,
+ Receive: receive,
+ Events: events,
+ Libraries: make(map[string]string),
+ }
+ // Function 4-byte signatures are stored in the same sequence
+ // as types, if available.
+ if len(fsigs) > i {
+ contracts[types[i]].FuncSigs = fsigs[i]
+ }
+ // Parse library references.
+ for pattern, name := range libs {
+ matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
+ if err != nil {
+ log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
+ }
+ if matched {
+ contracts[types[i]].Libraries[pattern] = name
+ // keep track that this type is a library
+ if _, ok := isLib[name]; !ok {
+ isLib[name] = struct{}{}
+ }
+ }
+ }
+ }
+ // Check if that type has already been identified as a library
+ for i := 0; i < len(types); i++ {
+ _, ok := isLib[types[i]]
+ contracts[types[i]].Library = ok
+ }
+ // Generate the contract template data content and render it
+ data := &tmplData{
+ Package: pkg,
+ Contracts: contracts,
+ Libraries: libs,
+ Structs: structs,
+ }
+ buffer := new(bytes.Buffer)
+
+ funcs := map[string]interface{}{
+ "bindtype": bindType[lang],
+ "bindtopictype": bindTopicType[lang],
+ "namedtype": namedType[lang],
+ "capitalise": capitalise,
+ "decapitalise": decapitalise,
+ }
+ tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
+ if err := tmpl.Execute(buffer, data); err != nil {
+ return "", err
+ }
+ // For Go bindings pass the code through gofmt to clean it up
+ if lang == LangGo {
+ code, err := format.Source(buffer.Bytes())
+ if err != nil {
+ return "", fmt.Errorf("%v\n%s", err, buffer)
+ }
+ return string(code), nil
+ }
+ // For all others just return as is for now
+ return buffer.String(), nil
+}
+
+// bindType is a set of type binders that convert Solidity types to some supported
+// programming language types.
+var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTypeGo,
+ LangJava: bindTypeJava,
+}
+
+// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
+func bindBasicTypeGo(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "common.Address"
+ case abi.IntTy, abi.UintTy:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ return fmt.Sprintf("%sint%s", parts[1], parts[2])
+ }
+ return "*big.Int"
+ case abi.FixedBytesTy:
+ return fmt.Sprintf("[%d]byte", kind.Size)
+ case abi.BytesTy:
+ return "[]byte"
+ case abi.FunctionTy:
+ return "[24]byte"
+ default:
+ // string, bool types
+ return kind.String()
+ }
+}
+
+// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
+// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.TupleRawName+kind.String()].Name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones.
+func bindBasicTypeJava(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "Address"
+ case abi.IntTy, abi.UintTy:
+ // Note that uint and int (without digits) are also matched,
+ // these are size 256, and will translate to BigInt (the default).
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ if len(parts) != 3 {
+ return kind.String()
+ }
+ // All unsigned integers should be translated to BigInt since gomobile doesn't
+ // support them.
+ if parts[1] == "u" {
+ return "BigInt"
+ }
+
+ namedSize := map[string]string{
+ "8": "byte",
+ "16": "short",
+ "32": "int",
+ "64": "long",
+ }[parts[2]]
+
+ // default to BigInt
+ if namedSize == "" {
+ namedSize = "BigInt"
+ }
+ return namedSize
+ case abi.FixedBytesTy, abi.BytesTy:
+ return "byte[]"
+ case abi.BoolTy:
+ return "boolean"
+ case abi.StringTy:
+ return "String"
+ case abi.FunctionTy:
+ return "byte[24]"
+ default:
+ return kind.String()
+ }
+}
+
+// pluralizeJavaType explicitly converts multidimensional types to predefined
+// types in go side.
+func pluralizeJavaType(typ string) string {
+ switch typ {
+ case "boolean":
+ return "Bools"
+ case "String":
+ return "Strings"
+ case "Address":
+ return "Addresses"
+ case "byte[]":
+ return "Binaries"
+ case "BigInt":
+ return "BigInts"
+ }
+ return typ + "[]"
+}
+
+// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
+// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.TupleRawName+kind.String()].Name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// bindTopicType is a set of type binders that convert Solidity types to some
+// supported programming language topic types.
+var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTopicTypeGo,
+ LangJava: bindTopicTypeJava,
+}
+
+// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
+// functionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeGo(kind, structs)
+
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert stringS and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+ if bound == "string" || bound == "[]byte" {
+ bound = "common.Hash"
+ }
+ return bound
+}
+
+// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
+// functionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeJava(kind, structs)
+
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert strings and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+ if bound == "String" || bound == "byte[]" {
+ bound = "Hash"
+ }
+ return bound
+}
+
+// bindStructType is a set of type binders that convert Solidity tuple types to some supported
+// programming language struct definition.
+var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindStructTypeGo,
+ LangJava: bindStructTypeJava,
+}
+
+// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ // We compose a raw struct name and a canonical parameter expression
+ // together here. The reason is before solidity v0.5.11, kind.TupleRawName
+ // is empty, so we use canonical parameter expression to distinguish
+ // different struct definition. From the consideration of backward
+ // compatibility, we concat these two together so that if kind.TupleRawName
+ // is not empty, it can have unique id.
+ id := kind.TupleRawName + kind.String()
+ if s, exist := structs[id]; exist {
+ return s.Name
+ }
+ var (
+ names = make(map[string]bool)
+ fields []*tmplField
+ )
+ for i, elem := range kind.TupleElems {
+ name := capitalise(kind.TupleRawNames[i])
+ name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
+ names[name] = true
+ fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
+ }
+ name := kind.TupleRawName
+ if name == "" {
+ name = fmt.Sprintf("Struct%d", len(structs))
+ }
+ name = capitalise(name)
+
+ structs[id] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindStructTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ // We compose a raw struct name and a canonical parameter expression
+ // together here. The reason is before solidity v0.5.11, kind.TupleRawName
+ // is empty, so we use canonical parameter expression to distinguish
+ // different struct definition. From the consideration of backward
+ // compatibility, we concat these two together so that if kind.TupleRawName
+ // is not empty, it can have unique id.
+ id := kind.TupleRawName + kind.String()
+ if s, exist := structs[id]; exist {
+ return s.Name
+ }
+ var fields []*tmplField
+ for i, elem := range kind.TupleElems {
+ field := bindStructTypeJava(*elem, structs)
+ fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
+ }
+ name := kind.TupleRawName
+ if name == "" {
+ name = fmt.Sprintf("Class%d", len(structs))
+ }
+ structs[id] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// namedType is a set of functions that transform language specific types to
+// named versions that may be used inside method names.
+var namedType = map[Lang]func(string, abi.Type) string{
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
+ LangJava: namedTypeJava,
+}
+
+// namedTypeJava converts some primitive data types to named variants that can
+// be used as parts of method names.
+func namedTypeJava(javaKind string, solKind abi.Type) string {
+ switch javaKind {
+ case "byte[]":
+ return "Binary"
+ case "boolean":
+ return "Bool"
+ default:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
+ if len(parts) != 4 {
+ return javaKind
+ }
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ if parts[3] == "" {
+ return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
+ }
+ return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
+
+ default:
+ return javaKind
+ }
+ }
+}
+
+// alias returns an alias of the given string based on the aliasing rules
+// or returns itself if no rule is matched.
+func alias(aliases map[string]string, n string) string {
+ if alias, exist := aliases[n]; exist {
+ return alias
+ }
+ return n
+}
+
+// methodNormalizer is a name transformer that modifies Solidity method names to
+// conform to target language naming conventions.
+var methodNormalizer = map[Lang]func(string) string{
+ LangGo: abi.ToCamelCase,
+ LangJava: decapitalise,
+}
+
+// capitalise makes a camel-case string which starts with an upper case character.
+var capitalise = abi.ToCamelCase
+
+// decapitalise makes a camel-case string which starts with a lower case character.
+func decapitalise(input string) string {
+ if len(input) == 0 {
+ return input
+ }
+
+ goForm := abi.ToCamelCase(input)
+ return strings.ToLower(goForm[:1]) + goForm[1:]
+}
+
+// structured checks whether a list of ABI data types has enough information to
+// operate through a proper Go struct or if flat returns are needed.
+func structured(args abi.Arguments) bool {
+ if len(args) < 2 {
+ return false
+ }
+ exists := make(map[string]bool)
+ for _, out := range args {
+ // If the name is anonymous, we can't organize into a struct
+ if out.Name == "" {
+ return false
+ }
+ // If the field name is empty when normalized or collides (var, Var, _var, _Var),
+ // we can't organize into a struct
+ field := capitalise(out.Name)
+ if field == "" || exists[field] {
+ return false
+ }
+ exists[field] = true
+ }
+ return true
+}
+
+// hasStruct returns an indicator whether the given type is struct, struct slice
+// or struct array.
+func hasStruct(t abi.Type) bool {
+ switch t.T {
+ case abi.SliceTy:
+ return hasStruct(*t.Elem)
+ case abi.ArrayTy:
+ return hasStruct(*t.Elem)
+ case abi.TupleTy:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
new file mode 100644
index 0000000..94f30cc
--- /dev/null
+++ b/accounts/abi/bind/template.go
@@ -0,0 +1,703 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bind
+
+import "github.com/astranetworld/ast/accounts/abi"
+
+// tmplData is the data structure required to fill the binding template.
+type tmplData struct {
+ Package string // Name of the package to place the generated file in
+ Contracts map[string]*tmplContract // List of contracts to generate into this file
+ Libraries map[string]string // Map the bytecode's link pattern to the library name
+ Structs map[string]*tmplStruct // Contract struct type definitions
+}
+
+// tmplContract contains the data needed to generate an individual contract binding.
+type tmplContract struct {
+ Type string // Type name of the main contract binding
+ InputABI string // JSON ABI used as the input to generate the binding from
+ InputBin string // Optional EVM bytecode used to generate deploy code from
+ FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
+ Constructor abi.Method // Contract constructor for deploy parametrization
+ Calls map[string]*tmplMethod // Contract calls that only read state data
+ Transacts map[string]*tmplMethod // Contract calls that write state data
+ Fallback *tmplMethod // Additional special fallback function
+ Receive *tmplMethod // Additional special receive function
+ Events map[string]*tmplEvent // Contract events accessors
+ Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
+ Library bool // Indicator whether the contract is a library
+}
+
+// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
+// and cached data fields.
+type tmplMethod struct {
+ Original abi.Method // Original method as parsed by the abi package
+ Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
+ Structured bool // Whether the returns should be accumulated into a struct
+}
+
+// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed
+// and cached data fields.
+type tmplEvent struct {
+ Original abi.Event // Original event as parsed by the abi package
+ Normalized abi.Event // Normalized version of the parsed fields
+}
+
+// tmplField is a wrapper around a struct field with binding language
+// struct type definition and relative filed name.
+type tmplField struct {
+ Type string // Field type representation depends on target binding language
+ Name string // Field name converted from the raw user-defined field name
+ SolKind abi.Type // Raw abi type information
+}
+
+// tmplStruct is a wrapper around an abi.tuple and contains an auto-generated
+// struct name.
+type tmplStruct struct {
+ Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
+ Fields []*tmplField // Struct fields definition depends on the binding language.
+}
+
+// tmplSource is language to template mapping containing all the supported
+// programming languages the package can generate to.
+var tmplSource = map[Lang]string{
+ LangGo: tmplSourceGo,
+ LangJava: tmplSourceJava,
+}
+
+// tmplSourceGo is the Go source template that the generated Go contract binding
+// is based on.
+const tmplSourceGo = `
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package {{.Package}}
+
+import (
+ "math/big"
+ "strings"
+ "errors"
+
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = ethereum.NotFound
+ _ = bind.Bind
+ _ = common.Big1
+ _ = types.BloomLookup
+ _ = event.NewSubscription
+ _ = abi.ConvertType
+)
+
+{{$structs := .Structs}}
+{{range $structs}}
+ // {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
+ type {{.Name}} struct {
+ {{range $field := .Fields}}
+ {{$field.Name}} {{$field.Type}}{{end}}
+ }
+{{end}}
+
+{{range $contract := .Contracts}}
+ // {{.Type}}MetaData contains all metadata data concerning the {{.Type}} contract.
+ var {{.Type}}MetaData = &bind.MetaData{
+ ABI: "{{.InputABI}}",
+ {{if $contract.FuncSigs -}}
+ Sigs: map[string]string{
+ {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
+ {{end}}
+ },
+ {{end -}}
+ {{if .InputBin -}}
+ Bin: "0x{{.InputBin}}",
+ {{end}}
+ }
+ // {{.Type}}ABI is the input ABI used to generate the binding from.
+ // Deprecated: Use {{.Type}}MetaData.ABI instead.
+ var {{.Type}}ABI = {{.Type}}MetaData.ABI
+
+ {{if $contract.FuncSigs}}
+ // Deprecated: Use {{.Type}}MetaData.Sigs instead.
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
+ {{end}}
+
+ {{if .InputBin}}
+ // {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
+ // Deprecated: Use {{.Type}}MetaData.Bin instead.
+ var {{.Type}}Bin = {{.Type}}MetaData.Bin
+
+ // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
+ parsed, err := {{.Type}}MetaData.GetAbi()
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ if parsed == nil {
+ return common.Address{}, nil, nil, errors.New("GetABI returned nil")
+ }
+ {{range $pattern, $name := .Libraries}}
+ {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
+ {{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
+ {{end}}
+ address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+ {{end}}
+
+ // {{.Type}} is an auto generated Go binding around an Ethereum contract.
+ type {{.Type}} struct {
+ {{.Type}}Caller // Read-only binding to the contract
+ {{.Type}}Transactor // Write-only binding to the contract
+ {{.Type}}Filterer // Log filterer for contract events
+ }
+
+ // {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
+ type {{.Type}}Caller struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Transactor is an auto generated write-only Go binding around an Ethereum contract.
+ type {{.Type}}Transactor struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
+ type {{.Type}}Filterer struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
+ // with pre-set call and transact options.
+ type {{.Type}}Session struct {
+ Contract *{{.Type}} // Generic contract binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}CallerSession is an auto generated read-only Go binding around an Ethereum contract,
+ // with pre-set call options.
+ type {{.Type}}CallerSession struct {
+ Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ }
+
+ // {{.Type}}TransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+ // with pre-set transact options.
+ type {{.Type}}TransactorSession struct {
+ Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}Raw is an auto generated low-level Go binding around an Ethereum contract.
+ type {{.Type}}Raw struct {
+ Contract *{{.Type}} // Generic contract binding to access the raw methods on
+ }
+
+ // {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+ type {{.Type}}CallerRaw struct {
+ Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
+ }
+
+ // {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+ type {{.Type}}TransactorRaw struct {
+ Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
+ }
+
+ // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
+ contract, err := bind{{.Type}}(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+
+ // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
+ contract, err := bind{{.Type}}(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Caller{contract: contract}, nil
+ }
+
+ // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
+ contract, err := bind{{.Type}}(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Transactor{contract: contract}, nil
+ }
+
+ // New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
+ contract, err := bind{{.Type}}(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}Filterer{contract: contract}, nil
+ }
+
+ // bind{{.Type}} binds a generic wrapper to an already deployed contract.
+ func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := {{.Type}}MetaData.GetAbi()
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
+ }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transfer(opts)
+ }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
+ }
+
+ {{range .Calls}}
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
+ var out []interface{}
+ err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ {{if .Structured}}
+ outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
+ if err != nil {
+ return *outstruct, err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return *outstruct, err
+ {{else}}
+ if err != nil {
+ return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
+ {{end}}
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{if .Fallback}}
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
+ }
+
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ }
+
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ }
+ {{end}}
+
+ {{if .Receive}}
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
+ }
+
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ }
+
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
+ return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ }
+ {{end}}
+
+ {{range .Events}}
+ // {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
+ Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
+
+ contract *bind.BoundContract // Generic contract to use for unpacking event data
+ event string // Event name to use for unpacking event data
+
+ logs chan types.Log // Log channel receiving the found contract events
+ sub ethereum.Subscription // Subscription for errors, completion and termination
+ done bool // Whether the subscription completed delivering logs
+ fail error // Occurred error to stop iteration
+ }
+ // Next advances the iterator to the subsequent event, returning whether there
+ // are any more events found. In case of a retrieval or parsing error, false is
+ // returned and Error() can be queried for the exact failure.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
+ // If the iterator failed, stop iterating
+ if (it.fail != nil) {
+ return false
+ }
+ // If the iterator completed, deliver directly whatever's available
+ if (it.done) {
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+ // Iterator still in progress, wait for either a data or an error event
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+ }
+ // Error returns any retrieval or parsing error occurred during filtering.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
+ return it.fail
+ }
+ // Close terminates the iteration process, releasing any pending underlying
+ // resources.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+ }
+
+ // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
+ {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
+ Raw types.Log // Blockchain specific contextual infos
+ }
+
+ // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
+ }
+
+ // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.Unsubscribe()
+ for {
+ select {
+ case log := <-logs:
+ // New log arrived, parse the event and forward to the user
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return err
+ }
+ event.Raw = log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+ }
+
+ // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+ }
+
+ {{end}}
+{{end}}
+`
+
+// tmplSourceJava is the Java source template that the generated Java contract binding
+// is based on.
+const tmplSourceJava = `
+// This file is an automatically generated Java binding. Do not modify as any
+// change will likely be lost upon the next re-generation!
+
+package {{.Package}};
+
+import org.ethereum.geth.*;
+import java.util.*;
+
+{{$structs := .Structs}}
+{{range $contract := .Contracts}}
+{{if not .Library}}public {{end}}class {{.Type}} {
+ // ABI is the input ABI used to generate the binding from.
+ public final static String ABI = "{{.InputABI}}";
+ {{if $contract.FuncSigs}}
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ public final static Map {{.Type}}FuncSigs;
+ static {
+ Hashtable temp = new Hashtable();
+ {{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
+ {{end}}
+ {{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
+ }
+ {{end}}
+ {{if .InputBin}}
+ // BYTECODE is the compiled bytecode used for deploying new contracts.
+ public final static String BYTECODE = "0x{{.InputBin}}";
+
+ // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
+ public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
+ String bytecode = BYTECODE;
+ {{if .Libraries}}
+
+ // "link" contract to dependent libraries by deploying them first.
+ {{range $pattern, $name := .Libraries}}
+ {{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
+ bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
+ {{end}}
+ {{end}}
+ {{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
+ }
+
+ // Internal constructor used by contract deployment.
+ private {{.Type}}(BoundContract deployment) {
+ this.Address = deployment.getAddress();
+ this.Deployer = deployment.getDeployer();
+ this.Contract = deployment;
+ }
+ {{end}}
+
+ // Ethereum address where this contract is located at.
+ public final Address Address;
+
+ // Ethereum transaction in which this contract was deployed (if known!).
+ public final Transaction Deployer;
+
+ // Contract instance bound to a blockchain address.
+ private final BoundContract Contract;
+
+ // Creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ public {{.Type}}(Address address, EthereumClient client) throws Exception {
+ this(Geth.bindContract(address, ABI, client));
+ }
+
+ {{range .Calls}}
+ {{if gt (len .Normalized.Outputs) 1}}
+ // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
+ public class {{capitalise .Normalized.Name}}Results {
+ {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type $structs}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
+ {{end}}
+ }
+ {{end}}
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+
+ Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
+ {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type $structs) .Type}}(); results.set({{$index}}, result{{$index}});
+ {{end}}
+
+ if (opts == null) {
+ opts = Geth.newCallOpts();
+ }
+ this.Contract.call(opts, results, "{{.Original.Name}}", args);
+ {{if gt (len .Normalized.Outputs) 1}}
+ {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
+ {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type $structs) .Type}}();
+ {{end}}
+ return result;
+ {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type $structs) .Type}}();{{end}}
+ {{end}}
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return this.Contract.transact(opts, "{{.Original.Name}}" , args);
+ }
+ {{end}}
+
+ {{if .Fallback}}
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
+ return this.Contract.rawTransact(opts, calldata);
+ }
+ {{end}}
+
+ {{if .Receive}}
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ public Transaction Receive(TransactOpts opts) throws Exception {
+ return this.Contract.rawTransact(opts, null);
+ }
+ {{end}}
+}
+{{end}}
+`
diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/util.go
new file mode 100644
index 0000000..e5ac004
--- /dev/null
+++ b/accounts/abi/bind/util.go
@@ -0,0 +1,81 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bind
+
+import (
+ "context"
+ "errors"
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "time"
+)
+
+// NotFound is returned by API methods if the requested item does not exist.
+var NotFound = errors.New("not found")
+
+// WaitMined waits for tx to be mined on the blockchain.
+// It stops waiting when the context is canceled.
+func WaitMined(ctx context.Context, b DeployBackend, tx *transaction.Transaction) (*block.Receipt, error) {
+ queryTicker := time.NewTicker(time.Second)
+ defer queryTicker.Stop()
+
+ logger := log.New("hash", tx.Hash())
+ for {
+ receipt, err := b.TransactionReceipt(ctx, tx.Hash())
+ if err == nil {
+ return receipt, nil
+ }
+
+ if errors.Is(err, NotFound) {
+ logger.Trace("Transaction not yet mined")
+ } else {
+ logger.Trace("Receipt retrieval failed", "err", err)
+ }
+
+ // Wait for the next round.
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-queryTicker.C:
+ }
+ }
+}
+
+// WaitDeployed waits for a contract deployment transaction and returns the on-chain
+// contract address when it is mined. It stops waiting when ctx is canceled.
+func WaitDeployed(ctx context.Context, b DeployBackend, tx *transaction.Transaction) (types.Address, error) {
+ if tx.To() != nil {
+ return types.Address{}, errors.New("tx is not contract creation")
+ }
+ receipt, err := WaitMined(ctx, b, tx)
+ if err != nil {
+ return types.Address{}, err
+ }
+ if receipt.ContractAddress == (types.Address{}) {
+ return types.Address{}, errors.New("zero address")
+ }
+ // Check that code has indeed been deployed at the address.
+ // This matters on pre-Homestead chains: OOG in the constructor
+ // could leave an empty account behind.
+ code, err := b.CodeAt(ctx, receipt.ContractAddress, nil)
+ if err == nil && len(code) == 0 {
+ err = ErrNoCodeAfterDeploy
+ }
+ return receipt.ContractAddress, err
+}
diff --git a/accounts/abi/doc.go b/accounts/abi/doc.go
new file mode 100644
index 0000000..0f9ebcd
--- /dev/null
+++ b/accounts/abi/doc.go
@@ -0,0 +1,26 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package abi implements the Ethereum ABI (Application Binary
+// Interface).
+//
+// The Ethereum ABI is strongly typed, known at compile time
+// and static. This ABI will handle basic type casting; unsigned
+// to signed and visa versa. It does not handle slice casting such
+// as unsigned slice to signed slice. Bit size type casting is also
+// handled. ints with a bit size of 32 will be properly cast to int256,
+// etc.
+package abi
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
new file mode 100644
index 0000000..5d2b282
--- /dev/null
+++ b/accounts/abi/error.go
@@ -0,0 +1,92 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "strings"
+)
+
+type Error struct {
+ Name string
+ Inputs Arguments
+ str string
+
+ // Sig contains the string signature according to the ABI spec.
+ // e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+
+ // ID returns the canonical representation of the error's signature used by the
+ // abi definition to identify event names and types.
+ ID types.Hash
+}
+
+func NewError(name string, inputs Arguments) Error {
+ // sanitize inputs to remove inputs without names
+ // and precompute string and sig representation.
+ names := make([]string, len(inputs))
+ typess := make([]string, len(inputs))
+ for i, input := range inputs {
+ if input.Name == "" {
+ inputs[i] = Argument{
+ Name: fmt.Sprintf("arg%d", i),
+ Indexed: input.Indexed,
+ Type: input.Type,
+ }
+ } else {
+ inputs[i] = input
+ }
+ // string representation
+ names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
+ if input.Indexed {
+ names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
+ }
+ // sig representation
+ typess[i] = input.Type.String()
+ }
+
+ str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
+ sig := fmt.Sprintf("%v(%v)", name, strings.Join(typess, ","))
+ id := types.BytesToHash(crypto.Keccak256([]byte(sig)))
+
+ return Error{
+ Name: name,
+ Inputs: inputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (e *Error) String() string {
+ return e.str
+}
+
+func (e *Error) Unpack(data []byte) (interface{}, error) {
+ if len(data) < 4 {
+ return "", errors.New("invalid data for unpacking")
+ }
+ if !bytes.Equal(data[:4], e.ID[:4]) {
+ return "", errors.New("invalid data for unpacking")
+ }
+ return e.Inputs.Unpack(data[4:])
+}
diff --git a/accounts/abi/error_handling.go b/accounts/abi/error_handling.go
new file mode 100644
index 0000000..4e24181
--- /dev/null
+++ b/accounts/abi/error_handling.go
@@ -0,0 +1,81 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ errBadBool = errors.New("abi: improperly encoded boolean value")
+)
+
+// formatSliceString formats the reflection kind with the given slice size
+// and returns a formatted string representation.
+func formatSliceString(kind reflect.Kind, sliceSize int) string {
+ if sliceSize == -1 {
+ return fmt.Sprintf("[]%v", kind)
+ }
+ return fmt.Sprintf("[%d]%v", sliceSize, kind)
+}
+
+// sliceTypeCheck checks that the given slice can by assigned to the reflection
+// type in t.
+func sliceTypeCheck(t Type, val reflect.Value) error {
+ if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
+ return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
+ }
+
+ if t.T == ArrayTy && val.Len() != t.Size {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+ }
+
+ if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
+ if val.Len() > 0 {
+ return sliceTypeCheck(*t.Elem, val.Index(0))
+ }
+ }
+
+ if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
+ }
+ return nil
+}
+
+// typeCheck checks that the given reflection value can be assigned to the reflection
+// type in t.
+func typeCheck(t Type, value reflect.Value) error {
+ if t.T == SliceTy || t.T == ArrayTy {
+ return sliceTypeCheck(t, value)
+ }
+
+ // Check base type validity. Element types will be checked later on.
+ if t.GetType().Kind() != value.Kind() {
+ return typeErr(t.GetType().Kind(), value.Kind())
+ } else if t.T == FixedBytesTy && t.Size != value.Len() {
+ return typeErr(t.GetType(), value.Type())
+ } else {
+ return nil
+ }
+}
+
+// typeErr returns a formatted type casting error.
+func typeErr(expected, got interface{}) error {
+ return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
+}
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
new file mode 100644
index 0000000..8d0932c
--- /dev/null
+++ b/accounts/abi/event.go
@@ -0,0 +1,102 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "strings"
+)
+
+// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
+// holds type information (inputs) about the yielded output. Anonymous events
+// don't get the signature canonical representation as the first LOG topic.
+type Event struct {
+ // Name is the event name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of event overloading.
+ //
+ // e.g.
+ // These are two events that have the same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The event name of the first one will be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+
+ // RawName is the raw event name parsed from ABI.
+ RawName string
+ Anonymous bool
+ Inputs Arguments
+ str string
+
+ // Sig contains the string signature according to the ABI spec.
+ // e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+
+ // ID returns the canonical representation of the event's signature used by the
+ // abi definition to identify event names and types.
+ ID types.Hash
+}
+
+// NewEvent creates a new Event.
+// It sanitizes the input arguments to remove unnamed arguments.
+// It also precomputes the id, signature and string representation
+// of the event.
+func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
+ // sanitize inputs to remove inputs without names
+ // and precompute string and sig representation.
+ names := make([]string, len(inputs))
+ typess := make([]string, len(inputs))
+ for i, input := range inputs {
+ if input.Name == "" {
+ inputs[i] = Argument{
+ Name: fmt.Sprintf("arg%d", i),
+ Indexed: input.Indexed,
+ Type: input.Type,
+ }
+ } else {
+ inputs[i] = input
+ }
+ // string representation
+ names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
+ if input.Indexed {
+ names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
+ }
+ // sig representation
+ typess[i] = input.Type.String()
+ }
+
+ str := fmt.Sprintf("event %v(%v)", rawName, strings.Join(names, ", "))
+ sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(typess, ","))
+ id := types.BytesToHash(crypto.Keccak256([]byte(sig)))
+
+ return Event{
+ Name: name,
+ RawName: rawName,
+ Anonymous: anonymous,
+ Inputs: inputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (e Event) String() string {
+ return e.str
+}
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
new file mode 100644
index 0000000..a076125
--- /dev/null
+++ b/accounts/abi/method.go
@@ -0,0 +1,166 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "strings"
+)
+
+// FunctionType represents different types of functions a contract might have.
+type FunctionType int
+
+const (
+ // Constructor represents the constructor of the contract.
+ // The constructor function is called while deploying a contract.
+ Constructor FunctionType = iota
+ // Fallback represents the fallback function.
+ // This function is executed if no other function matches the given function
+ // signature and no receive function is specified.
+ Fallback
+ // Receive represents the receive function.
+ // This function is executed on plain Ether transfers.
+ Receive
+ // Function represents a normal function.
+ Function
+)
+
+// Method represents a callable given a `Name` and whether the method is a constant.
+// If the method is `Const` no transaction needs to be created for this
+// particular Method call. It can easily be simulated using a local VM.
+// For example a `Balance()` method only needs to retrieve something
+// from the storage and therefore requires no Tx to be sent to the
+// network. A method such as `Transact` does require a Tx and thus will
+// be flagged `false`.
+// Input specifies the required input parameters for this gives method.
+type Method struct {
+ // Name is the method name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of a function overload.
+ //
+ // e.g.
+ // These are two functions that have the same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The method name of the first one will be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+ RawName string // RawName is the raw method name parsed from ABI
+
+ // Type indicates whether the method is a
+ // special fallback introduced in solidity v0.6.0
+ Type FunctionType
+
+ // StateMutability indicates the mutability state of method,
+ // the default value is nonpayable. It can be empty if the abi
+ // is generated by legacy compiler.
+ StateMutability string
+
+ // Legacy indicators generated by compiler before v0.6.0
+ Constant bool
+ Payable bool
+
+ Inputs Arguments
+ Outputs Arguments
+ str string
+ // Sig returns the methods string signature according to the ABI spec.
+ // e.g. function foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+ // ID returns the canonical representation of the method's signature used by the
+ // abi definition to identify method names and types.
+ ID []byte
+}
+
+// NewMethod creates a new Method.
+// A method should always be created using NewMethod.
+// It also precomputes the sig representation and the string representation
+// of the method.
+func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
+ var (
+ types = make([]string, len(inputs))
+ inputNames = make([]string, len(inputs))
+ outputNames = make([]string, len(outputs))
+ )
+ for i, input := range inputs {
+ inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
+ types[i] = input.Type.String()
+ }
+ for i, output := range outputs {
+ outputNames[i] = output.Type.String()
+ if len(output.Name) > 0 {
+ outputNames[i] += fmt.Sprintf(" %v", output.Name)
+ }
+ }
+ // calculate the signature and method id. Note only function
+ // has meaningful signature and id.
+ var (
+ sig string
+ id []byte
+ )
+ if funType == Function {
+ sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
+ id = crypto.Keccak256([]byte(sig))[:4]
+ }
+ // Extract meaningful state mutability of solidity method.
+ // If it's default value, never print it.
+ state := mutability
+ if state == "nonpayable" {
+ state = ""
+ }
+ if state != "" {
+ state = state + " "
+ }
+ identity := fmt.Sprintf("function %v", rawName)
+ if funType == Fallback {
+ identity = "fallback"
+ } else if funType == Receive {
+ identity = "receive"
+ } else if funType == Constructor {
+ identity = "constructor"
+ }
+ str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
+
+ return Method{
+ Name: name,
+ RawName: rawName,
+ Type: funType,
+ StateMutability: mutability,
+ Constant: isConst,
+ Payable: isPayable,
+ Inputs: inputs,
+ Outputs: outputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (method Method) String() string {
+ return method.str
+}
+
+// IsConstant returns the indicator whether the method is read-only.
+func (method Method) IsConstant() bool {
+ return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
+}
+
+// IsPayable returns the indicator whether the method can process
+// plain ether transfers.
+func (method Method) IsPayable() bool {
+ return method.StateMutability == "payable" || method.Payable
+}
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
new file mode 100644
index 0000000..4139bc4
--- /dev/null
+++ b/accounts/abi/pack.go
@@ -0,0 +1,85 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common"
+ "github.com/astranetworld/ast/common/math"
+ "github.com/astranetworld/ast/common/types"
+ "math/big"
+ "reflect"
+)
+
+// packBytesSlice packs the given bytes as [L, V] as the canonical representation
+// bytes slice.
+func packBytesSlice(bytes []byte, l int) []byte {
+ len := packNum(reflect.ValueOf(l))
+ return append(len, types.RightPadBytes(bytes, (l+31)/32*32)...)
+}
+
+// packElement packs the given reflect value according to the abi specification in
+// t.
+func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
+ switch t.T {
+ case IntTy, UintTy:
+ return packNum(reflectValue), nil
+ case StringTy:
+ return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()), nil
+ case AddressTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+
+ return types.LeftPadBytes(reflectValue.Bytes(), 32), nil
+ case BoolTy:
+ if reflectValue.Bool() {
+ return math.PaddedBigBytes(common.Big1, 32), nil
+ }
+ return math.PaddedBigBytes(common.Big0, 32), nil
+ case BytesTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ if reflectValue.Type() != reflect.TypeOf([]byte{}) {
+ return []byte{}, errors.New("Bytes type is neither slice nor array")
+ }
+ return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
+ case FixedBytesTy, FunctionTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ return types.RightPadBytes(reflectValue.Bytes(), 32), nil
+ default:
+ return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
+ }
+}
+
+// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation.
+func packNum(value reflect.Value) []byte {
+ switch kind := value.Kind(); kind {
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return math.U256Bytes(new(big.Int).SetUint64(value.Uint()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return math.U256Bytes(big.NewInt(value.Int()))
+ case reflect.Ptr:
+ return math.U256Bytes(new(big.Int).Set(value.Interface().(*big.Int)))
+ default:
+ panic("abi: fatal error")
+ }
+}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
new file mode 100644
index 0000000..7bbf882
--- /dev/null
+++ b/accounts/abi/reflect.go
@@ -0,0 +1,264 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+ "strings"
+)
+
+// ConvertType converts an interface of a runtime type into a interface of the
+// given type, e.g. turn this code:
+//
+// var fields []reflect.StructField
+//
+// fields = append(fields, reflect.StructField{
+// Name: "X",
+// Type: reflect.TypeOf(new(big.Int)),
+// Tag: reflect.StructTag("json:\"" + "x" + "\""),
+// }
+//
+// into:
+//
+// type TupleT struct { X *big.Int }
+func ConvertType(in interface{}, proto interface{}) interface{} {
+ protoType := reflect.TypeOf(proto)
+ if reflect.TypeOf(in).ConvertibleTo(protoType) {
+ return reflect.ValueOf(in).Convert(protoType).Interface()
+ }
+ // Use set as a last ditch effort
+ if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
+ panic(err)
+ }
+ return proto
+}
+
+// indirect recursively dereferences the value until it either gets the value
+// or finds a big.Int
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
+ return indirect(v.Elem())
+ }
+ return v
+}
+
+// reflectIntType returns the reflect using the given size and
+// unsignedness.
+func reflectIntType(unsigned bool, size int) reflect.Type {
+ if unsigned {
+ switch size {
+ case 8:
+ return reflect.TypeOf(uint8(0))
+ case 16:
+ return reflect.TypeOf(uint16(0))
+ case 32:
+ return reflect.TypeOf(uint32(0))
+ case 64:
+ return reflect.TypeOf(uint64(0))
+ }
+ }
+ switch size {
+ case 8:
+ return reflect.TypeOf(int8(0))
+ case 16:
+ return reflect.TypeOf(int16(0))
+ case 32:
+ return reflect.TypeOf(int32(0))
+ case 64:
+ return reflect.TypeOf(int64(0))
+ }
+ return reflect.TypeOf(&big.Int{})
+}
+
+// mustArrayToByteSlice creates a new byte slice with the exact same size as value
+// and copies the bytes in value to the new slice.
+func mustArrayToByteSlice(value reflect.Value) reflect.Value {
+ slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
+ reflect.Copy(slice, value)
+ return slice
+}
+
+// set attempts to assign src to dst by either setting, copying or otherwise.
+//
+// set is a bit more lenient when it comes to assignment and doesn't force an as
+// strict ruleset as bare `reflect` does.
+func set(dst, src reflect.Value) error {
+ dstType, srcType := dst.Type(), src.Type()
+ switch {
+ case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
+ return set(dst.Elem(), src)
+ case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
+ return set(dst.Elem(), src)
+ case srcType.AssignableTo(dstType) && dst.CanSet():
+ dst.Set(src)
+ case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet():
+ return setSlice(dst, src)
+ case dstType.Kind() == reflect.Array:
+ return setArray(dst, src)
+ case dstType.Kind() == reflect.Struct:
+ return setStruct(dst, src)
+ default:
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
+ }
+ return nil
+}
+
+// setSlice attempts to assign src to dst when slices are not assignable by default
+// e.g. src: [][]byte -> dst: [][15]byte
+// setSlice ignores if we cannot copy all of src' elements.
+func setSlice(dst, src reflect.Value) error {
+ slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
+ for i := 0; i < src.Len(); i++ {
+ if err := set(slice.Index(i), src.Index(i)); err != nil {
+ return err
+ }
+ }
+ if dst.CanSet() {
+ dst.Set(slice)
+ return nil
+ }
+ return errors.New("Cannot set slice, destination not settable")
+}
+
+func setArray(dst, src reflect.Value) error {
+ if src.Kind() == reflect.Ptr {
+ return set(dst, indirect(src))
+ }
+ array := reflect.New(dst.Type()).Elem()
+ min := src.Len()
+ if src.Len() > dst.Len() {
+ min = dst.Len()
+ }
+ for i := 0; i < min; i++ {
+ if err := set(array.Index(i), src.Index(i)); err != nil {
+ return err
+ }
+ }
+ if dst.CanSet() {
+ dst.Set(array)
+ return nil
+ }
+ return errors.New("Cannot set array, destination not settable")
+}
+
+func setStruct(dst, src reflect.Value) error {
+ for i := 0; i < src.NumField(); i++ {
+ srcField := src.Field(i)
+ dstField := dst.Field(i)
+ if !dstField.IsValid() || !srcField.IsValid() {
+ return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
+ }
+ if err := set(dstField, srcField); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// mapArgNamesToStructFields maps a slice of argument names to struct fields.
+//
+// first round: for each Exportable field that contains a `abi:""` tag and this field name
+// exists in the given argument name list, pair them together.
+//
+// second round: for each argument name that has not been already linked, find what
+// variable is expected to be mapped into, if it exists and has not been used, pair them.
+//
+// Note this function assumes the given value is a struct value.
+func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
+ typ := value.Type()
+
+ abi2struct := make(map[string]string)
+ struct2abi := make(map[string]string)
+
+ // first round ~~~
+ for i := 0; i < typ.NumField(); i++ {
+ structFieldName := typ.Field(i).Name
+
+ // skip private struct fields.
+ if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
+ continue
+ }
+ // skip fields that have no abi:"" tag.
+ tagName, ok := typ.Field(i).Tag.Lookup("abi")
+ if !ok {
+ continue
+ }
+ // check if tag is empty.
+ if tagName == "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
+ }
+ // check which argument field matches with the abi tag.
+ found := false
+ for _, arg := range argNames {
+ if arg == tagName {
+ if abi2struct[arg] != "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
+ }
+ // pair them
+ abi2struct[arg] = structFieldName
+ struct2abi[structFieldName] = arg
+ found = true
+ }
+ }
+ // check if this tag has been mapped.
+ if !found {
+ return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
+ }
+ }
+
+ // second round ~~~
+ for _, argName := range argNames {
+ structFieldName := ToCamelCase(argName)
+
+ if structFieldName == "" {
+ return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
+ }
+
+ // this abi has already been paired, skip it... unless there exists another, yet unassigned
+ // struct field with the same field name. If so, raise an error:
+ // abi: [ { "name": "value" } ]
+ // struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
+ if abi2struct[argName] != "" {
+ if abi2struct[argName] != structFieldName &&
+ struct2abi[structFieldName] == "" &&
+ value.FieldByName(structFieldName).IsValid() {
+ return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
+ }
+ continue
+ }
+
+ // return an error if this struct field has already been paired.
+ if struct2abi[structFieldName] != "" {
+ return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
+ }
+
+ if value.FieldByName(structFieldName).IsValid() {
+ // pair them
+ abi2struct[argName] = structFieldName
+ struct2abi[structFieldName] = argName
+ } else {
+ // not paired, but annotate as used, to detect cases like
+ // abi : [ { "name": "value" }, { "name": "_value" } ]
+ // struct { Value *big.Int }
+ struct2abi[structFieldName] = argName
+ }
+ }
+ return abi2struct, nil
+}
diff --git a/accounts/abi/selector_parser.go b/accounts/abi/selector_parser.go
new file mode 100644
index 0000000..63352f5
--- /dev/null
+++ b/accounts/abi/selector_parser.go
@@ -0,0 +1,176 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "fmt"
+)
+
+type SelectorMarshaling struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Inputs []ArgumentMarshaling `json:"inputs"`
+}
+
+func isDigit(c byte) bool {
+ return c >= '0' && c <= '9'
+}
+
+func isAlpha(c byte) bool {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+}
+
+func isIdentifierSymbol(c byte) bool {
+ return c == '$' || c == '_'
+}
+
+func parseToken(unescapedSelector string, isIdent bool) (string, string, error) {
+ if len(unescapedSelector) == 0 {
+ return "", "", fmt.Errorf("empty token")
+ }
+ firstChar := unescapedSelector[0]
+ position := 1
+ if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
+ return "", "", fmt.Errorf("invalid token start: %c", firstChar)
+ }
+ for position < len(unescapedSelector) {
+ char := unescapedSelector[position]
+ if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
+ break
+ }
+ position++
+ }
+ return unescapedSelector[:position], unescapedSelector[position:], nil
+}
+
+func parseIdentifier(unescapedSelector string) (string, string, error) {
+ return parseToken(unescapedSelector, true)
+}
+
+func parseElementaryType(unescapedSelector string) (string, string, error) {
+ parsedType, rest, err := parseToken(unescapedSelector, false)
+ if err != nil {
+ return "", "", fmt.Errorf("failed to parse elementary type: %v", err)
+ }
+ // handle arrays
+ for len(rest) > 0 && rest[0] == '[' {
+ parsedType = parsedType + string(rest[0])
+ rest = rest[1:]
+ for len(rest) > 0 && isDigit(rest[0]) {
+ parsedType = parsedType + string(rest[0])
+ rest = rest[1:]
+ }
+ if len(rest) == 0 || rest[0] != ']' {
+ return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
+ }
+ parsedType = parsedType + string(rest[0])
+ rest = rest[1:]
+ }
+ return parsedType, rest, nil
+}
+
+func parseCompositeType(unescapedSelector string) ([]interface{}, string, error) {
+ if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
+ return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
+ }
+ parsedType, rest, err := parseType(unescapedSelector[1:])
+ if err != nil {
+ return nil, "", fmt.Errorf("failed to parse type: %v", err)
+ }
+ result := []interface{}{parsedType}
+ for len(rest) > 0 && rest[0] != ')' {
+ parsedType, rest, err = parseType(rest[1:])
+ if err != nil {
+ return nil, "", fmt.Errorf("failed to parse type: %v", err)
+ }
+ result = append(result, parsedType)
+ }
+ if len(rest) == 0 || rest[0] != ')' {
+ return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
+ }
+ if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' {
+ return append(result, "[]"), rest[3:], nil
+ }
+ return result, rest[1:], nil
+}
+
+func parseType(unescapedSelector string) (interface{}, string, error) {
+ if len(unescapedSelector) == 0 {
+ return nil, "", fmt.Errorf("empty type")
+ }
+ if unescapedSelector[0] == '(' {
+ return parseCompositeType(unescapedSelector)
+ } else {
+ return parseElementaryType(unescapedSelector)
+ }
+}
+
+func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
+ arguments := make([]ArgumentMarshaling, 0)
+ for i, arg := range args {
+ // generate dummy name to avoid unmarshal issues
+ name := fmt.Sprintf("name%d", i)
+ if s, ok := arg.(string); ok {
+ arguments = append(arguments, ArgumentMarshaling{name, s, s, nil, false})
+ } else if components, ok := arg.([]interface{}); ok {
+ subArgs, err := assembleArgs(components)
+ if err != nil {
+ return nil, fmt.Errorf("failed to assemble components: %v", err)
+ }
+ tupleType := "tuple"
+ if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
+ subArgs = subArgs[:len(subArgs)-1]
+ tupleType = "tuple[]"
+ }
+ arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
+ } else {
+ return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
+ }
+ }
+ return arguments, nil
+}
+
+// ParseSelector converts a method selector into a struct that can be JSON encoded
+// and consumed by other functions in this package.
+// Note, although uppercase letters are not part of the ABI spec, this function
+// still accepts it as the general format is valid.
+func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
+ name, rest, err := parseIdentifier(unescapedSelector)
+ if err != nil {
+ return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
+ }
+ args := []interface{}{}
+ if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
+ rest = rest[2:]
+ } else {
+ args, rest, err = parseCompositeType(rest)
+ if err != nil {
+ return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
+ }
+ }
+ if len(rest) > 0 {
+ return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
+ }
+
+ // Reassemble the fake ABI and construct the JSON
+ fakeArgs, err := assembleArgs(args)
+ if err != nil {
+ return SelectorMarshaling{}, fmt.Errorf("failed to parse selector: %v", err)
+ }
+
+ return SelectorMarshaling{name, "function", fakeArgs}, nil
+}
diff --git a/accounts/abi/topics.go b/accounts/abi/topics.go
new file mode 100644
index 0000000..fbe08fc
--- /dev/null
+++ b/accounts/abi/topics.go
@@ -0,0 +1,172 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "math/big"
+ "reflect"
+)
+
+// MakeTopics converts a filter query argument list into a filter topic set.
+func MakeTopics(query ...[]interface{}) ([][]types.Hash, error) {
+ topics := make([][]types.Hash, len(query))
+ for i, filter := range query {
+ for _, rule := range filter {
+ var topic types.Hash
+
+ // Try to generate the topic based on simple types
+ switch rule := rule.(type) {
+ case types.Hash:
+ copy(topic[:], rule[:])
+ case types.Address:
+ copy(topic[types.HashLength-types.AddressLength:], rule[:])
+ case *big.Int:
+ blob := rule.Bytes()
+ copy(topic[types.HashLength-len(blob):], blob)
+ case bool:
+ if rule {
+ topic[types.HashLength-1] = 1
+ }
+ case int8:
+ copy(topic[:], genIntType(int64(rule), 1))
+ case int16:
+ copy(topic[:], genIntType(int64(rule), 2))
+ case int32:
+ copy(topic[:], genIntType(int64(rule), 4))
+ case int64:
+ copy(topic[:], genIntType(rule, 8))
+ case uint8:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[types.HashLength-len(blob):], blob)
+ case uint16:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[types.HashLength-len(blob):], blob)
+ case uint32:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[types.HashLength-len(blob):], blob)
+ case uint64:
+ blob := new(big.Int).SetUint64(rule).Bytes()
+ copy(topic[types.HashLength-len(blob):], blob)
+ case string:
+ hash := crypto.Keccak256Hash([]byte(rule))
+ copy(topic[:], hash[:])
+ case []byte:
+ hash := crypto.Keccak256Hash(rule)
+ copy(topic[:], hash[:])
+
+ default:
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert stringS and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+
+ // Attempt to generate the topic from funky types
+ val := reflect.ValueOf(rule)
+ switch {
+ // static byte array
+ case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
+ reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
+ default:
+ return nil, fmt.Errorf("unsupported indexed type: %T", rule)
+ }
+ }
+ topics[i] = append(topics[i], topic)
+ }
+ }
+ return topics, nil
+}
+
+func genIntType(rule int64, size uint) []byte {
+ var topic [types.HashLength]byte
+ if rule < 0 {
+ // if a rule is negative, we need to put it into two's complement.
+ // extended to common.HashLength bytes.
+ topic = [types.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
+ }
+ for i := uint(0); i < size; i++ {
+ topic[types.HashLength-i-1] = byte(rule >> (i * 8))
+ }
+ return topic[:]
+}
+
+// ParseTopics converts the indexed topic fields into actual log field values.
+func ParseTopics(out interface{}, fields Arguments, topics []types.Hash) error {
+ return parseTopicWithSetter(fields, topics,
+ func(arg Argument, reconstr interface{}) {
+ field := reflect.ValueOf(out).Elem().FieldByName(ToCamelCase(arg.Name))
+ field.Set(reflect.ValueOf(reconstr))
+ })
+}
+
+// ParseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs.
+func ParseTopicsIntoMap(out map[string]interface{}, fields Arguments, topics []types.Hash) error {
+ return parseTopicWithSetter(fields, topics,
+ func(arg Argument, reconstr interface{}) {
+ out[arg.Name] = reconstr
+ })
+}
+
+// parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
+// provided set function.
+//
+// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
+// hashes as the topic value!
+func parseTopicWithSetter(fields Arguments, topics []types.Hash, setter func(Argument, interface{})) error {
+ // Sanity check that the fields and topics match up
+ if len(fields) != len(topics) {
+ return errors.New("topic/field count mismatch")
+ }
+ // Iterate over all the fields and reconstruct them from topics
+ for i, arg := range fields {
+ if !arg.Indexed {
+ return errors.New("non-indexed field in topic reconstruction")
+ }
+ var reconstr interface{}
+ switch arg.Type.T {
+ case TupleTy:
+ return errors.New("tuple type in topic reconstruction")
+ case StringTy, BytesTy, SliceTy, ArrayTy:
+ // Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
+ // whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
+ reconstr = topics[i]
+ case FunctionTy:
+ if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
+ return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
+ }
+ var tmp [24]byte
+ copy(tmp[:], topics[i][8:32])
+ reconstr = tmp
+ default:
+ var err error
+ reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
+ if err != nil {
+ return err
+ }
+ }
+ // Use the setter function to store the value
+ setter(arg, reconstr)
+ }
+
+ return nil
+}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
new file mode 100644
index 0000000..22e5af2
--- /dev/null
+++ b/accounts/abi/type.go
@@ -0,0 +1,425 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/types"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Type enumerator
+const (
+ IntTy byte = iota
+ UintTy
+ BoolTy
+ StringTy
+ SliceTy
+ ArrayTy
+ TupleTy
+ AddressTy
+ FixedBytesTy
+ BytesTy
+ HashTy
+ FixedPointTy
+ FunctionTy
+)
+
+// Type is the reflection of the supported argument type.
+type Type struct {
+ Elem *Type
+ Size int
+ T byte // Our own type checking
+
+ stringKind string // holds the unparsed string for deriving signatures
+
+ // Tuple relative fields
+ TupleRawName string // Raw struct name defined in source code, may be empty.
+ TupleElems []*Type // Type information of all tuple fields
+ TupleRawNames []string // Raw field name of all tuple fields
+ TupleType reflect.Type // Underlying struct of the tuple
+}
+
+var (
+ // typeRegex parses the abi sub types
+ typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
+)
+
+// NewType creates a new reflection type of abi type given in t.
+func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
+ // check that array brackets are equal if they exist
+ if strings.Count(t, "[") != strings.Count(t, "]") {
+ return Type{}, fmt.Errorf("invalid arg type in abi")
+ }
+ typ.stringKind = t
+
+ // if there are brackets, get ready to go into slice/array mode and
+ // recursively create the type
+ if strings.Count(t, "[") != 0 {
+ // Note internalType can be empty here.
+ subInternal := internalType
+ if i := strings.LastIndex(internalType, "["); i != -1 {
+ subInternal = subInternal[:i]
+ }
+ // recursively embed the type
+ i := strings.LastIndex(t, "[")
+ embeddedType, err := NewType(t[:i], subInternal, components)
+ if err != nil {
+ return Type{}, err
+ }
+ // grab the last cell and create a type from there
+ sliced := t[i:]
+ // grab the slice size with regexp
+ re := regexp.MustCompile("[0-9]+")
+ intz := re.FindAllString(sliced, -1)
+
+ if len(intz) == 0 {
+ // is a slice
+ typ.T = SliceTy
+ typ.Elem = &embeddedType
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else if len(intz) == 1 {
+ // is an array
+ typ.T = ArrayTy
+ typ.Elem = &embeddedType
+ typ.Size, err = strconv.Atoi(intz[0])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else {
+ return Type{}, fmt.Errorf("invalid formatting of array type")
+ }
+ return typ, err
+ }
+ // parse the type and size of the abi-type.
+ matches := typeRegex.FindAllStringSubmatch(t, -1)
+ if len(matches) == 0 {
+ return Type{}, fmt.Errorf("invalid type '%v'", t)
+ }
+ parsedType := matches[0]
+
+ // varSize is the size of the variable
+ var varSize int
+ if len(parsedType[3]) > 0 {
+ var err error
+ varSize, err = strconv.Atoi(parsedType[2])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ } else {
+ if parsedType[0] == "uint" || parsedType[0] == "int" {
+ // this should fail because it means that there's something wrong with
+ // the abi type (the compiler should always format it to the size...always)
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+ }
+ // varType is the parsed abi type
+ switch varType := parsedType[1]; varType {
+ case "int":
+ typ.Size = varSize
+ typ.T = IntTy
+ case "uint":
+ typ.Size = varSize
+ typ.T = UintTy
+ case "bool":
+ typ.T = BoolTy
+ case "address":
+ typ.Size = 20
+ typ.T = AddressTy
+ case "string":
+ typ.T = StringTy
+ case "bytes":
+ if varSize == 0 {
+ typ.T = BytesTy
+ } else {
+ if varSize > 32 {
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+ typ.T = FixedBytesTy
+ typ.Size = varSize
+ }
+ case "tuple":
+ var (
+ fields []reflect.StructField
+ elems []*Type
+ names []string
+ expression string // canonical parameter expression
+ used = make(map[string]bool)
+ )
+ expression += "("
+ for idx, c := range components {
+ cType, err := NewType(c.Type, c.InternalType, c.Components)
+ if err != nil {
+ return Type{}, err
+ }
+ name := ToCamelCase(c.Name)
+ if name == "" {
+ return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
+ }
+ fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
+ if err != nil {
+ return Type{}, err
+ }
+ used[fieldName] = true
+ if !isValidFieldName(fieldName) {
+ return Type{}, fmt.Errorf("field %d has invalid name", idx)
+ }
+ fields = append(fields, reflect.StructField{
+ Name: fieldName, // reflect.StructOf will panic for any exported field.
+ Type: cType.GetType(),
+ Tag: reflect.StructTag("json:\"" + c.Name + "\""),
+ })
+ elems = append(elems, &cType)
+ names = append(names, c.Name)
+ expression += cType.stringKind
+ if idx != len(components)-1 {
+ expression += ","
+ }
+ }
+ expression += ")"
+
+ typ.TupleType = reflect.StructOf(fields)
+ typ.TupleElems = elems
+ typ.TupleRawNames = names
+ typ.T = TupleTy
+ typ.stringKind = expression
+
+ const structPrefix = "struct "
+ // After solidity 0.5.10, a new field of abi "internalType"
+ // is introduced. From that we can obtain the struct name
+ // user defined in the source code.
+ if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
+ // Foo.Bar type definition is not allowed in golang,
+ // convert the format to FooBar
+ typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
+ }
+
+ case "function":
+ typ.T = FunctionTy
+ typ.Size = 24
+ default:
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+
+ return
+}
+
+// GetType returns the reflection type of the ABI type.
+func (t Type) GetType() reflect.Type {
+ switch t.T {
+ case IntTy:
+ return reflectIntType(false, t.Size)
+ case UintTy:
+ return reflectIntType(true, t.Size)
+ case BoolTy:
+ return reflect.TypeOf(false)
+ case StringTy:
+ return reflect.TypeOf("")
+ case SliceTy:
+ return reflect.SliceOf(t.Elem.GetType())
+ case ArrayTy:
+ return reflect.ArrayOf(t.Size, t.Elem.GetType())
+ case TupleTy:
+ return t.TupleType
+ case AddressTy:
+ return reflect.TypeOf(types.Address{})
+ case FixedBytesTy:
+ return reflect.ArrayOf(t.Size, reflect.TypeOf(byte(0)))
+ case BytesTy:
+ return reflect.SliceOf(reflect.TypeOf(byte(0)))
+ case HashTy:
+ // hashtype currently not used
+ return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
+ case FixedPointTy:
+ // fixedpoint type currently not used
+ return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
+ case FunctionTy:
+ return reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
+ default:
+ panic("Invalid type")
+ }
+}
+
+// String implements Stringer.
+func (t Type) String() (out string) {
+ return t.stringKind
+}
+
+func (t Type) pack(v reflect.Value) ([]byte, error) {
+ // dereference pointer first if it's a pointer
+ v = indirect(v)
+ if err := typeCheck(t, v); err != nil {
+ return nil, err
+ }
+
+ switch t.T {
+ case SliceTy, ArrayTy:
+ var ret []byte
+
+ if t.requiresLengthPrefix() {
+ // append length
+ ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
+ }
+
+ // calculate offset if any
+ offset := 0
+ offsetReq := isDynamicType(*t.Elem)
+ if offsetReq {
+ offset = getTypeSize(*t.Elem) * v.Len()
+ }
+ var tail []byte
+ for i := 0; i < v.Len(); i++ {
+ val, err := t.Elem.pack(v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ if !offsetReq {
+ ret = append(ret, val...)
+ continue
+ }
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ offset += len(val)
+ tail = append(tail, val...)
+ }
+ return append(ret, tail...), nil
+ case TupleTy:
+ // (T1,...,Tk) for k >= 0 and any types T1, …, Tk
+ // enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
+ // where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
+ // type as
+ // head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
+ // and as
+ // head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
+ // tail(X(i)) = enc(X(i))
+ // otherwise, i.e. if Ti is a dynamic type.
+ fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
+ if err != nil {
+ return nil, err
+ }
+ // Calculate prefix occupied size.
+ offset := 0
+ for _, elem := range t.TupleElems {
+ offset += getTypeSize(*elem)
+ }
+ var ret, tail []byte
+ for i, elem := range t.TupleElems {
+ field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
+ if !field.IsValid() {
+ return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
+ }
+ val, err := elem.pack(field)
+ if err != nil {
+ return nil, err
+ }
+ if isDynamicType(*elem) {
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ tail = append(tail, val...)
+ offset += len(val)
+ } else {
+ ret = append(ret, val...)
+ }
+ }
+ return append(ret, tail...), nil
+
+ default:
+ return packElement(t, v)
+ }
+}
+
+// requireLengthPrefix returns whether the type requires any sort of length
+// prefixing.
+func (t Type) requiresLengthPrefix() bool {
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
+}
+
+// isDynamicType returns true if the type is dynamic.
+// The following types are called “dynamic”:
+// * bytes
+// * string
+// * T[] for any T
+// * T[k] for any dynamic T and any k >= 0
+// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
+func isDynamicType(t Type) bool {
+ if t.T == TupleTy {
+ for _, elem := range t.TupleElems {
+ if isDynamicType(*elem) {
+ return true
+ }
+ }
+ return false
+ }
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
+}
+
+// getTypeSize returns the size that this type needs to occupy.
+// We distinguish static and dynamic types. Static types are encoded in-place
+// and dynamic types are encoded at a separately allocated location after the
+// current block.
+// So for a static variable, the size returned represents the size that the
+// variable actually occupies.
+// For a dynamic variable, the returned size is fixed 32 bytes, which is used
+// to store the location reference for actual value storage.
+func getTypeSize(t Type) int {
+ if t.T == ArrayTy && !isDynamicType(*t.Elem) {
+ // Recursively calculate type size if it is a nested array
+ if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
+ return t.Size * getTypeSize(*t.Elem)
+ }
+ return t.Size * 32
+ } else if t.T == TupleTy && !isDynamicType(t) {
+ total := 0
+ for _, elem := range t.TupleElems {
+ total += getTypeSize(*elem)
+ }
+ return total
+ }
+ return 32
+}
+
+// isLetter reports whether a given 'rune' is classified as a Letter.
+// This method is copied from reflect/type.go
+func isLetter(ch rune) bool {
+ return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
+}
+
+// isValidFieldName checks if a string is a valid (struct) field name or not.
+//
+// According to the language spec, a field name should be an identifier.
+//
+// identifier = letter { letter | unicode_digit } .
+// letter = unicode_letter | "_" .
+// This method is copied from reflect/type.go
+func isValidFieldName(fieldName string) bool {
+ for i, c := range fieldName {
+ if i == 0 && !isLetter(c) {
+ return false
+ }
+
+ if !(isLetter(c) || unicode.IsDigit(c)) {
+ return false
+ }
+ }
+
+ return len(fieldName) > 0
+}
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
new file mode 100644
index 0000000..f4ef8dd
--- /dev/null
+++ b/accounts/abi/unpack.go
@@ -0,0 +1,297 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import (
+ "encoding/binary"
+ "fmt"
+ "github.com/astranetworld/ast/common"
+ "github.com/astranetworld/ast/common/types"
+ "math/big"
+ "reflect"
+)
+
+var (
+ // MaxUint256 is the maximum value that can be represented by a uint256.
+ MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
+ // MaxInt256 is the maximum value that can be represented by a int256.
+ MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1)
+)
+
+// ReadInteger reads the integer based on its kind and returns the appropriate value.
+func ReadInteger(typ Type, b []byte) interface{} {
+ if typ.T == UintTy {
+ switch typ.Size {
+ case 8:
+ return b[len(b)-1]
+ case 16:
+ return binary.BigEndian.Uint16(b[len(b)-2:])
+ case 32:
+ return binary.BigEndian.Uint32(b[len(b)-4:])
+ case 64:
+ return binary.BigEndian.Uint64(b[len(b)-8:])
+ default:
+ // the only case left for unsigned integer is uint256.
+ return new(big.Int).SetBytes(b)
+ }
+ }
+ switch typ.Size {
+ case 8:
+ return int8(b[len(b)-1])
+ case 16:
+ return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
+ case 32:
+ return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
+ case 64:
+ return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
+ default:
+ // the only case left for integer is int256
+ // big.SetBytes can't tell if a number is negative or positive in itself.
+ // On EVM, if the returned number > max int256, it is negative.
+ // A number is > max int256 if the bit at position 255 is set.
+ ret := new(big.Int).SetBytes(b)
+ if ret.Bit(255) == 1 {
+ ret.Add(MaxUint256, new(big.Int).Neg(ret))
+ ret.Add(ret, common.Big1)
+ ret.Neg(ret)
+ }
+ return ret
+ }
+}
+
+// readBool reads a bool.
+func readBool(word []byte) (bool, error) {
+ for _, b := range word[:31] {
+ if b != 0 {
+ return false, errBadBool
+ }
+ }
+ switch word[31] {
+ case 0:
+ return false, nil
+ case 1:
+ return true, nil
+ default:
+ return false, errBadBool
+ }
+}
+
+// A function type is simply the address with the function selection signature at the end.
+//
+// readFunctionType enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
+func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
+ if t.T != FunctionTy {
+ return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
+ }
+ if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
+ err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
+ } else {
+ copy(funcTy[:], word[0:24])
+ }
+ return
+}
+
+// ReadFixedBytes uses reflection to create a fixed array to be read from.
+func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
+ if t.T != FixedBytesTy {
+ return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
+ }
+ // convert
+ array := reflect.New(t.GetType()).Elem()
+
+ reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
+ return array.Interface(), nil
+}
+
+// forEachUnpack iteratively unpack elements.
+func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+ if size < 0 {
+ return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
+ }
+ if start+32*size > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
+ }
+
+ // this value will become our slice or our array, depending on the type
+ var refSlice reflect.Value
+
+ if t.T == SliceTy {
+ // declare our slice
+ refSlice = reflect.MakeSlice(t.GetType(), size, size)
+ } else if t.T == ArrayTy {
+ // declare our array
+ refSlice = reflect.New(t.GetType()).Elem()
+ } else {
+ return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
+ }
+
+ // Arrays have packed elements, resulting in longer unpack steps.
+ // Slices have just 32 bytes per element (pointing to the contents).
+ elemSize := getTypeSize(*t.Elem)
+
+ for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
+ inter, err := toGoType(i, *t.Elem, output)
+ if err != nil {
+ return nil, err
+ }
+
+ // append the item to our reflect slice
+ refSlice.Index(j).Set(reflect.ValueOf(inter))
+ }
+
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
+func forTupleUnpack(t Type, output []byte) (interface{}, error) {
+ retval := reflect.New(t.GetType()).Elem()
+ virtualArgs := 0
+ for index, elem := range t.TupleElems {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
+ if err != nil {
+ return nil, err
+ }
+ if elem.T == ArrayTy && !isDynamicType(*elem) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ } else if elem.T == TupleTy && !isDynamicType(*elem) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ }
+ retval.Field(index).Set(reflect.ValueOf(marshalledValue))
+ }
+ return retval.Interface(), nil
+}
+
+// toGoType parses the output bytes and recursively assigns the value of these bytes
+// into a go type with accordance with the ABI spec.
+func toGoType(index int, t Type, output []byte) (interface{}, error) {
+ if index+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ }
+
+ var (
+ returnOutput []byte
+ begin, length int
+ err error
+ )
+
+ // if we require a length prefix, find the beginning word and size returned.
+ if t.requiresLengthPrefix() {
+ begin, length, err = lengthPrefixPointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ returnOutput = output[index : index+32]
+ }
+
+ switch t.T {
+ case TupleTy:
+ if isDynamicType(t) {
+ begin, err := tuplePointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ return forTupleUnpack(t, output[begin:])
+ }
+ return forTupleUnpack(t, output[index:])
+ case SliceTy:
+ return forEachUnpack(t, output[begin:], 0, length)
+ case ArrayTy:
+ if isDynamicType(*t.Elem) {
+ offset := binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:])
+ if offset > uint64(len(output)) {
+ return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output))
+ }
+ return forEachUnpack(t, output[offset:], 0, t.Size)
+ }
+ return forEachUnpack(t, output[index:], 0, t.Size)
+ case StringTy: // variable arrays are written at the end of the return bytes
+ return string(output[begin : begin+length]), nil
+ case IntTy, UintTy:
+ return ReadInteger(t, returnOutput), nil
+ case BoolTy:
+ return readBool(returnOutput)
+ case AddressTy:
+ return types.BytesToAddress(returnOutput), nil
+ case HashTy:
+ return types.BytesToHash(returnOutput), nil
+ case BytesTy:
+ return output[begin : begin+length], nil
+ case FixedBytesTy:
+ return ReadFixedBytes(t, returnOutput)
+ case FunctionTy:
+ return readFunctionType(t, returnOutput)
+ default:
+ return nil, fmt.Errorf("abi: unknown type %v", t.T)
+ }
+}
+
+// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
+func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
+ bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
+ bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
+ outputLength := big.NewInt(int64(len(output)))
+
+ if bigOffsetEnd.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
+ }
+
+ if bigOffsetEnd.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
+ }
+
+ offsetEnd := int(bigOffsetEnd.Uint64())
+ lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
+
+ totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
+ if totalSize.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
+ }
+
+ if totalSize.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
+ }
+ start = int(bigOffsetEnd.Uint64())
+ length = int(lengthBig.Uint64())
+ return
+}
+
+// tuplePointsTo resolves the location reference for dynamic tuple.
+func tuplePointsTo(index int, output []byte) (start int, err error) {
+ offset := new(big.Int).SetBytes(output[index : index+32])
+ outputLen := big.NewInt(int64(len(output)))
+
+ if offset.Cmp(outputLen) > 0 {
+ return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
+ }
+ if offset.BitLen() > 63 {
+ return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
+ }
+ return int(offset.Uint64()), nil
+}
diff --git a/accounts/abi/utils.go b/accounts/abi/utils.go
new file mode 100644
index 0000000..6fdef8f
--- /dev/null
+++ b/accounts/abi/utils.go
@@ -0,0 +1,40 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package abi
+
+import "fmt"
+
+// ResolveNameConflict returns the next available name for a given thing.
+// This helper can be used for lots of purposes:
+//
+// - In solidity function overloading is supported, this function can fix
+// the name conflicts of overloaded functions.
+// - In golang binding generation, the parameter(in function, event, error,
+// and struct definition) name will be converted to camelcase style which
+// may eventually lead to name conflicts.
+//
+// Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains
+// Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send".
+func ResolveNameConflict(rawName string, used func(string) bool) string {
+ name := rawName
+ ok := used(name)
+ for idx := 0; ok; idx++ {
+ name = fmt.Sprintf("%s%d", rawName, idx)
+ ok = used(name)
+ }
+ return name
+}
diff --git a/accounts/accounts.go b/accounts/accounts.go
new file mode 100644
index 0000000..98bc9bc
--- /dev/null
+++ b/accounts/accounts.go
@@ -0,0 +1,226 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package accounts implements high level Ethereum account management.
+package accounts
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/astranetworld/ast/common"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ event "github.com/astranetworld/ast/modules/event/v2"
+ "golang.org/x/crypto/sha3"
+)
+
+// Account represents an Ethereum account located at a specific location defined
+// by the optional URL field.
+type Account struct {
+ Address types.Address `json:"address"` // Ethereum account address derived from the key
+ URL URL `json:"url"` // Optional resource locator within a backend
+}
+
+const (
+ MimetypeDataWithValidator = "data/validator"
+ MimetypeTypedData = "data/typed"
+ MimetypeClique = "application/x-clique-header"
+ MimetypeTextPlain = "text/plain"
+)
+
+// Wallet represents a software or hardware wallet that might contain one or more
+// accounts (derived from the same seed).
+type Wallet interface {
+ // URL retrieves the canonical path under which this wallet is reachable. It is
+ // used by upper layers to define a sorting order over all wallets from multiple
+ // backends.
+ URL() URL
+
+ // Status returns a textual status to aid the user in the current state of the
+ // wallet. It also returns an error indicating any failure the wallet might have
+ // encountered.
+ Status() (string, error)
+
+ // Open initializes access to a wallet instance. It is not meant to unlock or
+ // decrypt account keys, rather simply to establish a connection to hardware
+ // wallets and/or to access derivation seeds.
+ //
+ // The passphrase parameter may or may not be used by the implementation of a
+ // particular wallet instance. The reason there is no passwordless open method
+ // is to strive towards a uniform wallet handling, oblivious to the different
+ // backend providers.
+ //
+ // Please note, if you open a wallet, you must close it to release any allocated
+ // resources (especially important when working with hardware wallets).
+ Open(passphrase string) error
+
+ // Close releases any resources held by an open wallet instance.
+ Close() error
+
+ // Accounts retrieves the list of signing accounts the wallet is currently aware
+ // of. For hierarchical deterministic wallets, the list will not be exhaustive,
+ // rather only contain the accounts explicitly pinned during account derivation.
+ Accounts() []Account
+
+ // Contains returns whether an account is part of this particular wallet or not.
+ Contains(account Account) bool
+
+ // Derive attempts to explicitly derive a hierarchical deterministic account at
+ // the specified derivation path. If requested, the derived account will be added
+ // to the wallet's tracked account list.
+ Derive(path DerivationPath, pin bool) (Account, error)
+
+ // SelfDerive sets a base account derivation path from which the wallet attempts
+ // to discover non zero accounts and automatically add them to list of tracked
+ // accounts.
+ //
+ // Note, self derivation will increment the last component of the specified path
+ // opposed to descending into a child path to allow discovering accounts starting
+ // from non zero components.
+ //
+ // Some hardware wallets switched derivation paths through their evolution, so
+ // this method supports providing multiple bases to discover old user accounts
+ // too. Only the last base will be used to derive the next empty account.
+ //
+ // You can disable automatic account discovery by calling SelfDerive with a nil
+ // chain state reader.
+ SelfDerive(bases []DerivationPath, chain common.ChainStateReader)
+
+ // SignData requests the wallet to sign the hash of the given data
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code to verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignDataWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignData(account Account, mimeType string, data []byte) ([]byte, error)
+
+ // SignDataWithPassphrase is identical to SignData, but also takes a password
+ // NOTE: there's a chance that an erroneous call might mistake the two strings, and
+ // supply password in the mimetype field, or vice versa. Thus, an implementation
+ // should never echo the mimetype or return the mimetype in the error-response
+ SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error)
+
+ // SignText requests the wallet to sign the hash of a given piece of data, prefixed
+ // by the Ethereum prefix scheme
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code to verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignTextWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ //
+ // This method should return the signature in 'canonical' format, with v 0 or 1.
+ SignText(account Account, text []byte) ([]byte, error)
+
+ // SignTextWithPassphrase is identical to Signtext, but also takes a password
+ SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
+
+ // SignTx requests the wallet to sign the given transaction.
+ //
+ // It looks up the account specified either solely via its address contained within,
+ // or optionally with the aid of any location metadata from the embedded URL field.
+ //
+ // If the wallet requires additional authentication to sign the request (e.g.
+ // a password to decrypt the account, or a PIN code to verify the transaction),
+ // an AuthNeededError instance will be returned, containing infos for the user
+ // about which fields or actions are needed. The user may retry by providing
+ // the needed details via SignTxWithPassphrase, or by other means (e.g. unlock
+ // the account in a keystore).
+ SignTx(account Account, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error)
+
+ // SignTxWithPassphrase is identical to SignTx, but also takes a password
+ SignTxWithPassphrase(account Account, passphrase string, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error)
+}
+
+// Backend is a "wallet provider" that may contain a batch of accounts they can
+// sign transactions with and upon request, do so.
+type Backend interface {
+ // Wallets retrieves the list of wallets the backend is currently aware of.
+ //
+ // The returned wallets are not opened by default. For software HD wallets this
+ // means that no base seeds are decrypted, and for hardware wallets that no actual
+ // connection is established.
+ //
+ // The resulting wallet list will be sorted alphabetically based on its internal
+ // URL assigned by the backend. Since wallets (especially hardware) may come and
+ // go, the same wallet might appear at a different positions in the list during
+ // subsequent retrievals.
+ Wallets() []Wallet
+
+ // Subscribe creates an async subscription to receive notifications when the
+ // backend detects the arrival or departure of a wallet.
+ Subscribe(sink chan<- WalletEvent) event.Subscription
+}
+
+// TextHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calculated as
+//
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func TextHash(data []byte) []byte {
+ hash, _ := TextAndHash(data)
+ return hash
+}
+
+// TextAndHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calculated as
+//
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func TextAndHash(data []byte) ([]byte, string) {
+ msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
+ hasher := sha3.NewLegacyKeccak256()
+ hasher.Write([]byte(msg))
+ return hasher.Sum(nil), msg
+}
+
+// WalletEventType represents the different event types that can be fired by
+// the wallet subscription subsystem.
+type WalletEventType int
+
+const (
+ // WalletArrived is fired when a new wallet is detected either via USB or via
+ // a filesystem event in the keystore.
+ WalletArrived WalletEventType = iota
+
+ // WalletOpened is fired when a wallet is successfully opened with the purpose
+ // of starting any background processes such as automatic key derivation.
+ WalletOpened
+
+ // WalletDropped
+ WalletDropped
+)
+
+// WalletEvent is an event fired by an account backend when a wallet arrival or
+// departure is detected.
+type WalletEvent struct {
+ Wallet Wallet // Wallet instance arrived or departed
+ Kind WalletEventType // Event type that happened in the system
+}
diff --git a/accounts/accounts_test.go b/accounts/accounts_test.go
new file mode 100644
index 0000000..55ff70b
--- /dev/null
+++ b/accounts/accounts_test.go
@@ -0,0 +1,56 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "bytes"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/math"
+ "github.com/ledgerwatch/secp256k1"
+ "testing"
+
+ "github.com/astranetworld/ast/common/hexutil"
+)
+
+func TestTextHash(t *testing.T) {
+ hash := TextHash([]byte("Hello Joe"))
+ want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
+ if !bytes.Equal(hash, want) {
+ t.Fatalf("wrong hash: %x", hash)
+ }
+}
+
+func TestSign(t *testing.T) {
+ private, err := crypto.HexToECDSA("DEBF9EAE7820E23201EEE9D51413B6D2CDF06C320D7152C2D3BC1FB6C42DA23D")
+ if nil != err {
+ t.Error(err)
+ }
+ seckey := math.PaddedBigBytes(private.D, private.Params().BitSize/8)
+ defer zeroBytes(seckey)
+
+ msg, _ := hexutil.Decode("0x08712134afd46d42a45a5ed0e9311933138a06041b88062e590a613c2673c29f")
+ signature, err := secp256k1.Sign(msg, seckey)
+
+ t.Logf("%v", err)
+ t.Logf("%s", hexutil.Encode(signature))
+}
+
+func zeroBytes(bytes []byte) {
+ for i := range bytes {
+ bytes[i] = 0
+ }
+}
diff --git a/accounts/errors.go b/accounts/errors.go
new file mode 100644
index 0000000..fab58ed
--- /dev/null
+++ b/accounts/errors.go
@@ -0,0 +1,67 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "errors"
+ "fmt"
+)
+
+// ErrUnknownAccount is returned for any requested operation for which no backend
+// provides the specified account.
+var ErrUnknownAccount = errors.New("unknown account")
+
+// ErrUnknownWallet is returned for any requested operation for which no backend
+// provides the specified wallet.
+var ErrUnknownWallet = errors.New("unknown wallet")
+
+// ErrNotSupported is returned when an operation is requested from an account
+// backend that it does not support.
+var ErrNotSupported = errors.New("not supported")
+
+// ErrInvalidPassphrase is returned when a decryption operation receives a bad
+// passphrase.
+var ErrInvalidPassphrase = errors.New("invalid password")
+
+// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
+// second time.
+var ErrWalletAlreadyOpen = errors.New("wallet already open")
+
+// ErrWalletClosed is returned if a wallet is offline.
+var ErrWalletClosed = errors.New("wallet closed")
+
+// AuthNeededError is returned by backends for signing requests where the user
+// is required to provide further authentication before signing can succeed.
+//
+// This usually means either that a password needs to be supplied, or perhaps a
+// one time PIN code displayed by some hardware device.
+type AuthNeededError struct {
+ Needed string // Extra authentication the user needs to provide
+}
+
+// NewAuthNeededError creates a new authentication error with the extra details
+// about the needed fields set.
+func NewAuthNeededError(needed string) error {
+ return &AuthNeededError{
+ Needed: needed,
+ }
+}
+
+// Error implements the standard error interface.
+func (err *AuthNeededError) Error() string {
+ return fmt.Sprintf("authentication needed: %s", err.Needed)
+}
diff --git a/accounts/external/backend.go b/accounts/external/backend.go
new file mode 100644
index 0000000..4d7fbb1
--- /dev/null
+++ b/accounts/external/backend.go
@@ -0,0 +1,269 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package external
+
+//import (
+// "fmt"
+// "math/big"
+// "sync"
+//
+// "github.com/astranetworld/ast/accounts"
+// "github.com/astranetworld/ast/common"
+// types2 "github.com/astranetworld/ast/common/types"
+// "github.com/astranetworld/ast/internal/avm/common/hexutil"
+// "github.com/astranetworld/ast/internal/avm/types"
+// "github.com/astranetworld/ast/log"
+// event "github.com/astranetworld/ast/modules/event/v2"
+// "github.com/astranetworld/ast/rpc"
+// "github.com/astranetworld/ast/signer/core/apitypes"
+//)
+//
+//type ExternalBackend struct {
+// signers []accounts.Wallet
+//}
+//
+//func (eb *ExternalBackend) Wallets() []accounts.Wallet {
+// return eb.signers
+//}
+//
+//func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
+// signer, err := NewExternalSigner(endpoint)
+// if err != nil {
+// return nil, err
+// }
+// return &ExternalBackend{
+// signers: []accounts.Wallet{signer},
+// }, nil
+//}
+//
+//func (eb *ExternalBackend) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+// return event.NewSubscription(func(quit <-chan struct{}) error {
+// <-quit
+// return nil
+// })
+//}
+//
+//// ExternalSigner provides an API to interact with an external signer (clef)
+//// It proxies request to the external signer while forwarding relevant
+//// request headers
+//type ExternalSigner struct {
+// client *rpc.Client
+// endpoint string
+// status string
+// cacheMu sync.RWMutex
+// cache []accounts.Account
+//}
+//
+//func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
+// client, err := rpc.Dial(endpoint)
+// if err != nil {
+// return nil, err
+// }
+// extsigner := &ExternalSigner{
+// client: client,
+// endpoint: endpoint,
+// }
+// // Check if reachable
+// version, err := extsigner.pingVersion()
+// if err != nil {
+// return nil, err
+// }
+// extsigner.status = fmt.Sprintf("ok [version=%v]", version)
+// return extsigner, nil
+//}
+//
+//func (api *ExternalSigner) URL() accounts.URL {
+// return accounts.URL{
+// Scheme: "extapi",
+// Path: api.endpoint,
+// }
+//}
+//
+//func (api *ExternalSigner) Status() (string, error) {
+// return api.status, nil
+//}
+//
+//func (api *ExternalSigner) Open(passphrase string) error {
+// return fmt.Errorf("operation not supported on external signers")
+//}
+//
+//func (api *ExternalSigner) Close() error {
+// return fmt.Errorf("operation not supported on external signers")
+//}
+//
+//func (api *ExternalSigner) Accounts() []accounts.Account {
+// var accnts []accounts.Account
+// res, err := api.listAccounts()
+// if err != nil {
+// log.Error("account listing failed", "error", err)
+// return accnts
+// }
+// for _, addr := range res {
+// accnts = append(accnts, accounts.Account{
+// URL: accounts.URL{
+// Scheme: "extapi",
+// Path: api.endpoint,
+// },
+// Address: addr,
+// })
+// }
+// api.cacheMu.Lock()
+// api.cache = accnts
+// api.cacheMu.Unlock()
+// return accnts
+//}
+//
+//func (api *ExternalSigner) Contains(account accounts.Account) bool {
+// api.cacheMu.RLock()
+// defer api.cacheMu.RUnlock()
+// if api.cache == nil {
+// // If we haven't already fetched the accounts, it's time to do so now
+// api.cacheMu.RUnlock()
+// api.Accounts()
+// api.cacheMu.RLock()
+// }
+// for _, a := range api.cache {
+// if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
+// return true
+// }
+// }
+// return false
+//}
+//
+//func (api *ExternalSigner) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+// return accounts.Account{}, fmt.Errorf("operation not supported on external signers")
+//}
+//
+//func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain common.ChainStateReader) {
+// log.Error("operation SelfDerive not supported on external signers")
+//}
+//
+//// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
+//func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+// var res hexutil.Bytes
+// var signAddress = common.NewMixedcaseAddress(account.Address)
+// if err := api.client.Call(&res, "account_signData",
+// mimeType,
+// &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
+// hexutil.Encode(data)); err != nil {
+// return nil, err
+// }
+// // If V is on 27/28-form, convert to 0/1 for Clique
+// if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
+// res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
+// }
+// return res, nil
+//}
+//
+//func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
+// var signature hexutil.Bytes
+// var signAddress = common.NewMixedcaseAddress(account.Address)
+// if err := api.client.Call(&signature, "account_signData",
+// accounts.MimetypeTextPlain,
+// &signAddress, // Need to use the pointer here, because of how MarshalJSON is defined
+// hexutil.Encode(text)); err != nil {
+// return nil, err
+// }
+// if signature[64] == 27 || signature[64] == 28 {
+// // If clef is used as a backend, it may already have transformed
+// // the signature to ethereum-type signature.
+// signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
+// }
+// return signature, nil
+//}
+//
+//// signTransactionResult represents the signinig result returned by clef.
+//type signTransactionResult struct {
+// Raw hexutil.Bytes `json:"raw"`
+// Tx *types.Transaction `json:"tx"`
+//}
+//
+//// SignTx sends the transaction to the external signer.
+//// If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned
+//// by the external signer. For non-legacy transactions, the chain ID of the
+//// transaction overrides the chainID parameter.
+//func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+// data := hexutil.Bytes(tx.Data())
+// var to *common.MixedcaseAddress
+// if tx.To() != nil {
+// t := common.NewMixedcaseAddress(*tx.To())
+// to = &t
+// }
+// args := &apitypes.SendTxArgs{
+// Data: &data,
+// Nonce: hexutil.Uint64(tx.Nonce()),
+// Value: hexutil.Big(*tx.Value()),
+// Gas: hexutil.Uint64(tx.Gas()),
+// To: to,
+// From: common.NewMixedcaseAddress(account.Address),
+// }
+// switch tx.Type() {
+// case types.LegacyTxType, types.AccessListTxType:
+// args.GasPrice = (*hexutil.Big)(tx.GasPrice())
+// case types.DynamicFeeTxType:
+// args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
+// args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
+// default:
+// return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
+// }
+// // We should request the default chain id that we're operating with
+// // (the chain we're executing on)
+// if chainID != nil && chainID.Sign() != 0 {
+// args.ChainID = (*hexutil.Big)(chainID)
+// }
+// if tx.Type() != types.LegacyTxType {
+// // However, if the user asked for a particular chain id, then we should
+// // use that instead.
+// if tx.ChainId().Sign() != 0 {
+// args.ChainID = (*hexutil.Big)(tx.ChainId())
+// }
+// accessList := tx.AccessList()
+// args.AccessList = &accessList
+// }
+// var res signTransactionResult
+// if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
+// return nil, err
+// }
+// return res.Tx, nil
+//}
+//
+//func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+// return []byte{}, fmt.Errorf("password-operations not supported on external signers")
+//}
+//
+//func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+// return nil, fmt.Errorf("password-operations not supported on external signers")
+//}
+//func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+// return nil, fmt.Errorf("password-operations not supported on external signers")
+//}
+//
+//func (api *ExternalSigner) listAccounts() ([]types2.Address, error) {
+// var res []types2.Address
+// if err := api.client.Call(&res, "account_list"); err != nil {
+// return nil, err
+// }
+// return res, nil
+//}
+//
+//func (api *ExternalSigner) pingVersion() (string, error) {
+// var v string
+// if err := api.client.Call(&v, "account_version"); err != nil {
+// return "", err
+// }
+// return v, nil
+//}
diff --git a/accounts/hd.go b/accounts/hd.go
new file mode 100644
index 0000000..cab830e
--- /dev/null
+++ b/accounts/hd.go
@@ -0,0 +1,180 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "strings"
+)
+
+// DefaultRootDerivationPath is the root path to which custom derivation endpoints
+// are appended. As such, the first account will be at m/44'/60'/0'/0, the second
+// at m/44'/60'/0'/1, etc.
+var DefaultRootDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DefaultBaseDerivationPath is the base path from which custom derivation endpoints
+// are incremented. As such, the first account will be at m/44'/60'/0'/0/0, the second
+// at m/44'/60'/0'/0/1, etc.
+var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}
+
+// LegacyLedgerBaseDerivationPath is the legacy base path from which custom derivation
+// endpoints are incremented. As such, the first account will be at m/44'/60'/0'/0, the
+// second at m/44'/60'/0'/1, etc.
+var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
+
+// DerivationPath represents the computer friendly version of a hierarchical
+// deterministic wallet account derivation path.
+//
+// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
+// defines derivation paths to be of the form:
+//
+// m / purpose' / coin_type' / account' / change / address_index
+//
+// The BIP-44 spec https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
+// defines that the `purpose` be 44' (or 0x8000002C) for crypto currencies, and
+// SLIP-44 https://github.com/satoshilabs/slips/blob/master/slip-0044.md assigns
+// the `coin_type` 60' (or 0x8000003C) to Ethereum.
+//
+// The root path for Ethereum is m/44'/60'/0'/0 according to the specification
+// from https://github.com/ethereum/EIPs/issues/84, albeit it's not set in stone
+// yet whether accounts should increment the last component or the children of
+// that. We will go with the simpler approach of incrementing the last component.
+type DerivationPath []uint32
+
+// ParseDerivationPath converts a user specified derivation path string to the
+// internal binary representation.
+//
+// Full derivation paths need to start with the `m/` prefix, relative derivation
+// paths (which will get appended to the default root path) must not have prefixes
+// in front of the first element. Whitespace is ignored.
+func ParseDerivationPath(path string) (DerivationPath, error) {
+ var result DerivationPath
+
+ // Handle absolute or relative paths
+ components := strings.Split(path, "/")
+ switch {
+ case len(components) == 0:
+ return nil, errors.New("empty derivation path")
+
+ case strings.TrimSpace(components[0]) == "":
+ return nil, errors.New("ambiguous path: use 'm/' prefix for absolute paths, or no leading '/' for relative ones")
+
+ case strings.TrimSpace(components[0]) == "m":
+ components = components[1:]
+
+ default:
+ result = append(result, DefaultRootDerivationPath...)
+ }
+ // All remaining components are relative, append one by one
+ if len(components) == 0 {
+ return nil, errors.New("empty derivation path") // Empty relative paths
+ }
+ for _, component := range components {
+ // Ignore any user added whitespace
+ component = strings.TrimSpace(component)
+ var value uint32
+
+ // Handle hardened paths
+ if strings.HasSuffix(component, "'") {
+ value = 0x80000000
+ component = strings.TrimSpace(strings.TrimSuffix(component, "'"))
+ }
+ // Handle the non hardened component
+ bigval, ok := new(big.Int).SetString(component, 0)
+ if !ok {
+ return nil, fmt.Errorf("invalid component: %s", component)
+ }
+ max := math.MaxUint32 - value
+ if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
+ if value == 0 {
+ return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
+ }
+ return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
+ }
+ value += uint32(bigval.Uint64())
+
+ // Append and repeat
+ result = append(result, value)
+ }
+ return result, nil
+}
+
+// String implements the stringer interface, converting a binary derivation path
+// to its canonical representation.
+func (path DerivationPath) String() string {
+ result := "m"
+ for _, component := range path {
+ var hardened bool
+ if component >= 0x80000000 {
+ component -= 0x80000000
+ hardened = true
+ }
+ result = fmt.Sprintf("%s/%d", result, component)
+ if hardened {
+ result += "'"
+ }
+ }
+ return result
+}
+
+// MarshalJSON turns a derivation path into its json-serialized string
+func (path DerivationPath) MarshalJSON() ([]byte, error) {
+ return json.Marshal(path.String())
+}
+
+// UnmarshalJSON a json-serialized string back into a derivation path
+func (path *DerivationPath) UnmarshalJSON(b []byte) error {
+ var dp string
+ var err error
+ if err = json.Unmarshal(b, &dp); err != nil {
+ return err
+ }
+ *path, err = ParseDerivationPath(dp)
+ return err
+}
+
+// DefaultIterator creates a BIP-32 path iterator, which progresses by increasing the last component:
+// i.e. m/44'/60'/0'/0/0, m/44'/60'/0'/0/1, m/44'/60'/0'/0/2, ... m/44'/60'/0'/0/N.
+func DefaultIterator(base DerivationPath) func() DerivationPath {
+ path := make(DerivationPath, len(base))
+ copy(path[:], base[:])
+ // Set it back by one, so the first call gives the first result
+ path[len(path)-1]--
+ return func() DerivationPath {
+ path[len(path)-1]++
+ return path
+ }
+}
+
+// LedgerLiveIterator creates a bip44 path iterator for Ledger Live.
+// Ledger Live increments the third component rather than the fifth component
+// i.e. m/44'/60'/0'/0/0, m/44'/60'/1'/0/0, m/44'/60'/2'/0/0, ... m/44'/60'/N'/0/0.
+func LedgerLiveIterator(base DerivationPath) func() DerivationPath {
+ path := make(DerivationPath, len(base))
+ copy(path[:], base[:])
+ // Set it back by one, so the first call gives the first result
+ path[2]--
+ return func() DerivationPath {
+ // ledgerLivePathIterator iterates on the third component
+ path[2]++
+ return path
+ }
+}
diff --git a/accounts/hd_test.go b/accounts/hd_test.go
new file mode 100644
index 0000000..756aebd
--- /dev/null
+++ b/accounts/hd_test.go
@@ -0,0 +1,118 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+// Tests that HD derivation paths can be correctly parsed into our internal binary
+// representation.
+func TestHDPathParsing(t *testing.T) {
+ tests := []struct {
+ input string
+ output DerivationPath
+ }{
+ // Plain absolute derivation paths
+ {"m/44'/60'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/44'/60'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"m/44'/60'/0'/0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"m/44'/60'/0'/128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"m/2147483692/2147483708/2147483648/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/2147483692/2147483708/2147483648/2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Plain relative derivation paths
+ {"0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}},
+ {"128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 128}},
+ {"0'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}},
+ {"128'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 128}},
+ {"2147483648", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}},
+
+ // Hexadecimal absolute derivation paths
+ {"m/0x2C'/0x3c'/0x00'/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/0x2C'/0x3c'/0x00'/0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}},
+ {"m/0x2C'/0x3c'/0x00'/0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+ {"m/0x2C'/0x3c'/0x00'/0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 128}},
+ {"m/0x8000002C/0x8000003c/0x80000000/0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+ {"m/0x8000002C/0x8000003c/0x80000000/0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0x80000000 + 0}},
+
+ // Hexadecimal relative derivation paths
+ {"0x00", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0}},
+ {"0x80", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 128}},
+ {"0x00'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}},
+ {"0x80'", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 128}},
+ {"0x80000000", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0x80000000 + 0}},
+
+ // Weird inputs just to ensure they work
+ {" m / 44 '\n/\n 60 \n\n\t' /\n0 ' /\t\t 0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}},
+
+ // Invalid derivation paths
+ {"", nil}, // Empty relative derivation path
+ {"m", nil}, // Empty absolute derivation path
+ {"m/", nil}, // Missing last derivation component
+ {"/44'/60'/0'/0", nil}, // Absolute path without m prefix, might be user error
+ {"m/2147483648'", nil}, // Overflows 32 bit integer
+ {"m/-1'", nil}, // Cannot contain negative number
+ }
+ for i, tt := range tests {
+ if path, err := ParseDerivationPath(tt.input); !reflect.DeepEqual(path, tt.output) {
+ t.Errorf("test %d: parse mismatch: have %v (%v), want %v", i, path, err, tt.output)
+ } else if path == nil && err == nil {
+ t.Errorf("test %d: nil path and error: %v", i, err)
+ }
+ }
+}
+
+func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
+ t.Helper()
+ for i, want := range expected {
+ if have := next(); fmt.Sprintf("%v", have) != want {
+ t.Errorf("step %d, have %v, want %v", i, have, want)
+ }
+ }
+}
+
+func TestHdPathIteration(t *testing.T) {
+ testDerive(t, DefaultIterator(DefaultBaseDerivationPath),
+ []string{
+ "m/44'/60'/0'/0/0", "m/44'/60'/0'/0/1",
+ "m/44'/60'/0'/0/2", "m/44'/60'/0'/0/3",
+ "m/44'/60'/0'/0/4", "m/44'/60'/0'/0/5",
+ "m/44'/60'/0'/0/6", "m/44'/60'/0'/0/7",
+ "m/44'/60'/0'/0/8", "m/44'/60'/0'/0/9",
+ })
+
+ testDerive(t, DefaultIterator(LegacyLedgerBaseDerivationPath),
+ []string{
+ "m/44'/60'/0'/0", "m/44'/60'/0'/1",
+ "m/44'/60'/0'/2", "m/44'/60'/0'/3",
+ "m/44'/60'/0'/4", "m/44'/60'/0'/5",
+ "m/44'/60'/0'/6", "m/44'/60'/0'/7",
+ "m/44'/60'/0'/8", "m/44'/60'/0'/9",
+ })
+
+ testDerive(t, LedgerLiveIterator(DefaultBaseDerivationPath),
+ []string{
+ "m/44'/60'/0'/0/0", "m/44'/60'/1'/0/0",
+ "m/44'/60'/2'/0/0", "m/44'/60'/3'/0/0",
+ "m/44'/60'/4'/0/0", "m/44'/60'/5'/0/0",
+ "m/44'/60'/6'/0/0", "m/44'/60'/7'/0/0",
+ "m/44'/60'/8'/0/0", "m/44'/60'/9'/0/0",
+ })
+}
diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go
new file mode 100644
index 0000000..1e1d957
--- /dev/null
+++ b/accounts/keystore/account_cache.go
@@ -0,0 +1,317 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ mapset "github.com/deckarep/golang-set/v2"
+)
+
+// Minimum amount of time between cache reloads. This limit applies if the platform does
+// not support change notifications. It also applies if the keystore directory does not
+// exist yet, the code will attempt to create a watcher at most this often.
+const minReloadInterval = 2 * time.Second
+
+type accountsByURL []accounts.Account
+
+func (s accountsByURL) Len() int { return len(s) }
+func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
+func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// AmbiguousAddrError is returned when attempting to unlock
+// an address for which more than one file exists.
+type AmbiguousAddrError struct {
+ Addr types.Address
+ Matches []accounts.Account
+}
+
+func (err *AmbiguousAddrError) Error() string {
+ files := ""
+ for i, a := range err.Matches {
+ files += a.URL.Path
+ if i < len(err.Matches)-1 {
+ files += ", "
+ }
+ }
+ return fmt.Sprintf("multiple keys match address (%s)", files)
+}
+
+// accountCache is a live index of all accounts in the keystore.
+type accountCache struct {
+ keydir string
+ watcher *watcher
+ mu sync.Mutex
+ all accountsByURL
+ byAddr map[types.Address][]accounts.Account
+ throttle *time.Timer
+ notify chan struct{}
+ fileC fileCache
+}
+
+func newAccountCache(keydir string) (*accountCache, chan struct{}) {
+ ac := &accountCache{
+ keydir: keydir,
+ byAddr: make(map[types.Address][]accounts.Account),
+ notify: make(chan struct{}, 1),
+ fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
+ }
+ ac.watcher = newWatcher(ac)
+ return ac, ac.notify
+}
+
+func (ac *accountCache) accounts() []accounts.Account {
+ ac.maybeReload()
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ cpy := make([]accounts.Account, len(ac.all))
+ copy(cpy, ac.all)
+ return cpy
+}
+
+func (ac *accountCache) hasAddress(addr types.Address) bool {
+ ac.maybeReload()
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ return len(ac.byAddr[addr]) > 0
+}
+
+func (ac *accountCache) add(newAccount accounts.Account) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 })
+ if i < len(ac.all) && ac.all[i] == newAccount {
+ return
+ }
+ // newAccount is not in the cache.
+ ac.all = append(ac.all, accounts.Account{})
+ copy(ac.all[i+1:], ac.all[i:])
+ ac.all[i] = newAccount
+ ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
+}
+
+// note: removed needs to be unique here (i.e. both File and Address must be set).
+func (ac *accountCache) delete(removed accounts.Account) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+
+ ac.all = removeAccount(ac.all, removed)
+ if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
+ delete(ac.byAddr, removed.Address)
+ } else {
+ ac.byAddr[removed.Address] = ba
+ }
+}
+
+// deleteByFile removes an account referenced by the given path.
+func (ac *accountCache) deleteByFile(path string) {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Path >= path })
+
+ if i < len(ac.all) && ac.all[i].URL.Path == path {
+ removed := ac.all[i]
+ ac.all = append(ac.all[:i], ac.all[i+1:]...)
+ if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
+ delete(ac.byAddr, removed.Address)
+ } else {
+ ac.byAddr[removed.Address] = ba
+ }
+ }
+}
+
+// watcherStarted returns true if the watcher loop started running (even if it
+// has since also ended).
+func (ac *accountCache) watcherStarted() bool {
+ ac.mu.Lock()
+ defer ac.mu.Unlock()
+ return ac.watcher.running || ac.watcher.runEnded
+}
+
+func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
+ for i := range slice {
+ if slice[i] == elem {
+ return append(slice[:i], slice[i+1:]...)
+ }
+ }
+ return slice
+}
+
+// find returns the cached account for address if there is a unique match.
+// The exact matching rules are explained by the documentation of accounts.Account.
+// Callers must hold ac.mu.
+func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
+ // Limit search to address candidates if possible.
+ matches := ac.all
+ if (a.Address != types.Address{}) {
+ matches = ac.byAddr[a.Address]
+ }
+ if a.URL.Path != "" {
+ // If only the basename is specified, complete the path.
+ if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
+ a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
+ }
+ for i := range matches {
+ if matches[i].URL == a.URL {
+ return matches[i], nil
+ }
+ }
+ if (a.Address == types.Address{}) {
+ return accounts.Account{}, ErrNoMatch
+ }
+ }
+ switch len(matches) {
+ case 1:
+ return matches[0], nil
+ case 0:
+ return accounts.Account{}, ErrNoMatch
+ default:
+ err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
+ copy(err.Matches, matches)
+ sort.Sort(accountsByURL(err.Matches))
+ return accounts.Account{}, err
+ }
+}
+
+func (ac *accountCache) maybeReload() {
+ ac.mu.Lock()
+
+ if ac.watcher.running {
+ ac.mu.Unlock()
+ return // A watcher is running and will keep the cache up-to-date.
+ }
+ if ac.throttle == nil {
+ ac.throttle = time.NewTimer(0)
+ } else {
+ select {
+ case <-ac.throttle.C:
+ default:
+ ac.mu.Unlock()
+ return // The cache was reloaded recently.
+ }
+ }
+ // No watcher running, start it.
+ ac.watcher.start()
+ ac.throttle.Reset(minReloadInterval)
+ ac.mu.Unlock()
+ err := ac.scanAccounts()
+ if err != nil {
+ return
+ }
+}
+
+func (ac *accountCache) close() {
+ ac.mu.Lock()
+ ac.watcher.close()
+ if ac.throttle != nil {
+ ac.throttle.Stop()
+ }
+ if ac.notify != nil {
+ close(ac.notify)
+ ac.notify = nil
+ }
+ ac.mu.Unlock()
+}
+
+// scanAccounts checks if any changes have occurred on the filesystem, and
+// updates the account cache accordingly
+func (ac *accountCache) scanAccounts() error {
+ // Scan the entire folder metadata for file changes
+ creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
+ if err != nil {
+ log.Debug("Failed to reload keystore contents", "err", err)
+ return err
+ }
+ if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
+ return nil
+ }
+ // Create a helper method to scan the contents of the key files
+ var (
+ buf = new(bufio.Reader)
+ key struct {
+ Address string `json:"address"`
+ }
+ )
+ readAccount := func(path string) *accounts.Account {
+ fd, err := os.Open(path)
+ if err != nil {
+ //log.Trace("Failed to open keystore file", "path", path, "err", err)
+ log.Debug("Failed to open keystore file", "path", path, "err", err)
+ return nil
+ }
+ defer func(fd *os.File) {
+ err := fd.Close()
+ if err != nil {
+ return
+ }
+ }(fd)
+ buf.Reset(fd)
+ // Parse the address.
+ key.Address = ""
+ err = json.NewDecoder(buf).Decode(&key)
+ addr := types.HexToAddress(key.Address)
+ switch {
+ case err != nil:
+ log.Debug("Failed to decode keystore key", "path", path, "err", err)
+ case addr == types.Address{}:
+ log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
+ default:
+ return &accounts.Account{
+ Address: addr,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
+ }
+ }
+ return nil
+ }
+ // Process all the file diffs
+ start := time.Now()
+
+ for _, path := range creates.ToSlice() {
+ if a := readAccount(path); a != nil {
+ ac.add(*a)
+ }
+ }
+ for _, path := range deletes.ToSlice() {
+ ac.deleteByFile(path)
+ }
+ for _, path := range updates.ToSlice() {
+ ac.deleteByFile(path)
+ if a := readAccount(path); a != nil {
+ ac.add(*a)
+ }
+ }
+ end := time.Now()
+
+ select {
+ case ac.notify <- struct{}{}:
+ default:
+ }
+ log.Trace("Handled keystore changes", "time", end.Sub(start))
+ return nil
+}
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
new file mode 100644
index 0000000..19bf2ea
--- /dev/null
+++ b/accounts/keystore/account_cache_test.go
@@ -0,0 +1,404 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "testing"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+ common "github.com/astranetworld/ast/common/types"
+ "github.com/cespare/cp"
+ "github.com/davecgh/go-spew/spew"
+)
+
+var (
+ cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore"))
+ cachetestAccounts = []accounts.Account{
+ {
+ Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")},
+ },
+ {
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")},
+ },
+ {
+ Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")},
+ },
+ }
+)
+
+func TestWatchNewFile(t *testing.T) {
+ t.Parallel()
+
+ dir, ks := tmpKeyStore(t, false)
+
+ // Ensure the watcher is started before adding any files.
+ ks.Accounts()
+ time.Sleep(1000 * time.Millisecond)
+
+ // Move in the files.
+ wantAccounts := make([]accounts.Account, len(cachetestAccounts))
+ for i := range cachetestAccounts {
+ wantAccounts[i] = accounts.Account{
+ Address: cachetestAccounts[i].Address,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))},
+ }
+ if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // ks should see the accounts.
+ var list []accounts.Account
+ for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
+ list = ks.Accounts()
+ if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
+ return
+ }
+ time.Sleep(d)
+ }
+ t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
+}
+
+func TestWatchNoDir(t *testing.T) {
+ t.Parallel()
+
+ // Create ks but not the directory that it watches.
+ rand.Seed(time.Now().UnixNano())
+ dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
+ ks := NewKeyStore(dir, LightScryptN, LightScryptP)
+
+ list := ks.Accounts()
+ if len(list) > 0 {
+ t.Error("initial account list not empty:", list)
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ // Create the directory and copy a key file into it.
+ os.MkdirAll(dir, 0700)
+ defer os.RemoveAll(dir)
+ file := filepath.Join(dir, "aaa")
+ if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
+ t.Fatal(err)
+ }
+
+ // ks should see the account.
+ wantAccounts := []accounts.Account{cachetestAccounts[0]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
+ for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
+ list = ks.Accounts()
+ if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ t.Fatalf("wasn't notified of new accounts")
+ }
+ return
+ }
+ time.Sleep(d)
+ }
+ t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
+}
+
+func TestCacheInitialReload(t *testing.T) {
+ cache, _ := newAccountCache(cachetestDir)
+ accounts := cache.accounts()
+ if !reflect.DeepEqual(accounts, cachetestAccounts) {
+ t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
+ }
+}
+
+func TestCacheAddDeleteOrder(t *testing.T) {
+ cache, _ := newAccountCache("testdata/no-such-dir")
+ cache.watcher.running = true // prevent unexpected reloads
+
+ accs := []accounts.Account{
+ {
+ Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"},
+ },
+ {
+ Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"},
+ },
+ {
+ Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"},
+ },
+ {
+ Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"},
+ },
+ {
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"},
+ },
+ {
+ Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"},
+ },
+ }
+ for _, a := range accs {
+ cache.add(a)
+ }
+ // Add some of them twice to check that they don't get reinserted.
+ cache.add(accs[0])
+ cache.add(accs[2])
+
+ // Check that the account list is sorted by filename.
+ wantAccounts := make([]accounts.Account, len(accs))
+ copy(wantAccounts, accs)
+ sort.Sort(accountsByURL(wantAccounts))
+ list := cache.accounts()
+ if !reflect.DeepEqual(list, wantAccounts) {
+ t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
+ }
+ for _, a := range accs {
+ if !cache.hasAddress(a.Address) {
+ t.Errorf("expected hasAccount(%x) to return true", a.Address)
+ }
+ }
+ if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
+ t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
+ }
+
+ // Delete a few keys from the cache.
+ for i := 0; i < len(accs); i += 2 {
+ cache.delete(wantAccounts[i])
+ }
+ cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}})
+
+ // Check content again after deletion.
+ wantAccountsAfterDelete := []accounts.Account{
+ wantAccounts[1],
+ wantAccounts[3],
+ wantAccounts[5],
+ }
+ list = cache.accounts()
+ if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
+ t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
+ }
+ for _, a := range wantAccountsAfterDelete {
+ if !cache.hasAddress(a.Address) {
+ t.Errorf("expected hasAccount(%x) to return true", a.Address)
+ }
+ }
+ if cache.hasAddress(wantAccounts[0].Address) {
+ t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
+ }
+}
+
+func TestCacheFind(t *testing.T) {
+ dir := filepath.Join("testdata", "dir")
+ cache, _ := newAccountCache(dir)
+ cache.watcher.running = true // prevent unexpected reloads
+
+ accs := []accounts.Account{
+ {
+ Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")},
+ },
+ {
+ Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")},
+ },
+ {
+ Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")},
+ },
+ }
+ for _, a := range accs {
+ cache.add(a)
+ }
+
+ nomatchAccount := accounts.Account{
+ Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
+ }
+ tests := []struct {
+ Query accounts.Account
+ WantResult accounts.Account
+ WantError error
+ }{
+ // by address
+ {Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]},
+ // by file
+ {Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]},
+ // by basename
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]},
+ // by file and address
+ {Query: accs[0], WantResult: accs[0]},
+ // ambiguous address, tie resolved by file
+ {Query: accs[2], WantResult: accs[2]},
+ // ambiguous address error
+ {
+ Query: accounts.Account{Address: accs[2].Address},
+ WantError: &AmbiguousAddrError{
+ Addr: accs[2].Address,
+ Matches: []accounts.Account{accs[2], accs[3]},
+ },
+ },
+ // no match error
+ {Query: nomatchAccount, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch},
+ {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch},
+ {Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch},
+ }
+ for i, test := range tests {
+ a, err := cache.find(test.Query)
+ if !reflect.DeepEqual(err, test.WantError) {
+ t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
+ continue
+ }
+ if a != test.WantResult {
+ t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
+ continue
+ }
+ }
+}
+
+func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
+ var list []accounts.Account
+ for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
+ list = ks.Accounts()
+ if reflect.DeepEqual(list, wantAccounts) {
+ // ks should have also received change notifications
+ select {
+ case <-ks.changes:
+ default:
+ return fmt.Errorf("wasn't notified of new accounts")
+ }
+ return nil
+ }
+ time.Sleep(d)
+ }
+ return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
+}
+
+// TestUpdatedKeyfileContents tests that updating the contents of a keystore file
+// is noticed by the watcher, and the account cache is updated accordingly
+func TestUpdatedKeyfileContents(t *testing.T) {
+ t.Parallel()
+
+ // Create a temporary keystore to test with
+ rand.Seed(time.Now().UnixNano())
+ dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
+ ks := NewKeyStore(dir, LightScryptN, LightScryptP)
+
+ list := ks.Accounts()
+ if len(list) > 0 {
+ t.Error("initial account list not empty:", list)
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ // Create the directory and copy a key file into it.
+ os.MkdirAll(dir, 0700)
+ defer os.RemoveAll(dir)
+ file := filepath.Join(dir, "aaa")
+
+ // Place one of our testfiles in there
+ if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
+ t.Fatal(err)
+ }
+
+ // ks should see the account.
+ wantAccounts := []accounts.Account{cachetestAccounts[0]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
+ if err := waitForAccounts(wantAccounts, ks); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // needed so that modTime of `file` is different to its current value after forceCopyFile
+ time.Sleep(1000 * time.Millisecond)
+
+ // Now replace file contents
+ if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
+ t.Fatal(err)
+ return
+ }
+ wantAccounts = []accounts.Account{cachetestAccounts[1]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
+ if err := waitForAccounts(wantAccounts, ks); err != nil {
+ t.Errorf("First replacement failed")
+ t.Error(err)
+ return
+ }
+
+ // needed so that modTime of `file` is different to its current value after forceCopyFile
+ time.Sleep(1000 * time.Millisecond)
+
+ // Now replace file contents again
+ if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
+ t.Fatal(err)
+ return
+ }
+ wantAccounts = []accounts.Account{cachetestAccounts[2]}
+ wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
+ if err := waitForAccounts(wantAccounts, ks); err != nil {
+ t.Errorf("Second replacement failed")
+ t.Error(err)
+ return
+ }
+
+ // needed so that modTime of `file` is different to its current value after os.WriteFile
+ time.Sleep(1000 * time.Millisecond)
+
+ // Now replace file contents with crap
+ if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
+ t.Fatal(err)
+ return
+ }
+ if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
+ t.Errorf("Emptying account file failed")
+ t.Error(err)
+ return
+ }
+}
+
+// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
+func forceCopyFile(dst, src string) error {
+ data, err := os.ReadFile(src)
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(dst, data, 0644)
+}
diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go
new file mode 100644
index 0000000..45fa87b
--- /dev/null
+++ b/accounts/keystore/file_cache.go
@@ -0,0 +1,106 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "github.com/astranetworld/ast/log"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ mapset "github.com/deckarep/golang-set/v2"
+)
+
+// fileCache is a cache of files seen during scan of keystore.
+type fileCache struct {
+ all mapset.Set[string] // Set of all files from the keystore folder
+ lastMod time.Time // Last time instance when a file was modified
+ mu sync.Mutex
+}
+
+// scan performs a new scan on the given directory, compares against the already
+// cached filenames, and returns file sets: creates, deletes, updates.
+func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) {
+ t0 := time.Now()
+
+ // List all the files from the keystore folder
+ files, err := os.ReadDir(keyDir)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ t1 := time.Now()
+
+ fc.mu.Lock()
+ defer fc.mu.Unlock()
+
+ // Iterate all the files and gather their metadata
+ all := mapset.NewThreadUnsafeSet[string]()
+ mods := mapset.NewThreadUnsafeSet[string]()
+
+ var newLastMod time.Time
+ for _, fi := range files {
+ path := filepath.Join(keyDir, fi.Name())
+ // Skip any non-key files from the folder
+ if nonKeyFile(fi) {
+ //log.Trace("Ignoring file on account scan", "path", path)
+ log.Debug("Ignoring file on account scan", "path", path)
+ continue
+ }
+ // Gather the set of all and freshly modified files
+ all.Add(path)
+
+ info, err := fi.Info()
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ modified := info.ModTime()
+ if modified.After(fc.lastMod) {
+ mods.Add(path)
+ }
+ if modified.After(newLastMod) {
+ newLastMod = modified
+ }
+ }
+ t2 := time.Now()
+
+ // Update the tracked files and return the three sets
+ deletes := fc.all.Difference(all) // Deletes = previous - current
+ creates := all.Difference(fc.all) // Creates = current - previous
+ updates := mods.Difference(creates) // Updates = modified - creates
+
+ fc.all, fc.lastMod = all, newLastMod
+ t3 := time.Now()
+
+ // Report on the scanning stats and return
+ log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
+ return creates, deletes, updates, nil
+}
+
+// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
+func nonKeyFile(fi os.DirEntry) bool {
+ // Skip editor backups and UNIX-style hidden files.
+ if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
+ return true
+ }
+ // Skip misc special files, directories (yes, symlinks too).
+ if fi.IsDir() || !fi.Type().IsRegular() {
+ return true
+ }
+ return false
+}
diff --git a/accounts/keystore/key.go b/accounts/keystore/key.go
new file mode 100644
index 0000000..d6adbf1
--- /dev/null
+++ b/accounts/keystore/key.go
@@ -0,0 +1,236 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package keystore
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/google/uuid"
+)
+
+const (
+ version = 3
+)
+
+type Key struct {
+ Id uuid.UUID // Version 4 "random" for unique id not derived from key data
+ // to simplify lookups we also store the address
+ Address types.Address
+ // we only store privkey as pubkey/address can be derived from it
+ // privkey in this struct is always in plaintext
+ PrivateKey *ecdsa.PrivateKey
+}
+
+type keyStore interface {
+ // Loads and decrypts the key from disk.
+ GetKey(addr types.Address, filename string, auth string) (*Key, error)
+ // Writes and encrypts the key.
+ StoreKey(filename string, k *Key, auth string) error
+ // Joins filename with the key directory unless it is already absolute.
+ JoinPath(filename string) string
+}
+
+type plainKeyJSON struct {
+ Address string `json:"address"`
+ PrivateKey string `json:"privatekey"`
+ Id string `json:"id"`
+ Version int `json:"version"`
+}
+
+type encryptedKeyJSONV3 struct {
+ Address string `json:"address"`
+ Crypto CryptoJSON `json:"crypto"`
+ Id string `json:"id"`
+ Version int `json:"version"`
+}
+
+type encryptedKeyJSONV1 struct {
+ Address string `json:"address"`
+ Crypto CryptoJSON `json:"crypto"`
+ Id string `json:"id"`
+ Version string `json:"version"`
+}
+
+type CryptoJSON struct {
+ Cipher string `json:"cipher"`
+ CipherText string `json:"ciphertext"`
+ CipherParams cipherparamsJSON `json:"cipherparams"`
+ KDF string `json:"kdf"`
+ KDFParams map[string]interface{} `json:"kdfparams"`
+ MAC string `json:"mac"`
+}
+
+type cipherparamsJSON struct {
+ IV string `json:"iv"`
+}
+
+func (k *Key) MarshalJSON() (j []byte, err error) {
+ jStruct := plainKeyJSON{
+ hex.EncodeToString(k.Address[:]),
+ hex.EncodeToString(crypto.FromECDSA(k.PrivateKey)),
+ k.Id.String(),
+ version,
+ }
+ j, err = json.Marshal(jStruct)
+ return j, err
+}
+
+func (k *Key) UnmarshalJSON(j []byte) (err error) {
+ keyJSON := new(plainKeyJSON)
+ err = json.Unmarshal(j, &keyJSON)
+ if err != nil {
+ return err
+ }
+
+ u := new(uuid.UUID)
+ *u, err = uuid.Parse(keyJSON.Id)
+ if err != nil {
+ return err
+ }
+ k.Id = *u
+ addr, err := hex.DecodeString(keyJSON.Address)
+ if err != nil {
+ return err
+ }
+ privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
+ if err != nil {
+ return err
+ }
+
+ k.Address = types.BytesToAddress(addr)
+ k.PrivateKey = privkey
+
+ return nil
+}
+
+func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
+ id, err := uuid.NewRandom()
+ if err != nil {
+ panic(fmt.Sprintf("Could not create random uuid: %v", err))
+ }
+ key := &Key{
+ Id: id,
+ Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
+ PrivateKey: privateKeyECDSA,
+ }
+ return key
+}
+
+// NewKeyForDirectICAP generates a key whose address fits into < 155 bits so it can fit
+// into the Direct ICAP spec. for simplicity and easier compatibility with other libs, we
+// retry until the first byte is 0.
+func NewKeyForDirectICAP(rand io.Reader) *Key {
+ randBytes := make([]byte, 64)
+ _, err := rand.Read(randBytes)
+ if err != nil {
+ panic("key generation: could not read from random source: " + err.Error())
+ }
+ reader := bytes.NewReader(randBytes)
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
+ if err != nil {
+ panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
+ }
+ key := newKeyFromECDSA(privateKeyECDSA)
+ if !strings.HasPrefix(key.Address.Hex(), "0x00") {
+ return NewKeyForDirectICAP(rand)
+ }
+ return key
+}
+
+func newKey(rand io.Reader) (*Key, error) {
+ privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
+ if err != nil {
+ return nil, err
+ }
+ return newKeyFromECDSA(privateKeyECDSA), nil
+}
+
+func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
+ key, err := newKey(rand)
+ if err != nil {
+ return nil, accounts.Account{}, err
+ }
+ a := accounts.Account{
+ Address: key.Address,
+ URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
+ }
+ if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
+ zeroKey(key.PrivateKey)
+ return nil, a, err
+ }
+ return key, a, err
+}
+
+func writeTemporaryKeyFile(file string, content []byte) (string, error) {
+ // Create the keystore directory with appropriate permissions
+ // in case it is not present yet.
+ const dirPerm = 0700
+ if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
+ return "", err
+ }
+ // Atomic write: create a temporary hidden file first
+ // then move it into place. TempFile assigns mode 0600.
+ f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
+ if err != nil {
+ return "", err
+ }
+ if _, err := f.Write(content); err != nil {
+ f.Close()
+ os.Remove(f.Name())
+ return "", err
+ }
+ f.Close()
+ return f.Name(), nil
+}
+
+func writeKeyFile(file string, content []byte) error {
+ name, err := writeTemporaryKeyFile(file, content)
+ if err != nil {
+ return err
+ }
+ return os.Rename(name, file)
+}
+
+// keyFileName implements the naming convention for keyfiles:
+// UTC---
+func keyFileName(keyAddr types.Address) string {
+ ts := time.Now().UTC()
+ return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:]))
+}
+
+func toISO8601(t time.Time) string {
+ var tz string
+ name, offset := t.Zone()
+ if name == "UTC" {
+ tz = "Z"
+ } else {
+ tz = fmt.Sprintf("%03d00", offset/3600)
+ }
+ return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
+ t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
+}
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
new file mode 100644
index 0000000..ae8d196
--- /dev/null
+++ b/accounts/keystore/keystore.go
@@ -0,0 +1,515 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package keystore implements encrypted storage of secp256k1 private keys.
+//
+// Keys are stored as encrypted JSON files according to the Web3 Secret Storage specification.
+// See https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition for more information.
+package keystore
+
+import (
+ "crypto/ecdsa"
+ crand "crypto/rand"
+ "errors"
+ "math/big"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ event "github.com/astranetworld/ast/modules/event/v2"
+)
+
+var (
+ ErrLocked = accounts.NewAuthNeededError("password or unlock")
+ ErrNoMatch = errors.New("no key for given address or file")
+ ErrDecrypt = errors.New("could not decrypt key with given password")
+
+ // ErrAccountAlreadyExists is returned if an account attempted to import is
+ // already present in the keystore.
+ ErrAccountAlreadyExists = errors.New("account already exists")
+)
+
+// KeyStoreType is the reflect type of a keystore backend.
+var KeyStoreType = reflect.TypeOf(&KeyStore{})
+
+// KeyStoreScheme is the protocol scheme prefixing account and wallet URLs.
+const KeyStoreScheme = "keystore"
+
+// Maximum time between wallet refreshes (if filesystem notifications don't work).
+const walletRefreshCycle = 3 * time.Second
+
+// KeyStore manages a key storage directory on disk.
+type KeyStore struct {
+ storage keyStore // Storage backend, might be cleartext or encrypted
+ cache *accountCache // In-memory account cache over the filesystem storage
+ changes chan struct{} // Channel receiving change notifications from the cache
+ unlocked map[types.Address]*unlocked // Currently unlocked account (decrypted private keys)
+
+ wallets []accounts.Wallet // Wallet wrappers around the individual key files
+ updateFeed event.Feed // Event feed to notify wallet additions/removals
+ updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
+ updating bool // Whether the event notification loop is running
+
+ mu sync.RWMutex
+ importMu sync.Mutex // Import Mutex locks the import to prevent two insertions from racing
+}
+
+type unlocked struct {
+ *Key
+ abort chan struct{}
+}
+
+// NewKeyStore creates a keystore for the given directory.
+func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
+ ks.init(keydir)
+ return ks
+}
+
+// NewPlaintextKeyStore creates a keystore for the given directory.
+// Deprecated: Use NewKeyStore.
+func NewPlaintextKeyStore(keydir string) *KeyStore {
+ keydir, _ = filepath.Abs(keydir)
+ ks := &KeyStore{storage: &keyStorePlain{keydir}}
+ ks.init(keydir)
+ return ks
+}
+
+func (ks *KeyStore) init(keydir string) {
+ // Lock the mutex since the account cache might call back with events
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Initialize the set of unlocked keys and the account cache
+ ks.unlocked = make(map[types.Address]*unlocked)
+ ks.cache, ks.changes = newAccountCache(keydir)
+
+ // TODO: In order for this finalizer to work, there must be no references
+ // to ks. addressCache doesn't keep a reference but unlocked keys do,
+ // so the finalizer will not trigger until all timed unlocks have expired.
+ runtime.SetFinalizer(ks, func(m *KeyStore) {
+ m.cache.close()
+ })
+ // Create the initial list of wallets from the cache
+ accs := ks.cache.accounts()
+ ks.wallets = make([]accounts.Wallet, len(accs))
+ for i := 0; i < len(accs); i++ {
+ ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
+ }
+}
+
+// Wallets implements accounts.Backend, returning all single-key wallets from the
+// keystore directory.
+func (ks *KeyStore) Wallets() []accounts.Wallet {
+ // Make sure the list of wallets is in sync with the account cache
+ ks.refreshWallets()
+
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ cpy := make([]accounts.Wallet, len(ks.wallets))
+ copy(cpy, ks.wallets)
+ return cpy
+}
+
+// refreshWallets retrieves the current account list and based on that does any
+// necessary wallet refreshes.
+func (ks *KeyStore) refreshWallets() {
+ // Retrieve the current list of accounts
+ ks.mu.Lock()
+ accs := ks.cache.accounts()
+
+ // Transform the current list of wallets into the new one
+ var (
+ wallets = make([]accounts.Wallet, 0, len(accs))
+ events []accounts.WalletEvent
+ )
+
+ for _, account := range accs {
+ // Drop wallets while they were in front of the next account
+ for len(ks.wallets) > 0 && ks.wallets[0].URL().Cmp(account.URL) < 0 {
+ events = append(events, accounts.WalletEvent{Wallet: ks.wallets[0], Kind: accounts.WalletDropped})
+ ks.wallets = ks.wallets[1:]
+ }
+ // If there are no more wallets or the account is before the next, wrap new wallet
+ if len(ks.wallets) == 0 || ks.wallets[0].URL().Cmp(account.URL) > 0 {
+ wallet := &keystoreWallet{account: account, keystore: ks}
+
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
+ wallets = append(wallets, wallet)
+ continue
+ }
+ // If the account is the same as the first wallet, keep it
+ if ks.wallets[0].Accounts()[0] == account {
+ wallets = append(wallets, ks.wallets[0])
+ ks.wallets = ks.wallets[1:]
+ continue
+ }
+ }
+ // Drop any leftover wallets and set the new batch
+ for _, wallet := range ks.wallets {
+ events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
+ }
+ ks.wallets = wallets
+ ks.mu.Unlock()
+
+ // Fire all wallet events and return
+ for _, event := range events {
+ ks.updateFeed.Send(event)
+ }
+}
+
+// Subscribe implements accounts.Backend, creating an async subscription to
+// receive notifications on the addition or removal of keystore wallets.
+func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
+ // We need the mutex to reliably start/stop the update loop
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+
+ // Subscribe the caller and track the subscriber count
+ sub := ks.updateScope.Track(ks.updateFeed.Subscribe(sink))
+
+ // Subscribers require an active notification loop, start it
+ if !ks.updating {
+ ks.updating = true
+ go ks.updater()
+ }
+ return sub
+}
+
+// updater is responsible for maintaining an up-to-date list of wallets stored in
+// the keystore, and for firing wallet addition/removal events. It listens for
+// account change events from the underlying account cache, and also periodically
+// forces a manual refresh (only triggers for systems where the filesystem notifier
+// is not running).
+func (ks *KeyStore) updater() {
+ for {
+ // Wait for an account update or a refresh timeout
+ select {
+ case <-ks.changes:
+ case <-time.After(walletRefreshCycle):
+ }
+ // Run the wallet refresher
+ ks.refreshWallets()
+
+ // If all our subscribers left, stop the updater
+ ks.mu.Lock()
+ if ks.updateScope.Count() == 0 {
+ ks.updating = false
+ ks.mu.Unlock()
+ return
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// HasAddress reports whether a key with the given address is present.
+func (ks *KeyStore) HasAddress(addr types.Address) bool {
+ return ks.cache.hasAddress(addr)
+}
+
+// Accounts returns all key files present in the directory.
+func (ks *KeyStore) Accounts() []accounts.Account {
+ return ks.cache.accounts()
+}
+
+// Delete deletes the key matched by account if the passphrase is correct.
+// If the account contains no filename, the address must match a unique key.
+func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
+ // Decrypting the key isn't really necessary, but we do
+ // it anyway to check the password and zero out the key
+ // immediately afterwards.
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if key != nil {
+ zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return err
+ }
+ // The order is crucial here. The key is dropped from the
+ // cache after the file is gone so that a reload happening in
+ // between won't insert it into the cache again.
+ err = os.Remove(a.URL.Path)
+ if err == nil {
+ ks.cache.delete(a)
+ ks.refreshWallets()
+ }
+ return err
+}
+
+// SignHash calculates a ECDSA signature for the given hash. The produced
+// signature is in the [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHash(a accounts.Account, hash []byte) ([]byte, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Sign the hash using plain ECDSA operations
+ return crypto.Sign(hash, unlockedKey.PrivateKey)
+}
+
+// SignTx signs the given transaction with the requested account.
+func (ks *KeyStore) SignTx(a accounts.Account, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error) {
+ // Look up the key to sign with and abort if it cannot be found
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+
+ unlockedKey, found := ks.unlocked[a.Address]
+ if !found {
+ return nil, ErrLocked
+ }
+ // Depending on the presence of the chain ID, sign with 2718 or homestead
+ signer := transaction.LatestSignerForChainID(chainID)
+ return transaction.SignTx(tx, signer, unlockedKey.PrivateKey)
+}
+
+// SignHashWithPassphrase signs hash if the private key matching the given address
+// can be decrypted with the given passphrase. The produced signature is in the
+// [R || S || V] format where V is 0 or 1.
+func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string, hash []byte) (signature []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+ return crypto.Sign(hash, key.PrivateKey)
+}
+
+// SignTxWithPassphrase signs the transaction if the private key matching the
+// given address can be decrypted with the given passphrase.
+func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ defer zeroKey(key.PrivateKey)
+ // Depending on the presence of the chain ID, sign with or without replay protection.
+ signer := transaction.LatestSignerForChainID(chainID)
+ return transaction.SignTx(tx, signer, key.PrivateKey)
+}
+
+// Unlock unlocks the given account indefinitely.
+func (ks *KeyStore) Unlock(a accounts.Account, passphrase string) error {
+ return ks.TimedUnlock(a, passphrase, 0)
+}
+
+// Lock removes the private key with the given address from memory.
+func (ks *KeyStore) Lock(addr types.Address) error {
+ ks.mu.Lock()
+ if unl, found := ks.unlocked[addr]; found {
+ ks.mu.Unlock()
+ ks.expire(addr, unl, time.Duration(0)*time.Nanosecond)
+ } else {
+ ks.mu.Unlock()
+ }
+ return nil
+}
+
+// TimedUnlock unlocks the given account with the passphrase. The account
+// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
+// until the program exits. The account must match a unique key file.
+//
+// If the account address is already unlocked for a duration, TimedUnlock extends or
+// shortens the active unlock timeout. If the address was previously unlocked
+// indefinitely the timeout is not altered.
+func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout time.Duration) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+
+ ks.mu.Lock()
+ defer ks.mu.Unlock()
+ u, found := ks.unlocked[a.Address]
+ if found {
+ if u.abort == nil {
+ // The address was unlocked indefinitely, so unlocking
+ // it with a timeout would be confusing.
+ zeroKey(key.PrivateKey)
+ return nil
+ }
+ // Terminate the expire goroutine and replace it below.
+ close(u.abort)
+ }
+ if timeout > 0 {
+ u = &unlocked{Key: key, abort: make(chan struct{})}
+ go ks.expire(a.Address, u, timeout)
+ } else {
+ u = &unlocked{Key: key}
+ }
+ ks.unlocked[a.Address] = u
+ return nil
+}
+
+// Find resolves the given account into a unique entry in the keystore.
+func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
+ ks.cache.maybeReload()
+ ks.cache.mu.Lock()
+ a, err := ks.cache.find(a)
+ ks.cache.mu.Unlock()
+ return a, err
+}
+
+func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.Account, *Key, error) {
+ a, err := ks.Find(a)
+ if err != nil {
+ return a, nil, err
+ }
+ key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
+ return a, key, err
+}
+
+func (ks *KeyStore) expire(addr types.Address, u *unlocked, timeout time.Duration) {
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ select {
+ case <-u.abort:
+ // just quit
+ case <-t.C:
+ ks.mu.Lock()
+ // only drop if it's still the same key instance that dropLater
+ // was launched with. we can check that using pointer equality
+ // because the map stores a new pointer every time the key is
+ // unlocked.
+ if ks.unlocked[addr] == u {
+ zeroKey(u.PrivateKey)
+ delete(ks.unlocked, addr)
+ }
+ ks.mu.Unlock()
+ }
+}
+
+// NewAccount generates a new key and stores it into the key directory,
+// encrypting it with the passphrase.
+func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
+ _, account, err := storeNewKey(ks.storage, crand.Reader, passphrase)
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ // Add the account to the cache immediately rather
+ // than waiting for file system notifications to pick it up.
+ ks.cache.add(account)
+ ks.refreshWallets()
+ return account, nil
+}
+
+// Export exports as a JSON key, encrypted with newPassphrase.
+func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) {
+ _, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ var N, P int
+ if store, ok := ks.storage.(*keyStorePassphrase); ok {
+ N, P = store.scryptN, store.scryptP
+ } else {
+ N, P = StandardScryptN, StandardScryptP
+ }
+ return EncryptKey(key, newPassphrase, N, P)
+}
+
+// Import stores the given encrypted JSON key into the key directory.
+func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (accounts.Account, error) {
+ key, err := DecryptKey(keyJSON, passphrase)
+ if key != nil && key.PrivateKey != nil {
+ defer zeroKey(key.PrivateKey)
+ }
+ if err != nil {
+ return accounts.Account{}, err
+ }
+ ks.importMu.Lock()
+ defer ks.importMu.Unlock()
+
+ if ks.cache.hasAddress(key.Address) {
+ return accounts.Account{
+ Address: key.Address,
+ }, ErrAccountAlreadyExists
+ }
+ return ks.importKey(key, newPassphrase)
+}
+
+// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase.
+func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (accounts.Account, error) {
+ ks.importMu.Lock()
+ defer ks.importMu.Unlock()
+
+ key := newKeyFromECDSA(priv)
+ if ks.cache.hasAddress(key.Address) {
+ return accounts.Account{
+ Address: key.Address,
+ }, ErrAccountAlreadyExists
+ }
+ return ks.importKey(key, passphrase)
+}
+
+func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, error) {
+ a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.storage.JoinPath(keyFileName(key.Address))}}
+ if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
+ return accounts.Account{}, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// Update changes the passphrase of an existing account.
+func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string) error {
+ a, key, err := ks.getDecryptedKey(a, passphrase)
+ if err != nil {
+ return err
+ }
+ return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
+}
+
+// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
+// a key file in the key directory. The key file is encrypted with the same passphrase.
+func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (accounts.Account, error) {
+ a, _, err := importPreSaleKey(ks.storage, keyJSON, passphrase)
+ if err != nil {
+ return a, err
+ }
+ ks.cache.add(a)
+ ks.refreshWallets()
+ return a, nil
+}
+
+// isUpdating returns whether the event notification loop is running.
+// This method is mainly meant for tests.
+func (ks *KeyStore) isUpdating() bool {
+ ks.mu.RLock()
+ defer ks.mu.RUnlock()
+ return ks.updating
+}
+
+// zeroKey zeroes a private key in memory.
+func zeroKey(k *ecdsa.PrivateKey) {
+ b := k.D.Bits()
+ for i := range b {
+ b[i] = 0
+ }
+}
diff --git a/accounts/keystore/keystore_test.go b/accounts/keystore/keystore_test.go
new file mode 100644
index 0000000..aeff858
--- /dev/null
+++ b/accounts/keystore/keystore_test.go
@@ -0,0 +1,456 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "math/rand"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/avm/common"
+ event "github.com/astranetworld/ast/modules/event/v2"
+)
+
+var testSigData = make([]byte, 32)
+
+func TestKeyStore(t *testing.T) {
+ dir, ks := tmpKeyStore(t, true)
+
+ a, err := ks.NewAccount("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.HasPrefix(a.URL.Path, dir) {
+ t.Errorf("account file %s doesn't have dir prefix", a.URL)
+ }
+ stat, err := os.Stat(a.URL.Path)
+ if err != nil {
+ t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
+ }
+ if runtime.GOOS != "windows" && stat.Mode() != 0600 {
+ t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
+ }
+ if !ks.HasAddress(a.Address) {
+ t.Errorf("HasAccount(%x) should've returned true", a.Address)
+ }
+ if err := ks.Update(a, "foo", "bar"); err != nil {
+ t.Errorf("Update error: %v", err)
+ }
+ if err := ks.Delete(a, "bar"); err != nil {
+ t.Errorf("Delete error: %v", err)
+ }
+ if common.FileExist(a.URL.Path) {
+ t.Errorf("account file %s should be gone after Delete", a.URL)
+ }
+ if ks.HasAddress(a.Address) {
+ t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
+ }
+}
+
+func TestSign(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+
+ pass := "" // not used but required by API
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ks.Unlock(a1, ""); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestSignWithPassphrase(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+
+ pass := "passwd"
+ acc, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, unlocked := ks.unlocked[acc.Address]; unlocked {
+ t.Fatal("expected account to be locked")
+ }
+
+ _, err = ks.SignHashWithPassphrase(acc, pass, testSigData)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, unlocked := ks.unlocked[acc.Address]; unlocked {
+ t.Fatal("expected account to be locked")
+ }
+
+ if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
+ t.Fatal("expected SignHashWithPassphrase to fail with invalid password")
+ }
+}
+
+func TestTimedUnlock(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+
+ pass := "foo"
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase fails because account is locked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err)
+ }
+
+ // Signing with passphrase works
+ if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // Signing fails again after automatic locking
+ time.Sleep(250 * time.Millisecond)
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
+ }
+}
+
+func TestOverrideUnlock(t *testing.T) {
+ _, ks := tmpKeyStore(t, false)
+
+ pass := "foo"
+ a1, err := ks.NewAccount(pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Unlock indefinitely.
+ if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // reset unlock to a shorter period, invalidates the previous unlock
+ if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil {
+ t.Fatal(err)
+ }
+
+ // Signing without passphrase still works because account is temp unlocked
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != nil {
+ t.Fatal("Signing shouldn't return an error after unlocking, got ", err)
+ }
+
+ // Signing fails again after automatic locking
+ time.Sleep(250 * time.Millisecond)
+ _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
+ if err != ErrLocked {
+ t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
+ }
+}
+
+// This test should fail under -race if signing races the expiration goroutine.
+func TestSignRace(t *testing.T) {
+ _, ks := tmpKeyStore(t, false)
+
+ // Create a test account.
+ a1, err := ks.NewAccount("")
+ if err != nil {
+ t.Fatal("could not create the test account", err)
+ }
+
+ if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
+ t.Fatal("could not unlock the test account", err)
+ }
+ end := time.Now().Add(500 * time.Millisecond)
+ for time.Now().Before(end) {
+ if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
+ return
+ } else if err != nil {
+ t.Errorf("Sign error: %v", err)
+ return
+ }
+ time.Sleep(1 * time.Millisecond)
+ }
+ t.Errorf("Account did not lock within the timeout")
+}
+
+// Tests that the wallet notifier loop starts and stops correctly based on the
+// addition and removal of wallet event subscriptions.
+func TestWalletNotifierLifecycle(t *testing.T) {
+ // Create a temporary keystore to test with
+ _, ks := tmpKeyStore(t, false)
+
+ // Ensure that the notification updater is not running yet
+ time.Sleep(250 * time.Millisecond)
+ ks.mu.RLock()
+ updating := ks.updating
+ ks.mu.RUnlock()
+
+ if updating {
+ t.Errorf("wallet notifier running without subscribers")
+ }
+ // Subscribe to the wallet feed and ensure the updater boots up
+ updates := make(chan accounts.WalletEvent)
+
+ subs := make([]event.Subscription, 2)
+ for i := 0; i < len(subs); i++ {
+ // Create a new subscription
+ subs[i] = ks.Subscribe(updates)
+
+ // Ensure the notifier comes online
+ time.Sleep(250 * time.Millisecond)
+ ks.mu.RLock()
+ updating = ks.updating
+ ks.mu.RUnlock()
+
+ if !updating {
+ t.Errorf("sub %d: wallet notifier not running after subscription", i)
+ }
+ }
+ // Unsubscribe and ensure the updater terminates eventually
+ for i := 0; i < len(subs); i++ {
+ // Close an existing subscription
+ subs[i].Unsubscribe()
+
+ // Ensure the notifier shuts down at and only at the last close
+ for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ {
+ ks.mu.RLock()
+ updating = ks.updating
+ ks.mu.RUnlock()
+
+ if i < len(subs)-1 && !updating {
+ t.Fatalf("sub %d: event notifier stopped prematurely", i)
+ }
+ if i == len(subs)-1 && !updating {
+ return
+ }
+ time.Sleep(250 * time.Millisecond)
+ }
+ }
+ t.Errorf("wallet notifier didn't terminate after unsubscribe")
+}
+
+type walletEvent struct {
+ accounts.WalletEvent
+ a accounts.Account
+}
+
+// Tests that wallet notifications and correctly fired when accounts are added
+// or deleted from the keystore.
+func TestWalletNotifications(t *testing.T) {
+ _, ks := tmpKeyStore(t, false)
+
+ // Subscribe to the wallet feed and collect events.
+ var (
+ events []walletEvent
+ updates = make(chan accounts.WalletEvent)
+ sub = ks.Subscribe(updates)
+ )
+ defer sub.Unsubscribe()
+ go func() {
+ for {
+ select {
+ case ev := <-updates:
+ events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
+ case <-sub.Err():
+ close(updates)
+ return
+ }
+ }
+ }()
+
+ // Randomly add and remove accounts.
+ var (
+ live = make(map[types.Address]accounts.Account)
+ wantEvents []walletEvent
+ )
+ for i := 0; i < 1024; i++ {
+ if create := len(live) == 0 || rand.Int()%4 > 0; create {
+ // Add a new account and ensure wallet notifications arrives
+ account, err := ks.NewAccount("")
+ if err != nil {
+ t.Fatalf("failed to create test account: %v", err)
+ }
+ live[account.Address] = account
+ wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
+ } else {
+ // Delete a random account.
+ var account accounts.Account
+ for _, a := range live {
+ account = a
+ break
+ }
+ if err := ks.Delete(account, ""); err != nil {
+ t.Fatalf("failed to delete test account: %v", err)
+ }
+ delete(live, account.Address)
+ wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
+ }
+ }
+
+ // Shut down the event collector and check events.
+ sub.Unsubscribe()
+ for ev := range updates {
+ events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
+ }
+ checkAccounts(t, live, ks.Wallets())
+ checkEvents(t, wantEvents, events)
+}
+
+// TestImportExport tests the import functionality of a keystore.
+func TestImportECDSA(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ t.Fatalf("failed to generate key: %v", key)
+ }
+ if _, err = ks.ImportECDSA(key, "old"); err != nil {
+ t.Errorf("importing failed: %v", err)
+ }
+ if _, err = ks.ImportECDSA(key, "old"); err == nil {
+ t.Errorf("importing same key twice succeeded")
+ }
+ if _, err = ks.ImportECDSA(key, "new"); err == nil {
+ t.Errorf("importing same key twice succeeded")
+ }
+}
+
+// TestImportECDSA tests the import and export functionality of a keystore.
+func TestImportExport(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+ acc, err := ks.NewAccount("old")
+ if err != nil {
+ t.Fatalf("failed to create account: %v", acc)
+ }
+ json, err := ks.Export(acc, "old", "new")
+ if err != nil {
+ t.Fatalf("failed to export account: %v", acc)
+ }
+ _, ks2 := tmpKeyStore(t, true)
+ if _, err = ks2.Import(json, "old", "old"); err == nil {
+ t.Errorf("importing with invalid password succeeded")
+ }
+ acc2, err := ks2.Import(json, "new", "new")
+ if err != nil {
+ t.Errorf("importing failed: %v", err)
+ }
+ if acc.Address != acc2.Address {
+ t.Error("imported account does not match exported account")
+ }
+ if _, err = ks2.Import(json, "new", "new"); err == nil {
+ t.Errorf("importing a key twice succeeded")
+ }
+}
+
+// TestImportRace tests the keystore on races.
+// This test should fail under -race if importing races.
+func TestImportRace(t *testing.T) {
+ _, ks := tmpKeyStore(t, true)
+ acc, err := ks.NewAccount("old")
+ if err != nil {
+ t.Fatalf("failed to create account: %v", acc)
+ }
+ json, err := ks.Export(acc, "old", "new")
+ if err != nil {
+ t.Fatalf("failed to export account: %v", acc)
+ }
+ _, ks2 := tmpKeyStore(t, true)
+ var atom uint32
+ var wg sync.WaitGroup
+ wg.Add(2)
+ for i := 0; i < 2; i++ {
+ go func() {
+ defer wg.Done()
+ if _, err := ks2.Import(json, "new", "new"); err != nil {
+ atomic.AddUint32(&atom, 1)
+ }
+ }()
+ }
+ wg.Wait()
+ if atom != 1 {
+ t.Errorf("Import is racy")
+ }
+}
+
+// checkAccounts checks that all known live accounts are present in the wallet list.
+func checkAccounts(t *testing.T, live map[types.Address]accounts.Account, wallets []accounts.Wallet) {
+ if len(live) != len(wallets) {
+ t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live))
+ return
+ }
+ liveList := make([]accounts.Account, 0, len(live))
+ for _, account := range live {
+ liveList = append(liveList, account)
+ }
+ sort.Sort(accountsByURL(liveList))
+ for j, wallet := range wallets {
+ if accs := wallet.Accounts(); len(accs) != 1 {
+ t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
+ } else if accs[0] != liveList[j] {
+ t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j])
+ }
+ }
+}
+
+// checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times.
+func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
+ for _, wantEv := range want {
+ nmatch := 0
+ for ; len(have) > 0; nmatch++ {
+ if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
+ break
+ }
+ have = have[1:]
+ }
+ if nmatch == 0 {
+ t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
+ }
+ }
+}
+
+func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
+ d := t.TempDir()
+ newKs := NewPlaintextKeyStore
+ if encrypted {
+ newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
+ }
+ return d, newKs(d)
+}
diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go
new file mode 100644
index 0000000..0151f44
--- /dev/null
+++ b/accounts/keystore/passphrase.go
@@ -0,0 +1,364 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+/*
+
+This key store behaves as KeyStorePlain with the difference that
+the private key is encrypted and on disk uses another JSON encoding.
+
+The crypto is documented at https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
+
+*/
+
+package keystore
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/math"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/google/uuid"
+ "golang.org/x/crypto/pbkdf2"
+ "golang.org/x/crypto/scrypt"
+)
+
+const (
+ keyHeaderKDF = "scrypt"
+
+ // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptN = 1 << 18
+
+ // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
+ // memory and taking approximately 1s CPU time on a modern processor.
+ StandardScryptP = 1
+
+ // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptN = 1 << 12
+
+ // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
+ // memory and taking approximately 100ms CPU time on a modern processor.
+ LightScryptP = 6
+
+ scryptR = 8
+ scryptDKLen = 32
+)
+
+type keyStorePassphrase struct {
+ keysDirPath string
+ scryptN int
+ scryptP int
+ // skipKeyFileVerification disables the security-feature which does
+ // reads and decrypts any newly created keyfiles. This should be 'false' in all
+ // cases except tests -- setting this to 'true' is not recommended.
+ skipKeyFileVerification bool
+}
+
+func (ks keyStorePassphrase) GetKey(addr types.Address, filename, auth string) (*Key, error) {
+ // Load the key from the keystore and decrypt its contents
+ keyjson, err := os.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ key, err := DecryptKey(keyjson, auth)
+ if err != nil {
+ return nil, err
+ }
+ // Make sure we're really operating on the requested key (no swap attacks)
+ if key.Address != addr {
+ return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
+ }
+ return key, nil
+}
+
+// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
+func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
+ _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
+ return a, err
+}
+
+func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
+ keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
+ if err != nil {
+ return err
+ }
+ // Write into temporary file
+ tmpName, err := writeTemporaryKeyFile(filename, keyjson)
+ if err != nil {
+ return err
+ }
+ if !ks.skipKeyFileVerification {
+ // Verify that we can decrypt the file with the given password.
+ _, err = ks.GetKey(key.Address, tmpName, auth)
+ if err != nil {
+ msg := "An error was encountered when saving and verifying the keystore file. \n" +
+ "This indicates that the keystore is corrupted. \n" +
+ "The corrupted file is stored at \n%v\n" +
+ "Please file a ticket at:\n\n" +
+ "https://github.com/astranetworld/ast/issues." +
+ "The error was : %s"
+ //lint:ignore ST1005 This is a message for the user
+ return fmt.Errorf(msg, tmpName, err)
+ }
+ }
+ return os.Rename(tmpName, filename)
+}
+
+func (ks keyStorePassphrase) JoinPath(filename string) string {
+ if filepath.IsAbs(filename) {
+ return filename
+ }
+ return filepath.Join(ks.keysDirPath, filename)
+}
+
+// Encryptdata encrypts the data given as 'data' with the password 'auth'.
+func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
+ salt := make([]byte, 32)
+ if _, err := io.ReadFull(rand.Reader, salt); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+ derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
+ if err != nil {
+ return CryptoJSON{}, err
+ }
+ encryptKey := derivedKey[:16]
+
+ iv := make([]byte, aes.BlockSize) // 16
+ if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+ panic("reading from crypto/rand failed: " + err.Error())
+ }
+ cipherText, err := aesCTRXOR(encryptKey, data, iv)
+ if err != nil {
+ return CryptoJSON{}, err
+ }
+ mac := crypto.Keccak256(derivedKey[16:32], cipherText)
+
+ scryptParamsJSON := make(map[string]interface{}, 5)
+ scryptParamsJSON["n"] = scryptN
+ scryptParamsJSON["r"] = scryptR
+ scryptParamsJSON["p"] = scryptP
+ scryptParamsJSON["dklen"] = scryptDKLen
+ scryptParamsJSON["salt"] = hex.EncodeToString(salt)
+ cipherParamsJSON := cipherparamsJSON{
+ IV: hex.EncodeToString(iv),
+ }
+
+ cryptoStruct := CryptoJSON{
+ Cipher: "aes-128-ctr",
+ CipherText: hex.EncodeToString(cipherText),
+ CipherParams: cipherParamsJSON,
+ KDF: keyHeaderKDF,
+ KDFParams: scryptParamsJSON,
+ MAC: hex.EncodeToString(mac),
+ }
+ return cryptoStruct, nil
+}
+
+// EncryptKey encrypts a key using the specified scrypt parameters into a json
+// blob that can be decrypted later on.
+func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
+ keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
+ cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
+ if err != nil {
+ return nil, err
+ }
+ encryptedKeyJSONV3 := encryptedKeyJSONV3{
+ hex.EncodeToString(key.Address[:]),
+ cryptoStruct,
+ key.Id.String(),
+ version,
+ }
+ return json.Marshal(encryptedKeyJSONV3)
+}
+
+// DecryptKey decrypts a key from a json blob, returning the private key itself.
+func DecryptKey(keyjson []byte, auth string) (*Key, error) {
+ // Parse the json into a simple map to fetch the key version
+ m := make(map[string]interface{})
+ if err := json.Unmarshal(keyjson, &m); err != nil {
+ return nil, err
+ }
+ // Depending on the version try to parse one way or another
+ var (
+ keyBytes, keyId []byte
+ err error
+ )
+ if version, ok := m["version"].(string); ok && version == "1" {
+ k := new(encryptedKeyJSONV1)
+ if err := json.Unmarshal(keyjson, k); err != nil {
+ return nil, err
+ }
+ keyBytes, keyId, err = decryptKeyV1(k, auth)
+ } else {
+ k := new(encryptedKeyJSONV3)
+ if err := json.Unmarshal(keyjson, k); err != nil {
+ return nil, err
+ }
+ keyBytes, keyId, err = decryptKeyV3(k, auth)
+ }
+ // Handle any decryption errors and return the key
+ if err != nil {
+ return nil, err
+ }
+ key := crypto.ToECDSAUnsafe(keyBytes)
+ id, err := uuid.FromBytes(keyId)
+ if err != nil {
+ return nil, err
+ }
+ return &Key{
+ Id: id,
+ Address: crypto.PubkeyToAddress(key.PublicKey),
+ PrivateKey: key,
+ }, nil
+}
+
+func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
+ if cryptoJson.Cipher != "aes-128-ctr" {
+ return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
+ }
+ mac, err := hex.DecodeString(cryptoJson.MAC)
+ if err != nil {
+ return nil, err
+ }
+
+ iv, err := hex.DecodeString(cryptoJson.CipherParams.IV)
+ if err != nil {
+ return nil, err
+ }
+
+ cipherText, err := hex.DecodeString(cryptoJson.CipherText)
+ if err != nil {
+ return nil, err
+ }
+
+ derivedKey, err := getKDFKey(cryptoJson, auth)
+ if err != nil {
+ return nil, err
+ }
+
+ calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
+ if !bytes.Equal(calculatedMAC, mac) {
+ return nil, ErrDecrypt
+ }
+
+ plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv)
+ if err != nil {
+ return nil, err
+ }
+ return plainText, err
+}
+
+func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
+ if keyProtected.Version != version {
+ return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
+ }
+ keyUUID, err := uuid.Parse(keyProtected.Id)
+ if err != nil {
+ return nil, nil, err
+ }
+ keyId = keyUUID[:]
+ plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
+ if err != nil {
+ return nil, nil, err
+ }
+ return plainText, keyId, err
+}
+
+func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
+ keyUUID, err := uuid.Parse(keyProtected.Id)
+ if err != nil {
+ return nil, nil, err
+ }
+ keyId = keyUUID[:]
+ mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ iv, err := hex.DecodeString(keyProtected.Crypto.CipherParams.IV)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ cipherText, err := hex.DecodeString(keyProtected.Crypto.CipherText)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ derivedKey, err := getKDFKey(keyProtected.Crypto, auth)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText)
+ if !bytes.Equal(calculatedMAC, mac) {
+ return nil, nil, ErrDecrypt
+ }
+
+ plainText, err := aesCBCDecrypt(crypto.Keccak256(derivedKey[:16])[:16], cipherText, iv)
+ if err != nil {
+ return nil, nil, err
+ }
+ return plainText, keyId, err
+}
+
+func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
+ authArray := []byte(auth)
+ salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
+ if err != nil {
+ return nil, err
+ }
+ dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
+
+ if cryptoJSON.KDF == keyHeaderKDF {
+ n := ensureInt(cryptoJSON.KDFParams["n"])
+ r := ensureInt(cryptoJSON.KDFParams["r"])
+ p := ensureInt(cryptoJSON.KDFParams["p"])
+ return scrypt.Key(authArray, salt, n, r, p, dkLen)
+ } else if cryptoJSON.KDF == "pbkdf2" {
+ c := ensureInt(cryptoJSON.KDFParams["c"])
+ prf := cryptoJSON.KDFParams["prf"].(string)
+ if prf != "hmac-sha256" {
+ return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
+ }
+ key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
+ return key, nil
+ }
+
+ return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
+}
+
+// TODO: can we do without this when unmarshalling dynamic JSON?
+// why do integers in KDF params end up as float64 and not int after
+// unmarshal?
+func ensureInt(x interface{}) int {
+ res, ok := x.(int)
+ if !ok {
+ res = int(x.(float64))
+ }
+ return res
+}
diff --git a/accounts/keystore/passphrase_test.go b/accounts/keystore/passphrase_test.go
new file mode 100644
index 0000000..c9af94a
--- /dev/null
+++ b/accounts/keystore/passphrase_test.go
@@ -0,0 +1,60 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "os"
+ "testing"
+
+ common "github.com/astranetworld/ast/common/types"
+)
+
+const (
+ veryLightScryptN = 2
+ veryLightScryptP = 1
+)
+
+// Tests that a json key file can be decrypted and encrypted in multiple rounds.
+func TestKeyEncryptDecrypt(t *testing.T) {
+ keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ password := ""
+ address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8")
+
+ // Do a few rounds of decryption and encryption
+ for i := 0; i < 3; i++ {
+ // Try a bad password first
+ if _, err := DecryptKey(keyjson, password+"bad"); err == nil {
+ t.Errorf("test %d: json key decrypted with bad password", i)
+ }
+ // Decrypt with the correct password
+ key, err := DecryptKey(keyjson, password)
+ if err != nil {
+ t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
+ }
+ if key.Address != address {
+ t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
+ }
+ // Recrypt with a new password and start over
+ password += "new data appended" // nolint: gosec
+ if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
+ t.Errorf("test %d: failed to recrypt key %v", i, err)
+ }
+ }
+}
diff --git a/accounts/keystore/plain.go b/accounts/keystore/plain.go
new file mode 100644
index 0000000..840ea88
--- /dev/null
+++ b/accounts/keystore/plain.go
@@ -0,0 +1,61 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/astranetworld/ast/common/types"
+)
+
+type keyStorePlain struct {
+ keysDirPath string
+}
+
+func (ks keyStorePlain) GetKey(addr types.Address, filename, auth string) (*Key, error) {
+ fd, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer fd.Close()
+ key := new(Key)
+ if err := json.NewDecoder(fd).Decode(key); err != nil {
+ return nil, err
+ }
+ if key.Address != addr {
+ return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
+ }
+ return key, nil
+}
+
+func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
+ content, err := json.Marshal(key)
+ if err != nil {
+ return err
+ }
+ return writeKeyFile(filename, content)
+}
+
+func (ks keyStorePlain) JoinPath(filename string) string {
+ if filepath.IsAbs(filename) {
+ return filename
+ }
+ return filepath.Join(ks.keysDirPath, filename)
+}
diff --git a/accounts/keystore/plain_test.go b/accounts/keystore/plain_test.go
new file mode 100644
index 0000000..02e16c5
--- /dev/null
+++ b/accounts/keystore/plain_test.go
@@ -0,0 +1,257 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package keystore
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/avm/common"
+)
+
+func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
+ d := t.TempDir()
+ if encrypted {
+ ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
+ } else {
+ ks = &keyStorePlain{d}
+ }
+ return d, ks
+}
+
+func TestKeyStorePlain(t *testing.T) {
+ _, ks := tmpKeyStoreIface(t, false)
+
+ pass := "" // not used but required by API
+ k1, account, err := storeNewKey(ks, rand.Reader, pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(k1.Address, k2.Address) {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
+ t.Fatal(err)
+ }
+}
+
+func TestKeyStorePassphrase(t *testing.T) {
+ _, ks := tmpKeyStoreIface(t, true)
+
+ pass := "foo"
+ k1, account, err := storeNewKey(ks, rand.Reader, pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(k1.Address, k2.Address) {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
+ t.Fatal(err)
+ }
+}
+
+func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
+ _, ks := tmpKeyStoreIface(t, true)
+
+ pass := "foo"
+ k1, account, err := storeNewKey(ks, rand.Reader, pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
+ t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
+ }
+}
+
+func TestImportPreSaleKey(t *testing.T) {
+ dir, ks := tmpKeyStoreIface(t, true)
+
+ // file content of a presale key file generated with:
+ // python pyethsaletool.py genwallet
+ // with password "foo"
+ fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
+ pass := "foo"
+ account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if account.Address != types.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
+ t.Errorf("imported account has wrong address %x", account.Address)
+ }
+ if !strings.HasPrefix(account.URL.Path, dir) {
+ t.Errorf("imported account file not in keystore directory: %q", account.URL)
+ }
+}
+
+// Test and utils for the key store tests in the Ethereum JSON tests;
+// testdataKeyStoreTests/basic_tests.json
+type KeyStoreTestV3 struct {
+ Json encryptedKeyJSONV3
+ Password string
+ Priv string
+}
+
+type KeyStoreTestV1 struct {
+ Json encryptedKeyJSONV1
+ Password string
+ Priv string
+}
+
+func TestV3_PBKDF2_1(t *testing.T) {
+ t.Parallel()
+ tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
+ testDecryptV3(tests["wikipage_test_vector_pbkdf2"], t)
+}
+
+var testsSubmodule = filepath.Join("..", "..", "tests", "testdata", "KeyStoreTests")
+
+func skipIfSubmoduleMissing(t *testing.T) {
+ if !common.FileExist(testsSubmodule) {
+ t.Skipf("can't find JSON tests from submodule at %s", testsSubmodule)
+ }
+}
+
+func TestV3_PBKDF2_2(t *testing.T) {
+ skipIfSubmoduleMissing(t)
+ t.Parallel()
+ tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
+ testDecryptV3(tests["test1"], t)
+}
+
+func TestV3_PBKDF2_3(t *testing.T) {
+ skipIfSubmoduleMissing(t)
+ t.Parallel()
+ tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
+ testDecryptV3(tests["python_generated_test_with_odd_iv"], t)
+}
+
+func TestV3_PBKDF2_4(t *testing.T) {
+ skipIfSubmoduleMissing(t)
+ t.Parallel()
+ tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
+ testDecryptV3(tests["evilnonce"], t)
+}
+
+func TestV3_Scrypt_1(t *testing.T) {
+ t.Parallel()
+ tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
+ testDecryptV3(tests["wikipage_test_vector_scrypt"], t)
+}
+
+func TestV3_Scrypt_2(t *testing.T) {
+ skipIfSubmoduleMissing(t)
+ t.Parallel()
+ tests := loadKeyStoreTestV3(filepath.Join(testsSubmodule, "basic_tests.json"), t)
+ testDecryptV3(tests["test2"], t)
+}
+
+func TestV1_1(t *testing.T) {
+ t.Parallel()
+ tests := loadKeyStoreTestV1("testdata/v1_test_vector.json", t)
+ testDecryptV1(tests["test1"], t)
+}
+
+func TestV1_2(t *testing.T) {
+ t.Parallel()
+ ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
+ addr := types.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
+ file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
+ k, err := ks.GetKey(addr, file, "g")
+ if err != nil {
+ t.Fatal(err)
+ }
+ privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
+ expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
+ if privHex != expectedHex {
+ t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
+ }
+}
+
+func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
+ privBytes, _, err := decryptKeyV3(&test.Json, test.Password)
+ if err != nil {
+ t.Fatal(err)
+ }
+ privHex := hex.EncodeToString(privBytes)
+ if test.Priv != privHex {
+ t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
+ }
+}
+
+func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
+ privBytes, _, err := decryptKeyV1(&test.Json, test.Password)
+ if err != nil {
+ t.Fatal(err)
+ }
+ privHex := hex.EncodeToString(privBytes)
+ if test.Priv != privHex {
+ t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
+ }
+}
+
+func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
+ tests := make(map[string]KeyStoreTestV3)
+ err := common.LoadJSON(file, &tests)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return tests
+}
+
+func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
+ tests := make(map[string]KeyStoreTestV1)
+ err := common.LoadJSON(file, &tests)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return tests
+}
+
+func TestKeyForDirectICAP(t *testing.T) {
+ t.Parallel()
+ key := NewKeyForDirectICAP(rand.Reader)
+ if !strings.HasPrefix(key.Address.Hex(), "0x00") {
+ t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())
+ }
+}
+
+func TestV3_31_Byte_Key(t *testing.T) {
+ t.Parallel()
+ tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
+ testDecryptV3(tests["31_byte_key"], t)
+}
+
+func TestV3_30_Byte_Key(t *testing.T) {
+ t.Parallel()
+ tests := loadKeyStoreTestV3("testdata/v3_test_vector.json", t)
+ testDecryptV3(tests["30_byte_key"], t)
+}
diff --git a/accounts/keystore/presale.go b/accounts/keystore/presale.go
new file mode 100644
index 0000000..a8d7234
--- /dev/null
+++ b/accounts/keystore/presale.go
@@ -0,0 +1,150 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/google/uuid"
+ "golang.org/x/crypto/pbkdf2"
+)
+
+// creates a Key and stores that in the given KeyStore by decrypting a presale key JSON
+func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accounts.Account, *Key, error) {
+ key, err := decryptPreSaleKey(keyJSON, password)
+ if err != nil {
+ return accounts.Account{}, nil, err
+ }
+ key.Id, err = uuid.NewRandom()
+ if err != nil {
+ return accounts.Account{}, nil, err
+ }
+ a := accounts.Account{
+ Address: key.Address,
+ URL: accounts.URL{
+ Scheme: KeyStoreScheme,
+ Path: keyStore.JoinPath(keyFileName(key.Address)),
+ },
+ }
+ err = keyStore.StoreKey(a.URL.Path, key, password)
+ return a, key, err
+}
+
+func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error) {
+ preSaleKeyStruct := struct {
+ EncSeed string
+ EthAddr string
+ Email string
+ BtcAddr string
+ }{}
+ err = json.Unmarshal(fileContent, &preSaleKeyStruct)
+ if err != nil {
+ return nil, err
+ }
+ encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
+ if err != nil {
+ return nil, errors.New("invalid hex in encSeed")
+ }
+ if len(encSeedBytes) < 16 {
+ return nil, errors.New("invalid encSeed, too short")
+ }
+ iv := encSeedBytes[:16]
+ cipherText := encSeedBytes[16:]
+ /*
+ See https://github.com/ethereum/pyethsaletool
+
+ pyethsaletool generates the encryption key from password by
+ 2000 rounds of PBKDF2 with HMAC-SHA-256 using password as salt (:().
+ 16 byte key length within PBKDF2 and resulting key is used as AES key
+ */
+ passBytes := []byte(password)
+ derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
+ plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
+ if err != nil {
+ return nil, err
+ }
+ ethPriv := crypto.Keccak256(plainText)
+ ecKey := crypto.ToECDSAUnsafe(ethPriv)
+
+ key = &Key{
+ Id: uuid.UUID{},
+ Address: crypto.PubkeyToAddress(ecKey.PublicKey),
+ PrivateKey: ecKey,
+ }
+ derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
+ expectedAddr := preSaleKeyStruct.EthAddr
+ if derivedAddr != expectedAddr {
+ err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
+ }
+ return key, err
+}
+
+func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
+ // AES-128 is selected due to size of encryptKey.
+ aesBlock, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ stream := cipher.NewCTR(aesBlock, iv)
+ outText := make([]byte, len(inText))
+ stream.XORKeyStream(outText, inText)
+ return outText, err
+}
+
+func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
+ aesBlock, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
+ paddedPlaintext := make([]byte, len(cipherText))
+ decrypter.CryptBlocks(paddedPlaintext, cipherText)
+ plaintext := pkcs7Unpad(paddedPlaintext)
+ if plaintext == nil {
+ return nil, ErrDecrypt
+ }
+ return plaintext, err
+}
+
+// From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes
+func pkcs7Unpad(in []byte) []byte {
+ if len(in) == 0 {
+ return nil
+ }
+
+ padding := in[len(in)-1]
+ if int(padding) > len(in) || padding > aes.BlockSize {
+ return nil
+ } else if padding == 0 {
+ return nil
+ }
+
+ for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
+ if in[i] != padding {
+ return nil
+ }
+ }
+ return in[:len(in)-int(padding)]
+}
diff --git a/accounts/keystore/testdata/dupes/1 b/accounts/keystore/testdata/dupes/1
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/1
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/dupes/2 b/accounts/keystore/testdata/dupes/2
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/2
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/dupes/foo b/accounts/keystore/testdata/dupes/foo
new file mode 100644
index 0000000..c57060a
--- /dev/null
+++ b/accounts/keystore/testdata/dupes/foo
@@ -0,0 +1 @@
+{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/.hiddenfile b/accounts/keystore/testdata/keystore/.hiddenfile
new file mode 100644
index 0000000..d91facc
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/.hiddenfile
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
diff --git a/accounts/keystore/testdata/keystore/README b/accounts/keystore/testdata/keystore/README
new file mode 100644
index 0000000..6af9ac3
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/README
@@ -0,0 +1,21 @@
+This directory contains accounts for testing.
+The password that unlocks them is "foobar".
+
+The "good" key files which are supposed to be loadable are:
+
+- File: UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+ Address: 0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+- File: aaa
+ Address: 0xf466859ead1932d743d622cb74fc058882e8648a
+- File: zzz
+ Address: 0x289d485d9771714cce91d3393d764e1311907acc
+
+The other files (including this README) are broken in various ways
+and should not be picked up by package accounts:
+
+- File: no-address (missing address field, otherwise same as "aaa")
+- File: garbage (file with random data)
+- File: empty (file with no content)
+- File: swapfile~ (should be skipped)
+- File: .hiddenfile (should be skipped)
+- File: foo/... (should be skipped because it is a directory)
diff --git a/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
new file mode 100644
index 0000000..c57060a
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
@@ -0,0 +1 @@
+{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/aaa b/accounts/keystore/testdata/keystore/aaa
new file mode 100644
index 0000000..a3868ec
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/aaa
@@ -0,0 +1 @@
+{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/empty b/accounts/keystore/testdata/keystore/empty
new file mode 100644
index 0000000..e69de29
diff --git a/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
new file mode 100644
index 0000000..309841e
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/foo/fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e
@@ -0,0 +1 @@
+{"address":"fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e","crypto":{"cipher":"aes-128-ctr","ciphertext":"8124d5134aa4a927c79fd852989e4b5419397566f04b0936a1eb1d168c7c68a5","cipherparams":{"iv":"e2febe17176414dd2cda28287947eb2f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"44b415ede89f3bdd6830390a21b78965f571b347a589d1d943029f016c5e8bd5"},"mac":"5e149ff25bfd9dd45746a84bb2bcd2f015f2cbca2b6d25c5de8c29617f71fe5b"},"id":"d6ac5452-2b2c-4d3c-ad80-4bf0327d971c","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/garbage b/accounts/keystore/testdata/keystore/garbage
new file mode 100644
index 0000000..ff45091
Binary files /dev/null and b/accounts/keystore/testdata/keystore/garbage differ
diff --git a/accounts/keystore/testdata/keystore/no-address b/accounts/keystore/testdata/keystore/no-address
new file mode 100644
index 0000000..ad51269
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/no-address
@@ -0,0 +1 @@
+{"crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/zero b/accounts/keystore/testdata/keystore/zero
new file mode 100644
index 0000000..b52617f
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/zero
@@ -0,0 +1 @@
+{"address":"0000000000000000000000000000000000000000","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/keystore/zzz b/accounts/keystore/testdata/keystore/zzz
new file mode 100644
index 0000000..cfd8a47
--- /dev/null
+++ b/accounts/keystore/testdata/keystore/zzz
@@ -0,0 +1 @@
+{"address":"289d485d9771714cce91d3393d764e1311907acc","crypto":{"cipher":"aes-128-ctr","ciphertext":"faf32ca89d286b107f5e6d842802e05263c49b78d46eac74e6109e9a963378ab","cipherparams":{"iv":"558833eec4a665a8c55608d7d503407d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"d571fff447ffb24314f9513f5160246f09997b857ac71348b73e785aab40dc04"},"mac":"21edb85ff7d0dab1767b9bf498f2c3cb7be7609490756bd32300bb213b59effe"},"id":"3279afcf-55ba-43ff-8997-02dcc46a6525","version":3}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
new file mode 100644
index 0000000..498d813
--- /dev/null
+++ b/accounts/keystore/testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e
@@ -0,0 +1 @@
+{"address":"cb61d5a9c4896fb9658090b597ef0e7be6f7b67e","Crypto":{"cipher":"aes-128-cbc","ciphertext":"6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0","cipherparams":{"iv":"35337770fc2117994ecdcad026bccff4"},"kdf":"scrypt","kdfparams":{"n":262144,"r":8,"p":1,"dklen":32,"salt":"9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"},"mac":"3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644","version":"1"},"id":"e25f7c1f-d318-4f29-b62c-687190d4d299","version":"1"}
\ No newline at end of file
diff --git a/accounts/keystore/testdata/v1_test_vector.json b/accounts/keystore/testdata/v1_test_vector.json
new file mode 100644
index 0000000..3d09b55
--- /dev/null
+++ b/accounts/keystore/testdata/v1_test_vector.json
@@ -0,0 +1,28 @@
+{
+ "test1": {
+ "json": {
+ "Crypto": {
+ "cipher": "aes-128-cbc",
+ "cipherparams": {
+ "iv": "35337770fc2117994ecdcad026bccff4"
+ },
+ "ciphertext": "6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0",
+ "kdf": "scrypt",
+ "kdfparams": {
+ "dklen": 32,
+ "n": 262144,
+ "p": 1,
+ "r": 8,
+ "salt": "9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f"
+ },
+ "mac": "3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644",
+ "version": "1"
+ },
+ "address": "cb61d5a9c4896fb9658090b597ef0e7be6f7b67e",
+ "id": "e25f7c1f-d318-4f29-b62c-687190d4d299",
+ "version": "1"
+ },
+ "password": "g",
+ "priv": "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
+ }
+}
diff --git a/accounts/keystore/testdata/v3_test_vector.json b/accounts/keystore/testdata/v3_test_vector.json
new file mode 100644
index 0000000..1e7f790
--- /dev/null
+++ b/accounts/keystore/testdata/v3_test_vector.json
@@ -0,0 +1,97 @@
+{
+ "wikipage_test_vector_scrypt": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "83dbcc02d8ccb40e466191a123791e0e"
+ },
+ "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
+ "kdf" : "scrypt",
+ "kdfparams" : {
+ "dklen" : 32,
+ "n" : 262144,
+ "r" : 1,
+ "p" : 8,
+ "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
+ },
+ "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
+ },
+ "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
+ "version" : 3
+ },
+ "password": "testpassword",
+ "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
+ },
+ "wikipage_test_vector_pbkdf2": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"
+ },
+ "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
+ "kdf" : "pbkdf2",
+ "kdfparams" : {
+ "c" : 262144,
+ "dklen" : 32,
+ "prf" : "hmac-sha256",
+ "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
+ },
+ "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
+ },
+ "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
+ "version" : 3
+ },
+ "password": "testpassword",
+ "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"
+ },
+ "31_byte_key": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "e0c41130a323adc1446fc82f724bca2f"
+ },
+ "ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984",
+ "kdf" : "scrypt",
+ "kdfparams" : {
+ "dklen" : 32,
+ "n" : 2,
+ "r" : 8,
+ "p" : 1,
+ "salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63"
+ },
+ "mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5"
+ },
+ "id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c",
+ "version" : 3
+ },
+ "password": "foo",
+ "priv": "fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35"
+ },
+ "30_byte_key": {
+ "json": {
+ "crypto" : {
+ "cipher" : "aes-128-ctr",
+ "cipherparams" : {
+ "iv" : "3ca92af36ad7c2cd92454c59cea5ef00"
+ },
+ "ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa",
+ "kdf" : "scrypt",
+ "kdfparams" : {
+ "dklen" : 32,
+ "n" : 2,
+ "r" : 8,
+ "p" : 1,
+ "salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b"
+ },
+ "mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420"
+ },
+ "id" : "a37e1559-5955-450d-8075-7b8931b392b2",
+ "version" : 3
+ },
+ "password": "foo",
+ "priv": "81c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018"
+ }
+}
diff --git a/accounts/keystore/testdata/very-light-scrypt.json b/accounts/keystore/testdata/very-light-scrypt.json
new file mode 100644
index 0000000..d23b9b2
--- /dev/null
+++ b/accounts/keystore/testdata/very-light-scrypt.json
@@ -0,0 +1 @@
+{"address":"45dea0fb0bba44f4fcf290bba71fd57d7117cbb8","crypto":{"cipher":"aes-128-ctr","ciphertext":"b87781948a1befd247bff51ef4063f716cf6c2d3481163e9a8f42e1f9bb74145","cipherparams":{"iv":"dc4926b48a105133d2f16b96833abf1e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":2,"p":1,"r":8,"salt":"004244bbdc51cadda545b1cfa43cff9ed2ae88e08c61f1479dbb45410722f8f0"},"mac":"39990c1684557447940d4c69e06b1b82b2aceacb43f284df65c956daf3046b85"},"id":"ce541d8d-c79b-40f8-9f8c-20f59616faba","version":3}
diff --git a/accounts/keystore/wallet.go b/accounts/keystore/wallet.go
new file mode 100644
index 0000000..b5bc5a6
--- /dev/null
+++ b/accounts/keystore/wallet.go
@@ -0,0 +1,150 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package keystore
+
+import (
+ "math/big"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/common"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/transaction"
+)
+
+// keystoreWallet implements the accounts.Wallet interface for the original
+// keystore.
+type keystoreWallet struct {
+ account accounts.Account // Single account contained in this wallet
+ keystore *KeyStore // Keystore where the account originates from
+}
+
+// URL implements accounts.Wallet, returning the URL of the account within.
+func (w *keystoreWallet) URL() accounts.URL {
+ return w.account.URL
+}
+
+// Status implements accounts.Wallet, returning whether the account held by the
+// keystore wallet is unlocked or not.
+func (w *keystoreWallet) Status() (string, error) {
+ w.keystore.mu.RLock()
+ defer w.keystore.mu.RUnlock()
+
+ if _, ok := w.keystore.unlocked[w.account.Address]; ok {
+ return "Unlocked", nil
+ }
+ return "Locked", nil
+}
+
+// Open implements accounts.Wallet, but is a noop for plain wallets since there
+// is no connection or decryption step necessary to access the list of accounts.
+func (w *keystoreWallet) Open(passphrase string) error { return nil }
+
+// Close implements accounts.Wallet, but is a noop for plain wallets since there
+// is no meaningful open operation.
+func (w *keystoreWallet) Close() error { return nil }
+
+// Accounts implements accounts.Wallet, returning an account list consisting of
+// a single account that the plain keystore wallet contains.
+func (w *keystoreWallet) Accounts() []accounts.Account {
+ return []accounts.Account{w.account}
+}
+
+// Contains implements accounts.Wallet, returning whether a particular account is
+// or is not wrapped by this wallet instance.
+func (w *keystoreWallet) Contains(account accounts.Account) bool {
+ return account.Address == w.account.Address && (account.URL == (accounts.URL{}) || account.URL == w.account.URL)
+}
+
+// Derive implements accounts.Wallet, but is a noop for plain wallets since there
+// is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
+ return accounts.Account{}, accounts.ErrNotSupported
+}
+
+// SelfDerive implements accounts.Wallet, but is a noop for plain wallets since
+// there is no notion of hierarchical account derivation for plain keystore accounts.
+func (w *keystoreWallet) SelfDerive(bases []accounts.DerivationPath, chain common.ChainStateReader) {
+}
+
+// signHash attempts to sign the given hash with
+// the given account. If the wallet does not wrap this particular account, an
+// error is returned to avoid account leakage (even though in theory we may be
+// able to sign via our shared keystore backend).
+func (w *keystoreWallet) signHash(account accounts.Account, hash []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHash(account, hash)
+}
+
+// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed.
+func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
+ return w.signHash(account, crypto.Keccak256(data))
+}
+
+// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed.
+func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data))
+}
+
+// SignText implements accounts.Wallet, attempting to sign the hash of
+// the given text with the given account.
+func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ return w.signHash(account, accounts.TextHash(text))
+}
+
+// SignTextWithPassphrase implements accounts.Wallet, attempting to sign the
+// hash of the given text with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text))
+}
+
+// SignTx implements accounts.Wallet, attempting to sign the given transaction
+// with the given account. If the wallet does not wrap this particular account,
+// an error is returned to avoid account leakage (even though in theory we may
+// be able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignTx(account accounts.Account, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTx(account, tx, chainID)
+}
+
+// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
+// transaction with the given account using passphrase as extra authentication.
+func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *transaction.Transaction, chainID *big.Int) (*transaction.Transaction, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTxWithPassphrase(account, passphrase, tx, chainID)
+}
diff --git a/accounts/keystore/watch.go b/accounts/keystore/watch.go
new file mode 100644
index 0000000..b4116d6
--- /dev/null
+++ b/accounts/keystore/watch.go
@@ -0,0 +1,131 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
+// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
+
+package keystore
+
+import (
+ "github.com/fsnotify/fsnotify"
+ "time"
+
+ "github.com/astranetworld/ast/log"
+)
+
+type watcher struct {
+ ac *accountCache
+ running bool // set to true when runloop begins
+ runEnded bool // set to true when runloop ends
+ starting bool // set to true prior to runloop starting
+ quit chan struct{}
+}
+
+func newWatcher(ac *accountCache) *watcher {
+ return &watcher{
+ ac: ac,
+ quit: make(chan struct{}),
+ }
+}
+
+// enabled returns false on systems not supported.
+func (*watcher) enabled() bool { return true }
+
+// starts the watcher loop in the background.
+// Start a watcher in the background if that's not already in progress.
+// The caller must hold w.ac.mu.
+func (w *watcher) start() {
+ if w.starting || w.running {
+ return
+ }
+ w.starting = true
+ go w.loop()
+}
+
+func (w *watcher) close() {
+ close(w.quit)
+}
+
+func (w *watcher) loop() {
+ defer func() {
+ w.ac.mu.Lock()
+ w.running = false
+ w.starting = false
+ w.runEnded = true
+ w.ac.mu.Unlock()
+ }()
+ logger := log.New("path", w.ac.keydir)
+
+ // Create new watcher.
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Error("Failed to start filesystem watcher", "err", err)
+ return
+ }
+ defer watcher.Close()
+ if err := watcher.Add(w.ac.keydir); err != nil {
+ logger.Warn("Failed to watch keystore folder", "err", err)
+ return
+ }
+
+ logger.Trace("Started watching keystore folder", "folder", w.ac.keydir)
+ defer logger.Trace("Stopped watching keystore folder")
+
+ w.ac.mu.Lock()
+ w.running = true
+ w.ac.mu.Unlock()
+
+ // Wait for file system events and reload.
+ // When an event occurs, the reload call is delayed a bit so that
+ // multiple events arriving quickly only cause a single reload.
+ var (
+ debounceDuration = 500 * time.Millisecond
+ rescanTriggered = false
+ debounce = time.NewTimer(0)
+ )
+ // Ignore initial trigger
+ if !debounce.Stop() {
+ <-debounce.C
+ }
+ defer debounce.Stop()
+ for {
+ select {
+ case <-w.quit:
+ return
+ case _, ok := <-watcher.Events:
+ if !ok {
+ return
+ }
+ // Trigger the scan (with delay), if not already triggered
+ if !rescanTriggered {
+ debounce.Reset(debounceDuration)
+ rescanTriggered = true
+ }
+ // The fsnotify library does provide more granular event-info, it
+ // would be possible to refresh individual affected files instead
+ // of scheduling a full rescan. For most cases though, the
+ // full rescan is quick and obviously simplest.
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ return
+ }
+ log.Info("Filsystem watcher error", "err", err)
+ case <-debounce.C:
+ w.ac.scanAccounts()
+ rescanTriggered = false
+ }
+ }
+}
diff --git a/accounts/keystore/watch_fallback.go b/accounts/keystore/watch_fallback.go
new file mode 100644
index 0000000..ecd38da
--- /dev/null
+++ b/accounts/keystore/watch_fallback.go
@@ -0,0 +1,35 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
+// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
+
+// This is the fallback implementation of directory watching.
+// It is used on unsupported platforms.
+
+package keystore
+
+type watcher struct {
+ running bool
+ runEnded bool
+}
+
+func newWatcher(*accountCache) *watcher { return new(watcher) }
+func (*watcher) start() {}
+func (*watcher) close() {}
+
+// enabled returns false on systems not supported.
+func (*watcher) enabled() bool { return false }
diff --git a/accounts/manager.go b/accounts/manager.go
new file mode 100644
index 0000000..c07f24f
--- /dev/null
+++ b/accounts/manager.go
@@ -0,0 +1,272 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+
+ "github.com/astranetworld/ast/common/types"
+ event "github.com/astranetworld/ast/modules/event/v2"
+)
+
+// managerSubBufferSize determines how many incoming wallet events
+// the manager will buffer in its channel.
+const managerSubBufferSize = 50
+
+// Config contains the settings of the global account manager.
+//
+// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
+// is removed in favor of Clef.
+type Config struct {
+ InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
+}
+
+// newBackendEvent lets the manager know it should
+// track the given backend for wallet updates.
+type newBackendEvent struct {
+ backend Backend
+ processed chan struct{} // Informs event emitter that backend has been integrated
+}
+
+// Manager is an overarching account manager that can communicate with various
+// backends for signing transactions.
+type Manager struct {
+ config *Config // Global account manager configurations
+ backends map[reflect.Type][]Backend // Index of backends currently registered
+ updaters []event.Subscription // Wallet update subscriptions for all backends
+ updates chan WalletEvent // Subscription sink for backend wallet changes
+ newBackends chan newBackendEvent // Incoming backends to be tracked by the manager
+ wallets []Wallet // Cache of all wallets from all registered backends
+
+ quit chan chan error
+ term chan struct{} // Channel is closed upon termination of the update loop
+ lock sync.RWMutex
+}
+
+// NewManager creates a generic account manager to sign transaction via various
+// supported backends.
+func NewManager(config *Config, backends ...Backend) *Manager {
+ // Retrieve the initial list of wallets from the backends and sort by URL
+ var wallets []Wallet
+ for _, backend := range backends {
+ wallets = merge(wallets, backend.Wallets()...)
+ }
+ // Subscribe to wallet notifications from all backends
+ updates := make(chan WalletEvent, managerSubBufferSize)
+
+ subs := make([]event.Subscription, len(backends))
+ for i, backend := range backends {
+ subs[i] = backend.Subscribe(updates)
+ }
+ // Assemble the account manager and return
+ am := &Manager{
+ config: config,
+ backends: make(map[reflect.Type][]Backend),
+ updaters: subs,
+ updates: updates,
+ newBackends: make(chan newBackendEvent),
+ wallets: wallets,
+ quit: make(chan chan error),
+ term: make(chan struct{}),
+ }
+ for _, backend := range backends {
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ }
+ go am.update()
+
+ return am
+}
+
+// Close terminates the account manager's internal notification processes.
+func (am *Manager) Close() error {
+ errc := make(chan error)
+ am.quit <- errc
+ return <-errc
+}
+
+// Config returns the configuration of account manager.
+func (am *Manager) Config() *Config {
+ return am.config
+}
+
+// AddBackend starts the tracking of an additional backend for wallet updates.
+// cmd/geth assumes once this func returns the backends have been already integrated.
+func (am *Manager) AddBackend(backend Backend) {
+ done := make(chan struct{})
+ am.newBackends <- newBackendEvent{backend, done}
+ <-done
+}
+
+// update is the wallet event loop listening for notifications from the backends
+// and updating the cache of wallets.
+func (am *Manager) update() {
+ // Close all subscriptions when the manager terminates
+ defer func() {
+ am.lock.Lock()
+ for _, sub := range am.updaters {
+ sub.Unsubscribe()
+ }
+ am.updaters = nil
+ am.lock.Unlock()
+ }()
+
+ // Loop until termination
+ for {
+ select {
+ case walletEvent := <-am.updates:
+ // Wallet event arrived, update local cache
+ am.lock.Lock()
+ switch walletEvent.Kind {
+ case WalletArrived:
+ am.wallets = merge(am.wallets, walletEvent.Wallet)
+ case WalletDropped:
+ am.wallets = drop(am.wallets, walletEvent.Wallet)
+ }
+ am.lock.Unlock()
+
+ // Notify any listeners of the event
+
+ //am.feed.Send(&walletEvent)
+ event.GlobalEvent.Send(walletEvent)
+ case backendEvent := <-am.newBackends:
+ am.lock.Lock()
+ // Update caches
+ backend := backendEvent.backend
+ am.wallets = merge(am.wallets, backend.Wallets()...)
+ am.updaters = append(am.updaters, backend.Subscribe(am.updates))
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ am.lock.Unlock()
+ close(backendEvent.processed)
+ case errc := <-am.quit:
+ // Manager terminating, return
+ errc <- nil
+ // Signals event emitters the loop is not receiving values
+ // to prevent them from getting stuck.
+ close(am.term)
+ return
+ }
+ }
+}
+
+// Backends retrieves the backend(s) with the given type from the account manager.
+func (am *Manager) Backends(kind reflect.Type) []Backend {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ return am.backends[kind]
+}
+
+// Wallets returns all signer accounts registered under this account manager.
+func (am *Manager) Wallets() []Wallet {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ return am.walletsNoLock()
+}
+
+// walletsNoLock returns all registered wallets. Callers must hold am.lock.
+func (am *Manager) walletsNoLock() []Wallet {
+ cpy := make([]Wallet, len(am.wallets))
+ copy(cpy, am.wallets)
+ return cpy
+}
+
+// Wallet retrieves the wallet associated with a particular URL.
+func (am *Manager) Wallet(url string) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ parsed, err := parseURL(url)
+ if err != nil {
+ return nil, err
+ }
+ for _, wallet := range am.walletsNoLock() {
+ if wallet.URL() == parsed {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownWallet
+}
+
+// Accounts returns all account addresses of all wallets within the account manager
+func (am *Manager) Accounts() []types.Address {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ addresses := make([]types.Address, 0) // return [] instead of nil if empty
+ for _, wallet := range am.wallets {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
+ }
+ return addresses
+}
+
+// Find attempts to locate the wallet corresponding to a specific account. Since
+// accounts can be dynamically added to and removed from wallets, this method has
+// a linear runtime in the number of wallets.
+func (am *Manager) Find(account Account) (Wallet, error) {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
+ for _, wallet := range am.wallets {
+ if wallet.Contains(account) {
+ return wallet, nil
+ }
+ }
+ return nil, ErrUnknownAccount
+}
+
+// Subscribe creates an async subscription to receive notifications when the
+// manager detects the arrival or departure of a wallet from any of its backends.
+func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription {
+ return event.GlobalEvent.Subscribe(sink)
+}
+
+// merge is a sorted analogue of append for wallets, where the ordering of the
+// origin list is preserved by inserting new wallets at the correct position.
+//
+// The original slice is assumed to be already sorted by URL.
+func merge(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ slice = append(slice, wallet)
+ continue
+ }
+ slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
+ }
+ return slice
+}
+
+// drop is the couterpart of merge, which looks up wallets from within the sorted
+// cache and removes the ones specified.
+func drop(slice []Wallet, wallets ...Wallet) []Wallet {
+ for _, wallet := range wallets {
+ n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
+ if n == len(slice) {
+ // Wallet not found, may happen during startup
+ continue
+ }
+ slice = append(slice[:n], slice[n+1:]...)
+ }
+ return slice
+}
diff --git a/accounts/sort.go b/accounts/sort.go
new file mode 100644
index 0000000..a3ec6d1
--- /dev/null
+++ b/accounts/sort.go
@@ -0,0 +1,31 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+// AccountsByURL implements sort.Interface for []Account based on the URL field.
+type AccountsByURL []Account
+
+func (a AccountsByURL) Len() int { return len(a) }
+func (a AccountsByURL) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a AccountsByURL) Less(i, j int) bool { return a[i].URL.Cmp(a[j].URL) < 0 }
+
+// WalletsByURL implements sort.Interface for []Wallet based on the URL field.
+type WalletsByURL []Wallet
+
+func (w WalletsByURL) Len() int { return len(w) }
+func (w WalletsByURL) Swap(i, j int) { w[i], w[j] = w[j], w[i] }
+func (w WalletsByURL) Less(i, j int) bool { return w[i].URL().Cmp(w[j].URL()) < 0 }
diff --git a/accounts/url.go b/accounts/url.go
new file mode 100644
index 0000000..2350658
--- /dev/null
+++ b/accounts/url.go
@@ -0,0 +1,103 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+)
+
+// URL represents the canonical identification URL of a wallet or account.
+//
+// It is a simplified version of url.URL, with the important limitations (which
+// are considered features here) that it contains value-copyable components only,
+// as well as that it doesn't do any URL encoding/decoding of special characters.
+//
+// The former is important to allow an account to be copied without leaving live
+// references to the original version, whereas the latter is important to ensure
+// one single canonical form opposed to many allowed ones by the RFC 3986 spec.
+//
+// As such, these URLs should not be used outside of the scope of an Ethereum
+// wallet or account.
+type URL struct {
+ Scheme string // Protocol scheme to identify a capable account backend
+ Path string // Path for the backend to identify a unique entity
+}
+
+// parseURL converts a user supplied URL into the accounts specific structure.
+func parseURL(url string) (URL, error) {
+ parts := strings.Split(url, "://")
+ if len(parts) != 2 || parts[0] == "" {
+ return URL{}, errors.New("protocol scheme missing")
+ }
+ return URL{
+ Scheme: parts[0],
+ Path: parts[1],
+ }, nil
+}
+
+// String implements the stringer interface.
+func (u URL) String() string {
+ if u.Scheme != "" {
+ return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
+ }
+ return u.Path
+}
+
+// TerminalString implements the log.TerminalStringer interface.
+func (u URL) TerminalString() string {
+ url := u.String()
+ if len(url) > 32 {
+ return url[:31] + ".."
+ }
+ return url
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (u URL) MarshalJSON() ([]byte, error) {
+ return json.Marshal(u.String())
+}
+
+// UnmarshalJSON parses url.
+func (u *URL) UnmarshalJSON(input []byte) error {
+ var textURL string
+ err := json.Unmarshal(input, &textURL)
+ if err != nil {
+ return err
+ }
+ url, err := parseURL(textURL)
+ if err != nil {
+ return err
+ }
+ u.Scheme = url.Scheme
+ u.Path = url.Path
+ return nil
+}
+
+// Cmp compares x and y and returns:
+//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
+func (u URL) Cmp(url URL) int {
+ if u.Scheme == url.Scheme {
+ return strings.Compare(u.Path, url.Path)
+ }
+ return strings.Compare(u.Scheme, url.Scheme)
+}
diff --git a/accounts/url_test.go b/accounts/url_test.go
new file mode 100644
index 0000000..4822043
--- /dev/null
+++ b/accounts/url_test.go
@@ -0,0 +1,97 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package accounts
+
+import (
+ "testing"
+)
+
+func TestURLParsing(t *testing.T) {
+ url, err := parseURL("https://ethereum.org")
+ if err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+ if url.Scheme != "https" {
+ t.Errorf("expected: %v, got: %v", "https", url.Scheme)
+ }
+ if url.Path != "ethereum.org" {
+ t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
+ }
+
+ for _, u := range []string{"ethereum.org", ""} {
+ if _, err = parseURL(u); err == nil {
+ t.Errorf("input %v, expected err, got: nil", u)
+ }
+ }
+}
+
+func TestURLString(t *testing.T) {
+ url := URL{Scheme: "https", Path: "ethereum.org"}
+ if url.String() != "https://ethereum.org" {
+ t.Errorf("expected: %v, got: %v", "https://ethereum.org", url.String())
+ }
+
+ url = URL{Scheme: "", Path: "ethereum.org"}
+ if url.String() != "ethereum.org" {
+ t.Errorf("expected: %v, got: %v", "ethereum.org", url.String())
+ }
+}
+
+func TestURLMarshalJSON(t *testing.T) {
+ url := URL{Scheme: "https", Path: "ethereum.org"}
+ json, err := url.MarshalJSON()
+ if err != nil {
+ t.Errorf("unexpcted error: %v", err)
+ }
+ if string(json) != "\"https://ethereum.org\"" {
+ t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json))
+ }
+}
+
+func TestURLUnmarshalJSON(t *testing.T) {
+ url := &URL{}
+ err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
+ if err != nil {
+ t.Errorf("unexpcted error: %v", err)
+ }
+ if url.Scheme != "https" {
+ t.Errorf("expected: %v, got: %v", "https", url.Scheme)
+ }
+ if url.Path != "ethereum.org" {
+ t.Errorf("expected: %v, got: %v", "https", url.Path)
+ }
+}
+
+func TestURLComparison(t *testing.T) {
+ tests := []struct {
+ urlA URL
+ urlB URL
+ expect int
+ }{
+ {URL{"https", "ethereum.org"}, URL{"https", "ethereum.org"}, 0},
+ {URL{"http", "ethereum.org"}, URL{"https", "ethereum.org"}, -1},
+ {URL{"https", "ethereum.org/a"}, URL{"https", "ethereum.org"}, 1},
+ {URL{"https", "abc.org"}, URL{"https", "ethereum.org"}, -1},
+ }
+
+ for i, tt := range tests {
+ result := tt.urlA.Cmp(tt.urlB)
+ if result != tt.expect {
+ t.Errorf("test %d: cmp mismatch: expected: %d, got: %d", i, tt.expect, result)
+ }
+ }
+}
diff --git a/api/protocol/consensus_proto/consensus.pb.go b/api/protocol/consensus_proto/consensus.pb.go
new file mode 100644
index 0000000..ce759aa
--- /dev/null
+++ b/api/protocol/consensus_proto/consensus.pb.go
@@ -0,0 +1,383 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.0
+// protoc v3.20.0
+// source: consensus.proto
+
+package consensus_proto
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type PBSigners struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Signer []*PBSigner `protobuf:"bytes,1,rep,name=signer,proto3" json:"signer,omitempty"`
+}
+
+func (x *PBSigners) Reset() {
+ *x = PBSigners{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_consensus_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PBSigners) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PBSigners) ProtoMessage() {}
+
+func (x *PBSigners) ProtoReflect() protoreflect.Message {
+ mi := &file_consensus_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PBSigners.ProtoReflect.Descriptor instead.
+func (*PBSigners) Descriptor() ([]byte, []int) {
+ return file_consensus_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PBSigners) GetSigner() []*PBSigner {
+ if x != nil {
+ return x.Signer
+ }
+ return nil
+}
+
+type PBSigner struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Public string `protobuf:"bytes,1,opt,name=public,proto3" json:"public,omitempty"`
+ Address *types_pb.H160 `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
+ Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (x *PBSigner) Reset() {
+ *x = PBSigner{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_consensus_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PBSigner) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PBSigner) ProtoMessage() {}
+
+func (x *PBSigner) ProtoReflect() protoreflect.Message {
+ mi := &file_consensus_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PBSigner.ProtoReflect.Descriptor instead.
+func (*PBSigner) Descriptor() ([]byte, []int) {
+ return file_consensus_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PBSigner) GetPublic() string {
+ if x != nil {
+ return x.Public
+ }
+ return ""
+}
+
+func (x *PBSigner) GetAddress() *types_pb.H160 {
+ if x != nil {
+ return x.Address
+ }
+ return nil
+}
+
+func (x *PBSigner) GetVersion() string {
+ if x != nil {
+ return x.Version
+ }
+ return ""
+}
+
+type PBVote struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+}
+
+func (x *PBVote) Reset() {
+ *x = PBVote{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_consensus_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PBVote) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PBVote) ProtoMessage() {}
+
+func (x *PBVote) ProtoReflect() protoreflect.Message {
+ mi := &file_consensus_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PBVote.ProtoReflect.Descriptor instead.
+func (*PBVote) Descriptor() ([]byte, []int) {
+ return file_consensus_proto_rawDescGZIP(), []int{2}
+}
+
+type PBPoaInfo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Public string `protobuf:"bytes,1,opt,name=public,proto3" json:"public,omitempty"`
+ Sign []byte `protobuf:"bytes,2,opt,name=sign,proto3" json:"sign,omitempty"`
+ Type int64 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"`
+}
+
+func (x *PBPoaInfo) Reset() {
+ *x = PBPoaInfo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_consensus_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PBPoaInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PBPoaInfo) ProtoMessage() {}
+
+func (x *PBPoaInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_consensus_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PBPoaInfo.ProtoReflect.Descriptor instead.
+func (*PBPoaInfo) Descriptor() ([]byte, []int) {
+ return file_consensus_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *PBPoaInfo) GetPublic() string {
+ if x != nil {
+ return x.Public
+ }
+ return ""
+}
+
+func (x *PBPoaInfo) GetSign() []byte {
+ if x != nil {
+ return x.Sign
+ }
+ return nil
+}
+
+func (x *PBPoaInfo) GetType() int64 {
+ if x != nil {
+ return x.Type
+ }
+ return 0
+}
+
+var File_consensus_proto protoreflect.FileDescriptor
+
+var file_consensus_proto_rawDesc = []byte{
+ 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x0c, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x5f, 0x70, 0x62, 0x1a,
+ 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3b, 0x0a, 0x09, 0x50, 0x42, 0x53, 0x69, 0x67, 0x6e, 0x65,
+ 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x50, 0x42, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e,
+ 0x65, 0x72, 0x22, 0x66, 0x0a, 0x08, 0x50, 0x42, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x16,
+ 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+ 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
+ 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36, 0x30, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
+ 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x08, 0x0a, 0x06, 0x50, 0x42,
+ 0x56, 0x6f, 0x74, 0x65, 0x22, 0x4b, 0x0a, 0x09, 0x50, 0x42, 0x50, 0x6f, 0x61, 0x49, 0x6e, 0x66,
+ 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67,
+ 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x12, 0x0a,
+ 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x79, 0x70,
+ 0x65, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61, 0x6d, 0x63, 0x2f, 0x61,
+ 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x73,
+ 0x65, 0x6e, 0x73, 0x75, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_consensus_proto_rawDescOnce sync.Once
+ file_consensus_proto_rawDescData = file_consensus_proto_rawDesc
+)
+
+func file_consensus_proto_rawDescGZIP() []byte {
+ file_consensus_proto_rawDescOnce.Do(func() {
+ file_consensus_proto_rawDescData = protoimpl.X.CompressGZIP(file_consensus_proto_rawDescData)
+ })
+ return file_consensus_proto_rawDescData
+}
+
+var file_consensus_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_consensus_proto_goTypes = []interface{}{
+ (*PBSigners)(nil), // 0: consensus_pb.PBSigners
+ (*PBSigner)(nil), // 1: consensus_pb.PBSigner
+ (*PBVote)(nil), // 2: consensus_pb.PBVote
+ (*PBPoaInfo)(nil), // 3: consensus_pb.PBPoaInfo
+ (*types_pb.H160)(nil), // 4: types_pb.H160
+}
+var file_consensus_proto_depIdxs = []int32{
+ 1, // 0: consensus_pb.PBSigners.signer:type_name -> consensus_pb.PBSigner
+ 4, // 1: consensus_pb.PBSigner.address:type_name -> types_pb.H160
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_consensus_proto_init() }
+func file_consensus_proto_init() {
+ if File_consensus_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_consensus_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PBSigners); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_consensus_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PBSigner); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_consensus_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PBVote); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_consensus_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PBPoaInfo); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_consensus_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_consensus_proto_goTypes,
+ DependencyIndexes: file_consensus_proto_depIdxs,
+ MessageInfos: file_consensus_proto_msgTypes,
+ }.Build()
+ File_consensus_proto = out.File
+ file_consensus_proto_rawDesc = nil
+ file_consensus_proto_goTypes = nil
+ file_consensus_proto_depIdxs = nil
+}
diff --git a/api/protocol/consensus_proto/consensus.proto b/api/protocol/consensus_proto/consensus.proto
new file mode 100644
index 0000000..4ebc990
--- /dev/null
+++ b/api/protocol/consensus_proto/consensus.proto
@@ -0,0 +1,41 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package consensus_pb;
+
+import "types_pb/types.proto";
+option go_package = "github.com/astranetworld/ast/api/protocol/consensus_proto";
+
+message PBSigners {
+ repeated PBSigner signer = 1;
+}
+
+message PBSigner {
+ string public = 1;
+ types_pb.H160 address = 2;
+ string version = 3;
+}
+
+message PBVote {
+
+}
+
+message PBPoaInfo {
+ string public = 1;
+ bytes sign = 2;
+ int64 type = 3;
+}
diff --git a/api/protocol/consensus_proto/gen.go b/api/protocol/consensus_proto/gen.go
new file mode 100644
index 0000000..4143321
--- /dev/null
+++ b/api/protocol/consensus_proto/gen.go
@@ -0,0 +1,19 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package consensus_proto
+
+//go:generate protoc -I=../ -I=. -I=../include --go_out=paths=source_relative:. consensus.proto
diff --git a/api/protocol/ext/gen.go b/api/protocol/ext/gen.go
new file mode 100644
index 0000000..4c10c29
--- /dev/null
+++ b/api/protocol/ext/gen.go
@@ -0,0 +1,19 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package ext
+
+//go:generate protoc -I=../ -I=. -I=../include --go_out=paths=source_relative:. options.proto
diff --git a/api/protocol/ext/options.pb.go b/api/protocol/ext/options.pb.go
new file mode 100644
index 0000000..3bd9f7e
--- /dev/null
+++ b/api/protocol/ext/options.pb.go
@@ -0,0 +1,151 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.0
+// protoc v3.20.0
+// source: options.proto
+
+package ext
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+ reflect "reflect"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+var file_options_proto_extTypes = []protoimpl.ExtensionInfo{
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50000,
+ Name: "ext.cast_type",
+ Tag: "bytes,50000,opt,name=cast_type",
+ Filename: "options.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50001,
+ Name: "ext.ssz_size",
+ Tag: "bytes,50001,opt,name=ssz_size",
+ Filename: "options.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50002,
+ Name: "ext.ssz_max",
+ Tag: "bytes,50002,opt,name=ssz_max",
+ Filename: "options.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50003,
+ Name: "ext.spec_name",
+ Tag: "bytes,50003,opt,name=spec_name",
+ Filename: "options.proto",
+ },
+}
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+ // optional string cast_type = 50000;
+ E_CastType = &file_options_proto_extTypes[0]
+ // optional string ssz_size = 50001;
+ E_SszSize = &file_options_proto_extTypes[1]
+ // optional string ssz_max = 50002;
+ E_SszMax = &file_options_proto_extTypes[2]
+ // optional string spec_name = 50003;
+ E_SpecName = &file_options_proto_extTypes[3]
+)
+
+var File_options_proto protoreflect.FileDescriptor
+
+var file_options_proto_rawDesc = []byte{
+ 0x0a, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+ 0x03, 0x65, 0x78, 0x74, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x3c, 0x0a, 0x09, 0x63, 0x61, 0x73, 0x74, 0x5f, 0x74,
+ 0x79, 0x70, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x73, 0x74,
+ 0x54, 0x79, 0x70, 0x65, 0x3a, 0x3a, 0x0a, 0x08, 0x73, 0x73, 0x7a, 0x5f, 0x73, 0x69, 0x7a, 0x65,
+ 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+ 0xd1, 0x86, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x73, 0x7a, 0x53, 0x69, 0x7a, 0x65,
+ 0x3a, 0x38, 0x0a, 0x07, 0x73, 0x73, 0x7a, 0x5f, 0x6d, 0x61, 0x78, 0x12, 0x1d, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69,
+ 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd2, 0x86, 0x03, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x06, 0x73, 0x73, 0x7a, 0x4d, 0x61, 0x78, 0x3a, 0x3c, 0x0a, 0x09, 0x73, 0x70,
+ 0x65, 0x63, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd3, 0x86, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+ 0x73, 0x70, 0x65, 0x63, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68,
+ 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69,
+ 0x6e, 0x2f, 0x61, 0x6d, 0x63, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x6f, 0x6c, 0x2f, 0x65, 0x78, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_options_proto_goTypes = []interface{}{
+ (*descriptorpb.FieldOptions)(nil), // 0: google.protobuf.FieldOptions
+}
+var file_options_proto_depIdxs = []int32{
+ 0, // 0: ext.cast_type:extendee -> google.protobuf.FieldOptions
+ 0, // 1: ext.ssz_size:extendee -> google.protobuf.FieldOptions
+ 0, // 2: ext.ssz_max:extendee -> google.protobuf.FieldOptions
+ 0, // 3: ext.spec_name:extendee -> google.protobuf.FieldOptions
+ 4, // [4:4] is the sub-list for method output_type
+ 4, // [4:4] is the sub-list for method input_type
+ 4, // [4:4] is the sub-list for extension type_name
+ 0, // [0:4] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_options_proto_init() }
+func file_options_proto_init() {
+ if File_options_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_options_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 0,
+ NumExtensions: 4,
+ NumServices: 0,
+ },
+ GoTypes: file_options_proto_goTypes,
+ DependencyIndexes: file_options_proto_depIdxs,
+ ExtensionInfos: file_options_proto_extTypes,
+ }.Build()
+ File_options_proto = out.File
+ file_options_proto_rawDesc = nil
+ file_options_proto_goTypes = nil
+ file_options_proto_depIdxs = nil
+}
diff --git a/api/protocol/ext/options.proto b/api/protocol/ext/options.proto
new file mode 100644
index 0000000..9319e70
--- /dev/null
+++ b/api/protocol/ext/options.proto
@@ -0,0 +1,29 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+syntax = "proto3";
+
+package ext;
+
+import "google/protobuf/descriptor.proto";
+
+option go_package = "github.com/astranetworld/ast/api/protocol/ext";
+
+extend google.protobuf.FieldOptions {
+ string cast_type = 50000;
+ string ssz_size = 50001;
+ string ssz_max = 50002;
+ string spec_name = 50003;
+}
\ No newline at end of file
diff --git a/api/protocol/include/google/api/annotations.proto b/api/protocol/include/google/api/annotations.proto
new file mode 100644
index 0000000..85c361b
--- /dev/null
+++ b/api/protocol/include/google/api/annotations.proto
@@ -0,0 +1,31 @@
+// Copyright (c) 2015, Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/api/http.proto";
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "AnnotationsProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+extend google.protobuf.MethodOptions {
+ // See `HttpRule`.
+ HttpRule http = 72295728;
+}
diff --git a/api/protocol/include/google/api/http.proto b/api/protocol/include/google/api/http.proto
new file mode 100644
index 0000000..2bd3a19
--- /dev/null
+++ b/api/protocol/include/google/api/http.proto
@@ -0,0 +1,318 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package google.api;
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
+option java_multiple_files = true;
+option java_outer_classname = "HttpProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+
+// Defines the HTTP configuration for an API service. It contains a list of
+// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
+// to one or more HTTP REST API methods.
+message Http {
+ // A list of HTTP configuration rules that apply to individual API methods.
+ //
+ // **NOTE:** All service configuration rules follow "last one wins" order.
+ repeated HttpRule rules = 1;
+
+ // When set to true, URL path parmeters will be fully URI-decoded except in
+ // cases of single segment matches in reserved expansion, where "%2F" will be
+ // left encoded.
+ //
+ // The default behavior is to not decode RFC 6570 reserved characters in multi
+ // segment matches.
+ bool fully_decode_reserved_expansion = 2;
+}
+
+// `HttpRule` defines the mapping of an RPC method to one or more HTTP
+// REST API methods. The mapping specifies how different portions of the RPC
+// request message are mapped to URL path, URL query parameters, and
+// HTTP request body. The mapping is typically specified as an
+// `google.api.http` annotation on the RPC method,
+// see "google/api/annotations.proto" for details.
+//
+// The mapping consists of a field specifying the path template and
+// method kind. The path template can refer to fields in the request
+// message, as in the example below which describes a REST GET
+// operation on a resource collection of messages:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// SubMessage sub = 2; // `sub.subfield` is url-mapped
+// }
+// message Message {
+// string text = 1; // content of the resource
+// }
+//
+// The same http annotation can alternatively be expressed inside the
+// `GRPC API Configuration` YAML file.
+//
+// http:
+// rules:
+// - selector: .Messaging.GetMessage
+// get: /v1/messages/{message_id}/{sub.subfield}
+//
+// This definition enables an automatic, bidrectional mapping of HTTP
+// JSON to RPC. Example:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))`
+//
+// In general, not only fields but also field paths can be referenced
+// from a path pattern. Fields mapped to the path pattern cannot be
+// repeated and must have a primitive (non-message) type.
+//
+// Any fields in the request message which are not bound by the path
+// pattern automatically become (optional) HTTP query
+// parameters. Assume the following definition of the request message:
+//
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http).get = "/v1/messages/{message_id}";
+// }
+// }
+// message GetMessageRequest {
+// message SubMessage {
+// string subfield = 1;
+// }
+// string message_id = 1; // mapped to the URL
+// int64 revision = 2; // becomes a parameter
+// SubMessage sub = 3; // `sub.subfield` becomes a parameter
+// }
+//
+//
+// This enables a HTTP JSON to RPC mapping as below:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))`
+//
+// Note that fields which are mapped to HTTP parameters must have a
+// primitive type or a repeated primitive type. Message types are not
+// allowed. In the case of a repeated type, the parameter can be
+// repeated in the URL, as in `...?param=A¶m=B`.
+//
+// For HTTP method kinds which allow a request body, the `body` field
+// specifies the mapping. Consider a REST update method on the
+// message resource collection:
+//
+//
+// service Messaging {
+// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "message"
+// };
+// }
+// }
+// message UpdateMessageRequest {
+// string message_id = 1; // mapped to the URL
+// Message message = 2; // mapped to the body
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled, where the
+// representation of the JSON in the request body is determined by
+// protos JSON encoding:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
+//
+// The special name `*` can be used in the body mapping to define that
+// every field not bound by the path template should be mapped to the
+// request body. This enables the following alternative definition of
+// the update method:
+//
+// service Messaging {
+// rpc UpdateMessage(Message) returns (Message) {
+// option (google.api.http) = {
+// put: "/v1/messages/{message_id}"
+// body: "*"
+// };
+// }
+// }
+// message Message {
+// string message_id = 1;
+// string text = 2;
+// }
+//
+//
+// The following HTTP JSON to RPC mapping is enabled:
+//
+// HTTP | RPC
+// -----|-----
+// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")`
+//
+// Note that when using `*` in the body mapping, it is not possible to
+// have HTTP parameters, as all fields not bound by the path end in
+// the body. This makes this option more rarely used in practice of
+// defining REST APIs. The common usage of `*` is in custom methods
+// which don't use the URL at all for transferring data.
+//
+// It is possible to define multiple HTTP methods for one RPC by using
+// the `additional_bindings` option. Example:
+//
+// service Messaging {
+// rpc GetMessage(GetMessageRequest) returns (Message) {
+// option (google.api.http) = {
+// get: "/v1/messages/{message_id}"
+// additional_bindings {
+// get: "/v1/users/{user_id}/messages/{message_id}"
+// }
+// };
+// }
+// }
+// message GetMessageRequest {
+// string message_id = 1;
+// string user_id = 2;
+// }
+//
+//
+// This enables the following two alternative HTTP JSON to RPC
+// mappings:
+//
+// HTTP | RPC
+// -----|-----
+// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")`
+// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")`
+//
+// # Rules for HTTP mapping
+//
+// The rules for mapping HTTP path, query parameters, and body fields
+// to the request message are as follows:
+//
+// 1. The `body` field specifies either `*` or a field path, or is
+// omitted. If omitted, it indicates there is no HTTP request body.
+// 2. Leaf fields (recursive expansion of nested messages in the
+// request) can be classified into three types:
+// (a) Matched in the URL template.
+// (b) Covered by body (if body is `*`, everything except (a) fields;
+// else everything under the body field)
+// (c) All other fields.
+// 3. URL query parameters found in the HTTP request are mapped to (c) fields.
+// 4. Any body sent with an HTTP request can contain only (b) fields.
+//
+// The syntax of the path template is as follows:
+//
+// Template = "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+//
+// The syntax `*` matches a single path segment. The syntax `**` matches zero
+// or more path segments, which must be the last part of the path except the
+// `Verb`. The syntax `LITERAL` matches literal text in the path.
+//
+// The syntax `Variable` matches part of the URL path as specified by its
+// template. A variable template must not contain other variables. If a variable
+// matches a single path segment, its template may be omitted, e.g. `{var}`
+// is equivalent to `{var=*}`.
+//
+// If a variable contains exactly one path segment, such as `"{var}"` or
+// `"{var=*}"`, when such a variable is expanded into a URL path, all characters
+// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the
+// Discovery Document as `{var}`.
+//
+// If a variable contains one or more path segments, such as `"{var=foo/*}"`
+// or `"{var=**}"`, when such a variable is expanded into a URL path, all
+// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables
+// show up in the Discovery Document as `{+var}`.
+//
+// NOTE: While the single segment variable matches the semantics of
+// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2
+// Simple String Expansion, the multi segment variable **does not** match
+// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion
+// does not expand special characters like `?` and `#`, which would lead
+// to invalid URLs.
+//
+// NOTE: the field paths in variables and in the `body` must not refer to
+// repeated fields or map fields.
+message HttpRule {
+ // Selects methods to which this rule applies.
+ //
+ // Refer to [selector][google.api.DocumentationRule.selector] for syntax details.
+ string selector = 1;
+
+ // Determines the URL pattern is matched by this rules. This pattern can be
+ // used with any of the {get|put|post|delete|patch} methods. A custom method
+ // can be defined using the 'custom' field.
+ oneof pattern {
+ // Used for listing and getting information about resources.
+ string get = 2;
+
+ // Used for updating a resource.
+ string put = 3;
+
+ // Used for creating a resource.
+ string post = 4;
+
+ // Used for deleting a resource.
+ string delete = 5;
+
+ // Used for updating a resource.
+ string patch = 6;
+
+ // The custom pattern is used for specifying an HTTP method that is not
+ // included in the `pattern` field, such as HEAD, or "*" to leave the
+ // HTTP method unspecified for this rule. The wild-card rule is useful
+ // for services that provide content to Web (HTML) clients.
+ CustomHttpPattern custom = 8;
+ }
+
+ // The name of the request field whose value is mapped to the HTTP body, or
+ // `*` for mapping all fields not captured by the path pattern to the HTTP
+ // body. NOTE: the referred field must not be a repeated field and must be
+ // present at the top-level of request message type.
+ string body = 7;
+
+ // Optional. The name of the response field whose value is mapped to the HTTP
+ // body of response. Other response fields are ignored. When
+ // not set, the response message will be used as HTTP body of response.
+ string response_body = 12;
+
+ // Additional HTTP bindings for the selector. Nested bindings must
+ // not contain an `additional_bindings` field themselves (that is,
+ // the nesting may only be one level deep).
+ repeated HttpRule additional_bindings = 11;
+}
+
+// A custom pattern is used for defining custom HTTP verb.
+message CustomHttpPattern {
+ // The name of this custom HTTP verb.
+ string kind = 1;
+
+ // The path matched by this custom verb.
+ string path = 2;
+}
diff --git a/api/protocol/include/google/api/httpbody.proto b/api/protocol/include/google/api/httpbody.proto
new file mode 100644
index 0000000..4428515
--- /dev/null
+++ b/api/protocol/include/google/api/httpbody.proto
@@ -0,0 +1,78 @@
+// Copyright 2018 Google LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+syntax = "proto3";
+
+package google.api;
+
+import "google/protobuf/any.proto";
+
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody";
+option java_multiple_files = true;
+option java_outer_classname = "HttpBodyProto";
+option java_package = "com.google.api";
+option objc_class_prefix = "GAPI";
+
+// Message that represents an arbitrary HTTP body. It should only be used for
+// payload formats that can't be represented as JSON, such as raw binary or
+// an HTML page.
+//
+//
+// This message can be used both in streaming and non-streaming API methods in
+// the request as well as the response.
+//
+// It can be used as a top-level request field, which is convenient if one
+// wants to extract parameters from either the URL or HTTP template into the
+// request fields and also want access to the raw HTTP body.
+//
+// Example:
+//
+// message GetResourceRequest {
+// // A unique request id.
+// string request_id = 1;
+//
+// // The raw HTTP body is bound to this field.
+// google.api.HttpBody http_body = 2;
+// }
+//
+// service ResourceService {
+// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);
+// rpc UpdateResource(google.api.HttpBody) returns
+// (google.protobuf.Empty);
+// }
+//
+// Example with streaming methods:
+//
+// service CaldavService {
+// rpc GetCalendar(stream google.api.HttpBody)
+// returns (stream google.api.HttpBody);
+// rpc UpdateCalendar(stream google.api.HttpBody)
+// returns (stream google.api.HttpBody);
+// }
+//
+// Use of this type only changes how the request and response bodies are
+// handled, all other features will continue to work unchanged.
+message HttpBody {
+ // The HTTP Content-Type header value specifying the content type of the body.
+ string content_type = 1;
+
+ // The HTTP request/response body as raw binary.
+ bytes data = 2;
+
+ // Application specific response metadata. Must be set in the first response
+ // for streaming APIs.
+ repeated google.protobuf.Any extensions = 3;
+}
\ No newline at end of file
diff --git a/api/protocol/include/google/protobuf/any.proto b/api/protocol/include/google/protobuf/any.proto
new file mode 100644
index 0000000..6ed8a23
--- /dev/null
+++ b/api/protocol/include/google/protobuf/any.proto
@@ -0,0 +1,158 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "google.golang.org/protobuf/types/known/anypb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// `Any` contains an arbitrary serialized protocol buffer message along with a
+// URL that describes the type of the serialized message.
+//
+// Protobuf library provides support to pack/unpack Any values in the form
+// of utility functions or additional generated methods of the Any type.
+//
+// Example 1: Pack and unpack a message in C++.
+//
+// Foo foo = ...;
+// Any any;
+// any.PackFrom(foo);
+// ...
+// if (any.UnpackTo(&foo)) {
+// ...
+// }
+//
+// Example 2: Pack and unpack a message in Java.
+//
+// Foo foo = ...;
+// Any any = Any.pack(foo);
+// ...
+// if (any.is(Foo.class)) {
+// foo = any.unpack(Foo.class);
+// }
+//
+// Example 3: Pack and unpack a message in Python.
+//
+// foo = Foo(...)
+// any = Any()
+// any.Pack(foo)
+// ...
+// if any.Is(Foo.DESCRIPTOR):
+// any.Unpack(foo)
+// ...
+//
+// Example 4: Pack and unpack a message in Go
+//
+// foo := &pb.Foo{...}
+// any, err := anypb.New(foo)
+// if err != nil {
+// ...
+// }
+// ...
+// foo := &pb.Foo{}
+// if err := any.UnmarshalTo(foo); err != nil {
+// ...
+// }
+//
+// The pack methods provided by protobuf library will by default use
+// 'type.googleapis.com/full.type.name' as the type URL and the unpack
+// methods only use the fully qualified type name after the last '/'
+// in the type URL, for example "foo.bar.com/x/y.z" will yield type
+// name "y.z".
+//
+//
+// JSON
+// ====
+// The JSON representation of an `Any` value uses the regular
+// representation of the deserialized, embedded message, with an
+// additional field `@type` which contains the type URL. Example:
+//
+// package google.profile;
+// message Person {
+// string first_name = 1;
+// string last_name = 2;
+// }
+//
+// {
+// "@type": "type.googleapis.com/google.profile.Person",
+// "firstName": ,
+// "lastName":
+// }
+//
+// If the embedded message type is well-known and has a custom JSON
+// representation, that representation will be embedded adding a field
+// `value` which holds the custom JSON in addition to the `@type`
+// field. Example (for message [google.protobuf.Duration][]):
+//
+// {
+// "@type": "type.googleapis.com/google.protobuf.Duration",
+// "value": "1.212s"
+// }
+//
+message Any {
+ // A URL/resource name that uniquely identifies the type of the serialized
+ // protocol buffer message. This string must contain at least
+ // one "/" character. The last segment of the URL's path must represent
+ // the fully qualified name of the type (as in
+ // `path/google.protobuf.Duration`). The name should be in a canonical form
+ // (e.g., leading "." is not accepted).
+ //
+ // In practice, teams usually precompile into the binary all types that they
+ // expect it to use in the context of Any. However, for URLs which use the
+ // scheme `http`, `https`, or no scheme, one can optionally set up a type
+ // server that maps type URLs to message definitions as follows:
+ //
+ // * If no scheme is provided, `https` is assumed.
+ // * An HTTP GET on the URL must yield a [google.protobuf.Type][]
+ // value in binary format, or produce an error.
+ // * Applications are allowed to cache lookup results based on the
+ // URL, or have them precompiled into a binary to avoid any
+ // lookup. Therefore, binary compatibility needs to be preserved
+ // on changes to types. (Use versioned type names to manage
+ // breaking changes.)
+ //
+ // Note: this functionality is not currently available in the official
+ // protobuf release, and it is not used for type URLs beginning with
+ // type.googleapis.com.
+ //
+ // Schemes other than `http`, `https` (or the empty scheme) might be
+ // used with implementation specific semantics.
+ //
+ string type_url = 1;
+
+ // Must be a valid serialized protocol buffer of the above specified type.
+ bytes value = 2;
+}
diff --git a/api/protocol/include/google/protobuf/api.proto b/api/protocol/include/google/protobuf/api.proto
new file mode 100644
index 0000000..3d598fc
--- /dev/null
+++ b/api/protocol/include/google/protobuf/api.proto
@@ -0,0 +1,208 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+import "google/protobuf/source_context.proto";
+import "google/protobuf/type.proto";
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "ApiProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/protobuf/types/known/apipb";
+
+// Api is a light-weight descriptor for an API Interface.
+//
+// Interfaces are also described as "protocol buffer services" in some contexts,
+// such as by the "service" keyword in a .proto file, but they are different
+// from API Services, which represent a concrete implementation of an interface
+// as opposed to simply a description of methods and bindings. They are also
+// sometimes simply referred to as "APIs" in other contexts, such as the name of
+// this message itself. See https://cloud.google.com/apis/design/glossary for
+// detailed terminology.
+message Api {
+ // The fully qualified name of this interface, including package name
+ // followed by the interface's simple name.
+ string name = 1;
+
+ // The methods of this interface, in unspecified order.
+ repeated Method methods = 2;
+
+ // Any metadata attached to the interface.
+ repeated Option options = 3;
+
+ // A version string for this interface. If specified, must have the form
+ // `major-version.minor-version`, as in `1.10`. If the minor version is
+ // omitted, it defaults to zero. If the entire version field is empty, the
+ // major version is derived from the package name, as outlined below. If the
+ // field is not empty, the version in the package name will be verified to be
+ // consistent with what is provided here.
+ //
+ // The versioning schema uses [semantic
+ // versioning](http://semver.org) where the major version number
+ // indicates a breaking change and the minor version an additive,
+ // non-breaking change. Both version numbers are signals to users
+ // what to expect from different versions, and should be carefully
+ // chosen based on the product plan.
+ //
+ // The major version is also reflected in the package name of the
+ // interface, which must end in `v`, as in
+ // `google.feature.v1`. For major versions 0 and 1, the suffix can
+ // be omitted. Zero major versions must only be used for
+ // experimental, non-GA interfaces.
+ //
+ //
+ string version = 4;
+
+ // Source context for the protocol buffer service represented by this
+ // message.
+ SourceContext source_context = 5;
+
+ // Included interfaces. See [Mixin][].
+ repeated Mixin mixins = 6;
+
+ // The source syntax of the service.
+ Syntax syntax = 7;
+}
+
+// Method represents a method of an API interface.
+message Method {
+ // The simple name of this method.
+ string name = 1;
+
+ // A URL of the input message type.
+ string request_type_url = 2;
+
+ // If true, the request is streamed.
+ bool request_streaming = 3;
+
+ // The URL of the output message type.
+ string response_type_url = 4;
+
+ // If true, the response is streamed.
+ bool response_streaming = 5;
+
+ // Any metadata attached to the method.
+ repeated Option options = 6;
+
+ // The source syntax of this method.
+ Syntax syntax = 7;
+}
+
+// Declares an API Interface to be included in this interface. The including
+// interface must redeclare all the methods from the included interface, but
+// documentation and options are inherited as follows:
+//
+// - If after comment and whitespace stripping, the documentation
+// string of the redeclared method is empty, it will be inherited
+// from the original method.
+//
+// - Each annotation belonging to the service config (http,
+// visibility) which is not set in the redeclared method will be
+// inherited.
+//
+// - If an http annotation is inherited, the path pattern will be
+// modified as follows. Any version prefix will be replaced by the
+// version of the including interface plus the [root][] path if
+// specified.
+//
+// Example of a simple mixin:
+//
+// package google.acl.v1;
+// service AccessControl {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v1/{resource=**}:getAcl";
+// }
+// }
+//
+// package google.storage.v2;
+// service Storage {
+// rpc GetAcl(GetAclRequest) returns (Acl);
+//
+// // Get a data record.
+// rpc GetData(GetDataRequest) returns (Data) {
+// option (google.api.http).get = "/v2/{resource=**}";
+// }
+// }
+//
+// Example of a mixin configuration:
+//
+// apis:
+// - name: google.storage.v2.Storage
+// mixins:
+// - name: google.acl.v1.AccessControl
+//
+// The mixin construct implies that all methods in `AccessControl` are
+// also declared with same name and request/response types in
+// `Storage`. A documentation generator or annotation processor will
+// see the effective `Storage.GetAcl` method after inheriting
+// documentation and annotations as follows:
+//
+// service Storage {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v2/{resource=**}:getAcl";
+// }
+// ...
+// }
+//
+// Note how the version in the path pattern changed from `v1` to `v2`.
+//
+// If the `root` field in the mixin is specified, it should be a
+// relative path under which inherited HTTP paths are placed. Example:
+//
+// apis:
+// - name: google.storage.v2.Storage
+// mixins:
+// - name: google.acl.v1.AccessControl
+// root: acls
+//
+// This implies the following inherited HTTP annotation:
+//
+// service Storage {
+// // Get the underlying ACL object.
+// rpc GetAcl(GetAclRequest) returns (Acl) {
+// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
+// }
+// ...
+// }
+message Mixin {
+ // The fully qualified name of the interface which is included.
+ string name = 1;
+
+ // If non-empty specifies a path under which inherited HTTP paths
+ // are rooted.
+ string root = 2;
+}
diff --git a/api/protocol/include/google/protobuf/compiler/plugin.proto b/api/protocol/include/google/protobuf/compiler/plugin.proto
new file mode 100644
index 0000000..9242aac
--- /dev/null
+++ b/api/protocol/include/google/protobuf/compiler/plugin.proto
@@ -0,0 +1,183 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//
+// WARNING: The plugin interface is currently EXPERIMENTAL and is subject to
+// change.
+//
+// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is
+// just a program that reads a CodeGeneratorRequest from stdin and writes a
+// CodeGeneratorResponse to stdout.
+//
+// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
+// of dealing with the raw protocol defined here.
+//
+// A plugin executable needs only to be placed somewhere in the path. The
+// plugin should be named "protoc-gen-$NAME", and will then be used when the
+// flag "--${NAME}_out" is passed to protoc.
+
+syntax = "proto2";
+
+package google.protobuf.compiler;
+option java_package = "com.google.protobuf.compiler";
+option java_outer_classname = "PluginProtos";
+
+option go_package = "google.golang.org/protobuf/types/pluginpb";
+
+import "google/protobuf/descriptor.proto";
+
+// The version number of protocol compiler.
+message Version {
+ optional int32 major = 1;
+ optional int32 minor = 2;
+ optional int32 patch = 3;
+ // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
+ // be empty for mainline stable releases.
+ optional string suffix = 4;
+}
+
+// An encoded CodeGeneratorRequest is written to the plugin's stdin.
+message CodeGeneratorRequest {
+ // The .proto files that were explicitly listed on the command-line. The
+ // code generator should generate code only for these files. Each file's
+ // descriptor will be included in proto_file, below.
+ repeated string file_to_generate = 1;
+
+ // The generator parameter passed on the command-line.
+ optional string parameter = 2;
+
+ // FileDescriptorProtos for all files in files_to_generate and everything
+ // they import. The files will appear in topological order, so each file
+ // appears before any file that imports it.
+ //
+ // protoc guarantees that all proto_files will be written after
+ // the fields above, even though this is not technically guaranteed by the
+ // protobuf wire format. This theoretically could allow a plugin to stream
+ // in the FileDescriptorProtos and handle them one by one rather than read
+ // the entire set into memory at once. However, as of this writing, this
+ // is not similarly optimized on protoc's end -- it will store all fields in
+ // memory at once before sending them to the plugin.
+ //
+ // Type names of fields and extensions in the FileDescriptorProto are always
+ // fully qualified.
+ repeated FileDescriptorProto proto_file = 15;
+
+ // The version number of protocol compiler.
+ optional Version compiler_version = 3;
+
+}
+
+// The plugin writes an encoded CodeGeneratorResponse to stdout.
+message CodeGeneratorResponse {
+ // Error message. If non-empty, code generation failed. The plugin process
+ // should exit with status code zero even if it reports an error in this way.
+ //
+ // This should be used to indicate errors in .proto files which prevent the
+ // code generator from generating correct code. Errors which indicate a
+ // problem in protoc itself -- such as the input CodeGeneratorRequest being
+ // unparseable -- should be reported by writing a message to stderr and
+ // exiting with a non-zero status code.
+ optional string error = 1;
+
+ // A bitmask of supported features that the code generator supports.
+ // This is a bitwise "or" of values from the Feature enum.
+ optional uint64 supported_features = 2;
+
+ // Sync with code_generator.h.
+ enum Feature {
+ FEATURE_NONE = 0;
+ FEATURE_PROTO3_OPTIONAL = 1;
+ }
+
+ // Represents a single generated file.
+ message File {
+ // The file name, relative to the output directory. The name must not
+ // contain "." or ".." components and must be relative, not be absolute (so,
+ // the file cannot lie outside the output directory). "/" must be used as
+ // the path separator, not "\".
+ //
+ // If the name is omitted, the content will be appended to the previous
+ // file. This allows the generator to break large files into small chunks,
+ // and allows the generated text to be streamed back to protoc so that large
+ // files need not reside completely in memory at one time. Note that as of
+ // this writing protoc does not optimize for this -- it will read the entire
+ // CodeGeneratorResponse before writing files to disk.
+ optional string name = 1;
+
+ // If non-empty, indicates that the named file should already exist, and the
+ // content here is to be inserted into that file at a defined insertion
+ // point. This feature allows a code generator to extend the output
+ // produced by another code generator. The original generator may provide
+ // insertion points by placing special annotations in the file that look
+ // like:
+ // @@protoc_insertion_point(NAME)
+ // The annotation can have arbitrary text before and after it on the line,
+ // which allows it to be placed in a comment. NAME should be replaced with
+ // an identifier naming the point -- this is what other generators will use
+ // as the insertion_point. Code inserted at this point will be placed
+ // immediately above the line containing the insertion point (thus multiple
+ // insertions to the same point will come out in the order they were added).
+ // The double-@ is intended to make it unlikely that the generated code
+ // could contain things that look like insertion points by accident.
+ //
+ // For example, the C++ code generator places the following line in the
+ // .pb.h files that it generates:
+ // // @@protoc_insertion_point(namespace_scope)
+ // This line appears within the scope of the file's package namespace, but
+ // outside of any particular class. Another plugin can then specify the
+ // insertion_point "namespace_scope" to generate additional classes or
+ // other declarations that should be placed in this scope.
+ //
+ // Note that if the line containing the insertion point begins with
+ // whitespace, the same whitespace will be added to every line of the
+ // inserted text. This is useful for languages like Python, where
+ // indentation matters. In these languages, the insertion point comment
+ // should be indented the same amount as any inserted code will need to be
+ // in order to work correctly in that context.
+ //
+ // The code generator that generates the initial file and the one which
+ // inserts into it must both run as part of a single invocation of protoc.
+ // Code generators are executed in the order in which they appear on the
+ // command line.
+ //
+ // If |insertion_point| is present, |name| must also be present.
+ optional string insertion_point = 2;
+
+ // The file contents.
+ optional string content = 15;
+
+ // Information describing the file content being inserted. If an insertion
+ // point is used, this information will be appropriately offset and inserted
+ // into the code generation metadata for the generated files.
+ optional GeneratedCodeInfo generated_code_info = 16;
+ }
+ repeated File file = 15;
+}
diff --git a/api/protocol/include/google/protobuf/descriptor.proto b/api/protocol/include/google/protobuf/descriptor.proto
new file mode 100644
index 0000000..9f0ce6c
--- /dev/null
+++ b/api/protocol/include/google/protobuf/descriptor.proto
@@ -0,0 +1,909 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// The messages in this file describe the definitions found in .proto files.
+// A valid .proto file can be translated directly to a FileDescriptorProto
+// without any other information (e.g. without reading its imports).
+
+
+syntax = "proto2";
+
+package google.protobuf;
+
+option go_package = "google.golang.org/protobuf/types/descriptorpb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DescriptorProtos";
+option csharp_namespace = "Google.Protobuf.Reflection";
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+// descriptor.proto must be optimized for speed because reflection-based
+// algorithms don't work during bootstrapping.
+option optimize_for = SPEED;
+
+// The protocol compiler can output a FileDescriptorSet containing the .proto
+// files it parses.
+message FileDescriptorSet {
+ repeated FileDescriptorProto file = 1;
+}
+
+// Describes a complete .proto file.
+message FileDescriptorProto {
+ optional string name = 1; // file name, relative to root of source tree
+ optional string package = 2; // e.g. "foo", "foo.bar", etc.
+
+ // Names of files imported by this file.
+ repeated string dependency = 3;
+ // Indexes of the public imported files in the dependency list above.
+ repeated int32 public_dependency = 10;
+ // Indexes of the weak imported files in the dependency list.
+ // For Google-internal migration only. Do not use.
+ repeated int32 weak_dependency = 11;
+
+ // All top-level definitions in this file.
+ repeated DescriptorProto message_type = 4;
+ repeated EnumDescriptorProto enum_type = 5;
+ repeated ServiceDescriptorProto service = 6;
+ repeated FieldDescriptorProto extension = 7;
+
+ optional FileOptions options = 8;
+
+ // This field contains optional information about the original source code.
+ // You may safely remove this entire field without harming runtime
+ // functionality of the descriptors -- the information is needed only by
+ // development tools.
+ optional SourceCodeInfo source_code_info = 9;
+
+ // The syntax of the proto file.
+ // The supported values are "proto2" and "proto3".
+ optional string syntax = 12;
+}
+
+// Describes a message type.
+message DescriptorProto {
+ optional string name = 1;
+
+ repeated FieldDescriptorProto field = 2;
+ repeated FieldDescriptorProto extension = 6;
+
+ repeated DescriptorProto nested_type = 3;
+ repeated EnumDescriptorProto enum_type = 4;
+
+ message ExtensionRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Exclusive.
+
+ optional ExtensionRangeOptions options = 3;
+ }
+ repeated ExtensionRange extension_range = 5;
+
+ repeated OneofDescriptorProto oneof_decl = 8;
+
+ optional MessageOptions options = 7;
+
+ // Range of reserved tag numbers. Reserved tag numbers may not be used by
+ // fields or extension ranges in the same message. Reserved ranges may
+ // not overlap.
+ message ReservedRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Exclusive.
+ }
+ repeated ReservedRange reserved_range = 9;
+ // Reserved field names, which may not be used by fields in the same message.
+ // A given name may only be reserved once.
+ repeated string reserved_name = 10;
+}
+
+message ExtensionRangeOptions {
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+// Describes a field within a message.
+message FieldDescriptorProto {
+ enum Type {
+ // 0 is reserved for errors.
+ // Order is weird for historical reasons.
+ TYPE_DOUBLE = 1;
+ TYPE_FLOAT = 2;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
+ // negative values are likely.
+ TYPE_INT64 = 3;
+ TYPE_UINT64 = 4;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
+ // negative values are likely.
+ TYPE_INT32 = 5;
+ TYPE_FIXED64 = 6;
+ TYPE_FIXED32 = 7;
+ TYPE_BOOL = 8;
+ TYPE_STRING = 9;
+ // Tag-delimited aggregate.
+ // Group type is deprecated and not supported in proto3. However, Proto3
+ // implementations should still be able to parse the group wire format and
+ // treat group fields as unknown fields.
+ TYPE_GROUP = 10;
+ TYPE_MESSAGE = 11; // Length-delimited aggregate.
+
+ // New in version 2.
+ TYPE_BYTES = 12;
+ TYPE_UINT32 = 13;
+ TYPE_ENUM = 14;
+ TYPE_SFIXED32 = 15;
+ TYPE_SFIXED64 = 16;
+ TYPE_SINT32 = 17; // Uses ZigZag encoding.
+ TYPE_SINT64 = 18; // Uses ZigZag encoding.
+ }
+
+ enum Label {
+ // 0 is reserved for errors
+ LABEL_OPTIONAL = 1;
+ LABEL_REQUIRED = 2;
+ LABEL_REPEATED = 3;
+ }
+
+ optional string name = 1;
+ optional int32 number = 3;
+ optional Label label = 4;
+
+ // If type_name is set, this need not be set. If both this and type_name
+ // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
+ optional Type type = 5;
+
+ // For message and enum types, this is the name of the type. If the name
+ // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
+ // rules are used to find the type (i.e. first the nested types within this
+ // message are searched, then within the parent, on up to the root
+ // namespace).
+ optional string type_name = 6;
+
+ // For extensions, this is the name of the type being extended. It is
+ // resolved in the same manner as type_name.
+ optional string extendee = 2;
+
+ // For numeric types, contains the original text representation of the value.
+ // For booleans, "true" or "false".
+ // For strings, contains the default text contents (not escaped in any way).
+ // For bytes, contains the C escaped value. All bytes >= 128 are escaped.
+ // TODO(kenton): Base-64 encode?
+ optional string default_value = 7;
+
+ // If set, gives the index of a oneof in the containing type's oneof_decl
+ // list. This field is a member of that oneof.
+ optional int32 oneof_index = 9;
+
+ // JSON name of this field. The value is set by protocol compiler. If the
+ // user has set a "json_name" option on this field, that option's value
+ // will be used. Otherwise, it's deduced from the field's name by converting
+ // it to camelCase.
+ optional string json_name = 10;
+
+ optional FieldOptions options = 8;
+
+ // If true, this is a proto3 "optional". When a proto3 field is optional, it
+ // tracks presence regardless of field type.
+ //
+ // When proto3_optional is true, this field must be belong to a oneof to
+ // signal to old proto3 clients that presence is tracked for this field. This
+ // oneof is known as a "synthetic" oneof, and this field must be its sole
+ // member (each proto3 optional field gets its own synthetic oneof). Synthetic
+ // oneofs exist in the descriptor only, and do not generate any API. Synthetic
+ // oneofs must be ordered after all "real" oneofs.
+ //
+ // For message fields, proto3_optional doesn't create any semantic change,
+ // since non-repeated message fields always track presence. However it still
+ // indicates the semantic detail of whether the user wrote "optional" or not.
+ // This can be useful for round-tripping the .proto file. For consistency we
+ // give message fields a synthetic oneof also, even though it is not required
+ // to track presence. This is especially important because the parser can't
+ // tell if a field is a message or an enum, so it must always create a
+ // synthetic oneof.
+ //
+ // Proto2 optional fields do not set this flag, because they already indicate
+ // optional with `LABEL_OPTIONAL`.
+ optional bool proto3_optional = 17;
+}
+
+// Describes a oneof.
+message OneofDescriptorProto {
+ optional string name = 1;
+ optional OneofOptions options = 2;
+}
+
+// Describes an enum type.
+message EnumDescriptorProto {
+ optional string name = 1;
+
+ repeated EnumValueDescriptorProto value = 2;
+
+ optional EnumOptions options = 3;
+
+ // Range of reserved numeric values. Reserved values may not be used by
+ // entries in the same enum. Reserved ranges may not overlap.
+ //
+ // Note that this is distinct from DescriptorProto.ReservedRange in that it
+ // is inclusive such that it can appropriately represent the entire int32
+ // domain.
+ message EnumReservedRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Inclusive.
+ }
+
+ // Range of reserved numeric values. Reserved numeric values may not be used
+ // by enum values in the same enum declaration. Reserved ranges may not
+ // overlap.
+ repeated EnumReservedRange reserved_range = 4;
+
+ // Reserved enum value names, which may not be reused. A given name may only
+ // be reserved once.
+ repeated string reserved_name = 5;
+}
+
+// Describes a value within an enum.
+message EnumValueDescriptorProto {
+ optional string name = 1;
+ optional int32 number = 2;
+
+ optional EnumValueOptions options = 3;
+}
+
+// Describes a service.
+message ServiceDescriptorProto {
+ optional string name = 1;
+ repeated MethodDescriptorProto method = 2;
+
+ optional ServiceOptions options = 3;
+}
+
+// Describes a method of a service.
+message MethodDescriptorProto {
+ optional string name = 1;
+
+ // Input and output type names. These are resolved in the same way as
+ // FieldDescriptorProto.type_name, but must refer to a message type.
+ optional string input_type = 2;
+ optional string output_type = 3;
+
+ optional MethodOptions options = 4;
+
+ // Identifies if client streams multiple client messages
+ optional bool client_streaming = 5 [default = false];
+ // Identifies if server streams multiple server messages
+ optional bool server_streaming = 6 [default = false];
+}
+
+
+// ===================================================================
+// Options
+
+// Each of the definitions above may have "options" attached. These are
+// just annotations which may cause code to be generated slightly differently
+// or may contain hints for code that manipulates protocol messages.
+//
+// Clients may define custom options as extensions of the *Options messages.
+// These extensions may not yet be known at parsing time, so the parser cannot
+// store the values in them. Instead it stores them in a field in the *Options
+// message called uninterpreted_option. This field must have the same name
+// across all *Options messages. We then use this field to populate the
+// extensions when we build a descriptor, at which point all protos have been
+// parsed and so all extensions are known.
+//
+// Extension numbers for custom options may be chosen as follows:
+// * For options which will only be used within a single application or
+// organization, or for experimental options, use field numbers 50000
+// through 99999. It is up to you to ensure that you do not use the
+// same number for multiple options.
+// * For options which will be published and used publicly by multiple
+// independent entities, e-mail protobuf-global-extension-registry@google.com
+// to reserve extension numbers. Simply provide your project name (e.g.
+// Objective-C plugin) and your project website (if available) -- there's no
+// need to explain how you intend to use them. Usually you only need one
+// extension number. You can declare multiple options with only one extension
+// number by putting them in a sub-message. See the Custom Options section of
+// the docs for examples:
+// https://developers.google.com/protocol-buffers/docs/proto#options
+// If this turns out to be popular, a web service will be set up
+// to automatically assign option numbers.
+
+message FileOptions {
+
+ // Sets the Java package where classes generated from this .proto will be
+ // placed. By default, the proto package is used, but this is often
+ // inappropriate because proto packages do not normally start with backwards
+ // domain names.
+ optional string java_package = 1;
+
+
+ // If set, all the classes from the .proto file are wrapped in a single
+ // outer class with the given name. This applies to both Proto1
+ // (equivalent to the old "--one_java_file" option) and Proto2 (where
+ // a .proto always translates to a single class, but you may want to
+ // explicitly choose the class name).
+ optional string java_outer_classname = 8;
+
+ // If set true, then the Java code generator will generate a separate .java
+ // file for each top-level message, enum, and service defined in the .proto
+ // file. Thus, these types will *not* be nested inside the outer class
+ // named by java_outer_classname. However, the outer class will still be
+ // generated to contain the file's getDescriptor() method as well as any
+ // top-level extensions defined in the file.
+ optional bool java_multiple_files = 10 [default = false];
+
+ // This option does nothing.
+ optional bool java_generate_equals_and_hash = 20 [deprecated=true];
+
+ // If set true, then the Java2 code generator will generate code that
+ // throws an exception whenever an attempt is made to assign a non-UTF-8
+ // byte sequence to a string field.
+ // Message reflection will do the same.
+ // However, an extension field still accepts non-UTF-8 byte sequences.
+ // This option has no effect on when used with the lite runtime.
+ optional bool java_string_check_utf8 = 27 [default = false];
+
+
+ // Generated classes can be optimized for speed or code size.
+ enum OptimizeMode {
+ SPEED = 1; // Generate complete code for parsing, serialization,
+ // etc.
+ CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
+ LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
+ }
+ optional OptimizeMode optimize_for = 9 [default = SPEED];
+
+ // Sets the Go package where structs generated from this .proto will be
+ // placed. If omitted, the Go package will be derived from the following:
+ // - The basename of the package import path, if provided.
+ // - Otherwise, the package statement in the .proto file, if present.
+ // - Otherwise, the basename of the .proto file, without extension.
+ optional string go_package = 11;
+
+
+
+
+ // Should generic services be generated in each language? "Generic" services
+ // are not specific to any particular RPC system. They are generated by the
+ // main code generators in each language (without additional plugins).
+ // Generic services were the only kind of service generation supported by
+ // early versions of google.protobuf.
+ //
+ // Generic services are now considered deprecated in favor of using plugins
+ // that generate code specific to your particular RPC system. Therefore,
+ // these default to false. Old code which depends on generic services should
+ // explicitly set them to true.
+ optional bool cc_generic_services = 16 [default = false];
+ optional bool java_generic_services = 17 [default = false];
+ optional bool py_generic_services = 18 [default = false];
+ optional bool php_generic_services = 42 [default = false];
+
+ // Is this file deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for everything in the file, or it will be completely ignored; in the very
+ // least, this is a formalization for deprecating files.
+ optional bool deprecated = 23 [default = false];
+
+ // Enables the use of arenas for the proto messages in this file. This applies
+ // only to generated classes for C++.
+ optional bool cc_enable_arenas = 31 [default = true];
+
+
+ // Sets the objective c class prefix which is prepended to all objective c
+ // generated classes from this .proto. There is no default.
+ optional string objc_class_prefix = 36;
+
+ // Namespace for generated classes; defaults to the package.
+ optional string csharp_namespace = 37;
+
+ // By default Swift generators will take the proto package and CamelCase it
+ // replacing '.' with underscore and use that to prefix the types/symbols
+ // defined. When this options is provided, they will use this value instead
+ // to prefix the types/symbols defined.
+ optional string swift_prefix = 39;
+
+ // Sets the php class prefix which is prepended to all php generated classes
+ // from this .proto. Default is empty.
+ optional string php_class_prefix = 40;
+
+ // Use this option to change the namespace of php generated classes. Default
+ // is empty. When this option is empty, the package name will be used for
+ // determining the namespace.
+ optional string php_namespace = 41;
+
+ // Use this option to change the namespace of php generated metadata classes.
+ // Default is empty. When this option is empty, the proto file name will be
+ // used for determining the namespace.
+ optional string php_metadata_namespace = 44;
+
+ // Use this option to change the package of ruby generated classes. Default
+ // is empty. When this option is not set, the package name will be used for
+ // determining the ruby package.
+ optional string ruby_package = 45;
+
+
+ // The parser stores options it doesn't recognize here.
+ // See the documentation for the "Options" section above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message.
+ // See the documentation for the "Options" section above.
+ extensions 1000 to max;
+
+ reserved 38;
+}
+
+message MessageOptions {
+ // Set true to use the old proto1 MessageSet wire format for extensions.
+ // This is provided for backwards-compatibility with the MessageSet wire
+ // format. You should not use this for any other reason: It's less
+ // efficient, has fewer features, and is more complicated.
+ //
+ // The message must be defined exactly as follows:
+ // message Foo {
+ // option message_set_wire_format = true;
+ // extensions 4 to max;
+ // }
+ // Note that the message cannot have any defined fields; MessageSets only
+ // have extensions.
+ //
+ // All extensions of your type must be singular messages; e.g. they cannot
+ // be int32s, enums, or repeated messages.
+ //
+ // Because this is an option, the above two restrictions are not enforced by
+ // the protocol compiler.
+ optional bool message_set_wire_format = 1 [default = false];
+
+ // Disables the generation of the standard "descriptor()" accessor, which can
+ // conflict with a field of the same name. This is meant to make migration
+ // from proto1 easier; new code should avoid fields named "descriptor".
+ optional bool no_standard_descriptor_accessor = 2 [default = false];
+
+ // Is this message deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the message, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating messages.
+ optional bool deprecated = 3 [default = false];
+
+ // Whether the message is an automatically generated map entry type for the
+ // maps field.
+ //
+ // For maps fields:
+ // map map_field = 1;
+ // The parsed descriptor looks like:
+ // message MapFieldEntry {
+ // option map_entry = true;
+ // optional KeyType key = 1;
+ // optional ValueType value = 2;
+ // }
+ // repeated MapFieldEntry map_field = 1;
+ //
+ // Implementations may choose not to generate the map_entry=true message, but
+ // use a native map in the target language to hold the keys and values.
+ // The reflection APIs in such implementations still need to work as
+ // if the field is a repeated message field.
+ //
+ // NOTE: Do not set the option in .proto files. Always use the maps syntax
+ // instead. The option should only be implicitly set by the proto compiler
+ // parser.
+ optional bool map_entry = 7;
+
+ reserved 8; // javalite_serializable
+ reserved 9; // javanano_as_lite
+
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message FieldOptions {
+ // The ctype option instructs the C++ code generator to use a different
+ // representation of the field than it normally would. See the specific
+ // options below. This option is not yet implemented in the open source
+ // release -- sorry, we'll try to include it in a future version!
+ optional CType ctype = 1 [default = STRING];
+ enum CType {
+ // Default mode.
+ STRING = 0;
+
+ CORD = 1;
+
+ STRING_PIECE = 2;
+ }
+ // The packed option can be enabled for repeated primitive fields to enable
+ // a more efficient representation on the wire. Rather than repeatedly
+ // writing the tag and type for each element, the entire array is encoded as
+ // a single length-delimited blob. In proto3, only explicit setting it to
+ // false will avoid using packed encoding.
+ optional bool packed = 2;
+
+ // The jstype option determines the JavaScript type used for values of the
+ // field. The option is permitted only for 64 bit integral and fixed types
+ // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING
+ // is represented as JavaScript string, which avoids loss of precision that
+ // can happen when a large value is converted to a floating point JavaScript.
+ // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to
+ // use the JavaScript "number" type. The behavior of the default option
+ // JS_NORMAL is implementation dependent.
+ //
+ // This option is an enum to permit additional types to be added, e.g.
+ // goog.math.Integer.
+ optional JSType jstype = 6 [default = JS_NORMAL];
+ enum JSType {
+ // Use the default type.
+ JS_NORMAL = 0;
+
+ // Use JavaScript strings.
+ JS_STRING = 1;
+
+ // Use JavaScript numbers.
+ JS_NUMBER = 2;
+ }
+
+ // Should this field be parsed lazily? Lazy applies only to message-type
+ // fields. It means that when the outer message is initially parsed, the
+ // inner message's contents will not be parsed but instead stored in encoded
+ // form. The inner message will actually be parsed when it is first accessed.
+ //
+ // This is only a hint. Implementations are free to choose whether to use
+ // eager or lazy parsing regardless of the value of this option. However,
+ // setting this option true suggests that the protocol author believes that
+ // using lazy parsing on this field is worth the additional bookkeeping
+ // overhead typically needed to implement it.
+ //
+ // This option does not affect the public interface of any generated code;
+ // all method signatures remain the same. Furthermore, thread-safety of the
+ // interface is not affected by this option; const methods remain safe to
+ // call from multiple threads concurrently, while non-const methods continue
+ // to require exclusive access.
+ //
+ //
+ // Note that implementations may choose not to check required fields within
+ // a lazy sub-message. That is, calling IsInitialized() on the outer message
+ // may return true even if the inner message has missing required fields.
+ // This is necessary because otherwise the inner message would have to be
+ // parsed in order to perform the check, defeating the purpose of lazy
+ // parsing. An implementation which chooses not to check required fields
+ // must be consistent about it. That is, for any particular sub-message, the
+ // implementation must either *always* check its required fields, or *never*
+ // check its required fields, regardless of whether or not the message has
+ // been parsed.
+ optional bool lazy = 5 [default = false];
+
+ // Is this field deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for accessors, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating fields.
+ optional bool deprecated = 3 [default = false];
+
+ // For Google-internal migration only. Do not use.
+ optional bool weak = 10 [default = false];
+
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+
+ reserved 4; // removed jtype
+}
+
+message OneofOptions {
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumOptions {
+
+ // Set this option to true to allow mapping different tag names to the same
+ // value.
+ optional bool allow_alias = 2;
+
+ // Is this enum deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating enums.
+ optional bool deprecated = 3 [default = false];
+
+ reserved 5; // javanano_as_lite
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumValueOptions {
+ // Is this enum value deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum value, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating enum values.
+ optional bool deprecated = 1 [default = false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message ServiceOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this service deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the service, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating services.
+ optional bool deprecated = 33 [default = false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message MethodOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this method deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the method, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating methods.
+ optional bool deprecated = 33 [default = false];
+
+ // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
+ // or neither? HTTP based RPC implementation may choose GET verb for safe
+ // methods, and PUT verb for idempotent methods instead of the default POST.
+ enum IdempotencyLevel {
+ IDEMPOTENCY_UNKNOWN = 0;
+ NO_SIDE_EFFECTS = 1; // implies idempotent
+ IDEMPOTENT = 2; // idempotent, but may have side effects
+ }
+ optional IdempotencyLevel idempotency_level = 34
+ [default = IDEMPOTENCY_UNKNOWN];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+
+// A message representing a option the parser does not recognize. This only
+// appears in options protos created by the compiler::Parser class.
+// DescriptorPool resolves these when building Descriptor objects. Therefore,
+// options protos in descriptor objects (e.g. returned by Descriptor::options(),
+// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
+// in them.
+message UninterpretedOption {
+ // The name of the uninterpreted option. Each string represents a segment in
+ // a dot-separated name. is_extension is true iff a segment represents an
+ // extension (denoted with parentheses in options specs in .proto files).
+ // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
+ // "foo.(bar.baz).qux".
+ message NamePart {
+ required string name_part = 1;
+ required bool is_extension = 2;
+ }
+ repeated NamePart name = 2;
+
+ // The value of the uninterpreted option, in whatever type the tokenizer
+ // identified it as during parsing. Exactly one of these should be set.
+ optional string identifier_value = 3;
+ optional uint64 positive_int_value = 4;
+ optional int64 negative_int_value = 5;
+ optional double double_value = 6;
+ optional bytes string_value = 7;
+ optional string aggregate_value = 8;
+}
+
+// ===================================================================
+// Optional source code info
+
+// Encapsulates information about the original source file from which a
+// FileDescriptorProto was generated.
+message SourceCodeInfo {
+ // A Location identifies a piece of source code in a .proto file which
+ // corresponds to a particular definition. This information is intended
+ // to be useful to IDEs, code indexers, documentation generators, and similar
+ // tools.
+ //
+ // For example, say we have a file like:
+ // message Foo {
+ // optional string foo = 1;
+ // }
+ // Let's look at just the field definition:
+ // optional string foo = 1;
+ // ^ ^^ ^^ ^ ^^^
+ // a bc de f ghi
+ // We have the following locations:
+ // span path represents
+ // [a,i) [ 4, 0, 2, 0 ] The whole field definition.
+ // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
+ // [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
+ // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
+ // [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
+ //
+ // Notes:
+ // - A location may refer to a repeated field itself (i.e. not to any
+ // particular index within it). This is used whenever a set of elements are
+ // logically enclosed in a single code segment. For example, an entire
+ // extend block (possibly containing multiple extension definitions) will
+ // have an outer location whose path refers to the "extensions" repeated
+ // field without an index.
+ // - Multiple locations may have the same path. This happens when a single
+ // logical declaration is spread out across multiple places. The most
+ // obvious example is the "extend" block again -- there may be multiple
+ // extend blocks in the same scope, each of which will have the same path.
+ // - A location's span is not always a subset of its parent's span. For
+ // example, the "extendee" of an extension declaration appears at the
+ // beginning of the "extend" block and is shared by all extensions within
+ // the block.
+ // - Just because a location's span is a subset of some other location's span
+ // does not mean that it is a descendant. For example, a "group" defines
+ // both a type and a field in a single declaration. Thus, the locations
+ // corresponding to the type and field and their components will overlap.
+ // - Code which tries to interpret locations should probably be designed to
+ // ignore those that it doesn't understand, as more types of locations could
+ // be recorded in the future.
+ repeated Location location = 1;
+ message Location {
+ // Identifies which part of the FileDescriptorProto was defined at this
+ // location.
+ //
+ // Each element is a field number or an index. They form a path from
+ // the root FileDescriptorProto to the place where the definition. For
+ // example, this path:
+ // [ 4, 3, 2, 7, 1 ]
+ // refers to:
+ // file.message_type(3) // 4, 3
+ // .field(7) // 2, 7
+ // .name() // 1
+ // This is because FileDescriptorProto.message_type has field number 4:
+ // repeated DescriptorProto message_type = 4;
+ // and DescriptorProto.field has field number 2:
+ // repeated FieldDescriptorProto field = 2;
+ // and FieldDescriptorProto.name has field number 1:
+ // optional string name = 1;
+ //
+ // Thus, the above path gives the location of a field name. If we removed
+ // the last element:
+ // [ 4, 3, 2, 7 ]
+ // this path refers to the whole field declaration (from the beginning
+ // of the label to the terminating semicolon).
+ repeated int32 path = 1 [packed = true];
+
+ // Always has exactly three or four elements: start line, start column,
+ // end line (optional, otherwise assumed same as start line), end column.
+ // These are packed into a single field for efficiency. Note that line
+ // and column numbers are zero-based -- typically you will want to add
+ // 1 to each before displaying to a user.
+ repeated int32 span = 2 [packed = true];
+
+ // If this SourceCodeInfo represents a complete declaration, these are any
+ // comments appearing before and after the declaration which appear to be
+ // attached to the declaration.
+ //
+ // A series of line comments appearing on consecutive lines, with no other
+ // tokens appearing on those lines, will be treated as a single comment.
+ //
+ // leading_detached_comments will keep paragraphs of comments that appear
+ // before (but not connected to) the current element. Each paragraph,
+ // separated by empty lines, will be one comment element in the repeated
+ // field.
+ //
+ // Only the comment content is provided; comment markers (e.g. //) are
+ // stripped out. For block comments, leading whitespace and an asterisk
+ // will be stripped from the beginning of each line other than the first.
+ // Newlines are included in the output.
+ //
+ // Examples:
+ //
+ // optional int32 foo = 1; // Comment attached to foo.
+ // // Comment attached to bar.
+ // optional int32 bar = 2;
+ //
+ // optional string baz = 3;
+ // // Comment attached to baz.
+ // // Another line attached to baz.
+ //
+ // // Comment attached to qux.
+ // //
+ // // Another line attached to qux.
+ // optional double qux = 4;
+ //
+ // // Detached comment for corge. This is not leading or trailing comments
+ // // to qux or corge because there are blank lines separating it from
+ // // both.
+ //
+ // // Detached comment for corge paragraph 2.
+ //
+ // optional string corge = 5;
+ // /* Block comment attached
+ // * to corge. Leading asterisks
+ // * will be removed. */
+ // /* Block comment attached to
+ // * grault. */
+ // optional int32 grault = 6;
+ //
+ // // ignored detached comments.
+ optional string leading_comments = 3;
+ optional string trailing_comments = 4;
+ repeated string leading_detached_comments = 6;
+ }
+}
+
+// Describes the relationship between generated code and its original source
+// file. A GeneratedCodeInfo message is associated with only one generated
+// source file, but may contain references to different source .proto files.
+message GeneratedCodeInfo {
+ // An Annotation connects some span of text in generated code to an element
+ // of its generating .proto file.
+ repeated Annotation annotation = 1;
+ message Annotation {
+ // Identifies the element in the original source .proto file. This field
+ // is formatted the same as SourceCodeInfo.Location.path.
+ repeated int32 path = 1 [packed = true];
+
+ // Identifies the filesystem path to the original source .proto.
+ optional string source_file = 2;
+
+ // Identifies the starting offset in bytes in the generated code
+ // that relates to the identified object.
+ optional int32 begin = 3;
+
+ // Identifies the ending offset in bytes in the generated code that
+ // relates to the identified offset. The end offset should be one past
+ // the last relevant byte (so the length of the text = end - begin).
+ optional int32 end = 4;
+ }
+}
diff --git a/api/protocol/include/google/protobuf/duration.proto b/api/protocol/include/google/protobuf/duration.proto
new file mode 100644
index 0000000..81c3e36
--- /dev/null
+++ b/api/protocol/include/google/protobuf/duration.proto
@@ -0,0 +1,116 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/protobuf/types/known/durationpb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DurationProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Duration represents a signed, fixed-length span of time represented
+// as a count of seconds and fractions of seconds at nanosecond
+// resolution. It is independent of any calendar and concepts like "day"
+// or "month". It is related to Timestamp in that the difference between
+// two Timestamp values is a Duration and it can be added or subtracted
+// from a Timestamp. Range is approximately +-10,000 years.
+//
+// # Examples
+//
+// Example 1: Compute Duration from two Timestamps in pseudo code.
+//
+// Timestamp start = ...;
+// Timestamp end = ...;
+// Duration duration = ...;
+//
+// duration.seconds = end.seconds - start.seconds;
+// duration.nanos = end.nanos - start.nanos;
+//
+// if (duration.seconds < 0 && duration.nanos > 0) {
+// duration.seconds += 1;
+// duration.nanos -= 1000000000;
+// } else if (duration.seconds > 0 && duration.nanos < 0) {
+// duration.seconds -= 1;
+// duration.nanos += 1000000000;
+// }
+//
+// Example 2: Compute Timestamp from Timestamp + Duration in pseudo code.
+//
+// Timestamp start = ...;
+// Duration duration = ...;
+// Timestamp end = ...;
+//
+// end.seconds = start.seconds + duration.seconds;
+// end.nanos = start.nanos + duration.nanos;
+//
+// if (end.nanos < 0) {
+// end.seconds -= 1;
+// end.nanos += 1000000000;
+// } else if (end.nanos >= 1000000000) {
+// end.seconds += 1;
+// end.nanos -= 1000000000;
+// }
+//
+// Example 3: Compute Duration from datetime.timedelta in Python.
+//
+// td = datetime.timedelta(days=3, minutes=10)
+// duration = Duration()
+// duration.FromTimedelta(td)
+//
+// # JSON Mapping
+//
+// In JSON format, the Duration type is encoded as a string rather than an
+// object, where the string ends in the suffix "s" (indicating seconds) and
+// is preceded by the number of seconds, with nanoseconds expressed as
+// fractional seconds. For example, 3 seconds with 0 nanoseconds should be
+// encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should
+// be expressed in JSON format as "3.000000001s", and 3 seconds and 1
+// microsecond should be expressed in JSON format as "3.000001s".
+//
+//
+message Duration {
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ int64 seconds = 1;
+
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ int32 nanos = 2;
+}
diff --git a/api/protocol/include/google/protobuf/empty.proto b/api/protocol/include/google/protobuf/empty.proto
new file mode 100644
index 0000000..5f992de
--- /dev/null
+++ b/api/protocol/include/google/protobuf/empty.proto
@@ -0,0 +1,52 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "google.golang.org/protobuf/types/known/emptypb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "EmptyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+// A generic empty message that you can re-use to avoid defining duplicated
+// empty messages in your APIs. A typical example is to use it as the request
+// or the response type of an API method. For instance:
+//
+// service Foo {
+// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
+// }
+//
+// The JSON representation for `Empty` is empty JSON object `{}`.
+message Empty {}
diff --git a/api/protocol/include/google/protobuf/field_mask.proto b/api/protocol/include/google/protobuf/field_mask.proto
new file mode 100644
index 0000000..6b5104f
--- /dev/null
+++ b/api/protocol/include/google/protobuf/field_mask.proto
@@ -0,0 +1,245 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "FieldMaskProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/protobuf/types/known/fieldmaskpb";
+option cc_enable_arenas = true;
+
+// `FieldMask` represents a set of symbolic field paths, for example:
+//
+// paths: "f.a"
+// paths: "f.b.d"
+//
+// Here `f` represents a field in some root message, `a` and `b`
+// fields in the message found in `f`, and `d` a field found in the
+// message in `f.b`.
+//
+// Field masks are used to specify a subset of fields that should be
+// returned by a get operation or modified by an update operation.
+// Field masks also have a custom JSON encoding (see below).
+//
+// # Field Masks in Projections
+//
+// When used in the context of a projection, a response message or
+// sub-message is filtered by the API to only contain those fields as
+// specified in the mask. For example, if the mask in the previous
+// example is applied to a response message as follows:
+//
+// f {
+// a : 22
+// b {
+// d : 1
+// x : 2
+// }
+// y : 13
+// }
+// z: 8
+//
+// The result will not contain specific values for fields x,y and z
+// (their value will be set to the default, and omitted in proto text
+// output):
+//
+//
+// f {
+// a : 22
+// b {
+// d : 1
+// }
+// }
+//
+// A repeated field is not allowed except at the last position of a
+// paths string.
+//
+// If a FieldMask object is not present in a get operation, the
+// operation applies to all fields (as if a FieldMask of all fields
+// had been specified).
+//
+// Note that a field mask does not necessarily apply to the
+// top-level response message. In case of a REST get operation, the
+// field mask applies directly to the response, but in case of a REST
+// list operation, the mask instead applies to each individual message
+// in the returned resource list. In case of a REST custom method,
+// other definitions may be used. Where the mask applies will be
+// clearly documented together with its declaration in the API. In
+// any case, the effect on the returned resource/resources is required
+// behavior for APIs.
+//
+// # Field Masks in Update Operations
+//
+// A field mask in update operations specifies which fields of the
+// targeted resource are going to be updated. The API is required
+// to only change the values of the fields as specified in the mask
+// and leave the others untouched. If a resource is passed in to
+// describe the updated values, the API ignores the values of all
+// fields not covered by the mask.
+//
+// If a repeated field is specified for an update operation, new values will
+// be appended to the existing repeated field in the target resource. Note that
+// a repeated field is only allowed in the last position of a `paths` string.
+//
+// If a sub-message is specified in the last position of the field mask for an
+// update operation, then new value will be merged into the existing sub-message
+// in the target resource.
+//
+// For example, given the target message:
+//
+// f {
+// b {
+// d: 1
+// x: 2
+// }
+// c: [1]
+// }
+//
+// And an update message:
+//
+// f {
+// b {
+// d: 10
+// }
+// c: [2]
+// }
+//
+// then if the field mask is:
+//
+// paths: ["f.b", "f.c"]
+//
+// then the result will be:
+//
+// f {
+// b {
+// d: 10
+// x: 2
+// }
+// c: [1, 2]
+// }
+//
+// An implementation may provide options to override this default behavior for
+// repeated and message fields.
+//
+// In order to reset a field's value to the default, the field must
+// be in the mask and set to the default value in the provided resource.
+// Hence, in order to reset all fields of a resource, provide a default
+// instance of the resource and set all fields in the mask, or do
+// not provide a mask as described below.
+//
+// If a field mask is not present on update, the operation applies to
+// all fields (as if a field mask of all fields has been specified).
+// Note that in the presence of schema evolution, this may mean that
+// fields the client does not know and has therefore not filled into
+// the request will be reset to their default. If this is unwanted
+// behavior, a specific service may require a client to always specify
+// a field mask, producing an error if not.
+//
+// As with get operations, the location of the resource which
+// describes the updated values in the request message depends on the
+// operation kind. In any case, the effect of the field mask is
+// required to be honored by the API.
+//
+// ## Considerations for HTTP REST
+//
+// The HTTP kind of an update operation which uses a field mask must
+// be set to PATCH instead of PUT in order to satisfy HTTP semantics
+// (PUT must only be used for full updates).
+//
+// # JSON Encoding of Field Masks
+//
+// In JSON, a field mask is encoded as a single string where paths are
+// separated by a comma. Fields name in each path are converted
+// to/from lower-camel naming conventions.
+//
+// As an example, consider the following message declarations:
+//
+// message Profile {
+// User user = 1;
+// Photo photo = 2;
+// }
+// message User {
+// string display_name = 1;
+// string address = 2;
+// }
+//
+// In proto a field mask for `Profile` may look as such:
+//
+// mask {
+// paths: "user.display_name"
+// paths: "photo"
+// }
+//
+// In JSON, the same mask is represented as below:
+//
+// {
+// mask: "user.displayName,photo"
+// }
+//
+// # Field Masks and Oneof Fields
+//
+// Field masks treat fields in oneofs just as regular fields. Consider the
+// following message:
+//
+// message SampleMessage {
+// oneof test_oneof {
+// string name = 4;
+// SubMessage sub_message = 9;
+// }
+// }
+//
+// The field mask can be:
+//
+// mask {
+// paths: "name"
+// }
+//
+// Or:
+//
+// mask {
+// paths: "sub_message"
+// }
+//
+// Note that oneof type names ("test_oneof" in this case) cannot be used in
+// paths.
+//
+// ## Field Mask Verification
+//
+// The implementation of any API method which has a FieldMask type field in the
+// request should verify the included field paths, and return an
+// `INVALID_ARGUMENT` error if any path is unmappable.
+message FieldMask {
+ // The set of field mask paths.
+ repeated string paths = 1;
+}
diff --git a/api/protocol/include/google/protobuf/source_context.proto b/api/protocol/include/google/protobuf/source_context.proto
new file mode 100644
index 0000000..06bfc43
--- /dev/null
+++ b/api/protocol/include/google/protobuf/source_context.proto
@@ -0,0 +1,48 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "SourceContextProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/protobuf/types/known/sourcecontextpb";
+
+// `SourceContext` represents information about the source of a
+// protobuf element, like the file in which it is defined.
+message SourceContext {
+ // The path-qualified name of the .proto file that contained the associated
+ // protobuf element. For example: `"google/protobuf/source_context.proto"`.
+ string file_name = 1;
+}
diff --git a/api/protocol/include/google/protobuf/struct.proto b/api/protocol/include/google/protobuf/struct.proto
new file mode 100644
index 0000000..545215c
--- /dev/null
+++ b/api/protocol/include/google/protobuf/struct.proto
@@ -0,0 +1,95 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/protobuf/types/known/structpb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "StructProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// `Struct` represents a structured data value, consisting of fields
+// which map to dynamically typed values. In some languages, `Struct`
+// might be supported by a native representation. For example, in
+// scripting languages like JS a struct is represented as an
+// object. The details of that representation are described together
+// with the proto support for the language.
+//
+// The JSON representation for `Struct` is JSON object.
+message Struct {
+ // Unordered map of dynamically typed values.
+ map fields = 1;
+}
+
+// `Value` represents a dynamically typed value which can be either
+// null, a number, a string, a boolean, a recursive struct value, or a
+// list of values. A producer of value is expected to set one of that
+// variants, absence of any variant indicates an error.
+//
+// The JSON representation for `Value` is JSON value.
+message Value {
+ // The kind of value.
+ oneof kind {
+ // Represents a null value.
+ NullValue null_value = 1;
+ // Represents a double value.
+ double number_value = 2;
+ // Represents a string value.
+ string string_value = 3;
+ // Represents a boolean value.
+ bool bool_value = 4;
+ // Represents a structured value.
+ Struct struct_value = 5;
+ // Represents a repeated `Value`.
+ ListValue list_value = 6;
+ }
+}
+
+// `NullValue` is a singleton enumeration to represent the null value for the
+// `Value` type union.
+//
+// The JSON representation for `NullValue` is JSON `null`.
+enum NullValue {
+ // Null value.
+ NULL_VALUE = 0;
+}
+
+// `ListValue` is a wrapper around a repeated field of values.
+//
+// The JSON representation for `ListValue` is JSON array.
+message ListValue {
+ // Repeated field of dynamically typed values.
+ repeated Value values = 1;
+}
diff --git a/api/protocol/include/google/protobuf/timestamp.proto b/api/protocol/include/google/protobuf/timestamp.proto
new file mode 100644
index 0000000..3b2df6d
--- /dev/null
+++ b/api/protocol/include/google/protobuf/timestamp.proto
@@ -0,0 +1,147 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/protobuf/types/known/timestamppb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TimestampProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// A Timestamp represents a point in time independent of any time zone or local
+// calendar, encoded as a count of seconds and fractions of seconds at
+// nanosecond resolution. The count is relative to an epoch at UTC midnight on
+// January 1, 1970, in the proleptic Gregorian calendar which extends the
+// Gregorian calendar backwards to year one.
+//
+// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+// second table is needed for interpretation, using a [24-hour linear
+// smear](https://developers.google.com/time/smear).
+//
+// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+// restricting to that range, we ensure that we can convert to and from [RFC
+// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+//
+// # Examples
+//
+// Example 1: Compute Timestamp from POSIX `time()`.
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(time(NULL));
+// timestamp.set_nanos(0);
+//
+// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+//
+// struct timeval tv;
+// gettimeofday(&tv, NULL);
+//
+// Timestamp timestamp;
+// timestamp.set_seconds(tv.tv_sec);
+// timestamp.set_nanos(tv.tv_usec * 1000);
+//
+// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+//
+// FILETIME ft;
+// GetSystemTimeAsFileTime(&ft);
+// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+//
+// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+// Timestamp timestamp;
+// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+//
+// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+//
+// long millis = System.currentTimeMillis();
+//
+// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+// .setNanos((int) ((millis % 1000) * 1000000)).build();
+//
+//
+// Example 5: Compute Timestamp from Java `Instant.now()`.
+//
+// Instant now = Instant.now();
+//
+// Timestamp timestamp =
+// Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+// .setNanos(now.getNano()).build();
+//
+//
+// Example 6: Compute Timestamp from current time in Python.
+//
+// timestamp = Timestamp()
+// timestamp.GetCurrentTime()
+//
+// # JSON Mapping
+//
+// In JSON format, the Timestamp type is encoded as a string in the
+// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
+// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
+// where {year} is always expressed using four digits while {month}, {day},
+// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
+// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
+// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
+// is required. A proto3 JSON serializer should always use UTC (as indicated by
+// "Z") when printing the Timestamp type and a proto3 JSON parser should be
+// able to accept both UTC and other timezones (as indicated by an offset).
+//
+// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
+// 01:30 UTC on January 15, 2017.
+//
+// In JavaScript, one can convert a Date object to this format using the
+// standard
+// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+// method. In Python, a standard `datetime.datetime` object can be converted
+// to this format using
+// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
+// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
+// the Joda Time's [`ISODateTimeFormat.dateTime()`](
+// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
+// ) to obtain a formatter capable of generating timestamps in this format.
+//
+//
+message Timestamp {
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ int64 seconds = 1;
+
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ int32 nanos = 2;
+}
diff --git a/api/protocol/include/google/protobuf/type.proto b/api/protocol/include/google/protobuf/type.proto
new file mode 100644
index 0000000..d3f6a68
--- /dev/null
+++ b/api/protocol/include/google/protobuf/type.proto
@@ -0,0 +1,187 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/source_context.proto";
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option java_package = "com.google.protobuf";
+option java_outer_classname = "TypeProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+option go_package = "google.golang.org/protobuf/types/known/typepb";
+
+// A protocol buffer message type.
+message Type {
+ // The fully qualified message name.
+ string name = 1;
+ // The list of fields.
+ repeated Field fields = 2;
+ // The list of types appearing in `oneof` definitions in this type.
+ repeated string oneofs = 3;
+ // The protocol buffer options.
+ repeated Option options = 4;
+ // The source context.
+ SourceContext source_context = 5;
+ // The source syntax.
+ Syntax syntax = 6;
+}
+
+// A single field of a message type.
+message Field {
+ // Basic field types.
+ enum Kind {
+ // Field type unknown.
+ TYPE_UNKNOWN = 0;
+ // Field type double.
+ TYPE_DOUBLE = 1;
+ // Field type float.
+ TYPE_FLOAT = 2;
+ // Field type int64.
+ TYPE_INT64 = 3;
+ // Field type uint64.
+ TYPE_UINT64 = 4;
+ // Field type int32.
+ TYPE_INT32 = 5;
+ // Field type fixed64.
+ TYPE_FIXED64 = 6;
+ // Field type fixed32.
+ TYPE_FIXED32 = 7;
+ // Field type bool.
+ TYPE_BOOL = 8;
+ // Field type string.
+ TYPE_STRING = 9;
+ // Field type group. Proto2 syntax only, and deprecated.
+ TYPE_GROUP = 10;
+ // Field type message.
+ TYPE_MESSAGE = 11;
+ // Field type bytes.
+ TYPE_BYTES = 12;
+ // Field type uint32.
+ TYPE_UINT32 = 13;
+ // Field type enum.
+ TYPE_ENUM = 14;
+ // Field type sfixed32.
+ TYPE_SFIXED32 = 15;
+ // Field type sfixed64.
+ TYPE_SFIXED64 = 16;
+ // Field type sint32.
+ TYPE_SINT32 = 17;
+ // Field type sint64.
+ TYPE_SINT64 = 18;
+ }
+
+ // Whether a field is optional, required, or repeated.
+ enum Cardinality {
+ // For fields with unknown cardinality.
+ CARDINALITY_UNKNOWN = 0;
+ // For optional fields.
+ CARDINALITY_OPTIONAL = 1;
+ // For required fields. Proto2 syntax only.
+ CARDINALITY_REQUIRED = 2;
+ // For repeated fields.
+ CARDINALITY_REPEATED = 3;
+ }
+
+ // The field type.
+ Kind kind = 1;
+ // The field cardinality.
+ Cardinality cardinality = 2;
+ // The field number.
+ int32 number = 3;
+ // The field name.
+ string name = 4;
+ // The field type URL, without the scheme, for message or enumeration
+ // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
+ string type_url = 6;
+ // The index of the field type in `Type.oneofs`, for message or enumeration
+ // types. The first type has index 1; zero means the type is not in the list.
+ int32 oneof_index = 7;
+ // Whether to use alternative packed wire representation.
+ bool packed = 8;
+ // The protocol buffer options.
+ repeated Option options = 9;
+ // The field JSON name.
+ string json_name = 10;
+ // The string value of the default value of this field. Proto2 syntax only.
+ string default_value = 11;
+}
+
+// Enum type definition.
+message Enum {
+ // Enum type name.
+ string name = 1;
+ // Enum value definitions.
+ repeated EnumValue enumvalue = 2;
+ // Protocol buffer options.
+ repeated Option options = 3;
+ // The source context.
+ SourceContext source_context = 4;
+ // The source syntax.
+ Syntax syntax = 5;
+}
+
+// Enum value definition.
+message EnumValue {
+ // Enum value name.
+ string name = 1;
+ // Enum value number.
+ int32 number = 2;
+ // Protocol buffer options.
+ repeated Option options = 3;
+}
+
+// A protocol buffer option, which can be attached to a message, field,
+// enumeration, etc.
+message Option {
+ // The option's name. For protobuf built-in options (options defined in
+ // descriptor.proto), this is the short name. For example, `"map_entry"`.
+ // For custom options, it should be the fully-qualified name. For example,
+ // `"google.api.http"`.
+ string name = 1;
+ // The option's value packed in an Any message. If the value is a primitive,
+ // the corresponding wrapper type defined in google/protobuf/wrappers.proto
+ // should be used. If the value is an enum, it should be stored as an int32
+ // value using the google.protobuf.Int32Value type.
+ Any value = 2;
+}
+
+// The syntax in which a protocol buffer element is defined.
+enum Syntax {
+ // Syntax `proto2`.
+ SYNTAX_PROTO2 = 0;
+ // Syntax `proto3`.
+ SYNTAX_PROTO3 = 1;
+}
diff --git a/api/protocol/include/google/protobuf/wrappers.proto b/api/protocol/include/google/protobuf/wrappers.proto
new file mode 100644
index 0000000..d49dd53
--- /dev/null
+++ b/api/protocol/include/google/protobuf/wrappers.proto
@@ -0,0 +1,123 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+// Wrappers for primitive (non-message) types. These types are useful
+// for embedding primitives in the `google.protobuf.Any` type and for places
+// where we need to distinguish between the absence of a primitive
+// typed field and its default value.
+//
+// These wrappers have no meaningful use within repeated fields as they lack
+// the ability to detect presence on individual elements.
+// These wrappers have no meaningful use within a map or a oneof since
+// individual entries of a map or fields of a oneof can already detect presence.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option cc_enable_arenas = true;
+option go_package = "google.golang.org/protobuf/types/known/wrapperspb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "WrappersProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+// Wrapper message for `double`.
+//
+// The JSON representation for `DoubleValue` is JSON number.
+message DoubleValue {
+ // The double value.
+ double value = 1;
+}
+
+// Wrapper message for `float`.
+//
+// The JSON representation for `FloatValue` is JSON number.
+message FloatValue {
+ // The float value.
+ float value = 1;
+}
+
+// Wrapper message for `int64`.
+//
+// The JSON representation for `Int64Value` is JSON string.
+message Int64Value {
+ // The int64 value.
+ int64 value = 1;
+}
+
+// Wrapper message for `uint64`.
+//
+// The JSON representation for `UInt64Value` is JSON string.
+message UInt64Value {
+ // The uint64 value.
+ uint64 value = 1;
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+ // The int32 value.
+ int32 value = 1;
+}
+
+// Wrapper message for `uint32`.
+//
+// The JSON representation for `UInt32Value` is JSON number.
+message UInt32Value {
+ // The uint32 value.
+ uint32 value = 1;
+}
+
+// Wrapper message for `bool`.
+//
+// The JSON representation for `BoolValue` is JSON `true` and `false`.
+message BoolValue {
+ // The bool value.
+ bool value = 1;
+}
+
+// Wrapper message for `string`.
+//
+// The JSON representation for `StringValue` is JSON string.
+message StringValue {
+ // The string value.
+ string value = 1;
+}
+
+// Wrapper message for `bytes`.
+//
+// The JSON representation for `BytesValue` is JSON string.
+message BytesValue {
+ // The bytes value.
+ bytes value = 1;
+}
diff --git a/api/protocol/msg_proto/gen.go b/api/protocol/msg_proto/gen.go
new file mode 100644
index 0000000..947998e
--- /dev/null
+++ b/api/protocol/msg_proto/gen.go
@@ -0,0 +1,20 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package msg_proto
+
+////go:generate protoc -I=../ -I=. -I=../include --go_out=paths=source_relative:. msg.proto
+//go:generate protoc -I=../ -I=. -I=../include --go-cast_out=paths=source_relative:. msg.proto
diff --git a/api/protocol/msg_proto/msg.pb.go b/api/protocol/msg_proto/msg.pb.go
new file mode 100644
index 0000000..9210e5e
--- /dev/null
+++ b/api/protocol/msg_proto/msg.pb.go
@@ -0,0 +1,512 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.26.0
+// protoc v3.20.0
+// source: msg.proto
+
+package msg_proto
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type MessageData struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ClientVersion string `protobuf:"bytes,2,opt,name=clientVersion,proto3" json:"clientVersion,omitempty"` //client version
+ Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` //unix time
+ Id string `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"` // message id hash256(payload)
+ NodeID string `protobuf:"bytes,5,opt,name=nodeID,proto3" json:"nodeID,omitempty"` //id of peer that created the message, not the peer that may have send it.
+ NodePubKey []byte `protobuf:"bytes,6,opt,name=nodePubKey,proto3" json:"nodePubKey,omitempty"` // peer public key
+ Sign []byte `protobuf:"bytes,7,opt,name=sign,proto3" json:"sign,omitempty"` // signature of message data
+ Payload []byte `protobuf:"bytes,8,opt,name=payload,proto3" json:"payload,omitempty"` // payload
+ Gossip bool `protobuf:"varint,9,opt,name=gossip,proto3" json:"gossip,omitempty"`
+}
+
+func (x *MessageData) Reset() {
+ *x = MessageData{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_msg_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MessageData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MessageData) ProtoMessage() {}
+
+func (x *MessageData) ProtoReflect() protoreflect.Message {
+ mi := &file_msg_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MessageData.ProtoReflect.Descriptor instead.
+func (*MessageData) Descriptor() ([]byte, []int) {
+ return file_msg_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MessageData) GetClientVersion() string {
+ if x != nil {
+ return x.ClientVersion
+ }
+ return ""
+}
+
+func (x *MessageData) GetTimestamp() int64 {
+ if x != nil {
+ return x.Timestamp
+ }
+ return 0
+}
+
+func (x *MessageData) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *MessageData) GetNodeID() string {
+ if x != nil {
+ return x.NodeID
+ }
+ return ""
+}
+
+func (x *MessageData) GetNodePubKey() []byte {
+ if x != nil {
+ return x.NodePubKey
+ }
+ return nil
+}
+
+func (x *MessageData) GetSign() []byte {
+ if x != nil {
+ return x.Sign
+ }
+ return nil
+}
+
+func (x *MessageData) GetPayload() []byte {
+ if x != nil {
+ return x.Payload
+ }
+ return nil
+}
+
+func (x *MessageData) GetGossip() bool {
+ if x != nil {
+ return x.Gossip
+ }
+ return false
+}
+
+type NewBlockMessageData struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hash *types_pb.H256 `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"`
+ Number *types_pb.H256 `protobuf:"bytes,2,opt,name=Number,proto3" json:"Number,omitempty"`
+ Block *types_pb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"`
+}
+
+func (x *NewBlockMessageData) Reset() {
+ *x = NewBlockMessageData{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_msg_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *NewBlockMessageData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NewBlockMessageData) ProtoMessage() {}
+
+func (x *NewBlockMessageData) ProtoReflect() protoreflect.Message {
+ mi := &file_msg_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use NewBlockMessageData.ProtoReflect.Descriptor instead.
+func (*NewBlockMessageData) Descriptor() ([]byte, []int) {
+ return file_msg_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *NewBlockMessageData) GetHash() *types_pb.H256 {
+ if x != nil {
+ return x.Hash
+ }
+ return nil
+}
+
+func (x *NewBlockMessageData) GetNumber() *types_pb.H256 {
+ if x != nil {
+ return x.Number
+ }
+ return nil
+}
+
+func (x *NewBlockMessageData) GetBlock() *types_pb.Block {
+ if x != nil {
+ return x.Block
+ }
+ return nil
+}
+
+type ProtocolHandshakeMessage struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
+ GenesisHash *types_pb.H256 `protobuf:"bytes,2,opt,name=genesisHash,proto3" json:"genesisHash,omitempty"`
+ CurrentHeight *types_pb.H256 `protobuf:"bytes,3,opt,name=currentHeight,proto3" json:"currentHeight,omitempty"`
+}
+
+func (x *ProtocolHandshakeMessage) Reset() {
+ *x = ProtocolHandshakeMessage{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_msg_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ProtocolHandshakeMessage) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProtocolHandshakeMessage) ProtoMessage() {}
+
+func (x *ProtocolHandshakeMessage) ProtoReflect() protoreflect.Message {
+ mi := &file_msg_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProtocolHandshakeMessage.ProtoReflect.Descriptor instead.
+func (*ProtocolHandshakeMessage) Descriptor() ([]byte, []int) {
+ return file_msg_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ProtocolHandshakeMessage) GetVersion() string {
+ if x != nil {
+ return x.Version
+ }
+ return ""
+}
+
+func (x *ProtocolHandshakeMessage) GetGenesisHash() *types_pb.H256 {
+ if x != nil {
+ return x.GenesisHash
+ }
+ return nil
+}
+
+func (x *ProtocolHandshakeMessage) GetCurrentHeight() *types_pb.H256 {
+ if x != nil {
+ return x.CurrentHeight
+ }
+ return nil
+}
+
+type TopicScoreSnapshot struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Time a peer has spent in the gossip mesh.
+ TimeInMesh uint64 `protobuf:"varint,1,opt,name=time_in_mesh,json=timeInMesh,proto3" json:"time_in_mesh,omitempty"`
+ // This is the number of first message deliveries in the topic.
+ FirstMessageDeliveries float32 `protobuf:"fixed32,2,opt,name=first_message_deliveries,json=firstMessageDeliveries,proto3" json:"first_message_deliveries,omitempty"`
+ // This is the number of message deliveries in the mesh, within the MeshMessageDeliveriesWindow of
+ // message validation.It effectively tracks first and near-first
+ // deliveries, ie a message seen from a mesh peer before we have forwarded it to them.
+ MeshMessageDeliveries float32 `protobuf:"fixed32,3,opt,name=mesh_message_deliveries,json=meshMessageDeliveries,proto3" json:"mesh_message_deliveries,omitempty"`
+ // This is the number of invalid messages in the topic from the peer.
+ InvalidMessageDeliveries float32 `protobuf:"fixed32,4,opt,name=invalid_message_deliveries,json=invalidMessageDeliveries,proto3" json:"invalid_message_deliveries,omitempty"`
+}
+
+func (x *TopicScoreSnapshot) Reset() {
+ *x = TopicScoreSnapshot{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_msg_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TopicScoreSnapshot) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TopicScoreSnapshot) ProtoMessage() {}
+
+func (x *TopicScoreSnapshot) ProtoReflect() protoreflect.Message {
+ mi := &file_msg_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TopicScoreSnapshot.ProtoReflect.Descriptor instead.
+func (*TopicScoreSnapshot) Descriptor() ([]byte, []int) {
+ return file_msg_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *TopicScoreSnapshot) GetTimeInMesh() uint64 {
+ if x != nil {
+ return x.TimeInMesh
+ }
+ return 0
+}
+
+func (x *TopicScoreSnapshot) GetFirstMessageDeliveries() float32 {
+ if x != nil {
+ return x.FirstMessageDeliveries
+ }
+ return 0
+}
+
+func (x *TopicScoreSnapshot) GetMeshMessageDeliveries() float32 {
+ if x != nil {
+ return x.MeshMessageDeliveries
+ }
+ return 0
+}
+
+func (x *TopicScoreSnapshot) GetInvalidMessageDeliveries() float32 {
+ if x != nil {
+ return x.InvalidMessageDeliveries
+ }
+ return 0
+}
+
+var File_msg_proto protoreflect.FileDescriptor
+
+var file_msg_proto_rawDesc = []byte{
+ 0x0a, 0x09, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x6d, 0x73, 0x67,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdf, 0x01, 0x0a,
+ 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x0d,
+ 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
+ 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65,
+ 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6e, 0x6f,
+ 0x64, 0x65, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e,
+ 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x18, 0x0a, 0x07,
+ 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70,
+ 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70,
+ 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x67, 0x6f, 0x73, 0x73, 0x69, 0x70, 0x22, 0x88,
+ 0x01, 0x0a, 0x13, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x48, 0x32, 0x35, 0x36, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x26, 0x0a, 0x06, 0x4e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x4e, 0x75, 0x6d, 0x62,
+ 0x65, 0x72, 0x12, 0x25, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x9c, 0x01, 0x0a, 0x18, 0x50, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x4d,
+ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x12, 0x30, 0x0a, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x48, 0x61, 0x73, 0x68, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x48, 0x61,
+ 0x73, 0x68, 0x12, 0x34, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x69,
+ 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65,
+ 0x6e, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xe6, 0x01, 0x0a, 0x12, 0x54, 0x6f, 0x70,
+ 0x69, 0x63, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12,
+ 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x65, 0x73, 0x68, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x4d, 0x65, 0x73,
+ 0x68, 0x12, 0x38, 0x0a, 0x18, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x02, 0x52, 0x16, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x6d,
+ 0x65, 0x73, 0x68, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69,
+ 0x76, 0x65, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x15, 0x6d, 0x65,
+ 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
+ 0x69, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x6d,
+ 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65,
+ 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x52, 0x18, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
+ 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x69, 0x65,
+ 0x73, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61, 0x6d, 0x63, 0x2f, 0x61,
+ 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x6d, 0x73, 0x67, 0x5f,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_msg_proto_rawDescOnce sync.Once
+ file_msg_proto_rawDescData = file_msg_proto_rawDesc
+)
+
+func file_msg_proto_rawDescGZIP() []byte {
+ file_msg_proto_rawDescOnce.Do(func() {
+ file_msg_proto_rawDescData = protoimpl.X.CompressGZIP(file_msg_proto_rawDescData)
+ })
+ return file_msg_proto_rawDescData
+}
+
+var file_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_msg_proto_goTypes = []interface{}{
+ (*MessageData)(nil), // 0: msg_proto.MessageData
+ (*NewBlockMessageData)(nil), // 1: msg_proto.NewBlockMessageData
+ (*ProtocolHandshakeMessage)(nil), // 2: msg_proto.ProtocolHandshakeMessage
+ (*TopicScoreSnapshot)(nil), // 3: msg_proto.TopicScoreSnapshot
+ (*types_pb.H256)(nil), // 4: types_pb.H256
+ (*types_pb.Block)(nil), // 5: types_pb.Block
+}
+var file_msg_proto_depIdxs = []int32{
+ 4, // 0: msg_proto.NewBlockMessageData.hash:type_name -> types_pb.H256
+ 4, // 1: msg_proto.NewBlockMessageData.Number:type_name -> types_pb.H256
+ 5, // 2: msg_proto.NewBlockMessageData.block:type_name -> types_pb.Block
+ 4, // 3: msg_proto.ProtocolHandshakeMessage.genesisHash:type_name -> types_pb.H256
+ 4, // 4: msg_proto.ProtocolHandshakeMessage.currentHeight:type_name -> types_pb.H256
+ 5, // [5:5] is the sub-list for method output_type
+ 5, // [5:5] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_msg_proto_init() }
+func file_msg_proto_init() {
+ if File_msg_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_msg_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*MessageData); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_msg_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*NewBlockMessageData); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_msg_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ProtocolHandshakeMessage); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_msg_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*TopicScoreSnapshot); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_msg_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_msg_proto_goTypes,
+ DependencyIndexes: file_msg_proto_depIdxs,
+ MessageInfos: file_msg_proto_msgTypes,
+ }.Build()
+ File_msg_proto = out.File
+ file_msg_proto_rawDesc = nil
+ file_msg_proto_goTypes = nil
+ file_msg_proto_depIdxs = nil
+}
diff --git a/api/protocol/msg_proto/msg.proto b/api/protocol/msg_proto/msg.proto
new file mode 100644
index 0000000..d73337d
--- /dev/null
+++ b/api/protocol/msg_proto/msg.proto
@@ -0,0 +1,58 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package msg_proto;
+
+import "types_pb/types.proto";
+option go_package = "github.com/astranetworld/ast/api/protocol/msg_proto";
+
+message MessageData {
+ string clientVersion = 2; //client version
+ int64 timestamp = 3; //unix time
+ string id = 4; // message id hash256(payload)
+ string nodeID = 5; //id of peer that created the message, not the peer that may have send it.
+ bytes nodePubKey = 6; // peer public key
+ bytes sign = 7; // signature of message data
+ bytes payload = 8; // payload
+ bool gossip = 9;
+}
+
+message NewBlockMessageData {
+ types_pb.H256 hash = 1 ;
+ types_pb.H256 Number = 2;
+ types_pb.Block block = 3;
+}
+
+message ProtocolHandshakeMessage {
+ string version = 1;
+ types_pb.H256 genesisHash = 2;
+ types_pb.H256 currentHeight = 3;
+}
+
+
+message TopicScoreSnapshot {
+ // Time a peer has spent in the gossip mesh.
+ uint64 time_in_mesh = 1;
+ // This is the number of first message deliveries in the topic.
+ float first_message_deliveries = 2;
+ // This is the number of message deliveries in the mesh, within the MeshMessageDeliveriesWindow of
+ // message validation.It effectively tracks first and near-first
+ // deliveries, ie a message seen from a mesh peer before we have forwarded it to them.
+ float mesh_message_deliveries = 3;
+ // This is the number of invalid messages in the topic from the peer.
+ float invalid_message_deliveries = 4;
+}
diff --git a/api/protocol/state/gen.go b/api/protocol/state/gen.go
new file mode 100644
index 0000000..503eb98
--- /dev/null
+++ b/api/protocol/state/gen.go
@@ -0,0 +1,19 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package state
+
+//go:generate protoc -I=../ -I=. -I=../include --go_out=paths=source_relative:. state.proto
diff --git a/api/protocol/state/state.pb.go b/api/protocol/state/state.pb.go
new file mode 100644
index 0000000..5d87108
--- /dev/null
+++ b/api/protocol/state/state.pb.go
@@ -0,0 +1,293 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.0
+// protoc v3.20.0
+// source: state.proto
+
+package state
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type HashMap struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Key *types_pb.H256 `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ Value *types_pb.H256 `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (x *HashMap) Reset() {
+ *x = HashMap{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_state_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *HashMap) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HashMap) ProtoMessage() {}
+
+func (x *HashMap) ProtoReflect() protoreflect.Message {
+ mi := &file_state_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HashMap.ProtoReflect.Descriptor instead.
+func (*HashMap) Descriptor() ([]byte, []int) {
+ return file_state_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *HashMap) GetKey() *types_pb.H256 {
+ if x != nil {
+ return x.Key
+ }
+ return nil
+}
+
+func (x *HashMap) GetValue() *types_pb.H256 {
+ if x != nil {
+ return x.Value
+ }
+ return nil
+}
+
+type Account struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Initialised bool `protobuf:"varint,1,opt,name=Initialised,proto3" json:"Initialised,omitempty"`
+ Nonce uint64 `protobuf:"varint,2,opt,name=Nonce,proto3" json:"Nonce,omitempty"`
+ Balance *types_pb.H256 `protobuf:"bytes,3,opt,name=Balance,proto3" json:"Balance,omitempty"`
+ Root *types_pb.H256 `protobuf:"bytes,4,opt,name=Root,proto3" json:"Root,omitempty"`
+ CodeHash *types_pb.H256 `protobuf:"bytes,5,opt,name=CodeHash,proto3" json:"CodeHash,omitempty"`
+ Incarnation uint64 `protobuf:"varint,6,opt,name=Incarnation,proto3" json:"Incarnation,omitempty"`
+}
+
+func (x *Account) Reset() {
+ *x = Account{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_state_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Account) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Account) ProtoMessage() {}
+
+func (x *Account) ProtoReflect() protoreflect.Message {
+ mi := &file_state_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Account.ProtoReflect.Descriptor instead.
+func (*Account) Descriptor() ([]byte, []int) {
+ return file_state_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Account) GetInitialised() bool {
+ if x != nil {
+ return x.Initialised
+ }
+ return false
+}
+
+func (x *Account) GetNonce() uint64 {
+ if x != nil {
+ return x.Nonce
+ }
+ return 0
+}
+
+func (x *Account) GetBalance() *types_pb.H256 {
+ if x != nil {
+ return x.Balance
+ }
+ return nil
+}
+
+func (x *Account) GetRoot() *types_pb.H256 {
+ if x != nil {
+ return x.Root
+ }
+ return nil
+}
+
+func (x *Account) GetCodeHash() *types_pb.H256 {
+ if x != nil {
+ return x.CodeHash
+ }
+ return nil
+}
+
+func (x *Account) GetIncarnation() uint64 {
+ if x != nil {
+ return x.Incarnation
+ }
+ return 0
+}
+
+var File_state_proto protoreflect.FileDescriptor
+
+var file_state_proto_rawDesc = []byte{
+ 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x62, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x51, 0x0a,
+ 0x07, 0x48, 0x61, 0x73, 0x68, 0x4d, 0x61, 0x70, 0x12, 0x20, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x22, 0xdd, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b,
+ 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x12, 0x14,
+ 0x0a, 0x05, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x4e,
+ 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x22,
+ 0x0a, 0x04, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x04, 0x52, 0x6f,
+ 0x6f, 0x74, 0x12, 0x2a, 0x0a, 0x08, 0x43, 0x6f, 0x64, 0x65, 0x48, 0x61, 0x73, 0x68, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x48, 0x32, 0x35, 0x36, 0x52, 0x08, 0x43, 0x6f, 0x64, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x20,
+ 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x61, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x0b, 0x49, 0x6e, 0x63, 0x61, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61,
+ 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61, 0x6d, 0x63, 0x2f, 0x61, 0x70,
+ 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_state_proto_rawDescOnce sync.Once
+ file_state_proto_rawDescData = file_state_proto_rawDesc
+)
+
+func file_state_proto_rawDescGZIP() []byte {
+ file_state_proto_rawDescOnce.Do(func() {
+ file_state_proto_rawDescData = protoimpl.X.CompressGZIP(file_state_proto_rawDescData)
+ })
+ return file_state_proto_rawDescData
+}
+
+var file_state_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_state_proto_goTypes = []interface{}{
+ (*HashMap)(nil), // 0: state_pb.HashMap
+ (*Account)(nil), // 1: state_pb.Account
+ (*types_pb.H256)(nil), // 2: types_pb.H256
+}
+var file_state_proto_depIdxs = []int32{
+ 2, // 0: state_pb.HashMap.key:type_name -> types_pb.H256
+ 2, // 1: state_pb.HashMap.value:type_name -> types_pb.H256
+ 2, // 2: state_pb.Account.Balance:type_name -> types_pb.H256
+ 2, // 3: state_pb.Account.Root:type_name -> types_pb.H256
+ 2, // 4: state_pb.Account.CodeHash:type_name -> types_pb.H256
+ 5, // [5:5] is the sub-list for method output_type
+ 5, // [5:5] is the sub-list for method input_type
+ 5, // [5:5] is the sub-list for extension type_name
+ 5, // [5:5] is the sub-list for extension extendee
+ 0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_state_proto_init() }
+func file_state_proto_init() {
+ if File_state_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_state_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*HashMap); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_state_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Account); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_state_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_state_proto_goTypes,
+ DependencyIndexes: file_state_proto_depIdxs,
+ MessageInfos: file_state_proto_msgTypes,
+ }.Build()
+ File_state_proto = out.File
+ file_state_proto_rawDesc = nil
+ file_state_proto_goTypes = nil
+ file_state_proto_depIdxs = nil
+}
diff --git a/api/protocol/state/state.proto b/api/protocol/state/state.proto
new file mode 100644
index 0000000..ee13d03
--- /dev/null
+++ b/api/protocol/state/state.proto
@@ -0,0 +1,35 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package state_pb;
+
+import "types_pb/types.proto";
+option go_package = "github.com/astranetworld/ast/api/protocol/state";
+
+message HashMap {
+ types_pb.H256 key = 1;
+ types_pb.H256 value = 2 ;
+}
+
+message Account {
+ bool Initialised = 1;
+ uint64 Nonce = 2;
+ types_pb.H256 Balance = 3;
+ types_pb.H256 Root = 4;
+ types_pb.H256 CodeHash = 5;
+ uint64 Incarnation = 6;
+}
diff --git a/api/protocol/sync_pb/gen.go b/api/protocol/sync_pb/gen.go
new file mode 100644
index 0000000..dfcd84e
--- /dev/null
+++ b/api/protocol/sync_pb/gen.go
@@ -0,0 +1,21 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package sync_pb
+
+////go:generate protoc --plugin=/Users/mac/go/bin/protoc-gen-go-cast -I=../ -I=. -I=../include --go-cast_out=plugins=protoc-gen-go-cast,paths=source_relative:. types.proto
+//go:generate protoc -I=../ -I=. -I=../include --go-cast_out=paths=source_relative:. sync_pb.proto
+//go:generate sszgen -path=. -objs=BodiesByRangeRequest,HeadersByRangeRequest,Ping,ForkData,Status --include=../types_pb -output=generated.ssz.go
diff --git a/api/protocol/sync_pb/generated.ssz.go b/api/protocol/sync_pb/generated.ssz.go
new file mode 100644
index 0000000..ac21d2c
--- /dev/null
+++ b/api/protocol/sync_pb/generated.ssz.go
@@ -0,0 +1,549 @@
+// Code generated by fastssz. DO NOT EDIT.
+// Hash: b39bb565b34add934304fdd716d14cb913acca74f226a8fd1f0c0c390e3bc247
+package sync_pb
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ ssz "github.com/prysmaticlabs/fastssz"
+)
+
+// MarshalSSZ ssz marshals the HeadersByRangeRequest object
+func (h *HeadersByRangeRequest) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the HeadersByRangeRequest object to a target array
+func (h *HeadersByRangeRequest) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(20)
+
+ // Offset (0) 'StartBlockNumber'
+ dst = ssz.WriteOffset(dst, offset)
+ if h.StartBlockNumber == nil {
+ h.StartBlockNumber = new(types_pb.H256)
+ }
+ offset += h.StartBlockNumber.SizeSSZ()
+
+ // Field (1) 'Count'
+ dst = ssz.MarshalUint64(dst, h.Count)
+
+ // Field (2) 'Step'
+ dst = ssz.MarshalUint64(dst, h.Step)
+
+ // Field (0) 'StartBlockNumber'
+ if dst, err = h.StartBlockNumber.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the HeadersByRangeRequest object
+func (h *HeadersByRangeRequest) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 20 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0 uint64
+
+ // Offset (0) 'StartBlockNumber'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 20 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Field (1) 'Count'
+ h.Count = ssz.UnmarshallUint64(buf[4:12])
+
+ // Field (2) 'Step'
+ h.Step = ssz.UnmarshallUint64(buf[12:20])
+
+ // Field (0) 'StartBlockNumber'
+ {
+ buf = tail[o0:]
+ if h.StartBlockNumber == nil {
+ h.StartBlockNumber = new(types_pb.H256)
+ }
+ if err = h.StartBlockNumber.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the HeadersByRangeRequest object
+func (h *HeadersByRangeRequest) SizeSSZ() (size int) {
+ size = 20
+
+ // Field (0) 'StartBlockNumber'
+ if h.StartBlockNumber == nil {
+ h.StartBlockNumber = new(types_pb.H256)
+ }
+ size += h.StartBlockNumber.SizeSSZ()
+
+ return
+}
+
+// HashTreeRoot ssz hashes the HeadersByRangeRequest object
+func (h *HeadersByRangeRequest) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the HeadersByRangeRequest object with a hasher
+func (h *HeadersByRangeRequest) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'StartBlockNumber'
+ if err = h.StartBlockNumber.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Count'
+ hh.PutUint64(h.Count)
+
+ // Field (2) 'Step'
+ hh.PutUint64(h.Step)
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Ping object
+func (p *Ping) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(p)
+}
+
+// MarshalSSZTo ssz marshals the Ping object to a target array
+func (p *Ping) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'SeqNumber'
+ dst = ssz.MarshalUint64(dst, p.SeqNumber)
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Ping object
+func (p *Ping) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 8 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'SeqNumber'
+ p.SeqNumber = ssz.UnmarshallUint64(buf[0:8])
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Ping object
+func (p *Ping) SizeSSZ() (size int) {
+ size = 8
+ return
+}
+
+// HashTreeRoot ssz hashes the Ping object
+func (p *Ping) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(p)
+}
+
+// HashTreeRootWith ssz hashes the Ping object with a hasher
+func (p *Ping) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'SeqNumber'
+ hh.PutUint64(p.SeqNumber)
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Status object
+func (s *Status) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(s)
+}
+
+// MarshalSSZTo ssz marshals the Status object to a target array
+func (s *Status) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(8)
+
+ // Offset (0) 'GenesisHash'
+ dst = ssz.WriteOffset(dst, offset)
+ if s.GenesisHash == nil {
+ s.GenesisHash = new(types_pb.H256)
+ }
+ offset += s.GenesisHash.SizeSSZ()
+
+ // Offset (1) 'CurrentHeight'
+ dst = ssz.WriteOffset(dst, offset)
+ if s.CurrentHeight == nil {
+ s.CurrentHeight = new(types_pb.H256)
+ }
+ offset += s.CurrentHeight.SizeSSZ()
+
+ // Field (0) 'GenesisHash'
+ if dst, err = s.GenesisHash.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'CurrentHeight'
+ if dst, err = s.CurrentHeight.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Status object
+func (s *Status) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 8 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0, o1 uint64
+
+ // Offset (0) 'GenesisHash'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 8 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Offset (1) 'CurrentHeight'
+ if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
+ return ssz.ErrOffset
+ }
+
+ // Field (0) 'GenesisHash'
+ {
+ buf = tail[o0:o1]
+ if s.GenesisHash == nil {
+ s.GenesisHash = new(types_pb.H256)
+ }
+ if err = s.GenesisHash.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+
+ // Field (1) 'CurrentHeight'
+ {
+ buf = tail[o1:]
+ if s.CurrentHeight == nil {
+ s.CurrentHeight = new(types_pb.H256)
+ }
+ if err = s.CurrentHeight.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Status object
+func (s *Status) SizeSSZ() (size int) {
+ size = 8
+
+ // Field (0) 'GenesisHash'
+ if s.GenesisHash == nil {
+ s.GenesisHash = new(types_pb.H256)
+ }
+ size += s.GenesisHash.SizeSSZ()
+
+ // Field (1) 'CurrentHeight'
+ if s.CurrentHeight == nil {
+ s.CurrentHeight = new(types_pb.H256)
+ }
+ size += s.CurrentHeight.SizeSSZ()
+
+ return
+}
+
+// HashTreeRoot ssz hashes the Status object
+func (s *Status) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(s)
+}
+
+// HashTreeRootWith ssz hashes the Status object with a hasher
+func (s *Status) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'GenesisHash'
+ if err = s.GenesisHash.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'CurrentHeight'
+ if err = s.CurrentHeight.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the ForkData object
+func (f *ForkData) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(f)
+}
+
+// MarshalSSZTo ssz marshals the ForkData object to a target array
+func (f *ForkData) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(8)
+
+ // Offset (0) 'CurrentVersion'
+ dst = ssz.WriteOffset(dst, offset)
+ if f.CurrentVersion == nil {
+ f.CurrentVersion = new(types_pb.H256)
+ }
+ offset += f.CurrentVersion.SizeSSZ()
+
+ // Offset (1) 'GenesisValidatorsRoot'
+ dst = ssz.WriteOffset(dst, offset)
+ if f.GenesisValidatorsRoot == nil {
+ f.GenesisValidatorsRoot = new(types_pb.H256)
+ }
+ offset += f.GenesisValidatorsRoot.SizeSSZ()
+
+ // Field (0) 'CurrentVersion'
+ if dst, err = f.CurrentVersion.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'GenesisValidatorsRoot'
+ if dst, err = f.GenesisValidatorsRoot.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the ForkData object
+func (f *ForkData) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 8 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0, o1 uint64
+
+ // Offset (0) 'CurrentVersion'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 8 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Offset (1) 'GenesisValidatorsRoot'
+ if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
+ return ssz.ErrOffset
+ }
+
+ // Field (0) 'CurrentVersion'
+ {
+ buf = tail[o0:o1]
+ if f.CurrentVersion == nil {
+ f.CurrentVersion = new(types_pb.H256)
+ }
+ if err = f.CurrentVersion.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+
+ // Field (1) 'GenesisValidatorsRoot'
+ {
+ buf = tail[o1:]
+ if f.GenesisValidatorsRoot == nil {
+ f.GenesisValidatorsRoot = new(types_pb.H256)
+ }
+ if err = f.GenesisValidatorsRoot.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the ForkData object
+func (f *ForkData) SizeSSZ() (size int) {
+ size = 8
+
+ // Field (0) 'CurrentVersion'
+ if f.CurrentVersion == nil {
+ f.CurrentVersion = new(types_pb.H256)
+ }
+ size += f.CurrentVersion.SizeSSZ()
+
+ // Field (1) 'GenesisValidatorsRoot'
+ if f.GenesisValidatorsRoot == nil {
+ f.GenesisValidatorsRoot = new(types_pb.H256)
+ }
+ size += f.GenesisValidatorsRoot.SizeSSZ()
+
+ return
+}
+
+// HashTreeRoot ssz hashes the ForkData object
+func (f *ForkData) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(f)
+}
+
+// HashTreeRootWith ssz hashes the ForkData object with a hasher
+func (f *ForkData) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'CurrentVersion'
+ if err = f.CurrentVersion.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'GenesisValidatorsRoot'
+ if err = f.GenesisValidatorsRoot.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the BodiesByRangeRequest object
+func (b *BodiesByRangeRequest) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(b)
+}
+
+// MarshalSSZTo ssz marshals the BodiesByRangeRequest object to a target array
+func (b *BodiesByRangeRequest) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(20)
+
+ // Offset (0) 'StartBlockNumber'
+ dst = ssz.WriteOffset(dst, offset)
+ if b.StartBlockNumber == nil {
+ b.StartBlockNumber = new(types_pb.H256)
+ }
+ offset += b.StartBlockNumber.SizeSSZ()
+
+ // Field (1) 'Count'
+ dst = ssz.MarshalUint64(dst, b.Count)
+
+ // Field (2) 'Step'
+ dst = ssz.MarshalUint64(dst, b.Step)
+
+ // Field (0) 'StartBlockNumber'
+ if dst, err = b.StartBlockNumber.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the BodiesByRangeRequest object
+func (b *BodiesByRangeRequest) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 20 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0 uint64
+
+ // Offset (0) 'StartBlockNumber'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 20 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Field (1) 'Count'
+ b.Count = ssz.UnmarshallUint64(buf[4:12])
+
+ // Field (2) 'Step'
+ b.Step = ssz.UnmarshallUint64(buf[12:20])
+
+ // Field (0) 'StartBlockNumber'
+ {
+ buf = tail[o0:]
+ if b.StartBlockNumber == nil {
+ b.StartBlockNumber = new(types_pb.H256)
+ }
+ if err = b.StartBlockNumber.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the BodiesByRangeRequest object
+func (b *BodiesByRangeRequest) SizeSSZ() (size int) {
+ size = 20
+
+ // Field (0) 'StartBlockNumber'
+ if b.StartBlockNumber == nil {
+ b.StartBlockNumber = new(types_pb.H256)
+ }
+ size += b.StartBlockNumber.SizeSSZ()
+
+ return
+}
+
+// HashTreeRoot ssz hashes the BodiesByRangeRequest object
+func (b *BodiesByRangeRequest) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(b)
+}
+
+// HashTreeRootWith ssz hashes the BodiesByRangeRequest object with a hasher
+func (b *BodiesByRangeRequest) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'StartBlockNumber'
+ if err = b.StartBlockNumber.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Count'
+ hh.PutUint64(b.Count)
+
+ // Field (2) 'Step'
+ hh.PutUint64(b.Step)
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
diff --git a/api/protocol/sync_pb/sync_pb.pb.go b/api/protocol/sync_pb/sync_pb.pb.go
new file mode 100644
index 0000000..3d47a3d
--- /dev/null
+++ b/api/protocol/sync_pb/sync_pb.pb.go
@@ -0,0 +1,492 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.26.0
+// protoc v3.20.0
+// source: sync_pb.proto
+
+package sync_pb
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type HeadersByRangeRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ StartBlockNumber *types_pb.H256 `protobuf:"bytes,1,opt,name=startBlockNumber,proto3" json:"startBlockNumber,omitempty"`
+ Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
+ Step uint64 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"`
+}
+
+func (x *HeadersByRangeRequest) Reset() {
+ *x = HeadersByRangeRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_pb_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *HeadersByRangeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HeadersByRangeRequest) ProtoMessage() {}
+
+func (x *HeadersByRangeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_pb_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HeadersByRangeRequest.ProtoReflect.Descriptor instead.
+func (*HeadersByRangeRequest) Descriptor() ([]byte, []int) {
+ return file_sync_pb_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *HeadersByRangeRequest) GetStartBlockNumber() *types_pb.H256 {
+ if x != nil {
+ return x.StartBlockNumber
+ }
+ return nil
+}
+
+func (x *HeadersByRangeRequest) GetCount() uint64 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+func (x *HeadersByRangeRequest) GetStep() uint64 {
+ if x != nil {
+ return x.Step
+ }
+ return 0
+}
+
+// todo
+type Ping struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ SeqNumber uint64 `protobuf:"varint,1,opt,name=seq_number,json=seqNumber,proto3" json:"seq_number,omitempty"`
+}
+
+func (x *Ping) Reset() {
+ *x = Ping{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_pb_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Ping) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Ping) ProtoMessage() {}
+
+func (x *Ping) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_pb_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Ping.ProtoReflect.Descriptor instead.
+func (*Ping) Descriptor() ([]byte, []int) {
+ return file_sync_pb_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Ping) GetSeqNumber() uint64 {
+ if x != nil {
+ return x.SeqNumber
+ }
+ return 0
+}
+
+// v2
+type Status struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // string version = 1;
+ GenesisHash *types_pb.H256 `protobuf:"bytes,1,opt,name=genesisHash,proto3" json:"genesisHash,omitempty"`
+ CurrentHeight *types_pb.H256 `protobuf:"bytes,2,opt,name=currentHeight,proto3" json:"currentHeight,omitempty"`
+}
+
+func (x *Status) Reset() {
+ *x = Status{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_pb_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Status) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_pb_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
+func (*Status) Descriptor() ([]byte, []int) {
+ return file_sync_pb_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *Status) GetGenesisHash() *types_pb.H256 {
+ if x != nil {
+ return x.GenesisHash
+ }
+ return nil
+}
+
+func (x *Status) GetCurrentHeight() *types_pb.H256 {
+ if x != nil {
+ return x.CurrentHeight
+ }
+ return nil
+}
+
+type ForkData struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ CurrentVersion *types_pb.H256 `protobuf:"bytes,1,opt,name=current_version,json=currentVersion,proto3" json:"current_version,omitempty"`
+ GenesisValidatorsRoot *types_pb.H256 `protobuf:"bytes,2,opt,name=genesis_validators_root,json=genesisValidatorsRoot,proto3" json:"genesis_validators_root,omitempty"`
+}
+
+func (x *ForkData) Reset() {
+ *x = ForkData{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_pb_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ForkData) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ForkData) ProtoMessage() {}
+
+func (x *ForkData) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_pb_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ForkData.ProtoReflect.Descriptor instead.
+func (*ForkData) Descriptor() ([]byte, []int) {
+ return file_sync_pb_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ForkData) GetCurrentVersion() *types_pb.H256 {
+ if x != nil {
+ return x.CurrentVersion
+ }
+ return nil
+}
+
+func (x *ForkData) GetGenesisValidatorsRoot() *types_pb.H256 {
+ if x != nil {
+ return x.GenesisValidatorsRoot
+ }
+ return nil
+}
+
+type BodiesByRangeRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ StartBlockNumber *types_pb.H256 `protobuf:"bytes,1,opt,name=startBlockNumber,proto3" json:"startBlockNumber,omitempty"`
+ Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
+ Step uint64 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"`
+}
+
+func (x *BodiesByRangeRequest) Reset() {
+ *x = BodiesByRangeRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_pb_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BodiesByRangeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BodiesByRangeRequest) ProtoMessage() {}
+
+func (x *BodiesByRangeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_pb_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BodiesByRangeRequest.ProtoReflect.Descriptor instead.
+func (*BodiesByRangeRequest) Descriptor() ([]byte, []int) {
+ return file_sync_pb_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *BodiesByRangeRequest) GetStartBlockNumber() *types_pb.H256 {
+ if x != nil {
+ return x.StartBlockNumber
+ }
+ return nil
+}
+
+func (x *BodiesByRangeRequest) GetCount() uint64 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+func (x *BodiesByRangeRequest) GetStep() uint64 {
+ if x != nil {
+ return x.Step
+ }
+ return 0
+}
+
+var File_sync_pb_proto protoreflect.FileDescriptor
+
+var file_sync_pb_proto_rawDesc = []byte{
+ 0x0a, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+ 0x07, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x62, 0x70, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d,
+ 0x0a, 0x15, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x42, 0x79, 0x52, 0x61, 0x6e, 0x67, 0x65,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35,
+ 0x36, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d,
+ 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65,
+ 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x22, 0x25, 0x0a,
+ 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x71, 0x5f, 0x6e, 0x75, 0x6d,
+ 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x71, 0x4e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x22, 0x70, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x30,
+ 0x0a, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48,
+ 0x32, 0x35, 0x36, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x48, 0x61, 0x73, 0x68,
+ 0x12, 0x34, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68,
+ 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74,
+ 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x6b, 0x44,
+ 0x61, 0x74, 0x61, 0x12, 0x37, 0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0e, 0x63, 0x75,
+ 0x72, 0x72, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x17,
+ 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f,
+ 0x72, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
+ 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x15, 0x67,
+ 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x73,
+ 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x7c, 0x0a, 0x14, 0x42, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x42, 0x79,
+ 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x10,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12,
+ 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x74,
+ 0x65, 0x70, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61, 0x6d, 0x63, 0x2f,
+ 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x79, 0x6e,
+ 0x63, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_sync_pb_proto_rawDescOnce sync.Once
+ file_sync_pb_proto_rawDescData = file_sync_pb_proto_rawDesc
+)
+
+func file_sync_pb_proto_rawDescGZIP() []byte {
+ file_sync_pb_proto_rawDescOnce.Do(func() {
+ file_sync_pb_proto_rawDescData = protoimpl.X.CompressGZIP(file_sync_pb_proto_rawDescData)
+ })
+ return file_sync_pb_proto_rawDescData
+}
+
+var file_sync_pb_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_sync_pb_proto_goTypes = []interface{}{
+ (*HeadersByRangeRequest)(nil), // 0: sync_bp.HeadersByRangeRequest
+ (*Ping)(nil), // 1: sync_bp.Ping
+ (*Status)(nil), // 2: sync_bp.Status
+ (*ForkData)(nil), // 3: sync_bp.ForkData
+ (*BodiesByRangeRequest)(nil), // 4: sync_bp.BodiesByRangeRequest
+ (*types_pb.H256)(nil), // 5: types_pb.H256
+}
+var file_sync_pb_proto_depIdxs = []int32{
+ 5, // 0: sync_bp.HeadersByRangeRequest.startBlockNumber:type_name -> types_pb.H256
+ 5, // 1: sync_bp.Status.genesisHash:type_name -> types_pb.H256
+ 5, // 2: sync_bp.Status.currentHeight:type_name -> types_pb.H256
+ 5, // 3: sync_bp.ForkData.current_version:type_name -> types_pb.H256
+ 5, // 4: sync_bp.ForkData.genesis_validators_root:type_name -> types_pb.H256
+ 5, // 5: sync_bp.BodiesByRangeRequest.startBlockNumber:type_name -> types_pb.H256
+ 6, // [6:6] is the sub-list for method output_type
+ 6, // [6:6] is the sub-list for method input_type
+ 6, // [6:6] is the sub-list for extension type_name
+ 6, // [6:6] is the sub-list for extension extendee
+ 0, // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_sync_pb_proto_init() }
+func file_sync_pb_proto_init() {
+ if File_sync_pb_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_sync_pb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*HeadersByRangeRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_pb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Ping); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_pb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Status); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_pb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*ForkData); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_pb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*BodiesByRangeRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_sync_pb_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 5,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_sync_pb_proto_goTypes,
+ DependencyIndexes: file_sync_pb_proto_depIdxs,
+ MessageInfos: file_sync_pb_proto_msgTypes,
+ }.Build()
+ File_sync_pb_proto = out.File
+ file_sync_pb_proto_rawDesc = nil
+ file_sync_pb_proto_goTypes = nil
+ file_sync_pb_proto_depIdxs = nil
+}
diff --git a/api/protocol/sync_pb/sync_pb.proto b/api/protocol/sync_pb/sync_pb.proto
new file mode 100644
index 0000000..1d7263d
--- /dev/null
+++ b/api/protocol/sync_pb/sync_pb.proto
@@ -0,0 +1,50 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package sync_bp;
+option go_package = "github.com/astranetworld/ast/api/protocol/sync_pb";
+
+import "types_pb/types.proto";
+
+message HeadersByRangeRequest {
+ types_pb.H256 startBlockNumber = 1;
+ uint64 count = 2;
+ uint64 step = 3;
+}
+
+// todo
+message Ping {
+ uint64 seq_number = 1;
+}
+
+//v2
+message Status {
+ // string version = 1;
+ types_pb.H256 genesisHash = 1;
+ types_pb.H256 currentHeight = 2;
+}
+
+message ForkData {
+ types_pb.H256 current_version = 1;
+ types_pb.H256 genesis_validators_root = 2;
+}
+
+message BodiesByRangeRequest {
+ types_pb.H256 startBlockNumber = 1;
+ uint64 count = 2;
+ uint64 step = 3;
+}
diff --git a/api/protocol/sync_proto/gen.go b/api/protocol/sync_proto/gen.go
new file mode 100644
index 0000000..c637b3f
--- /dev/null
+++ b/api/protocol/sync_proto/gen.go
@@ -0,0 +1,19 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package sync_proto
+
+//go:generate protoc -I=../ -I=. -I=../include --go_out=paths=source_relative:. sync.proto
diff --git a/api/protocol/sync_proto/sync.pb.go b/api/protocol/sync_proto/sync.pb.go
new file mode 100644
index 0000000..e9887eb
--- /dev/null
+++ b/api/protocol/sync_proto/sync.pb.go
@@ -0,0 +1,1045 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.28.0
+// protoc v3.20.0
+// source: sync_pb.proto
+
+package sync_proto
+
+import (
+ types_pb "github.com/astranetworld/ast/api/protocol/types_pb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type SyncType int32
+
+const (
+ SyncType_FINDReq SyncType = 0
+ SyncType_FindRes SyncType = 1
+ SyncType_HeaderReq SyncType = 2
+ SyncType_HeaderRes SyncType = 3
+ SyncType_BodyReq SyncType = 4
+ SyncType_BodyRes SyncType = 5
+ SyncType_StateReq SyncType = 6
+ SyncType_StateRes SyncType = 7
+ SyncType_TransactionReq SyncType = 8
+ SyncType_TransactionRes SyncType = 9
+ SyncType_PeerInfoBroadcast SyncType = 10
+)
+
+// Enum value maps for SyncType.
+var (
+ SyncType_name = map[int32]string{
+ 0: "FINDReq",
+ 1: "FindRes",
+ 2: "HeaderReq",
+ 3: "HeaderRes",
+ 4: "BodyReq",
+ 5: "BodyRes",
+ 6: "StateReq",
+ 7: "StateRes",
+ 8: "TransactionReq",
+ 9: "TransactionRes",
+ 10: "PeerInfoBroadcast",
+ }
+ SyncType_value = map[string]int32{
+ "FINDReq": 0,
+ "FindRes": 1,
+ "HeaderReq": 2,
+ "HeaderRes": 3,
+ "BodyReq": 4,
+ "BodyRes": 5,
+ "StateReq": 6,
+ "StateRes": 7,
+ "TransactionReq": 8,
+ "TransactionRes": 9,
+ "PeerInfoBroadcast": 10,
+ }
+)
+
+func (x SyncType) Enum() *SyncType {
+ p := new(SyncType)
+ *p = x
+ return p
+}
+
+func (x SyncType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SyncType) Descriptor() protoreflect.EnumDescriptor {
+ return file_sync_proto_enumTypes[0].Descriptor()
+}
+
+func (SyncType) Type() protoreflect.EnumType {
+ return &file_sync_proto_enumTypes[0]
+}
+
+func (x SyncType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use SyncType.Descriptor instead.
+func (SyncType) EnumDescriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{0}
+}
+
+type SyncProtocol struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+}
+
+func (x *SyncProtocol) Reset() {
+ *x = SyncProtocol{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncProtocol) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncProtocol) ProtoMessage() {}
+
+func (x *SyncProtocol) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncProtocol.ProtoReflect.Descriptor instead.
+func (*SyncProtocol) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{0}
+}
+
+type Value struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"`
+ Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
+}
+
+func (x *Value) Reset() {
+ *x = Value{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Value) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Value) ProtoMessage() {}
+
+func (x *Value) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Value.ProtoReflect.Descriptor instead.
+func (*Value) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Value) GetHash() string {
+ if x != nil {
+ return x.Hash
+ }
+ return ""
+}
+
+func (x *Value) GetHeight() uint64 {
+ if x != nil {
+ return x.Height
+ }
+ return 0
+}
+
+type SyncBlockRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Number []*types_pb.H256 `protobuf:"bytes,1,rep,name=number,proto3" json:"number,omitempty"`
+}
+
+func (x *SyncBlockRequest) Reset() {
+ *x = SyncBlockRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncBlockRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncBlockRequest) ProtoMessage() {}
+
+func (x *SyncBlockRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncBlockRequest.ProtoReflect.Descriptor instead.
+func (*SyncBlockRequest) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *SyncBlockRequest) GetNumber() []*types_pb.H256 {
+ if x != nil {
+ return x.Number
+ }
+ return nil
+}
+
+type SyncBlockResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Blocks []*types_pb.Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"`
+}
+
+func (x *SyncBlockResponse) Reset() {
+ *x = SyncBlockResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncBlockResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncBlockResponse) ProtoMessage() {}
+
+func (x *SyncBlockResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncBlockResponse.ProtoReflect.Descriptor instead.
+func (*SyncBlockResponse) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *SyncBlockResponse) GetBlocks() []*types_pb.Block {
+ if x != nil {
+ return x.Blocks
+ }
+ return nil
+}
+
+type SyncHeaderRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Number *types_pb.H256 `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
+ Amount *types_pb.H256 `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"`
+}
+
+func (x *SyncHeaderRequest) Reset() {
+ *x = SyncHeaderRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncHeaderRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncHeaderRequest) ProtoMessage() {}
+
+func (x *SyncHeaderRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncHeaderRequest.ProtoReflect.Descriptor instead.
+func (*SyncHeaderRequest) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *SyncHeaderRequest) GetNumber() *types_pb.H256 {
+ if x != nil {
+ return x.Number
+ }
+ return nil
+}
+
+func (x *SyncHeaderRequest) GetAmount() *types_pb.H256 {
+ if x != nil {
+ return x.Amount
+ }
+ return nil
+}
+
+type SyncHeaderResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Headers []*types_pb.Header `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"`
+}
+
+func (x *SyncHeaderResponse) Reset() {
+ *x = SyncHeaderResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncHeaderResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncHeaderResponse) ProtoMessage() {}
+
+func (x *SyncHeaderResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncHeaderResponse.ProtoReflect.Descriptor instead.
+func (*SyncHeaderResponse) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *SyncHeaderResponse) GetHeaders() []*types_pb.Header {
+ if x != nil {
+ return x.Headers
+ }
+ return nil
+}
+
+type SyncTransactionRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Bloom []byte `protobuf:"bytes,1,opt,name=bloom,proto3" json:"bloom,omitempty"`
+}
+
+func (x *SyncTransactionRequest) Reset() {
+ *x = SyncTransactionRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncTransactionRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncTransactionRequest) ProtoMessage() {}
+
+func (x *SyncTransactionRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncTransactionRequest.ProtoReflect.Descriptor instead.
+func (*SyncTransactionRequest) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *SyncTransactionRequest) GetBloom() []byte {
+ if x != nil {
+ return x.Bloom
+ }
+ return nil
+}
+
+type SyncTransactionResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Transactions []*types_pb.Transaction `protobuf:"bytes,1,rep,name=transactions,proto3" json:"transactions,omitempty"`
+}
+
+func (x *SyncTransactionResponse) Reset() {
+ *x = SyncTransactionResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncTransactionResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncTransactionResponse) ProtoMessage() {}
+
+func (x *SyncTransactionResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncTransactionResponse.ProtoReflect.Descriptor instead.
+func (*SyncTransactionResponse) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *SyncTransactionResponse) GetTransactions() []*types_pb.Transaction {
+ if x != nil {
+ return x.Transactions
+ }
+ return nil
+}
+
+type SyncPeerInfoBroadcast struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Difficulty *types_pb.H256 `protobuf:"bytes,1,opt,name=Difficulty,proto3" json:"Difficulty,omitempty"`
+ Number *types_pb.H256 `protobuf:"bytes,2,opt,name=Number,proto3" json:"Number,omitempty"`
+}
+
+func (x *SyncPeerInfoBroadcast) Reset() {
+ *x = SyncPeerInfoBroadcast{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncPeerInfoBroadcast) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncPeerInfoBroadcast) ProtoMessage() {}
+
+func (x *SyncPeerInfoBroadcast) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncPeerInfoBroadcast.ProtoReflect.Descriptor instead.
+func (*SyncPeerInfoBroadcast) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *SyncPeerInfoBroadcast) GetDifficulty() *types_pb.H256 {
+ if x != nil {
+ return x.Difficulty
+ }
+ return nil
+}
+
+func (x *SyncPeerInfoBroadcast) GetNumber() *types_pb.H256 {
+ if x != nil {
+ return x.Number
+ }
+ return nil
+}
+
+type SyncTask struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // task id
+ Ok bool `protobuf:"varint,2,opt,name=ok,proto3" json:"ok,omitempty"`
+ SyncType SyncType `protobuf:"varint,3,opt,name=syncType,proto3,enum=sync_proto.SyncType" json:"syncType,omitempty"`
+ // Types that are assignable to Payload:
+ // *SyncTask_SyncHeaderRequest
+ // *SyncTask_SyncHeaderResponse
+ // *SyncTask_SyncBlockRequest
+ // *SyncTask_SyncBlockResponse
+ // *SyncTask_SyncTransactionRequest
+ // *SyncTask_SyncTransactionResponse
+ // *SyncTask_SyncPeerInfoBroadcast
+ Payload isSyncTask_Payload `protobuf_oneof:"payload"`
+}
+
+func (x *SyncTask) Reset() {
+ *x = SyncTask{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_sync_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *SyncTask) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SyncTask) ProtoMessage() {}
+
+func (x *SyncTask) ProtoReflect() protoreflect.Message {
+ mi := &file_sync_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SyncTask.ProtoReflect.Descriptor instead.
+func (*SyncTask) Descriptor() ([]byte, []int) {
+ return file_sync_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *SyncTask) GetId() uint64 {
+ if x != nil {
+ return x.Id
+ }
+ return 0
+}
+
+func (x *SyncTask) GetOk() bool {
+ if x != nil {
+ return x.Ok
+ }
+ return false
+}
+
+func (x *SyncTask) GetSyncType() SyncType {
+ if x != nil {
+ return x.SyncType
+ }
+ return SyncType_FINDReq
+}
+
+func (m *SyncTask) GetPayload() isSyncTask_Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncHeaderRequest() *SyncHeaderRequest {
+ if x, ok := x.GetPayload().(*SyncTask_SyncHeaderRequest); ok {
+ return x.SyncHeaderRequest
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncHeaderResponse() *SyncHeaderResponse {
+ if x, ok := x.GetPayload().(*SyncTask_SyncHeaderResponse); ok {
+ return x.SyncHeaderResponse
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncBlockRequest() *SyncBlockRequest {
+ if x, ok := x.GetPayload().(*SyncTask_SyncBlockRequest); ok {
+ return x.SyncBlockRequest
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncBlockResponse() *SyncBlockResponse {
+ if x, ok := x.GetPayload().(*SyncTask_SyncBlockResponse); ok {
+ return x.SyncBlockResponse
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncTransactionRequest() *SyncTransactionRequest {
+ if x, ok := x.GetPayload().(*SyncTask_SyncTransactionRequest); ok {
+ return x.SyncTransactionRequest
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncTransactionResponse() *SyncTransactionResponse {
+ if x, ok := x.GetPayload().(*SyncTask_SyncTransactionResponse); ok {
+ return x.SyncTransactionResponse
+ }
+ return nil
+}
+
+func (x *SyncTask) GetSyncPeerInfoBroadcast() *SyncPeerInfoBroadcast {
+ if x, ok := x.GetPayload().(*SyncTask_SyncPeerInfoBroadcast); ok {
+ return x.SyncPeerInfoBroadcast
+ }
+ return nil
+}
+
+type isSyncTask_Payload interface {
+ isSyncTask_Payload()
+}
+
+type SyncTask_SyncHeaderRequest struct {
+ //header
+ SyncHeaderRequest *SyncHeaderRequest `protobuf:"bytes,4,opt,name=syncHeaderRequest,proto3,oneof"`
+}
+
+type SyncTask_SyncHeaderResponse struct {
+ SyncHeaderResponse *SyncHeaderResponse `protobuf:"bytes,5,opt,name=syncHeaderResponse,proto3,oneof"`
+}
+
+type SyncTask_SyncBlockRequest struct {
+ //body
+ SyncBlockRequest *SyncBlockRequest `protobuf:"bytes,6,opt,name=syncBlockRequest,proto3,oneof"`
+}
+
+type SyncTask_SyncBlockResponse struct {
+ SyncBlockResponse *SyncBlockResponse `protobuf:"bytes,7,opt,name=syncBlockResponse,proto3,oneof"`
+}
+
+type SyncTask_SyncTransactionRequest struct {
+ //Transaction
+ SyncTransactionRequest *SyncTransactionRequest `protobuf:"bytes,8,opt,name=syncTransactionRequest,proto3,oneof"`
+}
+
+type SyncTask_SyncTransactionResponse struct {
+ SyncTransactionResponse *SyncTransactionResponse `protobuf:"bytes,9,opt,name=syncTransactionResponse,proto3,oneof"`
+}
+
+type SyncTask_SyncPeerInfoBroadcast struct {
+ //
+ SyncPeerInfoBroadcast *SyncPeerInfoBroadcast `protobuf:"bytes,10,opt,name=syncPeerInfoBroadcast,proto3,oneof"`
+}
+
+func (*SyncTask_SyncHeaderRequest) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncHeaderResponse) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncBlockRequest) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncBlockResponse) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncTransactionRequest) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncTransactionResponse) isSyncTask_Payload() {}
+
+func (*SyncTask_SyncPeerInfoBroadcast) isSyncTask_Payload() {}
+
+var File_sync_proto protoreflect.FileDescriptor
+
+var file_sync_proto_rawDesc = []byte{
+ 0x0a, 0x0a, 0x73, 0x79, 0x6e, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x73, 0x79,
+ 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0e,
+ 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x33,
+ 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68,
+ 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69,
+ 0x67, 0x68, 0x74, 0x22, 0x3a, 0x0a, 0x10, 0x53, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65,
+ 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22,
+ 0x3c, 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x63, 0x0a,
+ 0x11, 0x53, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x26, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32,
+ 0x35, 0x36, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x6d,
+ 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75,
+ 0x6e, 0x74, 0x22, 0x40, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x68, 0x65, 0x61,
+ 0x64, 0x65, 0x72, 0x73, 0x22, 0x2e, 0x0a, 0x16, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14,
+ 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62,
+ 0x6c, 0x6f, 0x6f, 0x6d, 0x22, 0x54, 0x0a, 0x17, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+ 0x39, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x74, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6f, 0x0a, 0x15, 0x53, 0x79,
+ 0x6e, 0x63, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
+ 0x61, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0a, 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75,
+ 0x6c, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48,
+ 0x32, 0x35, 0x36, 0x52, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0xbd, 0x05, 0x0a, 0x08,
+ 0x53, 0x79, 0x6e, 0x63, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x79, 0x6e, 0x63,
+ 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x73, 0x79, 0x6e,
+ 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x79, 0x70, 0x65,
+ 0x52, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x79,
+ 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x73, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x50, 0x0a, 0x12, 0x73, 0x79, 0x6e,
+ 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x12, 0x73, 0x79, 0x6e, 0x63, 0x48, 0x65, 0x61,
+ 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x10, 0x73,
+ 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18,
+ 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x10, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x79, 0x6e, 0x63, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+ 0x53, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x48, 0x00, 0x52, 0x11, 0x73, 0x79, 0x6e, 0x63, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x16, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x16, 0x73, 0x79,
+ 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x12, 0x5f, 0x0a, 0x17, 0x73, 0x79, 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e,
+ 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18,
+ 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x17, 0x73, 0x79,
+ 0x6e, 0x63, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x50, 0x65, 0x65,
+ 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x18, 0x0a,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x72,
+ 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x48, 0x00, 0x52, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x50,
+ 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74,
+ 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2a, 0xb7, 0x01, 0x0a, 0x08,
+ 0x53, 0x79, 0x6e, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x49, 0x4e, 0x44,
+ 0x52, 0x65, 0x71, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x73,
+ 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x10,
+ 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x10, 0x03,
+ 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x71, 0x10, 0x04, 0x12, 0x0b, 0x0a,
+ 0x07, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x65, 0x73, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74,
+ 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x74, 0x61, 0x74,
+ 0x65, 0x52, 0x65, 0x73, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x10, 0x08, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x72,
+ 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x10, 0x09, 0x12, 0x15,
+ 0x0a, 0x11, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63,
+ 0x61, 0x73, 0x74, 0x10, 0x0a, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61,
+ 0x6d, 0x63, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f,
+ 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x33,
+}
+
+var (
+ file_sync_proto_rawDescOnce sync.Once
+ file_sync_proto_rawDescData = file_sync_proto_rawDesc
+)
+
+func file_sync_proto_rawDescGZIP() []byte {
+ file_sync_proto_rawDescOnce.Do(func() {
+ file_sync_proto_rawDescData = protoimpl.X.CompressGZIP(file_sync_proto_rawDescData)
+ })
+ return file_sync_proto_rawDescData
+}
+
+var file_sync_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_sync_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
+var file_sync_proto_goTypes = []interface{}{
+ (SyncType)(0), // 0: sync_proto.SyncType
+ (*SyncProtocol)(nil), // 1: sync_proto.SyncProtocol
+ (*Value)(nil), // 2: sync_proto.Value
+ (*SyncBlockRequest)(nil), // 3: sync_proto.SyncBlockRequest
+ (*SyncBlockResponse)(nil), // 4: sync_proto.SyncBlockResponse
+ (*SyncHeaderRequest)(nil), // 5: sync_proto.SyncHeaderRequest
+ (*SyncHeaderResponse)(nil), // 6: sync_proto.SyncHeaderResponse
+ (*SyncTransactionRequest)(nil), // 7: sync_proto.SyncTransactionRequest
+ (*SyncTransactionResponse)(nil), // 8: sync_proto.SyncTransactionResponse
+ (*SyncPeerInfoBroadcast)(nil), // 9: sync_proto.SyncPeerInfoBroadcast
+ (*SyncTask)(nil), // 10: sync_proto.SyncTask
+ (*types_pb.H256)(nil), // 11: types_pb.H256
+ (*types_pb.Block)(nil), // 12: types_pb.Block
+ (*types_pb.Header)(nil), // 13: types_pb.Header
+ (*types_pb.Transaction)(nil), // 14: types_pb.Transaction
+}
+var file_sync_proto_depIdxs = []int32{
+ 11, // 0: sync_proto.SyncBlockRequest.number:type_name -> types_pb.H256
+ 12, // 1: sync_proto.SyncBlockResponse.blocks:type_name -> types_pb.Block
+ 11, // 2: sync_proto.SyncHeaderRequest.number:type_name -> types_pb.H256
+ 11, // 3: sync_proto.SyncHeaderRequest.amount:type_name -> types_pb.H256
+ 13, // 4: sync_proto.SyncHeaderResponse.headers:type_name -> types_pb.Header
+ 14, // 5: sync_proto.SyncTransactionResponse.transactions:type_name -> types_pb.Transaction
+ 11, // 6: sync_proto.SyncPeerInfoBroadcast.Difficulty:type_name -> types_pb.H256
+ 11, // 7: sync_proto.SyncPeerInfoBroadcast.Number:type_name -> types_pb.H256
+ 0, // 8: sync_proto.SyncTask.syncType:type_name -> sync_proto.SyncType
+ 5, // 9: sync_proto.SyncTask.syncHeaderRequest:type_name -> sync_proto.SyncHeaderRequest
+ 6, // 10: sync_proto.SyncTask.syncHeaderResponse:type_name -> sync_proto.SyncHeaderResponse
+ 3, // 11: sync_proto.SyncTask.syncBlockRequest:type_name -> sync_proto.SyncBlockRequest
+ 4, // 12: sync_proto.SyncTask.syncBlockResponse:type_name -> sync_proto.SyncBlockResponse
+ 7, // 13: sync_proto.SyncTask.syncTransactionRequest:type_name -> sync_proto.SyncTransactionRequest
+ 8, // 14: sync_proto.SyncTask.syncTransactionResponse:type_name -> sync_proto.SyncTransactionResponse
+ 9, // 15: sync_proto.SyncTask.syncPeerInfoBroadcast:type_name -> sync_proto.SyncPeerInfoBroadcast
+ 16, // [16:16] is the sub-list for method output_type
+ 16, // [16:16] is the sub-list for method input_type
+ 16, // [16:16] is the sub-list for extension type_name
+ 16, // [16:16] is the sub-list for extension extendee
+ 0, // [0:16] is the sub-list for field type_name
+}
+
+func init() { file_sync_proto_init() }
+func file_sync_proto_init() {
+ if File_sync_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_sync_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncProtocol); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Value); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncBlockRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncBlockResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncHeaderRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncHeaderResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncTransactionRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncTransactionResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncPeerInfoBroadcast); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_sync_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*SyncTask); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ file_sync_proto_msgTypes[9].OneofWrappers = []interface{}{
+ (*SyncTask_SyncHeaderRequest)(nil),
+ (*SyncTask_SyncHeaderResponse)(nil),
+ (*SyncTask_SyncBlockRequest)(nil),
+ (*SyncTask_SyncBlockResponse)(nil),
+ (*SyncTask_SyncTransactionRequest)(nil),
+ (*SyncTask_SyncTransactionResponse)(nil),
+ (*SyncTask_SyncPeerInfoBroadcast)(nil),
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_sync_proto_rawDesc,
+ NumEnums: 1,
+ NumMessages: 10,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_sync_proto_goTypes,
+ DependencyIndexes: file_sync_proto_depIdxs,
+ EnumInfos: file_sync_proto_enumTypes,
+ MessageInfos: file_sync_proto_msgTypes,
+ }.Build()
+ File_sync_proto = out.File
+ file_sync_proto_rawDesc = nil
+ file_sync_proto_goTypes = nil
+ file_sync_proto_depIdxs = nil
+}
diff --git a/api/protocol/sync_proto/sync.proto b/api/protocol/sync_proto/sync.proto
new file mode 100644
index 0000000..5ad4f24
--- /dev/null
+++ b/api/protocol/sync_proto/sync.proto
@@ -0,0 +1,96 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package sync_proto;
+option go_package = "github.com/astranetworld/ast/api/protocol/sync_proto";
+
+import "types_pb/types.proto";
+
+message SyncProtocol {
+
+}
+
+
+enum SyncType {
+ FINDReq = 0;
+ FindRes = 1;
+ HeaderReq = 2;
+ HeaderRes = 3;
+ BodyReq = 4;
+ BodyRes = 5;
+ StateReq = 6;
+ StateRes = 7;
+ TransactionReq = 8;
+ TransactionRes = 9;
+ PeerInfoBroadcast = 10;
+}
+
+message Value {
+ string hash = 1;
+ uint64 height = 2;
+}
+
+message SyncBlockRequest {
+ repeated types_pb.H256 number = 1;
+}
+
+message SyncBlockResponse {
+ repeated types_pb.Block blocks = 1;
+}
+
+message SyncHeaderRequest {
+ types_pb.H256 number = 1;
+ types_pb.H256 amount = 3;
+}
+
+message SyncHeaderResponse {
+ repeated types_pb.Header headers = 1;
+}
+
+message SyncTransactionRequest {
+ bytes bloom = 1;
+}
+
+message SyncTransactionResponse {
+ repeated types_pb.Transaction transactions = 1;
+}
+
+message SyncPeerInfoBroadcast {
+ types_pb.H256 Difficulty = 1;
+ types_pb.H256 Number = 2;
+}
+
+
+message SyncTask {
+ uint64 id = 1; // task id
+ bool ok = 2;
+ SyncType syncType = 3;
+ oneof payload {
+ //header
+ SyncHeaderRequest syncHeaderRequest = 4;
+ SyncHeaderResponse syncHeaderResponse = 5;
+ //body
+ SyncBlockRequest syncBlockRequest = 6;
+ SyncBlockResponse syncBlockResponse = 7;
+ //Transaction
+ SyncTransactionRequest syncTransactionRequest = 8;
+ SyncTransactionResponse syncTransactionResponse = 9;
+ //
+ SyncPeerInfoBroadcast syncPeerInfoBroadcast = 10;
+ }
+}
+
diff --git a/api/protocol/types_pb/gen.go b/api/protocol/types_pb/gen.go
new file mode 100644
index 0000000..80d7607
--- /dev/null
+++ b/api/protocol/types_pb/gen.go
@@ -0,0 +1,21 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types_pb
+
+////go:generate protoc --plugin=/Users/mac/go/bin/protoc-gen-go-cast -I=../ -I=. -I=../include --go-cast_out=plugins=protoc-gen-go-cast,paths=source_relative:. types.proto
+//go:generate protoc -I=../ -I=. -I=../include --go-cast_out=paths=source_relative:. types.proto
+//go:generate sszgen -path=. -objs=H128,H160,H256,H384,H768,H512,H1024,H2048,Header,Body,Block,Transaction -output=generated.ssz.go
diff --git a/api/protocol/types_pb/generated.ssz.go b/api/protocol/types_pb/generated.ssz.go
new file mode 100644
index 0000000..02194fb
--- /dev/null
+++ b/api/protocol/types_pb/generated.ssz.go
@@ -0,0 +1,1999 @@
+// Code generated by fastssz. DO NOT EDIT.
+// Hash: ff4c0802b1b743077781dae4de752322633988f240bb8e40a9f01dbceed1d869
+package types_pb
+
+import (
+ ssz "github.com/prysmaticlabs/fastssz"
+)
+
+// MarshalSSZ ssz marshals the H128 object
+func (h *H128) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H128 object to a target array
+func (h *H128) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ dst = ssz.MarshalUint64(dst, h.Hi)
+
+ // Field (1) 'Lo'
+ dst = ssz.MarshalUint64(dst, h.Lo)
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H128 object
+func (h *H128) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 16 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ h.Hi = ssz.UnmarshallUint64(buf[0:8])
+
+ // Field (1) 'Lo'
+ h.Lo = ssz.UnmarshallUint64(buf[8:16])
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H128 object
+func (h *H128) SizeSSZ() (size int) {
+ size = 16
+ return
+}
+
+// HashTreeRoot ssz hashes the H128 object
+func (h *H128) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H128 object with a hasher
+func (h *H128) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ hh.PutUint64(h.Hi)
+
+ // Field (1) 'Lo'
+ hh.PutUint64(h.Lo)
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H160 object
+func (h *H160) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H160 object to a target array
+func (h *H160) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H128)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ dst = ssz.MarshalUint32(dst, h.Lo)
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H160 object
+func (h *H160) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 20 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H128)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:16]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ h.Lo = ssz.UnmarshallUint32(buf[16:20])
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H160 object
+func (h *H160) SizeSSZ() (size int) {
+ size = 20
+ return
+}
+
+// HashTreeRoot ssz hashes the H160 object
+func (h *H160) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H160 object with a hasher
+func (h *H160) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ hh.PutUint32(h.Lo)
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H256 object
+func (h *H256) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H256 object to a target array
+func (h *H256) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H128)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H128)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H256 object
+func (h *H256) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 32 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H128)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:16]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H128)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[16:32]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H256 object
+func (h *H256) SizeSSZ() (size int) {
+ size = 32
+ return
+}
+
+// HashTreeRoot ssz hashes the H256 object
+func (h *H256) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H256 object with a hasher
+func (h *H256) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H384 object
+func (h *H384) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H384 object to a target array
+func (h *H384) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H256)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H128)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H384 object
+func (h *H384) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 48 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H256)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:32]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H128)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[32:48]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H384 object
+func (h *H384) SizeSSZ() (size int) {
+ size = 48
+ return
+}
+
+// HashTreeRoot ssz hashes the H384 object
+func (h *H384) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H384 object with a hasher
+func (h *H384) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H768 object
+func (h *H768) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H768 object to a target array
+func (h *H768) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H384)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H384)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H768 object
+func (h *H768) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 96 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H384)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:48]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H384)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[48:96]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H768 object
+func (h *H768) SizeSSZ() (size int) {
+ size = 96
+ return
+}
+
+// HashTreeRoot ssz hashes the H768 object
+func (h *H768) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H768 object with a hasher
+func (h *H768) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H512 object
+func (h *H512) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H512 object to a target array
+func (h *H512) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H256)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H256)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H512 object
+func (h *H512) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 64 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H256)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:32]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H256)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[32:64]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H512 object
+func (h *H512) SizeSSZ() (size int) {
+ size = 64
+ return
+}
+
+// HashTreeRoot ssz hashes the H512 object
+func (h *H512) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H512 object with a hasher
+func (h *H512) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H1024 object
+func (h *H1024) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H1024 object to a target array
+func (h *H1024) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H512)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H512)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H1024 object
+func (h *H1024) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 128 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H512)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:64]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H512)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[64:128]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H1024 object
+func (h *H1024) SizeSSZ() (size int) {
+ size = 128
+ return
+}
+
+// HashTreeRoot ssz hashes the H1024 object
+func (h *H1024) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H1024 object with a hasher
+func (h *H1024) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the H2048 object
+func (h *H2048) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the H2048 object to a target array
+func (h *H2048) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H1024)
+ }
+ if dst, err = h.Hi.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H1024)
+ }
+ if dst, err = h.Lo.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the H2048 object
+func (h *H2048) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 256 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Hi'
+ if h.Hi == nil {
+ h.Hi = new(H1024)
+ }
+ if err = h.Hi.UnmarshalSSZ(buf[0:128]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Lo'
+ if h.Lo == nil {
+ h.Lo = new(H1024)
+ }
+ if err = h.Lo.UnmarshalSSZ(buf[128:256]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the H2048 object
+func (h *H2048) SizeSSZ() (size int) {
+ size = 256
+ return
+}
+
+// HashTreeRoot ssz hashes the H2048 object
+func (h *H2048) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the H2048 object with a hasher
+func (h *H2048) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Hi'
+ if err = h.Hi.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Lo'
+ if err = h.Lo.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Block object
+func (b *Block) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(b)
+}
+
+// MarshalSSZTo ssz marshals the Block object to a target array
+func (b *Block) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(8)
+
+ // Offset (0) 'Header'
+ dst = ssz.WriteOffset(dst, offset)
+ if b.Header == nil {
+ b.Header = new(Header)
+ }
+ offset += b.Header.SizeSSZ()
+
+ // Offset (1) 'Body'
+ dst = ssz.WriteOffset(dst, offset)
+ if b.Body == nil {
+ b.Body = new(Body)
+ }
+ offset += b.Body.SizeSSZ()
+
+ // Field (0) 'Header'
+ if dst, err = b.Header.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Body'
+ if dst, err = b.Body.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Block object
+func (b *Block) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 8 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0, o1 uint64
+
+ // Offset (0) 'Header'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 8 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Offset (1) 'Body'
+ if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
+ return ssz.ErrOffset
+ }
+
+ // Field (0) 'Header'
+ {
+ buf = tail[o0:o1]
+ if b.Header == nil {
+ b.Header = new(Header)
+ }
+ if err = b.Header.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+
+ // Field (1) 'Body'
+ {
+ buf = tail[o1:]
+ if b.Body == nil {
+ b.Body = new(Body)
+ }
+ if err = b.Body.UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Block object
+func (b *Block) SizeSSZ() (size int) {
+ size = 8
+
+ // Field (0) 'Header'
+ if b.Header == nil {
+ b.Header = new(Header)
+ }
+ size += b.Header.SizeSSZ()
+
+ // Field (1) 'Body'
+ if b.Body == nil {
+ b.Body = new(Body)
+ }
+ size += b.Body.SizeSSZ()
+
+ return
+}
+
+// HashTreeRoot ssz hashes the Block object
+func (b *Block) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(b)
+}
+
+// HashTreeRootWith ssz hashes the Block object with a hasher
+func (b *Block) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Header'
+ if err = b.Header.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Body'
+ if err = b.Body.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Header object
+func (h *Header) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(h)
+}
+
+// MarshalSSZTo ssz marshals the Header object to a target array
+func (h *Header) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(664)
+
+ // Field (0) 'ParentHash'
+ if h.ParentHash == nil {
+ h.ParentHash = new(H256)
+ }
+ if dst, err = h.ParentHash.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Coinbase'
+ if h.Coinbase == nil {
+ h.Coinbase = new(H160)
+ }
+ if dst, err = h.Coinbase.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (2) 'Root'
+ if h.Root == nil {
+ h.Root = new(H256)
+ }
+ if dst, err = h.Root.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (3) 'TxHash'
+ if h.TxHash == nil {
+ h.TxHash = new(H256)
+ }
+ if dst, err = h.TxHash.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (4) 'ReceiptHash'
+ if h.ReceiptHash == nil {
+ h.ReceiptHash = new(H256)
+ }
+ if dst, err = h.ReceiptHash.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (5) 'Difficulty'
+ if h.Difficulty == nil {
+ h.Difficulty = new(H256)
+ }
+ if dst, err = h.Difficulty.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (6) 'Number'
+ if h.Number == nil {
+ h.Number = new(H256)
+ }
+ if dst, err = h.Number.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (7) 'GasLimit'
+ dst = ssz.MarshalUint64(dst, h.GasLimit)
+
+ // Field (8) 'GasUsed'
+ dst = ssz.MarshalUint64(dst, h.GasUsed)
+
+ // Field (9) 'Time'
+ dst = ssz.MarshalUint64(dst, h.Time)
+
+ // Field (10) 'Nonce'
+ dst = ssz.MarshalUint64(dst, h.Nonce)
+
+ // Field (11) 'BaseFee'
+ if h.BaseFee == nil {
+ h.BaseFee = new(H256)
+ }
+ if dst, err = h.BaseFee.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Offset (12) 'Extra'
+ dst = ssz.WriteOffset(dst, offset)
+ offset += len(h.Extra)
+
+ // Field (13) 'Signature'
+ if h.Signature == nil {
+ h.Signature = new(H768)
+ }
+ if dst, err = h.Signature.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (14) 'Bloom'
+ if h.Bloom == nil {
+ h.Bloom = new(H2048)
+ }
+ if dst, err = h.Bloom.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (15) 'MixDigest'
+ if h.MixDigest == nil {
+ h.MixDigest = new(H256)
+ }
+ if dst, err = h.MixDigest.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (12) 'Extra'
+ if size := len(h.Extra); size > 117 {
+ err = ssz.ErrBytesLengthFn("--.Extra", size, 117)
+ return
+ }
+ dst = append(dst, h.Extra...)
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Header object
+func (h *Header) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 664 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o12 uint64
+
+ // Field (0) 'ParentHash'
+ if h.ParentHash == nil {
+ h.ParentHash = new(H256)
+ }
+ if err = h.ParentHash.UnmarshalSSZ(buf[0:32]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Coinbase'
+ if h.Coinbase == nil {
+ h.Coinbase = new(H160)
+ }
+ if err = h.Coinbase.UnmarshalSSZ(buf[32:52]); err != nil {
+ return err
+ }
+
+ // Field (2) 'Root'
+ if h.Root == nil {
+ h.Root = new(H256)
+ }
+ if err = h.Root.UnmarshalSSZ(buf[52:84]); err != nil {
+ return err
+ }
+
+ // Field (3) 'TxHash'
+ if h.TxHash == nil {
+ h.TxHash = new(H256)
+ }
+ if err = h.TxHash.UnmarshalSSZ(buf[84:116]); err != nil {
+ return err
+ }
+
+ // Field (4) 'ReceiptHash'
+ if h.ReceiptHash == nil {
+ h.ReceiptHash = new(H256)
+ }
+ if err = h.ReceiptHash.UnmarshalSSZ(buf[116:148]); err != nil {
+ return err
+ }
+
+ // Field (5) 'Difficulty'
+ if h.Difficulty == nil {
+ h.Difficulty = new(H256)
+ }
+ if err = h.Difficulty.UnmarshalSSZ(buf[148:180]); err != nil {
+ return err
+ }
+
+ // Field (6) 'Number'
+ if h.Number == nil {
+ h.Number = new(H256)
+ }
+ if err = h.Number.UnmarshalSSZ(buf[180:212]); err != nil {
+ return err
+ }
+
+ // Field (7) 'GasLimit'
+ h.GasLimit = ssz.UnmarshallUint64(buf[212:220])
+
+ // Field (8) 'GasUsed'
+ h.GasUsed = ssz.UnmarshallUint64(buf[220:228])
+
+ // Field (9) 'Time'
+ h.Time = ssz.UnmarshallUint64(buf[228:236])
+
+ // Field (10) 'Nonce'
+ h.Nonce = ssz.UnmarshallUint64(buf[236:244])
+
+ // Field (11) 'BaseFee'
+ if h.BaseFee == nil {
+ h.BaseFee = new(H256)
+ }
+ if err = h.BaseFee.UnmarshalSSZ(buf[244:276]); err != nil {
+ return err
+ }
+
+ // Offset (12) 'Extra'
+ if o12 = ssz.ReadOffset(buf[276:280]); o12 > size {
+ return ssz.ErrOffset
+ }
+
+ if o12 < 664 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Field (13) 'Signature'
+ if h.Signature == nil {
+ h.Signature = new(H768)
+ }
+ if err = h.Signature.UnmarshalSSZ(buf[280:376]); err != nil {
+ return err
+ }
+
+ // Field (14) 'Bloom'
+ if h.Bloom == nil {
+ h.Bloom = new(H2048)
+ }
+ if err = h.Bloom.UnmarshalSSZ(buf[376:632]); err != nil {
+ return err
+ }
+
+ // Field (15) 'MixDigest'
+ if h.MixDigest == nil {
+ h.MixDigest = new(H256)
+ }
+ if err = h.MixDigest.UnmarshalSSZ(buf[632:664]); err != nil {
+ return err
+ }
+
+ // Field (12) 'Extra'
+ {
+ buf = tail[o12:]
+ if len(buf) > 117 {
+ return ssz.ErrBytesLength
+ }
+ if cap(h.Extra) == 0 {
+ h.Extra = make([]byte, 0, len(buf))
+ }
+ h.Extra = append(h.Extra, buf...)
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Header object
+func (h *Header) SizeSSZ() (size int) {
+ size = 664
+
+ // Field (12) 'Extra'
+ size += len(h.Extra)
+
+ return
+}
+
+// HashTreeRoot ssz hashes the Header object
+func (h *Header) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(h)
+}
+
+// HashTreeRootWith ssz hashes the Header object with a hasher
+func (h *Header) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'ParentHash'
+ if err = h.ParentHash.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Coinbase'
+ if err = h.Coinbase.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (2) 'Root'
+ if err = h.Root.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (3) 'TxHash'
+ if err = h.TxHash.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (4) 'ReceiptHash'
+ if err = h.ReceiptHash.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (5) 'Difficulty'
+ if err = h.Difficulty.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (6) 'Number'
+ if err = h.Number.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (7) 'GasLimit'
+ hh.PutUint64(h.GasLimit)
+
+ // Field (8) 'GasUsed'
+ hh.PutUint64(h.GasUsed)
+
+ // Field (9) 'Time'
+ hh.PutUint64(h.Time)
+
+ // Field (10) 'Nonce'
+ hh.PutUint64(h.Nonce)
+
+ // Field (11) 'BaseFee'
+ if err = h.BaseFee.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (12) 'Extra'
+ {
+ elemIndx := hh.Index()
+ byteLen := uint64(len(h.Extra))
+ if byteLen > 117 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ hh.PutBytes(h.Extra)
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(elemIndx, byteLen, (117+31)/32)
+ } else {
+ hh.MerkleizeWithMixin(elemIndx, byteLen, (117+31)/32)
+ }
+ }
+
+ // Field (13) 'Signature'
+ if err = h.Signature.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (14) 'Bloom'
+ if err = h.Bloom.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (15) 'MixDigest'
+ if err = h.MixDigest.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Verifier object
+func (v *Verifier) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(v)
+}
+
+// MarshalSSZTo ssz marshals the Verifier object to a target array
+func (v *Verifier) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'PublicKey'
+ if v.PublicKey == nil {
+ v.PublicKey = new(H384)
+ }
+ if dst, err = v.PublicKey.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Address'
+ if v.Address == nil {
+ v.Address = new(H160)
+ }
+ if dst, err = v.Address.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Verifier object
+func (v *Verifier) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 68 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'PublicKey'
+ if v.PublicKey == nil {
+ v.PublicKey = new(H384)
+ }
+ if err = v.PublicKey.UnmarshalSSZ(buf[0:48]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Address'
+ if v.Address == nil {
+ v.Address = new(H160)
+ }
+ if err = v.Address.UnmarshalSSZ(buf[48:68]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Verifier object
+func (v *Verifier) SizeSSZ() (size int) {
+ size = 68
+ return
+}
+
+// HashTreeRoot ssz hashes the Verifier object
+func (v *Verifier) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(v)
+}
+
+// HashTreeRootWith ssz hashes the Verifier object with a hasher
+func (v *Verifier) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'PublicKey'
+ if err = v.PublicKey.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Address'
+ if err = v.Address.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Reward object
+func (r *Reward) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(r)
+}
+
+// MarshalSSZTo ssz marshals the Reward object to a target array
+func (r *Reward) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+
+ // Field (0) 'Amount'
+ if r.Amount == nil {
+ r.Amount = new(H256)
+ }
+ if dst, err = r.Amount.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (1) 'Address'
+ if r.Address == nil {
+ r.Address = new(H160)
+ }
+ if dst, err = r.Address.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Reward object
+func (r *Reward) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size != 52 {
+ return ssz.ErrSize
+ }
+
+ // Field (0) 'Amount'
+ if r.Amount == nil {
+ r.Amount = new(H256)
+ }
+ if err = r.Amount.UnmarshalSSZ(buf[0:32]); err != nil {
+ return err
+ }
+
+ // Field (1) 'Address'
+ if r.Address == nil {
+ r.Address = new(H160)
+ }
+ if err = r.Address.UnmarshalSSZ(buf[32:52]); err != nil {
+ return err
+ }
+
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Reward object
+func (r *Reward) SizeSSZ() (size int) {
+ size = 52
+ return
+}
+
+// HashTreeRoot ssz hashes the Reward object
+func (r *Reward) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(r)
+}
+
+// HashTreeRootWith ssz hashes the Reward object with a hasher
+func (r *Reward) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Amount'
+ if err = r.Amount.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (1) 'Address'
+ if err = r.Address.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Body object
+func (b *Body) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(b)
+}
+
+// MarshalSSZTo ssz marshals the Body object to a target array
+func (b *Body) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(12)
+
+ // Offset (0) 'Txs'
+ dst = ssz.WriteOffset(dst, offset)
+ for ii := 0; ii < len(b.Txs); ii++ {
+ offset += 4
+ offset += b.Txs[ii].SizeSSZ()
+ }
+
+ // Offset (1) 'Verifiers'
+ dst = ssz.WriteOffset(dst, offset)
+ offset += len(b.Verifiers) * 68
+
+ // Offset (2) 'Rewards'
+ dst = ssz.WriteOffset(dst, offset)
+ offset += len(b.Rewards) * 52
+
+ // Field (0) 'Txs'
+ if size := len(b.Txs); size > 104857600 {
+ err = ssz.ErrListTooBigFn("--.Txs", size, 104857600)
+ return
+ }
+ {
+ offset = 4 * len(b.Txs)
+ for ii := 0; ii < len(b.Txs); ii++ {
+ dst = ssz.WriteOffset(dst, offset)
+ offset += b.Txs[ii].SizeSSZ()
+ }
+ }
+ for ii := 0; ii < len(b.Txs); ii++ {
+ if dst, err = b.Txs[ii].MarshalSSZTo(dst); err != nil {
+ return
+ }
+ }
+
+ // Field (1) 'Verifiers'
+ if size := len(b.Verifiers); size > 104857600 {
+ err = ssz.ErrListTooBigFn("--.Verifiers", size, 104857600)
+ return
+ }
+ for ii := 0; ii < len(b.Verifiers); ii++ {
+ if dst, err = b.Verifiers[ii].MarshalSSZTo(dst); err != nil {
+ return
+ }
+ }
+
+ // Field (2) 'Rewards'
+ if size := len(b.Rewards); size > 104857600 {
+ err = ssz.ErrListTooBigFn("--.Rewards", size, 104857600)
+ return
+ }
+ for ii := 0; ii < len(b.Rewards); ii++ {
+ if dst, err = b.Rewards[ii].MarshalSSZTo(dst); err != nil {
+ return
+ }
+ }
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Body object
+func (b *Body) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 12 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o0, o1, o2 uint64
+
+ // Offset (0) 'Txs'
+ if o0 = ssz.ReadOffset(buf[0:4]); o0 > size {
+ return ssz.ErrOffset
+ }
+
+ if o0 < 12 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Offset (1) 'Verifiers'
+ if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 {
+ return ssz.ErrOffset
+ }
+
+ // Offset (2) 'Rewards'
+ if o2 = ssz.ReadOffset(buf[8:12]); o2 > size || o1 > o2 {
+ return ssz.ErrOffset
+ }
+
+ // Field (0) 'Txs'
+ {
+ buf = tail[o0:o1]
+ num, err := ssz.DecodeDynamicLength(buf, 104857600)
+ if err != nil {
+ return err
+ }
+ b.Txs = make([]*Transaction, num)
+ err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) {
+ if b.Txs[indx] == nil {
+ b.Txs[indx] = new(Transaction)
+ }
+ if err = b.Txs[indx].UnmarshalSSZ(buf); err != nil {
+ return err
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
+
+ // Field (1) 'Verifiers'
+ {
+ buf = tail[o1:o2]
+ num, err := ssz.DivideInt2(len(buf), 68, 104857600)
+ if err != nil {
+ return err
+ }
+ b.Verifiers = make([]*Verifier, num)
+ for ii := 0; ii < num; ii++ {
+ if b.Verifiers[ii] == nil {
+ b.Verifiers[ii] = new(Verifier)
+ }
+ if err = b.Verifiers[ii].UnmarshalSSZ(buf[ii*68 : (ii+1)*68]); err != nil {
+ return err
+ }
+ }
+ }
+
+ // Field (2) 'Rewards'
+ {
+ buf = tail[o2:]
+ num, err := ssz.DivideInt2(len(buf), 52, 104857600)
+ if err != nil {
+ return err
+ }
+ b.Rewards = make([]*Reward, num)
+ for ii := 0; ii < num; ii++ {
+ if b.Rewards[ii] == nil {
+ b.Rewards[ii] = new(Reward)
+ }
+ if err = b.Rewards[ii].UnmarshalSSZ(buf[ii*52 : (ii+1)*52]); err != nil {
+ return err
+ }
+ }
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Body object
+func (b *Body) SizeSSZ() (size int) {
+ size = 12
+
+ // Field (0) 'Txs'
+ for ii := 0; ii < len(b.Txs); ii++ {
+ size += 4
+ size += b.Txs[ii].SizeSSZ()
+ }
+
+ // Field (1) 'Verifiers'
+ size += len(b.Verifiers) * 68
+
+ // Field (2) 'Rewards'
+ size += len(b.Rewards) * 52
+
+ return
+}
+
+// HashTreeRoot ssz hashes the Body object
+func (b *Body) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(b)
+}
+
+// HashTreeRootWith ssz hashes the Body object with a hasher
+func (b *Body) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Txs'
+ {
+ subIndx := hh.Index()
+ num := uint64(len(b.Txs))
+ if num > 104857600 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ for _, elem := range b.Txs {
+ if err = elem.HashTreeRootWith(hh); err != nil {
+ return
+ }
+ }
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(subIndx, num, 104857600)
+ } else {
+ hh.MerkleizeWithMixin(subIndx, num, 104857600)
+ }
+ }
+
+ // Field (1) 'Verifiers'
+ {
+ subIndx := hh.Index()
+ num := uint64(len(b.Verifiers))
+ if num > 104857600 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ for _, elem := range b.Verifiers {
+ if err = elem.HashTreeRootWith(hh); err != nil {
+ return
+ }
+ }
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(subIndx, num, 104857600)
+ } else {
+ hh.MerkleizeWithMixin(subIndx, num, 104857600)
+ }
+ }
+
+ // Field (2) 'Rewards'
+ {
+ subIndx := hh.Index()
+ num := uint64(len(b.Rewards))
+ if num > 104857600 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ for _, elem := range b.Rewards {
+ if err = elem.HashTreeRootWith(hh); err != nil {
+ return
+ }
+ }
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(subIndx, num, 104857600)
+ } else {
+ hh.MerkleizeWithMixin(subIndx, num, 104857600)
+ }
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
+
+// MarshalSSZ ssz marshals the Transaction object
+func (t *Transaction) MarshalSSZ() ([]byte, error) {
+ return ssz.MarshalSSZ(t)
+}
+
+// MarshalSSZTo ssz marshals the Transaction object to a target array
+func (t *Transaction) MarshalSSZTo(buf []byte) (dst []byte, err error) {
+ dst = buf
+ offset := int(336)
+
+ // Field (0) 'Type'
+ dst = ssz.MarshalUint64(dst, t.Type)
+
+ // Field (1) 'Nonce'
+ dst = ssz.MarshalUint64(dst, t.Nonce)
+
+ // Field (2) 'GasPrice'
+ if t.GasPrice == nil {
+ t.GasPrice = new(H256)
+ }
+ if dst, err = t.GasPrice.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (3) 'Gas'
+ dst = ssz.MarshalUint64(dst, t.Gas)
+
+ // Field (4) 'FeePerGas'
+ if t.FeePerGas == nil {
+ t.FeePerGas = new(H256)
+ }
+ if dst, err = t.FeePerGas.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (5) 'PriorityFeePerGas'
+ if t.PriorityFeePerGas == nil {
+ t.PriorityFeePerGas = new(H256)
+ }
+ if dst, err = t.PriorityFeePerGas.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (6) 'Value'
+ if t.Value == nil {
+ t.Value = new(H256)
+ }
+ if dst, err = t.Value.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Offset (7) 'Data'
+ dst = ssz.WriteOffset(dst, offset)
+ offset += len(t.Data)
+
+ // Offset (8) 'Sign'
+ dst = ssz.WriteOffset(dst, offset)
+ offset += len(t.Sign)
+
+ // Field (9) 'To'
+ if t.To == nil {
+ t.To = new(H160)
+ }
+ if dst, err = t.To.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (10) 'From'
+ if t.From == nil {
+ t.From = new(H160)
+ }
+ if dst, err = t.From.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (11) 'ChainID'
+ dst = ssz.MarshalUint64(dst, t.ChainID)
+
+ // Field (12) 'Hash'
+ if t.Hash == nil {
+ t.Hash = new(H256)
+ }
+ if dst, err = t.Hash.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (13) 'R'
+ if t.R == nil {
+ t.R = new(H256)
+ }
+ if dst, err = t.R.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (14) 'S'
+ if t.S == nil {
+ t.S = new(H256)
+ }
+ if dst, err = t.S.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (15) 'V'
+ if t.V == nil {
+ t.V = new(H256)
+ }
+ if dst, err = t.V.MarshalSSZTo(dst); err != nil {
+ return
+ }
+
+ // Field (7) 'Data'
+ if size := len(t.Data); size > 104857600 {
+ err = ssz.ErrBytesLengthFn("--.Data", size, 104857600)
+ return
+ }
+ dst = append(dst, t.Data...)
+
+ // Field (8) 'Sign'
+ if size := len(t.Sign); size > 104857600 {
+ err = ssz.ErrBytesLengthFn("--.Sign", size, 104857600)
+ return
+ }
+ dst = append(dst, t.Sign...)
+
+ return
+}
+
+// UnmarshalSSZ ssz unmarshals the Transaction object
+func (t *Transaction) UnmarshalSSZ(buf []byte) error {
+ var err error
+ size := uint64(len(buf))
+ if size < 336 {
+ return ssz.ErrSize
+ }
+
+ tail := buf
+ var o7, o8 uint64
+
+ // Field (0) 'Type'
+ t.Type = ssz.UnmarshallUint64(buf[0:8])
+
+ // Field (1) 'Nonce'
+ t.Nonce = ssz.UnmarshallUint64(buf[8:16])
+
+ // Field (2) 'GasPrice'
+ if t.GasPrice == nil {
+ t.GasPrice = new(H256)
+ }
+ if err = t.GasPrice.UnmarshalSSZ(buf[16:48]); err != nil {
+ return err
+ }
+
+ // Field (3) 'Gas'
+ t.Gas = ssz.UnmarshallUint64(buf[48:56])
+
+ // Field (4) 'FeePerGas'
+ if t.FeePerGas == nil {
+ t.FeePerGas = new(H256)
+ }
+ if err = t.FeePerGas.UnmarshalSSZ(buf[56:88]); err != nil {
+ return err
+ }
+
+ // Field (5) 'PriorityFeePerGas'
+ if t.PriorityFeePerGas == nil {
+ t.PriorityFeePerGas = new(H256)
+ }
+ if err = t.PriorityFeePerGas.UnmarshalSSZ(buf[88:120]); err != nil {
+ return err
+ }
+
+ // Field (6) 'Value'
+ if t.Value == nil {
+ t.Value = new(H256)
+ }
+ if err = t.Value.UnmarshalSSZ(buf[120:152]); err != nil {
+ return err
+ }
+
+ // Offset (7) 'Data'
+ if o7 = ssz.ReadOffset(buf[152:156]); o7 > size {
+ return ssz.ErrOffset
+ }
+
+ if o7 < 336 {
+ return ssz.ErrInvalidVariableOffset
+ }
+
+ // Offset (8) 'Sign'
+ if o8 = ssz.ReadOffset(buf[156:160]); o8 > size || o7 > o8 {
+ return ssz.ErrOffset
+ }
+
+ // Field (9) 'To'
+ if t.To == nil {
+ t.To = new(H160)
+ }
+ if err = t.To.UnmarshalSSZ(buf[160:180]); err != nil {
+ return err
+ }
+
+ // Field (10) 'From'
+ if t.From == nil {
+ t.From = new(H160)
+ }
+ if err = t.From.UnmarshalSSZ(buf[180:200]); err != nil {
+ return err
+ }
+
+ // Field (11) 'ChainID'
+ t.ChainID = ssz.UnmarshallUint64(buf[200:208])
+
+ // Field (12) 'Hash'
+ if t.Hash == nil {
+ t.Hash = new(H256)
+ }
+ if err = t.Hash.UnmarshalSSZ(buf[208:240]); err != nil {
+ return err
+ }
+
+ // Field (13) 'R'
+ if t.R == nil {
+ t.R = new(H256)
+ }
+ if err = t.R.UnmarshalSSZ(buf[240:272]); err != nil {
+ return err
+ }
+
+ // Field (14) 'S'
+ if t.S == nil {
+ t.S = new(H256)
+ }
+ if err = t.S.UnmarshalSSZ(buf[272:304]); err != nil {
+ return err
+ }
+
+ // Field (15) 'V'
+ if t.V == nil {
+ t.V = new(H256)
+ }
+ if err = t.V.UnmarshalSSZ(buf[304:336]); err != nil {
+ return err
+ }
+
+ // Field (7) 'Data'
+ {
+ buf = tail[o7:o8]
+ if len(buf) > 104857600 {
+ return ssz.ErrBytesLength
+ }
+ if cap(t.Data) == 0 {
+ t.Data = make([]byte, 0, len(buf))
+ }
+ t.Data = append(t.Data, buf...)
+ }
+
+ // Field (8) 'Sign'
+ {
+ buf = tail[o8:]
+ if len(buf) > 104857600 {
+ return ssz.ErrBytesLength
+ }
+ if cap(t.Sign) == 0 {
+ t.Sign = make([]byte, 0, len(buf))
+ }
+ t.Sign = append(t.Sign, buf...)
+ }
+ return err
+}
+
+// SizeSSZ returns the ssz encoded size in bytes for the Transaction object
+func (t *Transaction) SizeSSZ() (size int) {
+ size = 336
+
+ // Field (7) 'Data'
+ size += len(t.Data)
+
+ // Field (8) 'Sign'
+ size += len(t.Sign)
+
+ return
+}
+
+// HashTreeRoot ssz hashes the Transaction object
+func (t *Transaction) HashTreeRoot() ([32]byte, error) {
+ return ssz.HashWithDefaultHasher(t)
+}
+
+// HashTreeRootWith ssz hashes the Transaction object with a hasher
+func (t *Transaction) HashTreeRootWith(hh *ssz.Hasher) (err error) {
+ indx := hh.Index()
+
+ // Field (0) 'Type'
+ hh.PutUint64(t.Type)
+
+ // Field (1) 'Nonce'
+ hh.PutUint64(t.Nonce)
+
+ // Field (2) 'GasPrice'
+ if err = t.GasPrice.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (3) 'Gas'
+ hh.PutUint64(t.Gas)
+
+ // Field (4) 'FeePerGas'
+ if err = t.FeePerGas.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (5) 'PriorityFeePerGas'
+ if err = t.PriorityFeePerGas.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (6) 'Value'
+ if err = t.Value.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (7) 'Data'
+ {
+ elemIndx := hh.Index()
+ byteLen := uint64(len(t.Data))
+ if byteLen > 104857600 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ hh.PutBytes(t.Data)
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(elemIndx, byteLen, (104857600+31)/32)
+ } else {
+ hh.MerkleizeWithMixin(elemIndx, byteLen, (104857600+31)/32)
+ }
+ }
+
+ // Field (8) 'Sign'
+ {
+ elemIndx := hh.Index()
+ byteLen := uint64(len(t.Sign))
+ if byteLen > 104857600 {
+ err = ssz.ErrIncorrectListSize
+ return
+ }
+ hh.PutBytes(t.Sign)
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeWithMixinVectorizedHTR(elemIndx, byteLen, (104857600+31)/32)
+ } else {
+ hh.MerkleizeWithMixin(elemIndx, byteLen, (104857600+31)/32)
+ }
+ }
+
+ // Field (9) 'To'
+ if err = t.To.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (10) 'From'
+ if err = t.From.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (11) 'ChainID'
+ hh.PutUint64(t.ChainID)
+
+ // Field (12) 'Hash'
+ if err = t.Hash.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (13) 'R'
+ if err = t.R.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (14) 'S'
+ if err = t.S.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ // Field (15) 'V'
+ if err = t.V.HashTreeRootWith(hh); err != nil {
+ return
+ }
+
+ if ssz.EnableVectorizedHTR {
+ hh.MerkleizeVectorizedHTR(indx)
+ } else {
+ hh.Merkleize(indx)
+ }
+ return
+}
diff --git a/api/protocol/types_pb/types.pb.go b/api/protocol/types_pb/types.pb.go
new file mode 100644
index 0000000..96a6ff4
--- /dev/null
+++ b/api/protocol/types_pb/types.pb.go
@@ -0,0 +1,1924 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.26.0
+// protoc v3.20.0
+// source: types.proto
+
+package types_pb
+
+import (
+ _ "github.com/astranetworld/ast/api/protocol/ext"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type H128 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi uint64 `protobuf:"varint,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo uint64 `protobuf:"varint,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H128) Reset() {
+ *x = H128{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H128) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H128) ProtoMessage() {}
+
+func (x *H128) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H128.ProtoReflect.Descriptor instead.
+func (*H128) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *H128) GetHi() uint64 {
+ if x != nil {
+ return x.Hi
+ }
+ return 0
+}
+
+func (x *H128) GetLo() uint64 {
+ if x != nil {
+ return x.Lo
+ }
+ return 0
+}
+
+type H160 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H128 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo uint32 `protobuf:"varint,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H160) Reset() {
+ *x = H160{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H160) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H160) ProtoMessage() {}
+
+func (x *H160) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H160.ProtoReflect.Descriptor instead.
+func (*H160) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *H160) GetHi() *H128 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H160) GetLo() uint32 {
+ if x != nil {
+ return x.Lo
+ }
+ return 0
+}
+
+type H256 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H128 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H128 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H256) Reset() {
+ *x = H256{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H256) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H256) ProtoMessage() {}
+
+func (x *H256) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H256.ProtoReflect.Descriptor instead.
+func (*H256) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *H256) GetHi() *H128 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H256) GetLo() *H128 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type H384 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H256 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H128 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H384) Reset() {
+ *x = H384{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H384) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H384) ProtoMessage() {}
+
+func (x *H384) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H384.ProtoReflect.Descriptor instead.
+func (*H384) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *H384) GetHi() *H256 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H384) GetLo() *H128 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type H768 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H384 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H384 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H768) Reset() {
+ *x = H768{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H768) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H768) ProtoMessage() {}
+
+func (x *H768) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H768.ProtoReflect.Descriptor instead.
+func (*H768) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *H768) GetHi() *H384 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H768) GetLo() *H384 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type H512 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H256 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H256 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H512) Reset() {
+ *x = H512{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H512) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H512) ProtoMessage() {}
+
+func (x *H512) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H512.ProtoReflect.Descriptor instead.
+func (*H512) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *H512) GetHi() *H256 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H512) GetLo() *H256 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type H1024 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H512 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H512 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H1024) Reset() {
+ *x = H1024{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H1024) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H1024) ProtoMessage() {}
+
+func (x *H1024) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H1024.ProtoReflect.Descriptor instead.
+func (*H1024) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *H1024) GetHi() *H512 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H1024) GetLo() *H512 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type H2048 struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Hi *H1024 `protobuf:"bytes,1,opt,name=hi,proto3" json:"hi,omitempty"`
+ Lo *H1024 `protobuf:"bytes,2,opt,name=lo,proto3" json:"lo,omitempty"`
+}
+
+func (x *H2048) Reset() {
+ *x = H2048{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *H2048) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*H2048) ProtoMessage() {}
+
+func (x *H2048) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use H2048.ProtoReflect.Descriptor instead.
+func (*H2048) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *H2048) GetHi() *H1024 {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+func (x *H2048) GetLo() *H1024 {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+type Block struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Header *Header `protobuf:"bytes,1,opt,name=Header,proto3" json:"Header,omitempty"`
+ Body *Body `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
+}
+
+func (x *Block) Reset() {
+ *x = Block{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Block) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Block) ProtoMessage() {}
+
+func (x *Block) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Block.ProtoReflect.Descriptor instead.
+func (*Block) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *Block) GetHeader() *Header {
+ if x != nil {
+ return x.Header
+ }
+ return nil
+}
+
+func (x *Block) GetBody() *Body {
+ if x != nil {
+ return x.Body
+ }
+ return nil
+}
+
+type Header struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ParentHash *H256 `protobuf:"bytes,1,opt,name=ParentHash,proto3" json:"ParentHash,omitempty"`
+ Coinbase *H160 `protobuf:"bytes,2,opt,name=Coinbase,proto3" json:"Coinbase,omitempty"`
+ Root *H256 `protobuf:"bytes,3,opt,name=Root,proto3" json:"Root,omitempty"`
+ TxHash *H256 `protobuf:"bytes,4,opt,name=TxHash,proto3" json:"TxHash,omitempty"`
+ ReceiptHash *H256 `protobuf:"bytes,5,opt,name=ReceiptHash,proto3" json:"ReceiptHash,omitempty"`
+ Difficulty *H256 `protobuf:"bytes,6,opt,name=Difficulty,proto3" json:"Difficulty,omitempty"`
+ Number *H256 `protobuf:"bytes,7,opt,name=Number,proto3" json:"Number,omitempty"`
+ GasLimit uint64 `protobuf:"varint,8,opt,name=GasLimit,proto3" json:"GasLimit,omitempty"`
+ GasUsed uint64 `protobuf:"varint,9,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"`
+ Time uint64 `protobuf:"varint,10,opt,name=Time,proto3" json:"Time,omitempty"`
+ Nonce uint64 `protobuf:"varint,11,opt,name=Nonce,proto3" json:"Nonce,omitempty"`
+ BaseFee *H256 `protobuf:"bytes,12,opt,name=BaseFee,proto3" json:"BaseFee,omitempty"`
+ // 65+32 byte (clique)
+ Extra []byte `protobuf:"bytes,13,opt,name=Extra,proto3" json:"Extra,omitempty" ssz-max:"117"`
+ Signature *H768 `protobuf:"bytes,14,opt,name=Signature,proto3" json:"Signature,omitempty"`
+ Bloom *H2048 `protobuf:"bytes,15,opt,name=Bloom,proto3" json:"Bloom,omitempty"`
+ MixDigest *H256 `protobuf:"bytes,16,opt,name=MixDigest,proto3" json:"MixDigest,omitempty"`
+}
+
+func (x *Header) Reset() {
+ *x = Header{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Header) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Header) ProtoMessage() {}
+
+func (x *Header) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Header.ProtoReflect.Descriptor instead.
+func (*Header) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *Header) GetParentHash() *H256 {
+ if x != nil {
+ return x.ParentHash
+ }
+ return nil
+}
+
+func (x *Header) GetCoinbase() *H160 {
+ if x != nil {
+ return x.Coinbase
+ }
+ return nil
+}
+
+func (x *Header) GetRoot() *H256 {
+ if x != nil {
+ return x.Root
+ }
+ return nil
+}
+
+func (x *Header) GetTxHash() *H256 {
+ if x != nil {
+ return x.TxHash
+ }
+ return nil
+}
+
+func (x *Header) GetReceiptHash() *H256 {
+ if x != nil {
+ return x.ReceiptHash
+ }
+ return nil
+}
+
+func (x *Header) GetDifficulty() *H256 {
+ if x != nil {
+ return x.Difficulty
+ }
+ return nil
+}
+
+func (x *Header) GetNumber() *H256 {
+ if x != nil {
+ return x.Number
+ }
+ return nil
+}
+
+func (x *Header) GetGasLimit() uint64 {
+ if x != nil {
+ return x.GasLimit
+ }
+ return 0
+}
+
+func (x *Header) GetGasUsed() uint64 {
+ if x != nil {
+ return x.GasUsed
+ }
+ return 0
+}
+
+func (x *Header) GetTime() uint64 {
+ if x != nil {
+ return x.Time
+ }
+ return 0
+}
+
+func (x *Header) GetNonce() uint64 {
+ if x != nil {
+ return x.Nonce
+ }
+ return 0
+}
+
+func (x *Header) GetBaseFee() *H256 {
+ if x != nil {
+ return x.BaseFee
+ }
+ return nil
+}
+
+func (x *Header) GetExtra() []byte {
+ if x != nil {
+ return x.Extra
+ }
+ return nil
+}
+
+func (x *Header) GetSignature() *H768 {
+ if x != nil {
+ return x.Signature
+ }
+ return nil
+}
+
+func (x *Header) GetBloom() *H2048 {
+ if x != nil {
+ return x.Bloom
+ }
+ return nil
+}
+
+func (x *Header) GetMixDigest() *H256 {
+ if x != nil {
+ return x.MixDigest
+ }
+ return nil
+}
+
+type Verifier struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ PublicKey *H384 `protobuf:"bytes,1,opt,name=PublicKey,proto3" json:"PublicKey,omitempty"`
+ Address *H160 `protobuf:"bytes,2,opt,name=Address,proto3" json:"Address,omitempty"`
+}
+
+func (x *Verifier) Reset() {
+ *x = Verifier{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Verifier) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Verifier) ProtoMessage() {}
+
+func (x *Verifier) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Verifier.ProtoReflect.Descriptor instead.
+func (*Verifier) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *Verifier) GetPublicKey() *H384 {
+ if x != nil {
+ return x.PublicKey
+ }
+ return nil
+}
+
+func (x *Verifier) GetAddress() *H160 {
+ if x != nil {
+ return x.Address
+ }
+ return nil
+}
+
+type Reward struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Amount *H256 `protobuf:"bytes,1,opt,name=Amount,proto3" json:"Amount,omitempty"`
+ Address *H160 `protobuf:"bytes,2,opt,name=Address,proto3" json:"Address,omitempty"`
+}
+
+func (x *Reward) Reset() {
+ *x = Reward{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Reward) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Reward) ProtoMessage() {}
+
+func (x *Reward) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[11]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Reward.ProtoReflect.Descriptor instead.
+func (*Reward) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *Reward) GetAmount() *H256 {
+ if x != nil {
+ return x.Amount
+ }
+ return nil
+}
+
+func (x *Reward) GetAddress() *H160 {
+ if x != nil {
+ return x.Address
+ }
+ return nil
+}
+
+type Body struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Txs []*Transaction `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty" ssz-max:"104857600"`
+ Verifiers []*Verifier `protobuf:"bytes,2,rep,name=verifiers,proto3" json:"verifiers,omitempty" ssz-max:"104857600"`
+ Rewards []*Reward `protobuf:"bytes,3,rep,name=rewards,proto3" json:"rewards,omitempty" ssz-max:"104857600"`
+}
+
+func (x *Body) Reset() {
+ *x = Body{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Body) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Body) ProtoMessage() {}
+
+func (x *Body) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[12]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Body.ProtoReflect.Descriptor instead.
+func (*Body) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *Body) GetTxs() []*Transaction {
+ if x != nil {
+ return x.Txs
+ }
+ return nil
+}
+
+func (x *Body) GetVerifiers() []*Verifier {
+ if x != nil {
+ return x.Verifiers
+ }
+ return nil
+}
+
+func (x *Body) GetRewards() []*Reward {
+ if x != nil {
+ return x.Rewards
+ }
+ return nil
+}
+
+type Transaction struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Type uint64 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"`
+ Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
+ GasPrice *H256 `protobuf:"bytes,3,opt,name=gasPrice,proto3" json:"gasPrice,omitempty"`
+ Gas uint64 `protobuf:"varint,4,opt,name=gas,proto3" json:"gas,omitempty"`
+ FeePerGas *H256 `protobuf:"bytes,5,opt,name=feePerGas,proto3" json:"feePerGas,omitempty"`
+ PriorityFeePerGas *H256 `protobuf:"bytes,6,opt,name=priorityFeePerGas,proto3" json:"priorityFeePerGas,omitempty"`
+ Value *H256 `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"`
+ Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty" ssz-max:"104857600"`
+ Sign []byte `protobuf:"bytes,9,opt,name=sign,proto3" json:"sign,omitempty" ssz-max:"104857600"`
+ To *H160 `protobuf:"bytes,10,opt,name=to,proto3" json:"to,omitempty"`
+ From *H160 `protobuf:"bytes,11,opt,name=from,proto3" json:"from,omitempty"`
+ ChainID uint64 `protobuf:"varint,12,opt,name=chainID,proto3" json:"chainID,omitempty"`
+ Hash *H256 `protobuf:"bytes,13,opt,name=hash,proto3" json:"hash,omitempty"`
+ R *H256 `protobuf:"bytes,14,opt,name=r,proto3" json:"r,omitempty"`
+ S *H256 `protobuf:"bytes,15,opt,name=s,proto3" json:"s,omitempty"`
+ V *H256 `protobuf:"bytes,16,opt,name=v,proto3" json:"v,omitempty"`
+}
+
+func (x *Transaction) Reset() {
+ *x = Transaction{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Transaction) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Transaction) ProtoMessage() {}
+
+func (x *Transaction) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[13]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Transaction.ProtoReflect.Descriptor instead.
+func (*Transaction) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *Transaction) GetType() uint64 {
+ if x != nil {
+ return x.Type
+ }
+ return 0
+}
+
+func (x *Transaction) GetNonce() uint64 {
+ if x != nil {
+ return x.Nonce
+ }
+ return 0
+}
+
+func (x *Transaction) GetGasPrice() *H256 {
+ if x != nil {
+ return x.GasPrice
+ }
+ return nil
+}
+
+func (x *Transaction) GetGas() uint64 {
+ if x != nil {
+ return x.Gas
+ }
+ return 0
+}
+
+func (x *Transaction) GetFeePerGas() *H256 {
+ if x != nil {
+ return x.FeePerGas
+ }
+ return nil
+}
+
+func (x *Transaction) GetPriorityFeePerGas() *H256 {
+ if x != nil {
+ return x.PriorityFeePerGas
+ }
+ return nil
+}
+
+func (x *Transaction) GetValue() *H256 {
+ if x != nil {
+ return x.Value
+ }
+ return nil
+}
+
+func (x *Transaction) GetData() []byte {
+ if x != nil {
+ return x.Data
+ }
+ return nil
+}
+
+func (x *Transaction) GetSign() []byte {
+ if x != nil {
+ return x.Sign
+ }
+ return nil
+}
+
+func (x *Transaction) GetTo() *H160 {
+ if x != nil {
+ return x.To
+ }
+ return nil
+}
+
+func (x *Transaction) GetFrom() *H160 {
+ if x != nil {
+ return x.From
+ }
+ return nil
+}
+
+func (x *Transaction) GetChainID() uint64 {
+ if x != nil {
+ return x.ChainID
+ }
+ return 0
+}
+
+func (x *Transaction) GetHash() *H256 {
+ if x != nil {
+ return x.Hash
+ }
+ return nil
+}
+
+func (x *Transaction) GetR() *H256 {
+ if x != nil {
+ return x.R
+ }
+ return nil
+}
+
+func (x *Transaction) GetS() *H256 {
+ if x != nil {
+ return x.S
+ }
+ return nil
+}
+
+func (x *Transaction) GetV() *H256 {
+ if x != nil {
+ return x.V
+ }
+ return nil
+}
+
+type Receipts struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Receipts []*Receipt `protobuf:"bytes,1,rep,name=receipts,proto3" json:"receipts,omitempty"`
+}
+
+func (x *Receipts) Reset() {
+ *x = Receipts{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Receipts) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Receipts) ProtoMessage() {}
+
+func (x *Receipts) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[14]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Receipts.ProtoReflect.Descriptor instead.
+func (*Receipts) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *Receipts) GetReceipts() []*Receipt {
+ if x != nil {
+ return x.Receipts
+ }
+ return nil
+}
+
+type Receipt struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Type uint32 `protobuf:"varint,1,opt,name=Type,proto3" json:"Type,omitempty"`
+ PostState []byte `protobuf:"bytes,2,opt,name=PostState,proto3" json:"PostState,omitempty"`
+ Status uint64 `protobuf:"varint,3,opt,name=Status,proto3" json:"Status,omitempty"`
+ CumulativeGasUsed uint64 `protobuf:"varint,4,opt,name=CumulativeGasUsed,proto3" json:"CumulativeGasUsed,omitempty"`
+ Bloom *H2048 `protobuf:"bytes,5,opt,name=Bloom,proto3" json:"Bloom,omitempty"`
+ Logs []*Log `protobuf:"bytes,6,rep,name=Logs,proto3" json:"Logs,omitempty"`
+ TxHash *H256 `protobuf:"bytes,7,opt,name=TxHash,proto3" json:"TxHash,omitempty"`
+ ContractAddress *H160 `protobuf:"bytes,8,opt,name=ContractAddress,proto3" json:"ContractAddress,omitempty"`
+ GasUsed uint64 `protobuf:"varint,9,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"`
+ BlockHash *H256 `protobuf:"bytes,10,opt,name=BlockHash,proto3" json:"BlockHash,omitempty"`
+ BlockNumber *H256 `protobuf:"bytes,11,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"`
+ TransactionIndex uint64 `protobuf:"varint,12,opt,name=TransactionIndex,proto3" json:"TransactionIndex,omitempty"`
+}
+
+func (x *Receipt) Reset() {
+ *x = Receipt{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Receipt) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Receipt) ProtoMessage() {}
+
+func (x *Receipt) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[15]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Receipt.ProtoReflect.Descriptor instead.
+func (*Receipt) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *Receipt) GetType() uint32 {
+ if x != nil {
+ return x.Type
+ }
+ return 0
+}
+
+func (x *Receipt) GetPostState() []byte {
+ if x != nil {
+ return x.PostState
+ }
+ return nil
+}
+
+func (x *Receipt) GetStatus() uint64 {
+ if x != nil {
+ return x.Status
+ }
+ return 0
+}
+
+func (x *Receipt) GetCumulativeGasUsed() uint64 {
+ if x != nil {
+ return x.CumulativeGasUsed
+ }
+ return 0
+}
+
+func (x *Receipt) GetBloom() *H2048 {
+ if x != nil {
+ return x.Bloom
+ }
+ return nil
+}
+
+func (x *Receipt) GetLogs() []*Log {
+ if x != nil {
+ return x.Logs
+ }
+ return nil
+}
+
+func (x *Receipt) GetTxHash() *H256 {
+ if x != nil {
+ return x.TxHash
+ }
+ return nil
+}
+
+func (x *Receipt) GetContractAddress() *H160 {
+ if x != nil {
+ return x.ContractAddress
+ }
+ return nil
+}
+
+func (x *Receipt) GetGasUsed() uint64 {
+ if x != nil {
+ return x.GasUsed
+ }
+ return 0
+}
+
+func (x *Receipt) GetBlockHash() *H256 {
+ if x != nil {
+ return x.BlockHash
+ }
+ return nil
+}
+
+func (x *Receipt) GetBlockNumber() *H256 {
+ if x != nil {
+ return x.BlockNumber
+ }
+ return nil
+}
+
+func (x *Receipt) GetTransactionIndex() uint64 {
+ if x != nil {
+ return x.TransactionIndex
+ }
+ return 0
+}
+
+type Log struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Address *H160 `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
+ Topics []*H256 `protobuf:"bytes,2,rep,name=Topics,proto3" json:"Topics,omitempty"`
+ Data []byte `protobuf:"bytes,3,opt,name=Data,proto3" json:"Data,omitempty"`
+ BlockNumber *H256 `protobuf:"bytes,4,opt,name=BlockNumber,proto3" json:"BlockNumber,omitempty"`
+ TxHash *H256 `protobuf:"bytes,5,opt,name=TxHash,proto3" json:"TxHash,omitempty"`
+ TxIndex uint64 `protobuf:"varint,6,opt,name=TxIndex,proto3" json:"TxIndex,omitempty"`
+ BlockHash *H256 `protobuf:"bytes,7,opt,name=BlockHash,proto3" json:"BlockHash,omitempty"`
+ Index uint64 `protobuf:"varint,8,opt,name=Index,proto3" json:"Index,omitempty"`
+ Removed bool `protobuf:"varint,9,opt,name=Removed,proto3" json:"Removed,omitempty"`
+}
+
+func (x *Log) Reset() {
+ *x = Log{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Log) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Log) ProtoMessage() {}
+
+func (x *Log) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[16]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Log.ProtoReflect.Descriptor instead.
+func (*Log) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{16}
+}
+
+func (x *Log) GetAddress() *H160 {
+ if x != nil {
+ return x.Address
+ }
+ return nil
+}
+
+func (x *Log) GetTopics() []*H256 {
+ if x != nil {
+ return x.Topics
+ }
+ return nil
+}
+
+func (x *Log) GetData() []byte {
+ if x != nil {
+ return x.Data
+ }
+ return nil
+}
+
+func (x *Log) GetBlockNumber() *H256 {
+ if x != nil {
+ return x.BlockNumber
+ }
+ return nil
+}
+
+func (x *Log) GetTxHash() *H256 {
+ if x != nil {
+ return x.TxHash
+ }
+ return nil
+}
+
+func (x *Log) GetTxIndex() uint64 {
+ if x != nil {
+ return x.TxIndex
+ }
+ return 0
+}
+
+func (x *Log) GetBlockHash() *H256 {
+ if x != nil {
+ return x.BlockHash
+ }
+ return nil
+}
+
+func (x *Log) GetIndex() uint64 {
+ if x != nil {
+ return x.Index
+ }
+ return 0
+}
+
+func (x *Log) GetRemoved() bool {
+ if x != nil {
+ return x.Removed
+ }
+ return false
+}
+
+type Logs struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Logs []*Log `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"`
+}
+
+func (x *Logs) Reset() {
+ *x = Logs{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_types_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Logs) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Logs) ProtoMessage() {}
+
+func (x *Logs) ProtoReflect() protoreflect.Message {
+ mi := &file_types_proto_msgTypes[17]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Logs.ProtoReflect.Descriptor instead.
+func (*Logs) Descriptor() ([]byte, []int) {
+ return file_types_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *Logs) GetLogs() []*Log {
+ if x != nil {
+ return x.Logs
+ }
+ return nil
+}
+
+var File_types_proto protoreflect.FileDescriptor
+
+var file_types_proto_rawDesc = []byte{
+ 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x1a, 0x11, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x04, 0x48, 0x31,
+ 0x32, 0x38, 0x12, 0x0e, 0x0a, 0x02, 0x68, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02,
+ 0x68, 0x69, 0x12, 0x0e, 0x0a, 0x02, 0x6c, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02,
+ 0x6c, 0x6f, 0x22, 0x36, 0x0a, 0x04, 0x48, 0x31, 0x36, 0x30, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x31, 0x32, 0x38, 0x52, 0x02, 0x68, 0x69, 0x12, 0x0e, 0x0a, 0x02, 0x6c, 0x6f,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x6c, 0x6f, 0x22, 0x46, 0x0a, 0x04, 0x48, 0x32,
+ 0x35, 0x36, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x32, 0x38, 0x52, 0x02,
+ 0x68, 0x69, 0x12, 0x1e, 0x0a, 0x02, 0x6c, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x32, 0x38, 0x52, 0x02,
+ 0x6c, 0x6f, 0x22, 0x46, 0x0a, 0x04, 0x48, 0x33, 0x38, 0x34, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x02, 0x68, 0x69, 0x12, 0x1e, 0x0a, 0x02, 0x6c, 0x6f,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x31, 0x32, 0x38, 0x52, 0x02, 0x6c, 0x6f, 0x22, 0x46, 0x0a, 0x04, 0x48, 0x37,
+ 0x36, 0x38, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x33, 0x38, 0x34, 0x52, 0x02,
+ 0x68, 0x69, 0x12, 0x1e, 0x0a, 0x02, 0x6c, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x33, 0x38, 0x34, 0x52, 0x02,
+ 0x6c, 0x6f, 0x22, 0x46, 0x0a, 0x04, 0x48, 0x35, 0x31, 0x32, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x02, 0x68, 0x69, 0x12, 0x1e, 0x0a, 0x02, 0x6c, 0x6f,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x02, 0x6c, 0x6f, 0x22, 0x47, 0x0a, 0x05, 0x48, 0x31,
+ 0x30, 0x32, 0x34, 0x12, 0x1e, 0x0a, 0x02, 0x68, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x35, 0x31, 0x32, 0x52,
+ 0x02, 0x68, 0x69, 0x12, 0x1e, 0x0a, 0x02, 0x6c, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x35, 0x31, 0x32, 0x52,
+ 0x02, 0x6c, 0x6f, 0x22, 0x49, 0x0a, 0x05, 0x48, 0x32, 0x30, 0x34, 0x38, 0x12, 0x1f, 0x0a, 0x02,
+ 0x68, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x30, 0x32, 0x34, 0x52, 0x02, 0x68, 0x69, 0x12, 0x1f, 0x0a,
+ 0x02, 0x6c, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x30, 0x32, 0x34, 0x52, 0x02, 0x6c, 0x6f, 0x22, 0x55,
+ 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x28, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x12, 0x22, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52,
+ 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xe6, 0x04, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x12, 0x2e, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x48, 0x32, 0x35, 0x36, 0x52, 0x0a, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68,
+ 0x12, 0x2a, 0x0a, 0x08, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31,
+ 0x36, 0x30, 0x52, 0x08, 0x43, 0x6f, 0x69, 0x6e, 0x62, 0x61, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x04,
+ 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x04, 0x52, 0x6f, 0x6f, 0x74,
+ 0x12, 0x26, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36,
+ 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x30, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x65,
+ 0x69, 0x70, 0x74, 0x48, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
+ 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0b, 0x52,
+ 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x0a, 0x44, 0x69,
+ 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0a,
+ 0x44, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x26, 0x0a, 0x06, 0x4e, 0x75,
+ 0x6d, 0x62, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x4e, 0x75, 0x6d, 0x62,
+ 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x47, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18,
+ 0x0a, 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65,
+ 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05,
+ 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x4e, 0x6f, 0x6e,
+ 0x63, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x18, 0x0c, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48,
+ 0x32, 0x35, 0x36, 0x52, 0x07, 0x42, 0x61, 0x73, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1d, 0x0a, 0x05,
+ 0x45, 0x78, 0x74, 0x72, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x07, 0x92, 0xb5, 0x18,
+ 0x03, 0x31, 0x31, 0x37, 0x52, 0x05, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x2c, 0x0a, 0x09, 0x53,
+ 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x37, 0x36, 0x38, 0x52, 0x09,
+ 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x42, 0x6c, 0x6f,
+ 0x6f, 0x6d, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x30, 0x34, 0x38, 0x52, 0x05, 0x42, 0x6c, 0x6f, 0x6f, 0x6d,
+ 0x12, 0x2c, 0x0a, 0x09, 0x4d, 0x69, 0x78, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x10, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48,
+ 0x32, 0x35, 0x36, 0x52, 0x09, 0x4d, 0x69, 0x78, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x62,
+ 0x0a, 0x08, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x09, 0x50, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
+ 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x33, 0x38, 0x34, 0x52, 0x09, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36, 0x30, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x22, 0x5a, 0x0a, 0x06, 0x52, 0x65, 0x77, 0x61, 0x72, 0x64, 0x12, 0x26, 0x0a, 0x06,
+ 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x41, 0x6d,
+ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x48, 0x31, 0x36, 0x30, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xba,
+ 0x01, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x36, 0x0a, 0x03, 0x74, 0x78, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x92, 0xb5, 0x18,
+ 0x09, 0x31, 0x30, 0x34, 0x38, 0x35, 0x37, 0x36, 0x30, 0x30, 0x52, 0x03, 0x74, 0x78, 0x73, 0x12,
+ 0x3f, 0x0a, 0x09, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x56, 0x65,
+ 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x30, 0x34, 0x38,
+ 0x35, 0x37, 0x36, 0x30, 0x30, 0x52, 0x09, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73,
+ 0x12, 0x39, 0x0a, 0x07, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x77,
+ 0x61, 0x72, 0x64, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x30, 0x34, 0x38, 0x35, 0x37, 0x36,
+ 0x30, 0x30, 0x52, 0x07, 0x72, 0x65, 0x77, 0x61, 0x72, 0x64, 0x73, 0x22, 0xa9, 0x04, 0x0a, 0x0b,
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74,
+ 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
+ 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
+ 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63,
+ 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63,
+ 0x65, 0x12, 0x10, 0x0a, 0x03, 0x67, 0x61, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03,
+ 0x67, 0x61, 0x73, 0x12, 0x2c, 0x0a, 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73,
+ 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x09, 0x66, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61,
+ 0x73, 0x12, 0x3c, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65,
+ 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74,
+ 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x11, 0x70, 0x72,
+ 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12,
+ 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20,
+ 0x01, 0x28, 0x0c, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x30, 0x34, 0x38, 0x35, 0x37, 0x36,
+ 0x30, 0x30, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x04, 0x73, 0x69, 0x67, 0x6e,
+ 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0d, 0x92, 0xb5, 0x18, 0x09, 0x31, 0x30, 0x34, 0x38,
+ 0x35, 0x37, 0x36, 0x30, 0x30, 0x52, 0x04, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x1e, 0x0a, 0x02, 0x74,
+ 0x6f, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36, 0x30, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x22, 0x0a, 0x04, 0x66,
+ 0x72, 0x6f, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36, 0x30, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12,
+ 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04,
+ 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x04, 0x68, 0x61, 0x73,
+ 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f,
+ 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a,
+ 0x01, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x01, 0x72, 0x12, 0x1c, 0x0a, 0x01, 0x73,
+ 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70,
+ 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x01, 0x73, 0x12, 0x1c, 0x0a, 0x01, 0x76, 0x18, 0x10,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e,
+ 0x48, 0x32, 0x35, 0x36, 0x52, 0x01, 0x76, 0x22, 0x39, 0x0a, 0x08, 0x52, 0x65, 0x63, 0x65, 0x69,
+ 0x70, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70,
+ 0x74, 0x73, 0x22, 0xd3, 0x03, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x12,
+ 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x54, 0x79,
+ 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x50, 0x6f, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65,
+ 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
+ 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x43, 0x75, 0x6d, 0x75,
+ 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x18, 0x04, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x11, 0x43, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47,
+ 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62,
+ 0x2e, 0x48, 0x32, 0x30, 0x34, 0x38, 0x52, 0x05, 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x12, 0x21, 0x0a,
+ 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x74, 0x79,
+ 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x4c, 0x6f, 0x67, 0x73,
+ 0x12, 0x26, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36,
+ 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x38, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74,
+ 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36,
+ 0x30, 0x52, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65,
+ 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x18, 0x09, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x07, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x09,
+ 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52,
+ 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x30, 0x0a, 0x0b, 0x42, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52,
+ 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x10,
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78,
+ 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xbd, 0x02, 0x0a, 0x03, 0x4c, 0x6f, 0x67,
+ 0x12, 0x28, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x31, 0x36,
+ 0x30, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x26, 0x0a, 0x06, 0x54, 0x6f,
+ 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70,
+ 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x54, 0x6f, 0x70, 0x69,
+ 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e,
+ 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79,
+ 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x0b, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x54, 0x78, 0x48, 0x61,
+ 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
+ 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x06, 0x54, 0x78, 0x48, 0x61, 0x73, 0x68,
+ 0x12, 0x18, 0x0a, 0x07, 0x54, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x07, 0x54, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x09, 0x42, 0x6c,
+ 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
+ 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x48, 0x32, 0x35, 0x36, 0x52, 0x09, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x64, 0x65,
+ 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18,
+ 0x0a, 0x07, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x07, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73,
+ 0x12, 0x21, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d,
+ 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c,
+ 0x6f, 0x67, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x61, 0x6d, 0x61, 0x7a, 0x65, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x61, 0x6d, 0x63,
+ 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x74, 0x79,
+ 0x70, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_types_proto_rawDescOnce sync.Once
+ file_types_proto_rawDescData = file_types_proto_rawDesc
+)
+
+func file_types_proto_rawDescGZIP() []byte {
+ file_types_proto_rawDescOnce.Do(func() {
+ file_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_proto_rawDescData)
+ })
+ return file_types_proto_rawDescData
+}
+
+var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
+var file_types_proto_goTypes = []interface{}{
+ (*H128)(nil), // 0: types_pb.H128
+ (*H160)(nil), // 1: types_pb.H160
+ (*H256)(nil), // 2: types_pb.H256
+ (*H384)(nil), // 3: types_pb.H384
+ (*H768)(nil), // 4: types_pb.H768
+ (*H512)(nil), // 5: types_pb.H512
+ (*H1024)(nil), // 6: types_pb.H1024
+ (*H2048)(nil), // 7: types_pb.H2048
+ (*Block)(nil), // 8: types_pb.Block
+ (*Header)(nil), // 9: types_pb.Header
+ (*Verifier)(nil), // 10: types_pb.Verifier
+ (*Reward)(nil), // 11: types_pb.Reward
+ (*Body)(nil), // 12: types_pb.Body
+ (*Transaction)(nil), // 13: types_pb.Transaction
+ (*Receipts)(nil), // 14: types_pb.Receipts
+ (*Receipt)(nil), // 15: types_pb.Receipt
+ (*Log)(nil), // 16: types_pb.Log
+ (*Logs)(nil), // 17: types_pb.Logs
+}
+var file_types_proto_depIdxs = []int32{
+ 0, // 0: types_pb.H160.hi:type_name -> types_pb.H128
+ 0, // 1: types_pb.H256.hi:type_name -> types_pb.H128
+ 0, // 2: types_pb.H256.lo:type_name -> types_pb.H128
+ 2, // 3: types_pb.H384.hi:type_name -> types_pb.H256
+ 0, // 4: types_pb.H384.lo:type_name -> types_pb.H128
+ 3, // 5: types_pb.H768.hi:type_name -> types_pb.H384
+ 3, // 6: types_pb.H768.lo:type_name -> types_pb.H384
+ 2, // 7: types_pb.H512.hi:type_name -> types_pb.H256
+ 2, // 8: types_pb.H512.lo:type_name -> types_pb.H256
+ 5, // 9: types_pb.H1024.hi:type_name -> types_pb.H512
+ 5, // 10: types_pb.H1024.lo:type_name -> types_pb.H512
+ 6, // 11: types_pb.H2048.hi:type_name -> types_pb.H1024
+ 6, // 12: types_pb.H2048.lo:type_name -> types_pb.H1024
+ 9, // 13: types_pb.Block.Header:type_name -> types_pb.Header
+ 12, // 14: types_pb.Block.body:type_name -> types_pb.Body
+ 2, // 15: types_pb.Header.ParentHash:type_name -> types_pb.H256
+ 1, // 16: types_pb.Header.Coinbase:type_name -> types_pb.H160
+ 2, // 17: types_pb.Header.Root:type_name -> types_pb.H256
+ 2, // 18: types_pb.Header.TxHash:type_name -> types_pb.H256
+ 2, // 19: types_pb.Header.ReceiptHash:type_name -> types_pb.H256
+ 2, // 20: types_pb.Header.Difficulty:type_name -> types_pb.H256
+ 2, // 21: types_pb.Header.Number:type_name -> types_pb.H256
+ 2, // 22: types_pb.Header.BaseFee:type_name -> types_pb.H256
+ 4, // 23: types_pb.Header.Signature:type_name -> types_pb.H768
+ 7, // 24: types_pb.Header.Bloom:type_name -> types_pb.H2048
+ 2, // 25: types_pb.Header.MixDigest:type_name -> types_pb.H256
+ 3, // 26: types_pb.Verifier.PublicKey:type_name -> types_pb.H384
+ 1, // 27: types_pb.Verifier.Address:type_name -> types_pb.H160
+ 2, // 28: types_pb.Reward.Amount:type_name -> types_pb.H256
+ 1, // 29: types_pb.Reward.Address:type_name -> types_pb.H160
+ 13, // 30: types_pb.Body.txs:type_name -> types_pb.Transaction
+ 10, // 31: types_pb.Body.verifiers:type_name -> types_pb.Verifier
+ 11, // 32: types_pb.Body.rewards:type_name -> types_pb.Reward
+ 2, // 33: types_pb.Transaction.gasPrice:type_name -> types_pb.H256
+ 2, // 34: types_pb.Transaction.feePerGas:type_name -> types_pb.H256
+ 2, // 35: types_pb.Transaction.priorityFeePerGas:type_name -> types_pb.H256
+ 2, // 36: types_pb.Transaction.value:type_name -> types_pb.H256
+ 1, // 37: types_pb.Transaction.to:type_name -> types_pb.H160
+ 1, // 38: types_pb.Transaction.from:type_name -> types_pb.H160
+ 2, // 39: types_pb.Transaction.hash:type_name -> types_pb.H256
+ 2, // 40: types_pb.Transaction.r:type_name -> types_pb.H256
+ 2, // 41: types_pb.Transaction.s:type_name -> types_pb.H256
+ 2, // 42: types_pb.Transaction.v:type_name -> types_pb.H256
+ 15, // 43: types_pb.Receipts.receipts:type_name -> types_pb.Receipt
+ 7, // 44: types_pb.Receipt.Bloom:type_name -> types_pb.H2048
+ 16, // 45: types_pb.Receipt.Logs:type_name -> types_pb.Log
+ 2, // 46: types_pb.Receipt.TxHash:type_name -> types_pb.H256
+ 1, // 47: types_pb.Receipt.ContractAddress:type_name -> types_pb.H160
+ 2, // 48: types_pb.Receipt.BlockHash:type_name -> types_pb.H256
+ 2, // 49: types_pb.Receipt.BlockNumber:type_name -> types_pb.H256
+ 1, // 50: types_pb.Log.Address:type_name -> types_pb.H160
+ 2, // 51: types_pb.Log.Topics:type_name -> types_pb.H256
+ 2, // 52: types_pb.Log.BlockNumber:type_name -> types_pb.H256
+ 2, // 53: types_pb.Log.TxHash:type_name -> types_pb.H256
+ 2, // 54: types_pb.Log.BlockHash:type_name -> types_pb.H256
+ 16, // 55: types_pb.Logs.logs:type_name -> types_pb.Log
+ 56, // [56:56] is the sub-list for method output_type
+ 56, // [56:56] is the sub-list for method input_type
+ 56, // [56:56] is the sub-list for extension type_name
+ 56, // [56:56] is the sub-list for extension extendee
+ 0, // [0:56] is the sub-list for field type_name
+}
+
+func init() { file_types_proto_init() }
+func file_types_proto_init() {
+ if File_types_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H128); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H160); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H256); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H384); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H768); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H512); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H1024); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*H2048); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Block); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Header); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Verifier); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Reward); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Body); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Transaction); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Receipts); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Receipt); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Log); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_types_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Logs); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_types_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 18,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_types_proto_goTypes,
+ DependencyIndexes: file_types_proto_depIdxs,
+ MessageInfos: file_types_proto_msgTypes,
+ }.Build()
+ File_types_proto = out.File
+ file_types_proto_rawDesc = nil
+ file_types_proto_goTypes = nil
+ file_types_proto_depIdxs = nil
+}
diff --git a/api/protocol/types_pb/types.proto b/api/protocol/types_pb/types.proto
new file mode 100644
index 0000000..e77bf9e
--- /dev/null
+++ b/api/protocol/types_pb/types.proto
@@ -0,0 +1,160 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+syntax = "proto3";
+package types_pb;
+
+import "ext/options.proto";
+
+option go_package = "github.com/astranetworld/ast/api/protocol/types_pb";
+
+
+message H128 {
+ uint64 hi = 1;
+ uint64 lo = 2;
+}
+
+message H160 {
+ H128 hi = 1;
+ uint32 lo = 2;
+}
+
+message H256 {
+ H128 hi = 1;
+ H128 lo = 2;
+}
+
+message H384{ // 48 byte
+ H256 hi = 1;
+ H128 lo = 2;
+}
+
+message H768 { // 96 byte
+ H384 hi = 1;
+ H384 lo = 2;
+}
+
+message H512 {
+ H256 hi = 1;
+ H256 lo = 2;
+}
+
+message H1024 {
+ H512 hi = 1;
+ H512 lo = 2;
+}
+
+message H2048 {
+ H1024 hi = 1;
+ H1024 lo = 2;
+}
+
+
+message Block{
+ Header Header = 1;
+ Body body = 2;
+}
+
+message Header {
+ H256 ParentHash = 1 ;
+ H160 Coinbase = 2 ;
+ H256 Root = 3 ;
+ H256 TxHash = 4 ;
+ H256 ReceiptHash = 5 ;
+ H256 Difficulty = 6;
+ H256 Number = 7;
+ uint64 GasLimit = 8;
+ uint64 GasUsed = 9;
+ uint64 Time = 10;
+ uint64 Nonce = 11;
+ H256 BaseFee = 12 ;
+ // 65+32 byte (clique)
+ bytes Extra = 13 [(ext.ssz_max) = "117"];
+ H768 Signature = 14;
+ H2048 Bloom = 15;
+ H256 MixDigest = 16;
+}
+
+message Verifier {
+ H384 PublicKey = 1;
+ H160 Address = 2;
+}
+
+message Reward {
+ H256 Amount = 1;
+ H160 Address = 2;
+}
+
+message Body {
+ repeated Transaction txs = 1 [(ext.ssz_max) = "104857600"];
+ repeated Verifier verifiers = 2 [(ext.ssz_max) = "104857600"];
+ repeated Reward rewards = 3 [(ext.ssz_max) = "104857600"];
+}
+
+message Transaction {
+ uint64 type = 1;
+ uint64 nonce = 2;
+ H256 gasPrice = 3;
+ uint64 gas = 4;
+ H256 feePerGas = 5;
+ H256 priorityFeePerGas = 6;
+ H256 value = 7;
+ bytes data = 8 [(ext.ssz_max) = "104857600"];
+ bytes sign = 9 [(ext.ssz_max) = "104857600"];
+ H160 to = 10;
+ H160 from = 11;
+ uint64 chainID = 12;
+ H256 hash = 13;
+ H256 r = 14;
+ H256 s = 15;
+ H256 v = 16;
+}
+
+message Receipts {
+ repeated Receipt receipts = 1;
+}
+
+message Receipt {
+ uint32 Type = 1;
+ bytes PostState = 2;
+ uint64 Status = 3;
+ uint64 CumulativeGasUsed = 4;
+ H2048 Bloom = 5;
+ repeated Log Logs = 6;
+ H256 TxHash = 7 ;
+ H160 ContractAddress = 8;
+ uint64 GasUsed = 9;
+ H256 BlockHash = 10;
+ H256 BlockNumber = 11;
+ uint64 TransactionIndex = 12;
+}
+
+message Log {
+ H160 Address = 1;
+ repeated H256 Topics = 2;
+ bytes Data = 3;
+ H256 BlockNumber = 4;
+ H256 TxHash = 5;
+ uint64 TxIndex = 6;
+ H256 BlockHash = 7;
+ uint64 Index = 8;
+ bool Removed = 9;
+}
+
+message Logs {
+ repeated Log logs = 1;
+}
+
diff --git a/cmd/ast/accountcmd.go b/cmd/ast/accountcmd.go
new file mode 100644
index 0000000..9ff309a
--- /dev/null
+++ b/cmd/ast/accountcmd.go
@@ -0,0 +1,386 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/log"
+ "github.com/urfave/cli/v2"
+ "os"
+
+ "github.com/astranetworld/ast/cmd/utils"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/accounts/keystore"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/conf"
+ "github.com/astranetworld/ast/internal/node"
+)
+
+var (
+ walletCommand = &cli.Command{
+ Name: "wallet",
+ Usage: "Manage astranet presale wallets",
+ ArgsUsage: "",
+ Description: `
+ astranet wallet import /path/to/my/presale.wallet
+
+will prompt for your password and imports your ether presale account.
+It can be used non-interactively with the --password option taking a
+passwordfile as argument containing the wallet password in plaintext.`,
+ Subcommands: []*cli.Command{
+ {
+
+ Name: "import",
+ Usage: "Import Ethereum presale wallet",
+ ArgsUsage: "",
+ Action: importWallet,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ KeyStoreDirFlag,
+ PasswordFileFlag,
+ LightKDFFlag,
+ },
+ Description: `
+ astranet wallet [options] /path/to/my/presale.wallet
+
+will prompt for your password and imports your ether presale account.
+It can be used non-interactively with the --password option taking a
+passwordfile as argument containing the wallet password in plaintext.`,
+ },
+ },
+ }
+
+ accountCommand = &cli.Command{
+ Name: "account",
+ Usage: "Manage accounts",
+ Description: `
+
+Manage accounts, list all existing accounts, import a private key into a new
+account, create a new account or update an existing account.
+
+It supports interactive mode, when you are prompted for password as well as
+non-interactive mode where passwords are supplied via a given password file.
+Non-interactive mode is only meant for scripted use on test networks or known
+safe environments.
+
+Make sure you remember the password you gave when creating a new account (with
+either new or import). Without it you are not able to unlock your account.
+
+Note that exporting your key in unencrypted format is NOT supported.
+
+Keys are stored under /keystore.
+It is safe to transfer the entire directory or the individual keys therein
+between ethereum nodes by simply copying.
+
+Make sure you backup your keys regularly.`,
+ Subcommands: []*cli.Command{
+ {
+ Name: "list",
+ Usage: "Print summary of existing accounts",
+ Action: accountList,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ KeyStoreDirFlag,
+ },
+ Description: `
+ Print a short summary of all accounts`,
+ },
+ {
+ Name: "new",
+ Usage: "Create a new account",
+ Action: accountCreate,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ KeyStoreDirFlag,
+ PasswordFileFlag,
+ LightKDFFlag,
+ },
+ Description: `
+ astranet account new
+
+Creates a new account and prints the address.
+
+The account is saved in encrypted format, you are prompted for a password.
+
+You must remember this password to unlock your account in the future.
+
+For non-interactive use the password can be specified with the --password flag:
+
+Note, this is meant to be used for testing only, it is a bad idea to save your
+password to file or expose in any other way.
+`,
+ },
+ {
+ Name: "update",
+ Usage: "Update an existing account",
+ Action: accountUpdate,
+ ArgsUsage: "",
+ Flags: []cli.Flag{
+ DataDirFlag,
+ KeyStoreDirFlag,
+ LightKDFFlag,
+ },
+ Description: `
+ astranet account update
+
+Update an existing account.
+
+The account is saved in the newest version in encrypted format, you are prompted
+for a password to unlock the account and another to save the updated file.
+
+This same command can therefore be used to migrate an account of a deprecated
+format to the newest format or change the password for an account.
+
+For non-interactive use the password can be specified with the --password flag:
+
+ astranet account update [options]
+
+Since only one password can be given, only format update can be performed,
+changing your password is only possible interactively.
+`,
+ },
+ {
+ Name: "import",
+ Usage: "Import a private key into a new account",
+ Action: accountImport,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ KeyStoreDirFlag,
+ PasswordFileFlag,
+ LightKDFFlag,
+ },
+ ArgsUsage: "",
+ Description: `
+ astranet account import
+
+Imports an unencrypted private key from and creates a new account.
+Prints the address.
+
+The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
+
+The account is saved in encrypted format, you are prompted for a password.
+
+You must remember this password to unlock your account in the future.
+
+For non-interactive use the password can be specified with the -password flag:
+
+ astranet account import [options]
+
+Note:
+As you can directly copy your encrypted accounts to another ethereum instance,
+this import mechanism is not needed when you transfer an account between
+nodes.
+`,
+ },
+ },
+ }
+)
+
+func importWallet(ctx *cli.Context) error {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
+ }
+ keyfile := ctx.Args().First()
+ keyJSON, err := os.ReadFile(keyfile)
+ if err != nil {
+ utils.Fatalf("Could not read wallet file: %v", err)
+ }
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+
+ passphrase := utils.GetPassPhraseWithList("", false, 0, MakePasswordList(ctx))
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ fmt.Printf("Address: {%x}\n", acct.Address)
+ return nil
+}
+
+func accountList(ctx *cli.Context) error {
+ cfg := DefaultConfig
+ // Load config file.
+ if len(cfgFile) > 0 {
+ if err := conf.LoadConfigFromFile(cfgFile, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+
+ stack, err := node.NewNode(ctx, &cfg)
+ if err != nil {
+ return err
+ }
+
+ var index int
+ for _, wallet := range stack.AccountManager().Wallets() {
+ for _, account := range wallet.Accounts() {
+ fmt.Printf("Account #%d: {%s} %s\n", index, account.Address, &account.URL)
+ index++
+ }
+ }
+ return nil
+}
+
+// accountCreate creates a new account into the keystore defined by the CLI flags.
+func accountCreate(ctx *cli.Context) error {
+ cfg := DefaultConfig
+ // Load config file.
+ if len(cfgFile) > 0 {
+ if err := conf.LoadConfigFromFile(cfgFile, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+
+ keydir, err := cfg.NodeCfg.KeyDirConfig()
+ if err != nil {
+ utils.Fatalf("Failed to read configuration: %v", err)
+ }
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
+ if cfg.NodeCfg.UseLightweightKDF {
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
+ }
+
+ password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, MakePasswordList(ctx))
+
+ account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
+
+ if err != nil {
+ utils.Fatalf("Failed to create account: %v", err)
+ }
+ fmt.Printf("\nYour new key was generated\n\n")
+ fmt.Printf("Public address of the key: %s\n", account.Address.Hex())
+ fmt.Printf("Path of the secret key file: %s\n\n", account.URL.Path)
+ fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
+ fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
+ fmt.Printf("- You must BACKUP your key file! Without the key, it's impossible to access account funds!\n")
+ fmt.Printf("- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!\n\n")
+ return nil
+}
+
+// tries unlocking the specified account a few times.
+func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
+ account, err := utils.MakeAddress(ks, address)
+ if err != nil {
+ utils.Fatalf("Could not list accounts: %v", err)
+ }
+ for trials := 0; trials < 3; trials++ {
+ prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
+ password := utils.GetPassPhraseWithList(prompt, false, i, passwords)
+ err = ks.Unlock(account, password)
+ if err == nil {
+ log.Info("Unlocked account", "address", account.Address.Hex())
+ return account, password
+ }
+ if err, ok := err.(*keystore.AmbiguousAddrError); ok {
+ log.Info("Unlocked account", "address", account.Address.Hex())
+ return ambiguousAddrRecovery(ks, err, password), password
+ }
+ if err != keystore.ErrDecrypt {
+ // No need to prompt again if the error is not decryption-related.
+ break
+ }
+ }
+ // All trials expended to unlock account, bail out
+ utils.Fatalf("Failed to unlock account %s (%v)", address, err)
+
+ return accounts.Account{}, ""
+}
+
+func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
+ fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
+ for _, a := range err.Matches {
+ fmt.Println(" ", a.URL)
+ }
+ fmt.Println("Testing your password against all of them...")
+ var match *accounts.Account
+ for i, a := range err.Matches {
+ if e := ks.Unlock(a, auth); e == nil {
+ match = &err.Matches[i]
+ break
+ }
+ }
+ if match == nil {
+ utils.Fatalf("None of the listed files could be unlocked.")
+ return accounts.Account{}
+ }
+ fmt.Printf("Your password unlocked %s\n", match.URL)
+ fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
+ for _, a := range err.Matches {
+ if a != *match {
+ fmt.Println(" ", a.URL)
+ }
+ }
+ return *match
+}
+
+// accountUpdate transitions an account from a previous format to the current
+// one, also providing the possibility to change the pass-phrase.
+func accountUpdate(ctx *cli.Context) error {
+ if ctx.Args().Len() == 0 {
+ utils.Fatalf("No accounts specified to update")
+ }
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ for _, addr := range ctx.Args().Slice() {
+ account, oldPassword := unlockAccount(ks, addr, 0, nil)
+ newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil)
+ if err := ks.Update(account, oldPassword, newPassword); err != nil {
+ utils.Fatalf("Could not update the account: %v", err)
+ }
+ }
+ return nil
+}
+
+func accountImport(ctx *cli.Context) error {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
+ }
+ keyfile := ctx.Args().First()
+ key, err := crypto.LoadECDSA(keyfile)
+ if err != nil {
+ utils.Fatalf("Failed to load the private key: %v", err)
+ }
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+
+ passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, MakePasswordList(ctx))
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportECDSA(key, passphrase)
+ if err != nil {
+ utils.Fatalf("Could not create the account: %v", err)
+ }
+ fmt.Printf("Address: {%x}\n", acct.Address)
+ return nil
+}
diff --git a/cmd/ast/app.go b/cmd/ast/app.go
new file mode 100644
index 0000000..2bfbde1
--- /dev/null
+++ b/cmd/ast/app.go
@@ -0,0 +1,242 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "github.com/urfave/cli/v2"
+ "net/http"
+ _ "net/http/pprof"
+ "os"
+ "os/signal"
+ "runtime"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/astranetworld/ast/accounts"
+
+ "github.com/astranetworld/ast/accounts/keystore"
+ "github.com/astranetworld/ast/cmd/utils"
+
+ "github.com/astranetworld/ast/conf"
+ "github.com/astranetworld/ast/internal/node"
+)
+
+func appRun(ctx *cli.Context) error {
+ if len(cfgFile) > 0 {
+ if err := conf.LoadConfigFromFile(cfgFile, &DefaultConfig); err != nil {
+ return err
+ }
+ } else {
+ DefaultConfig.NetworkCfg.ListenersAddress = listenAddress.Value()
+ DefaultConfig.NetworkCfg.BootstrapPeers = bootstraps.Value()
+ if len(privateKey) > 0 {
+ DefaultConfig.NetworkCfg.LocalPeerKey = privateKey
+ }
+
+ DefaultConfig.P2PCfg.StaticPeers = p2pStaticPeers.Value()
+ DefaultConfig.P2PCfg.BootstrapNodeAddr = p2pBootstrapNode.Value()
+ DefaultConfig.P2PCfg.DenyListCIDR = p2pDenyList.Value()
+
+ //
+ DefaultConfig.P2PCfg.DataDir = DefaultConfig.NodeCfg.DataDir
+ }
+
+ log.Init(DefaultConfig.NodeCfg, DefaultConfig.LoggerCfg)
+
+ if DefaultConfig.PprofCfg.Pprof {
+ if DefaultConfig.PprofCfg.MaxCpu > 0 {
+ runtime.GOMAXPROCS(DefaultConfig.PprofCfg.MaxCpu)
+ }
+ if DefaultConfig.PprofCfg.TraceMutex {
+ runtime.SetMutexProfileFraction(1)
+ }
+ if DefaultConfig.PprofCfg.TraceBlock {
+ runtime.SetBlockProfileRate(1)
+ }
+
+ go func() {
+ if err := http.ListenAndServe(fmt.Sprintf(":%d", DefaultConfig.PprofCfg.Port), nil); err != nil {
+ log.Error("failed to setup go pprof", "err", err)
+ os.Exit(0)
+ }
+ }()
+ }
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ log.Error("Failed start Node", "err", err)
+ return err
+ }
+
+ StartNode(ctx, stack, false)
+
+ // Unlock any account specifically requested
+ unlockAccounts(ctx, stack, &DefaultConfig)
+
+ // Register wallet event handlers to open and auto-derive wallets
+ events := make(chan accounts.WalletEvent, 16)
+ stack.AccountManager().Subscribe(events)
+
+ go func() {
+ // Open any wallets already attached
+ for _, wallet := range stack.AccountManager().Wallets() {
+ if err := wallet.Open(""); err != nil {
+ log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
+ }
+ }
+ // Listen for wallet event till termination
+ for event := range events {
+ switch event.Kind {
+ case accounts.WalletArrived:
+ if err := event.Wallet.Open(""); err != nil {
+ log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
+ }
+ case accounts.WalletOpened:
+ status, _ := event.Wallet.Status()
+ log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
+
+ var derivationPaths []accounts.DerivationPath
+ if event.Wallet.URL().Scheme == "ledger" {
+ derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
+ }
+ derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
+
+ event.Wallet.SelfDerive(derivationPaths, nil)
+
+ case accounts.WalletDropped:
+ log.Info("Old wallet dropped", "url", event.Wallet.URL())
+ event.Wallet.Close()
+ }
+ }
+ }()
+
+ stack.Wait()
+
+ return nil
+}
+
+// unlockAccounts unlocks any account specifically requested.
+func unlockAccounts(ctx *cli.Context, stack *node.Node, cfg *conf.Config) {
+ var unlocks []string
+ inputs := strings.Split(ctx.String(UnlockedAccountFlag.Name), ",")
+ for _, input := range inputs {
+ if trimmed := strings.TrimSpace(input); trimmed != "" {
+ unlocks = append(unlocks, trimmed)
+ }
+ }
+
+ // Short circuit if there is no account to unlock.
+ if len(unlocks) == 0 {
+ return
+ }
+ // If insecure account unlocking is not allowed if node's APIs are exposed to external.
+ // Print warning log to user and skip unlocking.
+ if !cfg.NodeCfg.InsecureUnlockAllowed && cfg.NodeCfg.ExtRPCEnabled() {
+ utils.Fatalf("Account unlock with HTTP access is forbidden!")
+ }
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ passwords := MakePasswordList(ctx)
+ for i, account := range unlocks {
+ unlockAccount(ks, account, i, passwords)
+ }
+}
+
+// MakePasswordList reads password lines from the file specified by the global --password flag.
+func MakePasswordList(ctx *cli.Context) []string {
+ path := ctx.Path(PasswordFileFlag.Name)
+ if path == "" {
+ return nil
+ }
+ text, err := os.ReadFile(path)
+ if err != nil {
+ log.Error("Failed to read password ", "file", err)
+ }
+ lines := strings.Split(string(text), "\n")
+ // Sanitise DOS line endings.
+ for i := range lines {
+ lines[i] = strings.TrimRight(lines[i], "\r")
+ }
+ return lines
+}
+
+func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) {
+ if err := stack.Start(); err != nil {
+ log.Critf("Error starting protocol stack: %v", err)
+ }
+ go func() {
+ sigc := make(chan os.Signal, 1)
+ signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
+ defer signal.Stop(sigc)
+
+ if ctx.IsSet(MinFreeDiskSpaceFlag.Name) {
+ minFreeDiskSpace := ctx.Int(MinFreeDiskSpaceFlag.Name)
+ go monitorFreeDiskSpace(sigc, stack.InstanceDir(), uint64(minFreeDiskSpace)*1024*1024*1024)
+ }
+
+ shutdown := func() {
+ log.Info("Got interrupt, shutting down...")
+ go stack.Close()
+ for i := 10; i > 0; i-- {
+ <-sigc
+ if i > 1 {
+ log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
+ }
+ }
+ panic("Panic closing the ast node")
+ }
+
+ if isConsole {
+ // In JS console mode, SIGINT is ignored because it's handled by the console.
+ // However, SIGTERM still shuts down the node.
+ for {
+ sig := <-sigc
+ if sig == syscall.SIGTERM {
+ shutdown()
+ return
+ }
+ }
+ } else {
+ <-sigc
+ shutdown()
+ }
+ }()
+}
+
+func monitorFreeDiskSpace(sigc chan os.Signal, path string, freeDiskSpaceCritical uint64) {
+ if path == "" {
+ return
+ }
+ for {
+ freeSpace, err := getFreeDiskSpace(path)
+ if err != nil {
+ log.Warn("Failed to get free disk space", "path", path, "err", err)
+ break
+ }
+ if freeSpace < freeDiskSpaceCritical {
+ log.Error("Low disk space. Gracefully shutting down Geth to prevent database corruption.", "available", types.StorageSize(freeSpace), "path", path)
+ sigc <- syscall.SIGTERM
+ break
+ } else if freeSpace < 2*freeDiskSpaceCritical {
+ log.Warn("Disk space is running low. Geth will shutdown if disk space runs below critical level.", "available", types.StorageSize(freeSpace), "critical_level", types.StorageSize(freeDiskSpaceCritical), "path", path)
+ }
+ time.Sleep(30 * time.Second)
+ }
+}
diff --git a/cmd/ast/cmd.go b/cmd/ast/cmd.go
new file mode 100644
index 0000000..5e6bd67
--- /dev/null
+++ b/cmd/ast/cmd.go
@@ -0,0 +1,558 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "github.com/astranetworld/ast/params/networkname"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ privateKey string
+ //engine string
+ miner bool
+ // todo
+ listenAddress = cli.NewStringSlice()
+ bootstraps = cli.NewStringSlice()
+ cfgFile string
+
+ p2pStaticPeers = cli.NewStringSlice()
+ p2pBootstrapNode = cli.NewStringSlice()
+ p2pDenyList = cli.NewStringSlice()
+)
+
+var rootCmd []*cli.Command
+
+var networkFlags = []cli.Flag{
+ &cli.StringSliceFlag{
+ Name: "p2p.listen",
+ Usage: "p2p listen address",
+ Value: cli.NewStringSlice(),
+ Destination: listenAddress,
+ },
+
+ &cli.StringSliceFlag{
+ Name: "p2p.bootstrap",
+ Usage: "bootstrap node info",
+ Value: cli.NewStringSlice(),
+ Destination: bootstraps,
+ },
+
+ &cli.StringFlag{
+ Name: "p2p.key",
+ Usage: "private key of p2p node",
+ Value: "",
+ Destination: &DefaultConfig.NetworkCfg.LocalPeerKey,
+ },
+}
+
+var nodeFlg = []cli.Flag{
+ &cli.StringFlag{
+ Name: "node.key",
+ Usage: "node private",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.NodePrivate,
+ },
+}
+
+var rpcFlags = []cli.Flag{
+
+ &cli.StringFlag{
+ Name: "ipcpath",
+ Usage: "Filename for IPC socket/pipe within the data dir (explicit paths escape it)",
+ Value: DefaultConfig.NodeCfg.IPCPath,
+ Destination: &DefaultConfig.NodeCfg.IPCPath,
+ },
+
+ &cli.BoolFlag{
+ Name: "http",
+ Usage: "Enable the HTTP json-rpc server",
+ Value: false,
+ Destination: &DefaultConfig.NodeCfg.HTTP,
+ },
+ &cli.StringFlag{
+ Name: "http.addr",
+ Usage: "HTTP server listening interface",
+ Value: DefaultConfig.NodeCfg.HTTPHost,
+ Destination: &DefaultConfig.NodeCfg.HTTPHost,
+ },
+ &cli.StringFlag{
+ Name: "http.port",
+ Usage: "HTTP server listening port",
+ Value: "20012",
+ Destination: &DefaultConfig.NodeCfg.HTTPPort,
+ },
+ &cli.StringFlag{
+ Name: "http.api",
+ Usage: "API's offered over the HTTP-RPC interface",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.HTTPApi,
+ },
+
+ &cli.StringFlag{
+ Name: "http.corsdomain",
+ Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.HTTPCors,
+ },
+
+ &cli.BoolFlag{
+ Name: "ws",
+ Usage: "Enable the WS-RPC server",
+ Value: false,
+ Destination: &DefaultConfig.NodeCfg.WS,
+ },
+ &cli.StringFlag{
+ Name: "ws.addr",
+ Usage: "WS-RPC server listening interface",
+ Value: DefaultConfig.NodeCfg.WSHost,
+ Destination: &DefaultConfig.NodeCfg.WSHost,
+ },
+ &cli.StringFlag{
+ Name: "ws.port",
+ Usage: "WS-RPC server listening port",
+ Value: "20013",
+ Destination: &DefaultConfig.NodeCfg.WSPort,
+ },
+
+ &cli.StringFlag{
+ Name: "ws.api",
+ Usage: "API's offered over the WS-RPC interface",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.WSApi,
+ },
+
+ &cli.StringFlag{
+ Name: "ws.origins",
+ Usage: "Origins from which to accept websockets requests",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.WSOrigins,
+ },
+}
+
+var consensusFlag = []cli.Flag{
+ //&cli.StringFlag{
+ // Name: "engine.type",
+ // Usage: "consensus engine",
+ // Value: "APosEngine", //APoaEngine,APosEngine
+ // Destination: &DefaultConfig.ChainCfg.Consensus,
+ //},
+ &cli.BoolFlag{
+ Name: "engine.miner",
+ Usage: "miner",
+ Value: false,
+ Destination: &DefaultConfig.NodeCfg.Miner,
+ },
+ &cli.StringFlag{
+ Name: "engine.etherbase",
+ Usage: "consensus etherbase",
+ Value: "",
+ Destination: &DefaultConfig.Miner.Etherbase,
+ },
+}
+
+var configFlag = []cli.Flag{
+ &cli.StringFlag{
+ Name: "blockchain",
+ Usage: "Loading a Configuration File",
+ Destination: &cfgFile,
+ },
+}
+
+var pprofCfg = []cli.Flag{
+ &cli.BoolFlag{
+ Name: "pprof",
+ Usage: "Enable the pprof HTTP server",
+ Value: false,
+ Destination: &DefaultConfig.PprofCfg.Pprof,
+ },
+
+ &cli.BoolFlag{
+ Name: "pprof.block",
+ Usage: "Turn on block profiling",
+ Value: false,
+ Destination: &DefaultConfig.PprofCfg.TraceBlock,
+ },
+ &cli.BoolFlag{
+ Name: "pprof.mutex",
+ Usage: "Turn on mutex profiling",
+ Value: false,
+ Destination: &DefaultConfig.PprofCfg.TraceMutex,
+ },
+ &cli.IntFlag{
+ Name: "pprof.maxcpu",
+ Usage: "setup number of cpu",
+ Value: 0,
+ Destination: &DefaultConfig.PprofCfg.MaxCpu,
+ },
+ &cli.IntFlag{
+ Name: "pprof.port",
+ Usage: "pprof HTTP server listening port",
+ Value: 0,
+ Destination: &DefaultConfig.PprofCfg.Port,
+ },
+}
+
+var loggerFlag = []cli.Flag{
+ &cli.StringFlag{
+ Name: "log.name",
+ Usage: "logger file name and path",
+ Value: "ast.log",
+ Destination: &DefaultConfig.LoggerCfg.LogFile,
+ },
+
+ &cli.StringFlag{
+ Name: "log.level",
+ Usage: "logger output level (value:[debug,info,warn,error,dpanic,panic,fatal])",
+ Value: "debug",
+ Destination: &DefaultConfig.LoggerCfg.Level,
+ },
+
+ &cli.IntFlag{
+ Name: "log.maxSize",
+ Usage: "logger file max size M",
+ Value: 10,
+ Destination: &DefaultConfig.LoggerCfg.MaxSize,
+ },
+ &cli.IntFlag{
+ Name: "log.maxBackups",
+ Usage: "logger file max backups",
+ Value: 10,
+ Destination: &DefaultConfig.LoggerCfg.MaxBackups,
+ },
+ &cli.IntFlag{
+ Name: "log.maxAge",
+ Usage: "logger file max age",
+ Value: 30,
+ Destination: &DefaultConfig.LoggerCfg.MaxAge,
+ },
+ &cli.BoolFlag{
+ Name: "log.compress",
+ Usage: "logger file compress",
+ Value: false,
+ Destination: &DefaultConfig.LoggerCfg.Compress,
+ },
+}
+var (
+ // P2PNoDiscovery specifies whether we are running a local network and have no need for connecting
+ // to the bootstrap nodes in the cloud
+ P2PNoDiscovery = &cli.BoolFlag{
+ Name: "p2p.no-discovery",
+ Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes.",
+ Destination: &DefaultConfig.P2PCfg.NoDiscovery,
+ }
+ // P2PStaticPeers specifies a set of peers to connect to explicitly.
+ P2PStaticPeers = &cli.StringSliceFlag{
+ Name: "p2p.peer",
+ Usage: "Connect with this peer. This flag may be used multiple times.",
+ Destination: p2pStaticPeers,
+ }
+ // P2PBootstrapNode tells the beacon node which bootstrap node to connect to
+ P2PBootstrapNode = &cli.StringSliceFlag{
+ Name: "p2p.bootstrap-node",
+ Usage: "The address of bootstrap node. Beacon node will connect for peer discovery via DHT. Multiple nodes can be passed by using the flag multiple times but not comma-separated. You can also pass YAML files containing multiple nodes.",
+ Destination: p2pBootstrapNode,
+ }
+ // P2PRelayNode tells the beacon node which relay node to connect to.
+ P2PRelayNode = &cli.StringFlag{
+ Name: "p2p.relay-node",
+ Usage: "The address of relay node. The beacon node will connect to the " +
+ "relay node and advertise their address via the relay node to other peers",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.RelayNodeAddr,
+ }
+ // P2PUDPPort defines the port to be used by discv5.
+ P2PUDPPort = &cli.IntFlag{
+ Name: "p2p.udp-port",
+ Usage: "The port used by discv5.",
+ Value: 61015,
+ Destination: &DefaultConfig.P2PCfg.UDPPort,
+ }
+ // P2PTCPPort defines the port to be used by libp2p.
+ P2PTCPPort = &cli.IntFlag{
+ Name: "p2p.tcp-port",
+ Usage: "The port used by libp2p.",
+ Value: 61016,
+ Destination: &DefaultConfig.P2PCfg.TCPPort,
+ }
+ // P2PIP defines the local IP to be used by libp2p.
+ P2PIP = &cli.StringFlag{
+ Name: "p2p.local-ip",
+ Usage: "The local ip address to listen for incoming data.",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.LocalIP,
+ }
+ // P2PHost defines the host IP to be used by libp2p.
+ P2PHost = &cli.StringFlag{
+ Name: "p2p.host-ip",
+ Usage: "The IP address advertised by libp2p. This may be used to advertise an external IP.",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.HostAddress,
+ }
+ // P2PHostDNS defines the host DNS to be used by libp2p.
+ P2PHostDNS = &cli.StringFlag{
+ Name: "p2p.host-dns",
+ Usage: "The DNS address advertised by libp2p. This may be used to advertise an external DNS.",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.HostDNS,
+ }
+ // P2PPrivKey defines a flag to specify the location of the private key file for libp2p.
+ P2PPrivKey = &cli.StringFlag{
+ Name: "p2p.priv-key",
+ Usage: "The file containing the private key to use in communications with other peers.",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.PrivateKey,
+ }
+ P2PStaticID = &cli.BoolFlag{
+ Name: "p2p.static-id",
+ Usage: "Enables the peer id of the node to be fixed by saving the generated network key to the default key path.",
+ Value: true,
+ Destination: &DefaultConfig.P2PCfg.StaticPeerID,
+ }
+ // P2PMetadata defines a flag to specify the location of the peer metadata file.
+ P2PMetadata = &cli.StringFlag{
+ Name: "p2p.metadata",
+ Usage: "The file containing the metadata to communicate with other peers.",
+ Value: "",
+ Destination: &DefaultConfig.P2PCfg.MetaDataDir,
+ }
+ // P2PMaxPeers defines a flag to specify the max number of peers in libp2p.
+ P2PMaxPeers = &cli.IntFlag{
+ Name: "p2p.max-peers",
+ Usage: "The max number of p2p peers to maintain.",
+ Value: 5,
+ Destination: &DefaultConfig.P2PCfg.MaxPeers,
+ }
+ // P2PAllowList defines a CIDR subnet to exclusively allow connections.
+ P2PAllowList = &cli.StringFlag{
+ Name: "p2p.allowlist",
+ Usage: "The CIDR subnet for allowing only certain peer connections. " +
+ "Using \"public\" would allow only public subnets. Example: " +
+ "192.168.0.0/16 would permit connections to peers on your local network only. The " +
+ "default is to accept all connections.",
+ Destination: &DefaultConfig.P2PCfg.AllowListCIDR,
+ }
+ // P2PDenyList defines a list of CIDR subnets to disallow connections from them.
+ P2PDenyList = &cli.StringSliceFlag{
+ Name: "p2p.denylist",
+ Usage: "The CIDR subnets for denying certainty peer connections. " +
+ "Using \"private\" would deny all private subnets. Example: " +
+ "192.168.0.0/16 would deny connections from peers on your local network only. The " +
+ "default is to accept all connections.",
+ Destination: p2pDenyList,
+ }
+
+ // P2PMinSyncPeers specifies the required number of successful peer handshakes in order
+ // to start syncing with external peers.
+ P2PMinSyncPeers = &cli.IntFlag{
+ Name: "p2p.min-sync-peers",
+ Usage: "The required number of valid peers to connect with before syncing.",
+ Value: 1,
+ Destination: &DefaultConfig.P2PCfg.MinSyncPeers,
+ }
+
+ // P2PBlockBatchLimit specifies the requested block batch size.
+ P2PBlockBatchLimit = &cli.IntFlag{
+ Name: "p2p.limit.block-batch",
+ Usage: "The amount of blocks the local peer is bounded to request and respond to in a batch.",
+ Value: 64,
+ Destination: &DefaultConfig.P2PCfg.P2PLimit.BlockBatchLimit,
+ }
+ // P2PBlockBatchLimitBurstFactor specifies the factor by which block batch size may increase.
+ P2PBlockBatchLimitBurstFactor = &cli.IntFlag{
+ Name: "p2p.limit.block-burst-factor",
+ Usage: "The factor by which block batch limit may increase on burst.",
+ Value: 2,
+ Destination: &DefaultConfig.P2PCfg.P2PLimit.BlockBatchLimitBurstFactor,
+ }
+ // P2PBlockBatchLimiterPeriod Period to calculate expected limit for a single peer.
+ P2PBlockBatchLimiterPeriod = &cli.IntFlag{
+ Name: "p2p.limit.block-limiter-period",
+ Usage: "Period to calculate expected limit for a single peer.",
+ Value: 5,
+ Destination: &DefaultConfig.P2PCfg.P2PLimit.BlockBatchLimiterPeriod,
+ }
+)
+
+var (
+ DataDirFlag = &cli.StringFlag{
+ Name: "data.dir",
+ Usage: "data save dir",
+ Value: "./ast/",
+ Destination: &DefaultConfig.NodeCfg.DataDir,
+ }
+
+ MinFreeDiskSpaceFlag = &cli.IntFlag{
+ Name: "data.dir.minfreedisk",
+ Usage: "Minimum free disk space in GB, once reached triggers auto shut down (default = 10GB, 0 = disabled)",
+ Value: 10,
+ Destination: &DefaultConfig.NodeCfg.MinFreeDiskSpace,
+ }
+
+ FromDataDirFlag = &cli.StringFlag{
+ Name: "chaindata.from",
+ Usage: "source data dir",
+ }
+ ToDataDirFlag = &cli.StringFlag{
+ Name: "chaindata.to",
+ Usage: "to data dir",
+ }
+
+ ChainFlag = &cli.StringFlag{
+ Name: "chain",
+ Usage: "Name of the testnet to join (value:[mainnet,testnet,private])",
+ Value: networkname.MainnetChainName,
+ Destination: &DefaultConfig.NodeCfg.Chain,
+ }
+)
+
+var (
+ AuthRPCFlag = &cli.BoolFlag{
+ Name: "authrpc",
+ Usage: "Enable the AUTH-RPC server",
+ Value: false,
+ Destination: &DefaultConfig.NodeCfg.AuthRPC,
+ }
+ // Authenticated RPC HTTP settings
+ AuthRPCListenFlag = &cli.StringFlag{
+ Name: "authrpc.addr",
+ Usage: "Listening address for authenticated APIs",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.AuthAddr,
+ }
+ AuthRPCPortFlag = &cli.IntFlag{
+ Name: "authrpc.port",
+ Usage: "Listening port for authenticated APIs",
+ Destination: &DefaultConfig.NodeCfg.AuthPort,
+ }
+ JWTSecretFlag = &cli.StringFlag{
+ Name: "authrpc.jwtsecret",
+ Usage: "Path to a JWT secret to use for authenticated RPC endpoints",
+ Value: "",
+ Destination: &DefaultConfig.NodeCfg.JWTSecret,
+ }
+)
+
+var (
+ // Account settings
+ UnlockedAccountFlag = &cli.StringFlag{
+ Name: "account.unlock",
+ Usage: "Comma separated list of accounts to unlock",
+ Value: "",
+ }
+ PasswordFileFlag = &cli.PathFlag{
+ Name: "account.password",
+ Usage: "Password file to use for non-interactive password input",
+ Destination: &DefaultConfig.NodeCfg.PasswordFile,
+ }
+ LightKDFFlag = &cli.BoolFlag{
+ Name: "account.lightkdf",
+ Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
+ }
+ KeyStoreDirFlag = &cli.PathFlag{
+ Name: "account.keystore",
+ Usage: "Directory for the keystore (default = inside the datadir)",
+ TakesFile: true,
+ Destination: &DefaultConfig.NodeCfg.KeyStoreDir,
+ }
+ InsecureUnlockAllowedFlag = &cli.BoolFlag{
+ Name: "account.allow.insecure.unlock",
+ Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
+ Value: false,
+ Destination: &DefaultConfig.NodeCfg.InsecureUnlockAllowed,
+ }
+
+ // MetricsEnabledFlag Metrics flags
+ MetricsEnabledFlag = &cli.BoolFlag{
+ Name: "metrics",
+ Usage: "Enable metrics collection and reporting",
+ Value: false,
+ Destination: &DefaultConfig.MetricsCfg.Enable,
+ }
+
+ // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint.
+ // Since the pprof service enables sensitive/vulnerable behavior, this allows a user
+ // to enable a public-OK metrics endpoint without having to worry about ALSO exposing
+ // other profiling behavior or information.
+ MetricsHTTPFlag = &cli.StringFlag{
+ Name: "metrics.addr",
+ Usage: `Enable stand-alone metrics HTTP server listening interface.`,
+ //Category: flags.MetricsCategory,
+ Value: "127.0.0.1",
+ Destination: &DefaultConfig.MetricsCfg.HTTP,
+ }
+ MetricsPortFlag = &cli.IntFlag{
+ Name: "metrics.port",
+ Usage: `Metrics HTTP server listening port.
+Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.`,
+ Value: 6060,
+ // Category: flags.MetricsCategory,
+ Destination: &DefaultConfig.MetricsCfg.Port,
+ }
+)
+
+var (
+ authRPCFlag = []cli.Flag{
+ AuthRPCFlag,
+ AuthRPCListenFlag,
+ AuthRPCPortFlag,
+ JWTSecretFlag,
+ }
+ settingFlag = []cli.Flag{
+ DataDirFlag,
+ ChainFlag,
+ MinFreeDiskSpaceFlag,
+ }
+ accountFlag = []cli.Flag{
+ PasswordFileFlag,
+ KeyStoreDirFlag,
+ LightKDFFlag,
+ InsecureUnlockAllowedFlag,
+ UnlockedAccountFlag,
+ }
+
+ metricsFlags = []cli.Flag{
+ MetricsEnabledFlag,
+ MetricsHTTPFlag,
+ MetricsPortFlag,
+ }
+
+ p2pFlags = []cli.Flag{
+ P2PNoDiscovery,
+ P2PAllowList,
+ P2PBootstrapNode,
+ P2PDenyList,
+ P2PIP,
+ P2PHost,
+ P2PMaxPeers,
+ P2PMetadata,
+ P2PStaticID,
+ P2PPrivKey,
+ P2PHostDNS,
+ P2PRelayNode,
+ P2PStaticPeers,
+ P2PUDPPort,
+ P2PTCPPort,
+ P2PMinSyncPeers,
+ }
+
+ p2pLimitFlags = []cli.Flag{
+ P2PBlockBatchLimit,
+ P2PBlockBatchLimitBurstFactor,
+ P2PBlockBatchLimiterPeriod,
+ }
+)
diff --git a/cmd/ast/config.go b/cmd/ast/config.go
new file mode 100644
index 0000000..1048e41
--- /dev/null
+++ b/cmd/ast/config.go
@@ -0,0 +1,78 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "github.com/astranetworld/ast/params"
+ "math/big"
+ "time"
+
+ "github.com/astranetworld/ast/conf"
+)
+
+var DefaultConfig = conf.Config{
+ NodeCfg: conf.NodeConfig{
+ NodePrivate: "",
+ HTTP: true,
+ HTTPHost: "127.0.0.1",
+ HTTPPort: "8545",
+ IPCPath: "ast.ipc",
+ Miner: false,
+ },
+ NetworkCfg: conf.NetWorkConfig{
+ Bootstrapped: true,
+ },
+ LoggerCfg: conf.LoggerConfig{
+ LogFile: "./logger.log",
+ Level: "debug",
+ MaxSize: 10,
+ MaxBackups: 10,
+ MaxAge: 30,
+ Compress: true,
+ },
+ PprofCfg: conf.PprofConfig{
+ MaxCpu: 0,
+ Port: 6060,
+ TraceMutex: true,
+ TraceBlock: true,
+ Pprof: false,
+ },
+ DatabaseCfg: conf.DatabaseConfig{
+ DBType: "lmdb",
+ DBPath: "chaindata",
+ DBName: "ast",
+ SubDB: []string{"chain"},
+ Debug: false,
+ IsMem: false,
+ MaxDB: 100,
+ MaxReaders: 1000,
+ },
+ MetricsCfg: conf.MetricsConfig{
+ Port: 6060,
+ HTTP: "127.0.0.1",
+ },
+
+ P2PCfg: &conf.P2PConfig{P2PLimit: &conf.P2PLimit{}},
+
+ //GenesisCfg: ReadGenesis("allocs/mainnet.json"),
+ GPO: conf.FullNodeGPO,
+ Miner: conf.MinerConfig{
+ GasCeil: 30000000,
+ GasPrice: big.NewInt(params.GWei),
+ Recommit: 4 * time.Second,
+ },
+}
diff --git a/cmd/ast/diskusage.go b/cmd/ast/diskusage.go
new file mode 100644
index 0000000..b22b426
--- /dev/null
+++ b/cmd/ast/diskusage.go
@@ -0,0 +1,44 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build !windows && !openbsd
+// +build !windows,!openbsd
+
+package main
+
+import (
+ "fmt"
+
+ "golang.org/x/sys/unix"
+)
+
+func getFreeDiskSpace(path string) (uint64, error) {
+ var stat unix.Statfs_t
+ if err := unix.Statfs(path, &stat); err != nil {
+ return 0, fmt.Errorf("failed to call Statfs: %v", err)
+ }
+
+ // Available blocks * size per block = available space in bytes
+ var bavail = stat.Bavail
+ // nolint:staticcheck
+ if stat.Bavail < 0 {
+ // FreeBSD can have a negative number of blocks available
+ // because of the grace limit.
+ bavail = 0
+ }
+ //nolint:unconvert
+ return uint64(bavail) * uint64(stat.Bsize), nil
+}
diff --git a/cmd/ast/diskusage_openbsd.go b/cmd/ast/diskusage_openbsd.go
new file mode 100644
index 0000000..707c028
--- /dev/null
+++ b/cmd/ast/diskusage_openbsd.go
@@ -0,0 +1,44 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build openbsd
+// +build openbsd
+
+package main
+
+import (
+ "fmt"
+
+ "golang.org/x/sys/unix"
+)
+
+func getFreeDiskSpace(path string) (uint64, error) {
+ var stat unix.Statfs_t
+ if err := unix.Statfs(path, &stat); err != nil {
+ return 0, fmt.Errorf("failed to call Statfs: %v", err)
+ }
+
+ // Available blocks * size per block = available space in bytes
+ var bavail = stat.F_bavail
+ // Not sure if the following check is necessary for OpenBSD
+ if stat.F_bavail < 0 {
+ // FreeBSD can have a negative number of blocks available
+ // because of the grace limit.
+ bavail = 0
+ }
+ //nolint:unconvert
+ return uint64(bavail) * uint64(stat.F_bsize), nil
+}
diff --git a/cmd/ast/diskusage_windows.go b/cmd/ast/diskusage_windows.go
new file mode 100644
index 0000000..a5576ec
--- /dev/null
+++ b/cmd/ast/diskusage_windows.go
@@ -0,0 +1,38 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "fmt"
+
+ "golang.org/x/sys/windows"
+)
+
+func getFreeDiskSpace(path string) (uint64, error) {
+
+ cwd, err := windows.UTF16PtrFromString(path)
+ if err != nil {
+ return 0, fmt.Errorf("failed to call UTF16PtrFromString: %v", err)
+ }
+
+ var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
+ if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil {
+ return 0, fmt.Errorf("failed to call GetDiskFreeSpaceEx: %v", err)
+ }
+
+ return freeBytesAvailableToCaller, nil
+}
diff --git a/cmd/ast/exportcmd.go b/cmd/ast/exportcmd.go
new file mode 100644
index 0000000..ef0a7c5
--- /dev/null
+++ b/cmd/ast/exportcmd.go
@@ -0,0 +1,228 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/account"
+ common "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/node"
+ "github.com/astranetworld/ast/log"
+ "github.com/astranetworld/ast/modules"
+ "github.com/astranetworld/ast/params"
+ "github.com/astranetworld/ast/turbo/backup"
+ "github.com/holiman/uint256"
+ "github.com/ledgerwatch/erigon-lib/kv"
+ "github.com/urfave/cli/v2"
+ "math/big"
+ "os"
+ "time"
+)
+
+var (
+ exportCommand = &cli.Command{
+ Name: "export",
+ Usage: "Export astranet data",
+ ArgsUsage: "",
+ Description: ``,
+ Subcommands: []*cli.Command{
+ {
+ Name: "txs",
+ Usage: "Export All astranet Transactions",
+ ArgsUsage: "",
+ Action: exportTransactions,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ },
+ Description: ``,
+ },
+ {
+ Name: "balance",
+ Usage: "Export All astranet account balance",
+ ArgsUsage: "",
+ Action: exportBalance,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ },
+ Description: ``,
+ },
+ {
+ Name: "dbState",
+ Usage: "Export All MDBX Buckets disk space",
+ ArgsUsage: "",
+ Action: exportDBState,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ },
+ Description: ``,
+ },
+ {
+ Name: "dbCopy",
+ Usage: "copy data from '--chaindata' to '--chaindata.to'",
+ ArgsUsage: "",
+ Action: dbCopy,
+ Flags: []cli.Flag{
+ FromDataDirFlag,
+ ToDataDirFlag,
+ },
+ Description: ``,
+ },
+ },
+ }
+)
+
+func exportTransactions(ctx *cli.Context) error {
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+
+ blockChain := stack.BlockChain()
+ defer stack.Close()
+
+ currentBlock := blockChain.CurrentBlock()
+
+ for i := uint64(0); i < currentBlock.Number64().Uint64(); i++ {
+
+ block, err := blockChain.GetBlockByNumber(uint256.NewInt(i + 1))
+ if err != nil {
+ panic("cannot get block")
+ }
+ for _, transaction := range block.Transactions() {
+ if transaction.To() == nil {
+ continue
+ }
+ fmt.Printf("%d,%s,%s,%d,%s,%.2f\n",
+ block.Number64().Uint64(),
+ time.Unix(int64(block.Time()), 0).Format(time.RFC3339),
+ transaction.From().Hex(),
+ transaction.Nonce(),
+ transaction.To().Hex(),
+ new(big.Float).Quo(new(big.Float).SetInt(transaction.Value().ToBig()), new(big.Float).SetInt(big.NewInt(params.AMT))),
+ )
+ }
+ }
+
+ return nil
+}
+
+func exportBalance(ctx *cli.Context) error {
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+ db := stack.Database()
+ defer stack.Close()
+
+ roTX, err := db.BeginRo(ctx.Context)
+ if err != nil {
+ return err
+ }
+ defer roTX.Rollback()
+ //kv.ReadAhead(ctx.Context, roTX.(kv.RoDB), atomic.NewBool(false), name, nil, 1<<32-1) // MaxUint32
+ //
+ srcC, err := roTX.Cursor("Account")
+ if err != nil {
+ return err
+ }
+
+ for k, v, err := srcC.First(); k != nil; k, v, err = srcC.Next() {
+ if err != nil {
+ return err
+ }
+ var acc account.StateAccount
+ if err = acc.DecodeForStorage(v); err != nil {
+ return err
+ }
+
+ fmt.Printf("%x, %.2f\n",
+ k,
+ new(big.Float).Quo(new(big.Float).SetInt(acc.Balance.ToBig()), new(big.Float).SetInt(big.NewInt(params.AMT))),
+ )
+ }
+
+ return nil
+}
+
+func exportDBState(ctx *cli.Context) error {
+
+ stack, err := node.NewNode(ctx, &DefaultConfig)
+ if err != nil {
+ return err
+ }
+ db := stack.Database()
+ defer stack.Close()
+
+ var tsize uint64
+
+ roTX, err := db.BeginRo(ctx.Context)
+ if err != nil {
+ return err
+ }
+ defer roTX.Rollback()
+
+ migrator, ok := roTX.(kv.BucketMigrator)
+ if !ok {
+ return fmt.Errorf("cannot open db as BucketMigrator")
+ }
+ Buckets, err := migrator.ListBuckets()
+ for _, Bucket := range Buckets {
+ size, _ := roTX.BucketSize(Bucket)
+ tsize += size
+ Cursor, _ := roTX.Cursor(Bucket)
+ count, _ := Cursor.Count()
+ Cursor.Close()
+ if count != 0 {
+ fmt.Printf("%30v count %10d size: %s \r\n", Bucket, count, common.StorageSize(size))
+ }
+ }
+ fmt.Printf("total %s \n", common.StorageSize(tsize))
+ return nil
+}
+
+func dbCopy(ctx *cli.Context) error {
+
+ modules.AstInit()
+ kv.ChaindataTablesCfg = modules.AstTableCfg
+
+ fromChaindata := ctx.String(FromDataDirFlag.Name)
+ toChaindata := ctx.String(ToDataDirFlag.Name)
+
+ if f, err := os.Stat(fromChaindata); err != nil || !f.IsDir() {
+ log.Errorf("fromChaindata do not exists or is not a dir, err: %s", err)
+ return err
+ }
+ if f, err := os.Stat(toChaindata); err != nil || !f.IsDir() {
+ log.Errorf("toChaindata do not exists or is not a dir, err: %s", err)
+ return err
+ }
+
+ from, to := backup.OpenPair(fromChaindata, toChaindata, kv.ChainDB, 0)
+ err := backup.Kv2kv(ctx.Context, from, to, nil, backup.ReadAheadThreads)
+ if err != nil && !errors.Is(err, context.Canceled) {
+ if !errors.Is(err, context.Canceled) {
+ log.Error(err.Error())
+ }
+ return nil
+ }
+
+ return nil
+}
diff --git a/cmd/ast/initcmd.go b/cmd/ast/initcmd.go
new file mode 100644
index 0000000..bbe7dd5
--- /dev/null
+++ b/cmd/ast/initcmd.go
@@ -0,0 +1,91 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "github.com/astranetworld/ast/cmd/utils"
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/conf"
+ "github.com/astranetworld/ast/internal/node"
+ "github.com/astranetworld/ast/log"
+ "github.com/astranetworld/ast/modules/rawdb"
+ "github.com/ledgerwatch/erigon-lib/kv"
+ "github.com/urfave/cli/v2"
+ "os"
+)
+
+var (
+ initCommand = &cli.Command{
+ Name: "init",
+ Usage: "Bootstrap and initialize a new genesis block",
+ ArgsUsage: "",
+ Action: initGenesis,
+ Flags: []cli.Flag{
+ DataDirFlag,
+ },
+ Description: `
+The init command initializes a new genesis block and definition for the network.
+This is a destructive action and changes the network in which you will be
+participating.
+
+It expects the genesis file as argument.`,
+ }
+)
+
+// initGenesis will initialise the given JSON format genesis file and writes it as
+// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
+func initGenesis(cliCtx *cli.Context) error {
+ var (
+ err error
+ genesisBlock *block.Block
+ )
+
+ // Make sure we have a valid genesis JSON
+ genesisPath := cliCtx.Args().First()
+ if len(genesisPath) == 0 {
+ utils.Fatalf("Must supply path to genesis JSON file")
+ }
+
+ file, err := os.Open(genesisPath)
+ if err != nil {
+ utils.Fatalf("Failed to read genesis file: %v", err)
+ }
+ defer file.Close()
+
+ genesis := new(conf.Genesis)
+ if err := json.NewDecoder(file).Decode(genesis); err != nil {
+ utils.Fatalf("invalid genesis file: %v", err)
+ }
+
+ chaindb, err := node.OpenDatabase(&DefaultConfig, nil, kv.ChainDB.String())
+ if err != nil {
+ utils.Fatalf("Failed to open database: %v", err)
+ }
+ defer chaindb.Close()
+
+ if err := chaindb.Update(context.TODO(), func(tx kv.RwTx) error {
+ storedHash, err := rawdb.ReadCanonicalHash(tx, 0)
+ if err != nil {
+ return err
+ }
+
+ if storedHash != (types.Hash{}) {
+ return fmt.Errorf("genesis state are already exists")
+
+ }
+ genesisBlock, err = node.WriteGenesisBlock(tx, genesis)
+ if nil != err {
+ return err
+ }
+ if err := node.WriteChainConfig(tx, genesisBlock.Hash(), genesis); err != nil {
+ return err
+ }
+ return nil
+ }); err != nil {
+ utils.Fatalf("Failed to wrote genesis state to database: %w", err)
+ }
+ log.Info("Successfully wrote genesis state", "hash", genesisBlock.Hash())
+ return nil
+}
diff --git a/cmd/ast/logger.go b/cmd/ast/logger.go
new file mode 100644
index 0000000..4628253
--- /dev/null
+++ b/cmd/ast/logger.go
@@ -0,0 +1,87 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+//
+//var (
+// once sync.Once
+//)
+//
+//func Init(nodeConfig *conf.NodeConfig, config *conf.LoggerConfig) (*zap.Logger, error) {
+//
+// var level zapcore.Level
+// if err := level.Set(config.Level); err != nil {
+// return nil, err
+// }
+//
+// infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+// return lvl >= level && lvl < zapcore.ErrorLevel
+// })
+//
+// errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
+// return lvl >= zapcore.ErrorLevel
+// })
+//
+// var Logger *zap.Logger
+//
+// once.Do(func() {
+// encode := getEncoder()
+// core := zapcore.NewTee(
+// zapcore.NewCore(encode, getLogWriter(nodeConfig, config), infoLevel),
+// zapcore.NewCore(encode, zapcore.AddSync(os.Stdout), level),
+// zapcore.NewCore(encode, getErrorWriter(nodeConfig, config), errorLevel),
+// )
+//
+// Logger = zap.New(core, zap.AddCaller())
+// })
+//
+// return Logger, nil
+//}
+//
+//func getEncoder() zapcore.Encoder {
+// encoderConfig := zap.NewProductionEncoderConfig()
+// encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+// encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
+// encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
+// encoderConfig.EncodeName = zapcore.FullNameEncoder
+// return zapcore.NewConsoleEncoder(encoderConfig)
+//}
+//
+//func getLumberjack(path string, maxSize, maxBackups, maxAge int, compress bool) *lumberjackV2.Logger {
+// return &lumberjackV2.Logger{
+// Filename: path,
+// MaxSize: maxSize,
+// MaxBackups: maxBackups,
+// MaxAge: maxAge,
+// Compress: compress,
+// }
+//}
+//
+//func getLogWriter(nodeConfig *conf.NodeConfig, config *conf.LoggerConfig) zapcore.WriteSyncer {
+// return zapcore.AddSync(getLumberjack(fmt.Sprintf("%s/log/%s", nodeConfig.DataDir, config.LogFile), config.MaxSize, config.MaxBackups, config.MaxAge, config.Compress))
+//}
+//
+//func getErrorWriter(nodeConfig *conf.NodeConfig, config *conf.LoggerConfig) zapcore.WriteSyncer {
+// var name string
+// i := strings.LastIndex(config.LogFile, ".")
+// if i <= 0 {
+// name = fmt.Sprintf("%s_error.log", config.LogFile)
+// } else {
+// name = fmt.Sprintf("%s_error.log", config.LogFile[:i])
+// }
+// return zapcore.AddSync(getLumberjack(fmt.Sprintf("%s/log/%s", nodeConfig.DataDir, name), config.MaxSize, config.MaxBackups, config.MaxAge, config.Compress))
+//}
diff --git a/cmd/ast/main.go b/cmd/ast/main.go
new file mode 100644
index 0000000..ef74db8
--- /dev/null
+++ b/cmd/ast/main.go
@@ -0,0 +1,62 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "github.com/urfave/cli/v2"
+ "os"
+
+ "github.com/astranetworld/ast/params"
+ // Force-load the tracer engines to trigger registration
+ _ "github.com/astranetworld/ast/internal/tracers/js"
+ _ "github.com/astranetworld/ast/internal/tracers/native"
+)
+
+func main() {
+ flags := append(networkFlags, consensusFlag...)
+ flags = append(flags, loggerFlag...)
+ flags = append(flags, pprofCfg...)
+ flags = append(flags, nodeFlg...)
+ flags = append(flags, rpcFlags...)
+ flags = append(flags, authRPCFlag...)
+ flags = append(flags, configFlag...)
+ flags = append(flags, settingFlag...)
+ flags = append(flags, accountFlag...)
+ flags = append(flags, metricsFlags...)
+ flags = append(flags, p2pFlags...)
+ flags = append(flags, p2pLimitFlags...)
+
+ rootCmd = append(rootCmd, walletCommand, accountCommand, exportCommand, initCommand)
+ commands := rootCmd
+
+ app := &cli.App{
+ Name: "ast",
+ Usage: "astranet system",
+ Flags: flags,
+ Commands: commands,
+ //Version: version.FormatVersion(),
+ Version: params.VersionWithCommit(params.GitCommit, ""),
+ UseShortOptionHandling: true,
+ Action: appRun,
+ }
+
+ err := app.Run(os.Args)
+ if err != nil {
+ fmt.Printf("failed ast system setup %v", err)
+ }
+}
diff --git a/cmd/evmsdk/blst.go b/cmd/evmsdk/blst.go
new file mode 100644
index 0000000..db98c4f
--- /dev/null
+++ b/cmd/evmsdk/blst.go
@@ -0,0 +1,57 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package evmsdk
+
+import (
+ "encoding/hex"
+
+ "github.com/astranetworld/ast/common/crypto/bls"
+)
+
+func BlsSign(privKey, msg string) (interface{}, error) {
+ msgBytes, err := hex.DecodeString(msg)
+ if err != nil {
+ return nil, err
+ }
+ privKeyBytes, err := hex.DecodeString(privKey)
+ if err != nil {
+ return nil, err
+ }
+ arr := [32]byte{}
+ copy(arr[:], privKeyBytes[:])
+ sk, err := bls.SecretKeyFromRandom32Byte(arr)
+ if err != nil {
+ return nil, err
+ }
+ resBytes := sk.Sign(msgBytes).Marshal()
+ return hex.EncodeToString(resBytes), nil
+}
+
+func BlsPublicKey(privKey string) (interface{}, error) {
+ privKeyBytes, err := hex.DecodeString(privKey)
+ if err != nil {
+ return nil, err
+ }
+ arr := [32]byte{}
+ copy(arr[:], privKeyBytes[:])
+ sk, err := bls.SecretKeyFromRandom32Byte(arr)
+ if err != nil {
+ return nil, err
+ }
+ resBytes := sk.PublicKey().Marshal()
+ return hex.EncodeToString(resBytes), nil
+}
diff --git a/cmd/evmsdk/common.go b/cmd/evmsdk/common.go
new file mode 100644
index 0000000..2d76bd7
--- /dev/null
+++ b/cmd/evmsdk/common.go
@@ -0,0 +1,958 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package evmsdk
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/crypto/ecies"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/holiman/uint256"
+ "golang.org/x/crypto/sha3"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ commTyp "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/go-kit/kit/transport/http/jsonrpc"
+ "github.com/gorilla/websocket"
+
+ "github.com/astranetworld/ast/common/crypto/bls"
+)
+
+const (
+ VERSION = ""
+ EngineStateRunning = "running"
+ EngineStateStopped = "stopped"
+
+ RequestPath = ""
+ ConfigPath = "evm"
+ LogFile = "vertification_debug_log.txt"
+)
+
+var (
+ IS_DEBUG = true
+)
+
+var EE *EvmEngine = &EvmEngine{}
+
+func Emit(jsonText string) string {
+ defer func() {
+ if err := recover(); err != nil {
+ simpleLog("engine panic,err=%+v", err)
+ if err := EE.Stop(); err != nil {
+ simpleLog("engine stop error,err=%+v", err)
+ }
+ }
+ }()
+ er := new(EmitResponse)
+ eq := new(EmitRequest)
+ if err := json.Unmarshal([]byte(jsonText), eq); err != nil {
+ er.Code = 1
+ er.Message = err.Error()
+ return emitJSON(er)
+ }
+
+ if eq.Val != nil {
+ simpleLog("emit:", eq.Typ, eq.Val)
+ } else {
+ simpleLog("emit:", eq.Typ)
+ }
+
+ switch eq.Typ {
+ case "start":
+ if err := EE.Start(); err != nil {
+ er.Code, er.Message = 1, err.Error()
+ if innerErr, ok := err.(*InnerError); ok {
+ er.Code = innerErr.Code
+ er.Message = innerErr.Msg
+ }
+ return emitJSON(er)
+ }
+ case "stop":
+ if err := EE.Stop(); err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ case "setting":
+ if err := EE.Setting(eq); err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ case "list":
+ data, err := EE.List()
+ if err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ er.Data = data
+ case "blssign":
+ data, err := EE.BlsSign(eq.Val)
+ if err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ er.Data = data
+ case "blspubk":
+ data, err := EE.BlsPublicKey(eq.Val)
+ if err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ er.Data = data
+ case "state":
+ data := EE.State()
+ er.Data = data
+ case "decrypt":
+ data, err := EE.Decrypt(eq)
+ if err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ er.Data = data
+ case "encrypt":
+ data, err := EE.Encrypt(eq)
+ if err != nil {
+ er.Code, er.Message = 1, err.Error()
+ return emitJSON(er)
+ }
+ er.Data = data
+ case "test":
+ er.Message = Test()
+ }
+
+ return emitJSON(er)
+}
+
+func emitJSON(j interface{}) string {
+ a, _ := json.Marshal(j)
+ return string(a)
+}
+
+func (e *EvmEngine) initLogger(lvl string) {
+ IS_DEBUG = lvl == "debug"
+ if len(lvl) != 0 {
+ ensureLogFileExisted()
+ log.InitMobileLogger(filepath.Join(EE.AppBasePath, LogFile), IS_DEBUG)
+ }
+}
+
+func ensureLogFileExisted() {
+ logFilePath := path.Join(EE.AppBasePath, LogFile)
+ f, err := os.Open(logFilePath)
+ if err != nil {
+ f, err = os.Create(logFilePath)
+ if err != nil {
+ fmt.Printf("create log file error,err=%+v\n", err)
+ return
+ }
+ }
+ if err = f.Close(); err != nil {
+ fmt.Printf("log file close error,err=%+v", err)
+ }
+}
+
+func simpleLog(txt string, params ...interface{}) {
+ if IS_DEBUG {
+ ifces := make([]interface{}, 0, len(params)+1)
+ ifces = append(ifces, txt)
+ ifces = append(ifces, params...)
+ fmt.Println(ifces...)
+ log.Debug(txt, params...)
+ }
+}
+
+func simpleLogf(txt string, params ...interface{}) {
+ if IS_DEBUG {
+ fmt.Printf(txt+"\n", params...)
+ log.Debugf(txt, params...)
+ }
+}
+
+type Setting struct {
+ Height int
+ AppBasePath string
+ Account string
+
+ PrivKey string
+}
+
+type EmitRequest struct {
+ Typ string `json:"type"`
+ Val interface{} `json:"val"`
+}
+type EmitResponse struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ Data interface{} `json:"data"`
+}
+
+type EvmEngine struct {
+ mu sync.Mutex `json:"-"`
+ ctx context.Context `json:"-"`
+ cancelFunc context.CancelFunc `json:"-"`
+ errChan chan []error `json:"-"`
+
+ Account string `json:"account"`
+ AppBasePath string `json:"app_base_path"`
+ EngineState string `json:"state"`
+ BlockChan chan string `json:"-"`
+ PrivKey string `json:"priv_key"`
+ ServerUri string `json:"server_uri"`
+ LogLevel string `json:"log_level"`
+}
+
+type AccountReward struct {
+ Account string
+ Number *uint256.Int
+ Value *uint256.Int
+}
+type AccountRewards []*AccountReward
+
+func (r AccountRewards) Len() int {
+ return len(r)
+}
+
+func (r AccountRewards) Less(i, j int) bool {
+ return strings.Compare(r[i].Account, r[j].Account) > 0
+}
+
+func (r AccountRewards) Swap(i, j int) {
+ r[i], r[j] = r[j], r[i]
+}
+
+type InnerError struct {
+ Code int
+ Msg string
+}
+
+func (i InnerError) Error() string { return fmt.Sprintf("code:%+v,msg:%+v", i.Code, i.Msg) }
+
+type AggSign struct {
+ Number uint64 `json:"number"`
+ StateRoot commTyp.Hash `json:"stateRoot"`
+ Sign commTyp.Signature `json:"sign"`
+ PublicKey commTyp.PublicKey `json:"publicKey"`
+ Address commTyp.Address `json:"address"`
+}
+
+func (e *EvmEngine) Start() error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ e.ctx, e.cancelFunc = context.WithCancel(context.Background())
+ if e.EngineState == EngineStateRunning {
+ return fmt.Errorf("evme is running")
+ }
+ e.EngineState = EngineStateRunning
+
+ if err := e.verificationTaskBg(); err != nil {
+ e.EngineState = EngineStateStopped
+ simpleLogf("launch verification task failed,err=%+v", err.Error())
+ return err
+ }
+ return nil
+}
+
+func (e *EvmEngine) Stop() error {
+ e.mu.Lock()
+ e.EngineState = EngineStateStopped
+ if e.cancelFunc != nil {
+ e.cancelFunc()
+ }
+ if e.ctx != nil {
+ e.ctx.Done()
+ }
+ e.mu.Unlock()
+ return nil
+}
+
+func (e *EvmEngine) State() string {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ if e.EngineState == EngineStateRunning {
+ return "started"
+ } else {
+ return "stopped"
+ }
+}
+
+/*
+ {
+ "type":"setting",
+ "val":{
+ "app_base_path":"/sd/0/evm/",
+ "account":"abcdef1234"
+ }
+ }
+*/
+func (e *EvmEngine) Setting(req *EmitRequest) error {
+ m, ok := req.Val.(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("type assert error,any==>smap")
+ }
+
+ appBasePath, ok := m["app_base_path"]
+ if ok {
+ if appBasePathStr, ok := appBasePath.(string); ok {
+ e.mu.Lock()
+ e.AppBasePath = appBasePathStr
+ e.mu.Unlock()
+ }
+ }
+ if privKeyIfce, ok := m["priv_key"]; ok {
+ if privKey, ok := privKeyIfce.(string); ok {
+ e.PrivKey = privKey
+ }
+ }
+ if serverUriIfce, ok := m["server_uri"]; ok {
+ if serverUri, ok := serverUriIfce.(string); ok {
+ e.ServerUri = serverUri
+ }
+ }
+ if accIfce, ok := m["account"]; ok {
+ if account, ok := accIfce.(string); ok {
+ e.Account = account
+ }
+ }
+
+ if logLvlIfce, ok := m["log_level"]; ok {
+ if loglvlStr, ok := logLvlIfce.(string); ok {
+ e.initLogger(loglvlStr)
+ }
+ }
+
+ return nil
+}
+func (e *EvmEngine) List() (interface{}, error) {
+ type l struct {
+ A string `json:"a"`
+ B bool `json:"b"`
+ C int `json:"c"`
+ }
+
+ ll := []*l{
+ {
+ A: "a",
+ B: true,
+ C: 123,
+ }, {
+ A: "aa",
+ B: false,
+ C: 456,
+ },
+ }
+ return ll, nil
+}
+func (e *EvmEngine) SaveFile() error {
+ return nil
+}
+func (e *EvmEngine) ReloadFile() error {
+ return nil
+}
+func (e *EvmEngine) BlsSign(req interface{}) (interface{}, error) {
+ var privKey, msg string
+ blsSignMap, ok := req.(map[string]interface{})
+ if !ok {
+ return nil, errors.New("empty input value")
+ }
+ if privKeyIfce, ok := blsSignMap["priv_key"]; ok {
+ if privKey, ok = privKeyIfce.(string); !ok {
+ return nil, errors.New("priv_key type : array byte")
+ }
+ } else {
+ privKey = e.PrivKey
+ }
+ if msgIfce, ok := blsSignMap["msg"]; ok {
+ if msg, ok = msgIfce.(string); !ok {
+ return nil, errors.New("msg type : array byte")
+ }
+ }
+ return BlsSign(privKey, msg)
+}
+func (e *EvmEngine) BlsPublicKey(req interface{}) (interface{}, error) {
+ var privKey string
+ blsPublicKeyMap, ok := req.(map[string]interface{})
+ if !ok {
+ return nil, errors.New("empty input value")
+ }
+ if privKeyIfce, ok := blsPublicKeyMap["priv_key"]; ok {
+ if privKey, ok = privKeyIfce.(string); !ok {
+ return nil, errors.New("priv_key type: array byte")
+ }
+ } else {
+ privKey = e.PrivKey
+ }
+ return BlsPublicKey(privKey)
+}
+
+func (e *EvmEngine) verificationTaskBg() error {
+ simpleLog("gen pubk")
+ pubk, err := BlsPublicKey(e.PrivKey)
+ if err != nil {
+ simpleLog("generate public key error,", err)
+ return err
+ }
+ simpleLog("init websocket")
+ wssvr, err := NewWebSocketService(e.ServerUri, e.Account)
+ if err != nil {
+ return err
+ }
+ simpleLog("init websocket chats")
+ ochan, ichan, err := wssvr.Chans(pubk.(string))
+ if err != nil {
+ return err
+ }
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ buf := make([]byte, 4096)
+ runtime.Stack(buf, true)
+ simpleLog("vertification task down", "err", err)
+ simpleLog(string(buf))
+ // simpleLogf("vertification task down,err=%+v,stk:%s:%d", err, f, l)
+ }
+ e.mu.Lock()
+ e.EngineState = EngineStateStopped
+ e.mu.Unlock()
+ }()
+
+ for {
+ select {
+ case <-e.ctx.Done():
+ simpleLog("task has been cancelled")
+ return
+ case msg, ok := <-ochan:
+ if !ok {
+ simpleLog("task closed")
+ return
+ }
+ entire, err := e.unwrapJSONRPC(msg)
+ if err != nil {
+ simpleLog("unwrap jsonrpc message error,err=", err)
+ continue
+ }
+ resp, err := e.vertify(entire)
+ if err != nil {
+ simpleLog("ee verification failed", err)
+ continue
+ }
+ ichan <- resp
+ }
+ }
+ }()
+ return nil
+}
+
+func (e *EvmEngine) Decrypt(req *EmitRequest) (interface{}, error) {
+
+ var (
+ params map[string]interface{}
+ privateKeyInterface interface{}
+ privateKeyString string
+ privateKey *ecdsa.PrivateKey
+ messageInterface interface{}
+ message string
+ messageBytes []byte
+ ok bool
+ err error
+ )
+
+ if params, ok = req.Val.(map[string]interface{}); !ok {
+ return nil, fmt.Errorf("empty input value")
+ }
+
+ if privateKeyInterface, ok = params["priv_key"]; !ok {
+ return nil, fmt.Errorf("privateKey is empty")
+ }
+
+ if privateKeyString, ok = privateKeyInterface.(string); !ok {
+ return nil, fmt.Errorf("privateKey type is not string")
+ }
+
+ if privateKey, err = crypto.HexToECDSA(privateKeyString); err != nil {
+ return nil, fmt.Errorf("cannot decode private key")
+ }
+
+ if messageInterface, ok = params["msg"]; !ok {
+ return nil, fmt.Errorf("msg is empty")
+ }
+ if message, ok = messageInterface.(string); !ok {
+ return nil, fmt.Errorf("msg type is not string")
+ }
+ if messageBytes, err = hex.DecodeString(message); err != nil {
+ return nil, fmt.Errorf("msg cannote decode to bytes")
+ }
+
+ priKey := ecies.ImportECDSA(privateKey)
+
+ ms, err := priKey.Decrypt(messageBytes, nil, nil)
+ return hex.EncodeToString(ms), err
+}
+
+func (e *EvmEngine) Encrypt(req *EmitRequest) (interface{}, error) {
+
+ var (
+ params map[string]interface{}
+ publicKeyInterface interface{}
+ publicKeyString string
+ publicKeyBytes []byte
+ publicKey *ecdsa.PublicKey
+ messageInterface interface{}
+ message string
+ messageBytes []byte
+ ok bool
+ err error
+ )
+
+ if params, ok = req.Val.(map[string]interface{}); !ok {
+ return nil, fmt.Errorf("empty input value")
+ }
+
+ if publicKeyInterface, ok = params["public_key"]; !ok {
+ return nil, fmt.Errorf("public_key is empty")
+ }
+
+ if publicKeyString, ok = publicKeyInterface.(string); !ok {
+ return nil, fmt.Errorf("public_key type is not string")
+ }
+
+ if publicKeyBytes, err = hex.DecodeString(publicKeyString); err != nil {
+ return nil, fmt.Errorf("public_key cannote decode to bytes")
+ }
+
+ if publicKey, err = crypto.DecompressPubkey(publicKeyBytes); err != nil {
+ return nil, fmt.Errorf("cannot decode public key")
+ }
+
+ if messageInterface, ok = params["msg"]; !ok {
+ return nil, fmt.Errorf("msg is empty")
+ }
+ if message, ok = messageInterface.(string); !ok {
+ return nil, fmt.Errorf("msg type is not string")
+ }
+
+ if messageBytes, err = hex.DecodeString(message); err != nil {
+ return nil, fmt.Errorf("msg cannote decode to bytes")
+ }
+
+ pubKey := ecies.ImportECDSAPublic(publicKey)
+ ct, err := ecies.Encrypt(rand.Reader, pubKey, messageBytes, nil, nil)
+
+ return hex.EncodeToString(ct), err
+}
+
+//type innerEntireCode state.EntireCode
+//
+//func (h *innerEntireCode) UnmarshalJSON(in []byte) error {
+// m := map[string]json.RawMessage{}
+// if err := json.Unmarshal(in, &m); err != nil {
+// return err
+// }
+//
+// if err := json.Unmarshal(m["coinBase"], &h.CoinBase); err != nil {
+// return err
+// }
+// if err := json.Unmarshal(m["codes"], &h.Codes); err != nil {
+// return err
+// }
+// if rewardsBytes, ok := m["rewards"]; ok {
+// m := []json.RawMessage{}
+// if err := json.Unmarshal(rewardsBytes, &m); err == nil && len(m) != 0 {
+// h.Rewards = make([]block.Reward, len(m))
+// for i, _ := range h.Rewards {
+// rewardBean := block.Reward{
+// Amount: commTyp.NewInt64(0),
+// }
+// json.Unmarshal(m[i], &rewardBean)
+// h.Rewards[i] = rewardBean
+// }
+// }
+// }
+// // if err := json.Unmarshal(m["rewards"], &h.Rewards); err != nil {
+// // return err
+// // }
+//
+// h.Entire.Header = &block.Header{
+// Difficulty: commTyp.NewInt64(0),
+// Number: commTyp.NewInt64(0),
+// BaseFee: commTyp.NewInt64(0),
+// }
+// if err := json.Unmarshal(m["entire"], &h.Entire); err != nil {
+// return err
+// }
+//
+// return nil
+//}
+
+func (e *EvmEngine) vertify(in []byte) ([]byte, error) {
+ var bean state.EntireCode
+ if err := json.Unmarshal(in, &bean); err != nil {
+ simpleLog("unmarshal vertify input error,err=", err)
+ return nil, err
+ }
+
+ simpleLog("start verify ", "blockNr", bean.Entire.Header.Number.Uint64())
+
+ if bean.Entire.Header == nil {
+ return nil, errors.New("nil pointer found")
+ }
+ // before state verify
+ var hash commTyp.Hash
+ hasher := sha3.NewLegacyKeccak256()
+ state.EncodeBeforeState(hasher, bean.Entire.Snap.Items, bean.Codes)
+ hasher.(crypto.KeccakState).Read(hash[:])
+ if bean.Entire.Header.MixDigest != hash {
+ simpleLog("misMatch state hash", "want", bean.Entire.Header.MixDigest, "get", hash, "block", bean.Entire.Header.Number.Uint64())
+ return nil, errors.New("state verify failed")
+ }
+
+ entirecode := state.EntireCode(bean)
+ stateRoot := verify(e.ctx, &entirecode)
+
+ res := AggSign{}
+ //stateroot
+ copy(res.StateRoot[:], stateRoot[:])
+ if pubkIfce, err := BlsPublicKey(e.PrivKey); err == nil {
+ if pubkStr, ok := pubkIfce.(string); ok {
+ //publickey
+ copy(res.PublicKey[:], []byte(pubkStr))
+ }
+ }
+
+ simpleLog("calculated stateRoot:", "stateRoot", hexutil.Encode(res.StateRoot[:]))
+
+ //privkey
+ res.Number = bean.Entire.Header.Number.Uint64()
+ privKeyBytes, err := hex.DecodeString(e.PrivKey)
+ if err != nil {
+ return nil, err
+ }
+ arr := [32]byte{}
+ copy(arr[:], privKeyBytes[:])
+ sk, err := bls.SecretKeyFromRandom32Byte(arr)
+ if err != nil {
+ return nil, err
+ }
+
+ //sign
+ copy(res.Sign[:], sk.Sign(res.StateRoot[:]).Marshal())
+
+ simpleLog("sign stateRoot:", "Sign", hexutil.Encode(res.Sign[:]))
+
+ //address
+ res.Address = commTyp.HexToAddress(e.Account)
+
+ resBytes, err := json.Marshal(res)
+ if err != nil {
+ return nil, err
+ }
+
+ return resBytes, nil
+}
+
+func (e *EvmEngine) unwrapJSONRPC(in []byte) ([]byte, error) {
+ //"{\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32000,\"message\":\"unauthed address: 0xeB156a42dcaFcf155B07f3638892440C7dE5d564\"}}\n"
+ //ws consumer received msg:%!(EXTRA string=ws consumer received msg:, string={"jsonrpc":"2.0","id":1,"result":"0x96410b68a9f8875bb20fde06823eb861"}
+ req := new(jsonrpc.Request)
+ if err := json.Unmarshal(in, req); err != nil {
+ return nil, err
+ }
+ if len(req.Params) == 0 {
+ return []byte{}, errors.New("empty request params")
+ }
+
+ //type innerProtocolEntire struct {
+ // Entire json.RawMessage `json:"Entire"`
+ //}
+ type innerProtocol struct {
+ Subscription string `json:"subscription"`
+ Result json.RawMessage `json:"result"`
+ }
+
+ innerReq := new(innerProtocol)
+ if err := json.Unmarshal(req.Params, innerReq); err != nil {
+ return nil, err
+ }
+
+ return innerReq.Result, nil
+}
+
+//======test methods
+
+func Test() string {
+ var sw strings.Builder
+
+ sw.WriteString("version:" + VERSION + "\r\n")
+
+ engJsonBytes, err := json.Marshal(EE)
+ if err == nil {
+ sw.WriteString(string(engJsonBytes) + "\r\n")
+ }
+
+ sw.WriteString("gettime:" + strconv.Itoa(int(GetTime())) + "\r\n")
+
+ sw.WriteString("getapppath:" + GetAppPath() + "\r\n")
+
+ sw.WriteString("getjson:" + GetJson() + "\r\n")
+
+ sw.WriteString("readfile_before:" + ReadTouchedFile() + "\r\n")
+ sw.WriteString("touchfile:" + TouchFile() + "\r\n")
+ sw.WriteString("readfile_after:" + ReadTouchedFile() + "\r\n")
+
+ ns := GetNetInfos()
+ sw.WriteString("getnetinfos:resplen:" + strconv.Itoa(len(ns)) + "\r\n")
+
+ sw.WriteString("backgroundthread:" + BackgroundLoop() + "\r\n")
+
+ sw.WriteString("bls tests " + BlsTest() + "\r\n")
+
+ //block here
+ sw.WriteString("wsocket:" + GetWebSocketConnect() + "\r\n")
+
+ return sw.String()
+}
+
+func GetTime() int64 {
+ return time.Now().Unix()
+}
+
+func GetAppPath() string {
+ path, err := os.Getwd()
+ if err != nil {
+ return err.Error()
+ }
+
+ return fmt.Sprintf("exec path:%+v;;setting_path:%+v", path, EE.AppBasePath)
+}
+
+func GetJson() string {
+ return `{
+ "a":"b",
+ "c":"d"
+ }`
+}
+
+func TouchFile() string {
+ path := path.Join(EE.AppBasePath, "evm_touched_file.txt")
+ file, err := os.Create(path)
+ if err != nil {
+ return err.Error()
+ }
+ defer file.Close()
+ t := time.Now().Unix()
+ n, err := file.WriteString(strconv.Itoa(int(t)) + "|NAME=wux PWD=$WORK/src GOOS=android GOARCH=386 CC=$ANDROID_HOME/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang CXX=$ANDROID_HOME/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android21-clang++ CGO_ENABLED=1 GOPATH=$WORK:$GOPATH go mod tidy")
+ if err != nil {
+ return err.Error()
+ }
+ if n == 0 {
+ return "n==0"
+ }
+ return file.Name()
+}
+
+func ReadTouchedFile() string {
+ path := path.Join(EE.AppBasePath, "evm_touched_file.txt")
+ file, err := os.Open(path)
+ if err != nil {
+ return fmt.Sprintln("open touched file error,err=", err.Error())
+ }
+ defer file.Close()
+ allBytes, err := io.ReadAll(file)
+ if err != nil {
+ return fmt.Sprintln("read touched file error,err=", err.Error())
+ }
+ return string(allBytes)
+}
+
+func GetNetInfos() string {
+ resp, err := http.DefaultClient.Get("https://www.baidu.com")
+ if err != nil {
+ return err.Error()
+ }
+ htmlBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return err.Error()
+ }
+ return string(htmlBytes)
+}
+
+func RunLoop() string {
+ for {
+ <-time.After(2 * time.Second)
+ fmt.Println("1")
+ }
+}
+
+var backgroundLoopState = ""
+
+func BackgroundLoop() string {
+ if len(backgroundLoopState) != 0 {
+ fmt.Println("BackgroundLoop running already")
+ return "BackgroundLoop running"
+ }
+ backgroundLoopState = "123"
+ go func() {
+ for {
+ <-time.After(2 * time.Second)
+ fmt.Println("alive")
+ }
+ }()
+ return "OK"
+}
+
+func GetWebSocketConnect() string {
+ var sw strings.Builder
+ conn, connResp, err := websocket.DefaultDialer.Dial("ws://54.175.247.94:20013", nil)
+ if err != nil {
+ return fmt.Sprintf("dial error,err=%+v \r\n", err)
+ }
+ defer conn.Close()
+ connRespBytes, err := ioutil.ReadAll(connResp.Body)
+ if err != nil {
+ return fmt.Sprintf("connresp return error,err=%+v", err)
+ }
+ sw.WriteString(string(connRespBytes))
+ err = conn.WriteMessage(websocket.TextMessage, []byte(`{
+ "jsonrpc":"2.0",
+ "method":"eth_subscribe",
+ "params":["newHeads"],
+ "id":1
+}`))
+ if err != nil {
+ return fmt.Sprintf("connresp write message error,err=%+v", err)
+ }
+
+ _, msg, err := conn.ReadMessage()
+ fmt.Println(string(msg))
+ if err != nil {
+ sw.WriteString("read message error,err=" + err.Error())
+ }
+ sw.WriteString("received message:" + string(msg))
+
+ go func() {
+ innerConn, _, err := websocket.DefaultDialer.Dial("ws://174.129.114.74:8546", nil)
+ if err != nil {
+ fmt.Printf("bg dial error,err=%+v \r\n", err)
+ return
+ }
+ defer innerConn.Close()
+ err = innerConn.WriteMessage(websocket.TextMessage, []byte(`{
+ "jsonrpc":"2.0",
+ "method":"eth_subscribe",
+ "params":["newHeads"],
+ "id":1
+ }`))
+ if err != nil {
+ fmt.Printf("bg writemsg error,err=%+v \r\n", err)
+ return
+ }
+ for {
+ fmt.Println("bg readmsg:waitting")
+ _, msg, err := innerConn.ReadMessage()
+ if err != nil {
+ fmt.Println("bg readmsg error,err=" + err.Error())
+ return
+ }
+ fmt.Println("bg readed msg:" + string(msg))
+ }
+ }()
+
+ return sw.String()
+}
+
+func BlsTest() string {
+ var sw strings.Builder
+
+ var err error
+ err = bls.TestSignVerify2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestAggregateVerify2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestAggregateVerify_CompressedSignatures2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestFastAggregateVerify2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestVerifyCompressed2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestMultipleSignatureVerification2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestEth2FastAggregateVerify2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestEth2FastAggregateVerify_ReturnsFalseOnEmptyPubKeyList2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestEth2FastAggregateVerify_ReturnsTrueOnG2PointAtInfinity2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestSignatureFromBytes2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestMultipleSignatureFromBytes2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestCopy2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+ err = bls.TestSecretKeyFromBytes2()
+ if err != nil {
+ sw.WriteString(err.Error() + "\r\n")
+ }
+
+ sw.WriteString("bls test done.")
+ sw.WriteString("==============")
+
+ return sw.String()
+}
diff --git a/cmd/evmsdk/common_test.go b/cmd/evmsdk/common_test.go
new file mode 100644
index 0000000..48e405e
--- /dev/null
+++ b/cmd/evmsdk/common_test.go
@@ -0,0 +1,429 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package evmsdk
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "testing"
+)
+
+func TestGetNetInfos(t *testing.T) {
+ tests := []struct {
+ name string
+ want string
+ }{
+ // TODO: Add test cases.
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := GetNetInfos(); got != tt.want {
+ t.Errorf("GetNetInfos() = %v, want %v", got, tt.want)
+ } else {
+ t.Log(got)
+ fmt.Println(got)
+ }
+ })
+ }
+}
+
+//func TestEmit(t *testing.T) {
+// type args struct {
+// jsonText string
+// }
+//
+// //settttting
+// aa := Emit(`{"type":"test","val":{"app_base_path":"/Users/xan/misc"}}`)
+// fmt.Println(aa)
+//
+// // tests := []struct {
+// // name string
+// // args args
+// // }{{
+// // name: "t1",
+// // args: args{
+// // jsonText: `{"type":"start"}`,
+// // },
+// // }, {
+// // name: "t2",
+// // args: args{
+// // jsonText: `{"type":"test"}`,
+// // },
+// // },
+// // // {
+// // // name: "t3",
+// // // args: args{
+// // // jsonText: `{"type":"list"}`,
+// // // },
+// // // },
+// // }
+// // for _, tt := range tests {
+// // t.Run(tt.name, func(t *testing.T) {
+// // got := Emit(tt.args.jsonText)
+// // if len(got) == 0 {
+// // t.Errorf("Emit() = %v", got)
+// // }
+// // t.Log(got)
+// // fmt.Println(got)
+// // })
+// // }
+//
+// // <-time.After(60 * time.Second)
+//}
+
+func TestBlsSign(t *testing.T) {
+ pk := make([]byte, 32)
+ _, err := rand.Read(pk)
+ if err != nil {
+ t.Error(err)
+ }
+
+ req := `
+{
+ "type":"blssign",
+ "val":{
+ "priv_key":"` + hex.EncodeToString(pk) + `",
+ "msg":"123123"
+ }
+}
+ `
+ resp := Emit(req)
+ _ = resp
+ fmt.Println(resp)
+}
+
+//func TestEvmEngine_Start(t *testing.T) {
+// type fields struct {
+// mu sync.Mutex
+// ctx context.Context
+// cancelFunc context.CancelFunc
+// Account string
+// AppBasePath string
+// State string
+// BlockChan chan string
+// PrivKey string
+// ServerUri string
+// }
+// tests := []struct {
+// name string
+// fields fields
+// wantErr bool
+// }{
+// {
+// name: "t1",
+// fields: fields{
+// PrivKey: `b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSgodcFy6hKA9enyiAzEKfpg7Rib5AFx3w33V0NsMYOAoUXtbQtFIOIIgNsTBj9ei1DdGJ4QSbCgw3w37X7oXvkAAAAqAK68x8CuvMfAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKCh1wXLqEoD16fKIDMQp+mDtGJvkAXHfDfdXQ2wxg4ChRe1tC0Ug4giA2xMGP16LUN0YnhBJsKDDfDftfuhe+QAAAAhAMhPG7U/g7k9+YWm66Yk1yDhUvwkHmLMWvV/bTtGeElWAAAACW1hY0Bib2dvbgECAwQFBg==`,
+// ServerUri: "ws://127.0.0.1:20013",
+// Account: "0x01",
+// },
+// wantErr: false,
+// },
+// }
+// for _, tt := range tests {
+// t.Run(tt.name, func(t *testing.T) {
+// e := &EvmEngine{
+// mu: tt.fields.mu,
+// ctx: tt.fields.ctx,
+// cancelFunc: tt.fields.cancelFunc,
+// Account: tt.fields.Account,
+// AppBasePath: tt.fields.AppBasePath,
+// EngineState: tt.fields.State,
+// BlockChan: tt.fields.BlockChan,
+// PrivKey: tt.fields.PrivKey,
+// ServerUri: tt.fields.ServerUri,
+// }
+// if err := e.Start(); (err != nil) != tt.wantErr {
+// t.Errorf("EvmEngine.Start() error = %v, wantErr %v", err, tt.wantErr)
+// }
+// })
+// }
+//}
+
+//func TestEvmEngine_vertify(t *testing.T) {
+// type fields struct {
+// mu sync.Mutex
+// ctx context.Context
+// cancelFunc context.CancelFunc
+// Account string
+// AppBasePath string
+// State string
+// BlockChan chan string
+// PrivKey string
+// ServerUri string
+// }
+// type args struct {
+// in []byte
+// }
+// tests := []struct {
+// name string
+// fields fields
+// args args
+// want []byte
+// wantErr bool
+// }{
+// {
+// name: "t1",
+// fields: fields{
+// PrivKey: `37d15846af852c47649005fd6dfe33483d394aaa60c14e7c56f4deadb116329e`,
+// ServerUri: "ws://127.0.0.1:20013",
+// Account: "0x01",
+// },
+// args: args{
+// in: []byte(`
+// {
+// "entire":
+// {
+// "header":{
+// "parentHash":"0xeab8f2dca7682e72c594bfe9da4d83fb623741b21c729f57a7ff06a69aa3be38",
+// "sha3Uncles":"0xeab8f2dca7682e72c594bfe9da4d83fb623741b21c729f57a7ff06a69aa3be38",
+// "miner":"0x588639773Bc6F163aa262245CDa746c120676431",
+// "stateRoot":"0xf9e3e616232713a12a90a2abc2302d5fd2b6360764c204ebe2acda342390841b",
+// "transactionsRoot":"0xf9e3e616232713a12a90a2abc2302d5fd2b6360764c204ebe2acda342390841b",
+// "receiptsRoot":"0xf9e3e616232713a12a90a2abc2302d5fd2b6360764c204ebe2acda342390841b",
+// "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+// "difficulty":"0x2a65b3",
+// "number":"0x1532d9",
+// "gasLimit":"0x1c9c380",
+// "gasUsed": "0x0",
+// "timestamp":"0x638f0e18",
+// "extraData": "0xd883010a17846765746888676f312e31382e35856c696e757800000000000000ab9c084cefea3b41830c827c91b8908b12f64b08a243ee4fabd28bdd4556154f00d9a08f7c4a6c2794a63100d92910595e6747db4af69d576c224373c707db4400"
+// },
+// "uncles":[],
+// "transactions":[],
+// "pprof":"0xf9e3e616232713a12a90a2abc2302d5fd2b6360764c204ebe2acda342390841b",
+// "senders":[]
+// },
+// "codes":[]
+// }`),
+// },
+// want: []byte("321123bb"),
+// wantErr: false,
+// },
+// }
+// for _, tt := range tests {
+// t.Run(tt.name, func(t *testing.T) {
+// e := &EvmEngine{
+// mu: tt.fields.mu,
+// ctx: tt.fields.ctx,
+// cancelFunc: tt.fields.cancelFunc,
+// Account: tt.fields.Account,
+// AppBasePath: tt.fields.AppBasePath,
+// EngineState: tt.fields.State,
+// BlockChan: tt.fields.BlockChan,
+// PrivKey: tt.fields.PrivKey,
+// ServerUri: tt.fields.ServerUri,
+// }
+// got, err := e.vertify(tt.args.in)
+// if (err != nil) != tt.wantErr {
+// t.Errorf("EvmEngine.vertify() error = %v, wantErr %v", err, tt.wantErr)
+// return
+// }
+// if !reflect.DeepEqual(got, tt.want) {
+// t.Errorf("EvmEngine.vertify() = %v, want %v", got, tt.want)
+// }
+// })
+// }
+//}
+
+//func TestEngineStart(t *testing.T) {
+// EE.Setting(&EmitRequest{
+// Typ: "setting",
+// Val: map[string]interface{}{
+// "app_base_path": "/Users/xan/misc",
+// "priv_key": "37d15846af852c47649005fd6dfe33483d394aaa60c14e7c56f4deadb116329e", //2d09d9f4e166f35a4ab0a2edd599e2a23bbe86b312b2e05b34d9fbe5693b1e48
+// "server_uri": "ws://127.0.0.1:20013",
+// "account": "0x588639773bc6f163aa262245cda746c120676431", //0x7Ac869Ff8b6232f7cfC4370A2df4a81641Cba3d9
+// },
+// })
+// if err := EE.Start(); err != nil {
+// t.Error(err)
+// }
+// select {}
+//}
+
+/*
+0xb1fd8daE392D8E7c5aA0c8D405EFC56e1F55c42c
+732d6346fc020087ac84bd1a42adb444e0f783fe907759ba7fa977aab3bf66fc
+*/
+//func TestEmitComplexEmulation(t *testing.T) {
+// EE.Setting(&EmitRequest{
+// Typ: "setting",
+// Val: map[string]interface{}{
+// "app_base_path": "/Users/mac/misc",
+// "priv_key": "de4b76c3dca3d8e10aea7644f77b316a68a6476fbd119d441ead5c6131aa42a7", //2d09d9f4e166f35a4ab0a2edd599e2a23bbe86b312b2e05b34d9fbe5693b1e48
+// "server_uri": "ws://54.175.247.94:20013",
+// "account": "0x65081DBA9E7B5398ec4d40f1794003c54dB11B79", //0x7Ac869Ff8b6232f7cfC4370A2df4a81641Cba3d9
+// },
+// })
+// if s := Emit(`{"type":"start"}`); len(s) != 0 {
+// fmt.Println(s)
+// }
+//
+// // i := 0
+// // for {
+// // if s := Emit(`{"type":"state"}`); len(s) != 0 {
+// // fmt.Println(s)
+// // }
+// // <-time.After(1 * time.Second)
+// // if i%20 == 0 {
+// // Emit(`{"type":"start"}`)
+// // }
+// // i++
+// // }
+//
+// select {}
+//}
+
+//func TestEngineState(t *testing.T) {
+// EE.Setting(&EmitRequest{
+// Typ: "setting",
+// Val: map[string]interface{}{
+// "app_base_path": "/Users/xan/misc",
+// "priv_key": "37d15846af852c47649005fd6dfe33483d394aaa60c14e7c56f4deadb116329e",
+// "server_uri": "ws://127.0.0.1:20013",
+// "account": "0x7Ac869Ff8b6232f7cfC4370A2df4a81641Cba3d9",
+// },
+// })
+// if ss := EE.State(); ss != "stopped" {
+// t.Error("unstopped")
+// }
+// if err := EE.Start(); err != nil {
+// t.Error(err)
+// }
+// if ss := EE.State(); ss != "started" {
+// t.Error("unstarted")
+// }
+// select {}
+//}
+
+//func TestUnmarshalInput(t *testing.T) {
+// EE.Setting(&EmitRequest{
+// Typ: "setting",
+// Val: map[string]interface{}{
+// "app_base_path": "/Users/xan/misc",
+// "priv_key": "37d15846af852c47649005fd6dfe33483d394aaa60c14e7c56f4deadb116329e",
+// "server_uri": "ws://127.0.0.1:20013",
+// "account": "0x7Ac869Ff8b6232f7cfC4370A2df4a81641Cba3d9",
+// },
+// })
+// if ss := EE.State(); ss != "stopped" {
+// t.Error("unstopped")
+// }
+// if err := EE.Start(); err != nil {
+// t.Error(err)
+// }
+// if ss := EE.State(); ss != "started" {
+// t.Error("unstarted")
+// }
+// select {}
+//}
+
+func TestBlssign(t *testing.T) {
+ resp := Emit(`
+{"type": "blssign","val": {"priv_key": "202cbf36864a88e348c8f573aa0bc79f5a7119e58251c3580bb08af70cb2dfed", "msg" : "8ac7230489e80000"}}
+ `)
+ _ = resp
+}
+
+func TestEngineStop(t *testing.T) {
+ if err := EE.Stop(); err != nil {
+ t.Error(err)
+ }
+}
+
+/*
+{
+ "jsonrpc": "2.0",
+ "method": "eth_subscription",
+ "params": {
+ "subscription": "0x7e6684b10d6c906c47253690b8e9c7fa",
+ "result": {
+ "Entire": {
+ "entire": {
+ "header": {
+ "parentHash": "0x76b286a66229e34455f5238b9f9c0d516903c222b4e5874a6662820e25dcde98",
+ "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "miner": "0x0000000000000000000000000000000000000000",
+ "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "difficulty": "0x2",
+ "number": "0xd2",
+ "gasLimit": "0x18462971",
+ "gasUsed": "0x0",
+ "timestamp": "0x63a15be4",
+ "extraData": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "nonce": "0x0000000000000000",
+ "baseFeePerGas": null,
+ "hash": "0x051510ec6c9998e61126d285169ed6b24114b27c264ce0e5dad5e0a7bbac2f42"
+ },
+ "uncles": null,
+ "transactions": [],
+ "snap": {
+ "items": [
+ {
+ "key": "uelEd/X4i16Noul+hQbW5PzwTls=",
+ "value": "CAEaEzB4NjgxNTVhNDM2NzZlMDAwMDAiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKiDF0kYBhvcjPJJ+fbLcxwPA5QC2U8qCJzt7+tgEXYWkcA=="
+ },
+ {
+ "key": "m6M2g1Qiuu/FN9dWQpWdKoZlAKM=",
+ "value": "CAEaEzB4NjgxNTVhNDM2NzZlMDAwMDAiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKiDF0kYBhvcjPJJ+fbLcxwPA5QC2U8qCJzt7+tgEXYWkcA=="
+ },
+ {
+ "key": "RUH8HMtOBCo7rf5GkE+dItEntoI=",
+ "value": "CAEaEzB4NjgxNTVhNDM2NzZlMDAwMDAiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKiDF0kYBhvcjPJJ+fbLcxwPA5QC2U8qCJzt7+tgEXYWkcA=="
+ }
+ ],
+ "outHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
+ },
+ "proof": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "senders": null
+ },
+ "codes": [],
+ "headers": [],
+ "rewards": {
+ "0x4541fc1ccb4e042a3badfe46904f9d22d127b682": {
+ "Int": "0x20f5b1eaad8d80000"
+ },
+ "0x9ba336835422baefc537d75642959d2a866500a3": {
+ "Int": "0x1d7d843dc3b480000"
+ },
+ "0xb9e94477f5f88b5e8da2e97e8506d6e4fcf04e5b": {
+ "Int": "0x1bc16d674ec800000"
+ }
+ }
+ }
+ }
+ }
+}
+*/
+
+func TestBeanchMark(t *testing.T) {
+
+ var (
+ key, _ = crypto.HexToECDSA("d6d8d19bd786d6676819b806694b1100a4414a94e51e9a82a351bd8f7f3f3658")
+ addr = crypto.PubkeyToAddress(key.PublicKey)
+ //signer = new(types.HomesteadSigner)
+ //content = context.Background()
+ )
+
+ fmt.Println(addr)
+}
diff --git a/cmd/evmsdk/verify.go b/cmd/evmsdk/verify.go
new file mode 100644
index 0000000..bb685b3
--- /dev/null
+++ b/cmd/evmsdk/verify.go
@@ -0,0 +1,140 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package evmsdk
+
+import (
+ "context"
+ "fmt"
+ "github.com/astranetworld/ast/common/hexutil"
+ "unsafe"
+
+ common2 "github.com/astranetworld/ast/common"
+ block2 "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal"
+ "github.com/astranetworld/ast/internal/consensus/apos"
+ "github.com/astranetworld/ast/internal/consensus/misc"
+ "github.com/astranetworld/ast/internal/vm"
+ "github.com/astranetworld/ast/modules/ethdb/olddb"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/astranetworld/ast/params"
+)
+
+func verify(ctx context.Context, msg *state.EntireCode) types.Hash {
+ codeMap := make(map[types.Hash][]byte)
+ for _, pair := range msg.Codes {
+ codeMap[pair.Hash] = pair.Code
+ }
+
+ readCodeF := func(hash types.Hash) ([]byte, error) {
+ if code, ok := codeMap[hash]; ok {
+ return code, nil
+ }
+ return nil, nil
+ }
+
+ hashMap := make(map[uint64]types.Hash)
+ for _, h := range msg.Headers {
+ hashMap[h.Number.Uint64()] = h.Hash()
+ }
+ getNumberHash := func(n uint64) types.Hash {
+ if hash, ok := hashMap[n]; ok {
+ return hash
+ }
+ return types.Hash{}
+ }
+
+ var txs transaction.Transactions
+ for _, tByte := range msg.Entire.Transactions {
+ tmp := &transaction.Transaction{}
+ if err := tmp.Unmarshal(tByte); nil != err {
+ panic(err)
+ }
+ txs = append(txs, tmp)
+ }
+
+ body := &block2.Body{
+ Txs: txs,
+ }
+
+ block := block2.NewBlockFromStorage(msg.Entire.Header.Hash(), msg.Entire.Header, body)
+ batch := olddb.NewHashBatch(nil, ctx.Done(), "")
+ defer batch.Rollback()
+ old := make(map[string][]byte, len(msg.Entire.Snap.Items))
+ for _, v := range msg.Entire.Snap.Items {
+ old[*(*string)(unsafe.Pointer(&v.Key))] = v.Value
+ }
+ stateReader := olddb.NewStateReader(old, nil, batch, block.Number64().Uint64())
+ stateReader.SetReadCodeF(readCodeF)
+ ibs := state.New(stateReader)
+ ibs.SetSnapshot(msg.Entire.Snap)
+ ibs.SetHeight(block.Number64().Uint64())
+ ibs.SetGetOneFun(batch.GetOne)
+
+ root, err := checkBlock2(getNumberHash, block, ibs, msg.CoinBase, msg.Rewards)
+ if nil != err {
+ panic(err)
+ }
+ return root
+}
+
+func checkBlock2(getHashF func(n uint64) types.Hash, block *block2.Block, ibs *state.IntraBlockState, coinbase types.Address, rewards []*block2.Reward) (types.Hash, error) {
+ header := block.Header().(*block2.Header)
+ chainConfig := params.MainnetChainConfig
+ if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number64().ToBig()) == 0 {
+ misc.ApplyDAOHardFork(ibs)
+ }
+ noop := state.NewNoopWriter()
+
+ usedGas := new(uint64)
+ gp := new(common2.GasPool)
+ gp.AddGas(block.GasLimit())
+ cfg := vm.Config{}
+ //cfg := vm.Config{Debug: true, Tracer: logger.NewMarkdownLogger(nil, os.Stdout)}
+
+ engine := apos.NewFaker()
+ for i, tx := range block.Transactions() {
+ //
+ simpleLog("apply transaction", "transactionIndex", i, "from", tx.From().Hex(), "to", tx.To().Hex(), "value", tx.Value().Uint64(), "data", hexutil.Encode(tx.Data()), "gas", tx.Gas(), "gasPrice", tx.GasPrice().Uint64())
+ //
+ ibs.Prepare(tx.Hash(), block.Hash(), i)
+ _, _, err := internal.ApplyTransaction(chainConfig, getHashF, engine, &coinbase, gp, ibs, noop, header, tx, usedGas, cfg)
+ if err != nil {
+
+ return types.Hash{}, err
+ }
+ }
+
+ if !cfg.StatelessExec && *usedGas != header.GasUsed {
+ return types.Hash{}, fmt.Errorf("gas used by execution: %d, in header: %d", *usedGas, header.GasUsed)
+ }
+
+ if len(rewards) > 0 {
+ for _, reward := range rewards {
+ if reward.Amount != nil && !reward.Amount.IsZero() {
+ if !ibs.Exist(reward.Address) {
+ ibs.CreateAccount(reward.Address, false)
+ }
+ ibs.AddBalance(reward.Address, reward.Amount)
+ }
+ }
+ ibs.SoftFinalise()
+ }
+
+ return ibs.IntermediateRoot(), nil
+}
diff --git a/cmd/evmsdk/ws.go b/cmd/evmsdk/ws.go
new file mode 100644
index 0000000..990c5c3
--- /dev/null
+++ b/cmd/evmsdk/ws.go
@@ -0,0 +1,199 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package evmsdk
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "runtime"
+
+ "github.com/gorilla/websocket"
+)
+
+type WebSocketService struct {
+ addr string
+ acc string
+ readConn *websocket.Conn
+ writeConn *websocket.Conn
+}
+
+func NewWebSocketService(addr, acc string) (*WebSocketService, error) {
+ simpleLog("init ws read conn")
+ readConn, readResp, err := websocket.DefaultDialer.Dial(addr, nil)
+ if err != nil {
+ simpleLog("init ws read conn error,err=%+v", err)
+ return nil, err
+ }
+ if readResp != nil {
+ readRespBytes, err := ioutil.ReadAll(readResp.Body)
+ if err != nil {
+ simpleLog("readresp error", "err", err)
+ }
+ simpleLog("readresp", "readRespBytes", string(readRespBytes))
+ }
+ readConn.SetPongHandler(func(appData string) error { simpleLog("read pong", appData); return nil })
+ readConn.SetPingHandler(func(appData string) error { simpleLog("read ping", appData); return nil })
+ simpleLog("init ws read conn")
+
+ writeConn, writeResp, err := websocket.DefaultDialer.Dial(addr, nil)
+ if err != nil {
+ simpleLog("init ws write conn error,err=%+v", err)
+ return nil, err
+ }
+ if writeResp != nil {
+ readRespBytes, err := ioutil.ReadAll(writeResp.Body)
+ if err != nil {
+ simpleLog("writeresp error", "err", err)
+ }
+ simpleLog("writeresp", "writeRespBytes", string(readRespBytes))
+ }
+ writeConn.SetPongHandler(func(appData string) error { simpleLog("write pong", appData); return nil })
+ writeConn.SetPingHandler(func(appData string) error { simpleLog("write ping", appData); return nil })
+ simpleLog("init dial ws done")
+ return &WebSocketService{
+ addr: addr,
+ readConn: readConn,
+ writeConn: writeConn,
+ acc: acc,
+ }, nil
+}
+
+func (ws *WebSocketService) Chans(pubk string) (<-chan []byte, chan<- []byte, error) {
+ simpleLog("ping read conn")
+ err := ws.readConn.PingHandler()("")
+ if err != nil {
+ simpleLog("ping read conn error,err=%+v", err)
+ return nil, nil, err
+ }
+ simpleLog("ping done")
+ msg := `{
+ "jsonrpc": "2.0",
+ "method": "eth_subscribe",
+ "params": [
+ "minedBlock",
+ "` + ws.acc + `"
+ ],
+ "id": 1
+}`
+ simpleLog("minedBlock message:%+v", msg)
+
+ if err := ws.readConn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
+ simpleLog("minedBlock message write message error,err=%+v", err)
+ return nil, nil, err
+ }
+
+ _, msg0, err := ws.readConn.ReadMessage()
+ if err != nil {
+ simpleLog("init read conn readmessage error,err=", err)
+ return nil, nil, err
+ }
+ msg0bean := new(JSONRPCRequest)
+ if err := json.Unmarshal(msg0, msg0bean); err != nil {
+ simpleLog("unmarshal message0 error,err=", err)
+ return nil, nil, err
+ }
+ if msg0bean.Error != nil {
+ if msgCode := msg0bean.Error["code"]; msgCode != nil {
+ if msgCodeFloat, ok := msgCode.(float64); ok && msgCodeFloat != 0 {
+ simpleLogf("init read conn error,code=%+v ,message=%+v", msgCodeFloat, msg0bean.Error["message"])
+ return nil, nil, &InnerError{Code: int(msgCodeFloat), Msg: msg0bean.Error["message"].(string)}
+ }
+ }
+ }
+
+ chO, chI := make(chan []byte, 0), make(chan []byte, 0)
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ buf := make([]byte, 4096)
+ runtime.Stack(buf, true)
+ simpleLog("readconn goroutine down", "err", err)
+ simpleLog(string(buf))
+ }
+ EE.Stop()
+ }()
+ for {
+ msgType, b, err := ws.readConn.ReadMessage()
+ if err != nil {
+ simpleLog("ws closed:", err.Error())
+ close(chO)
+ return
+ }
+ if msgType == websocket.TextMessage {
+ chO <- b
+ }
+ simpleLog("received msg:", string(b))
+ }
+ }()
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ buf := make([]byte, 4096)
+ runtime.Stack(buf, true)
+ simpleLog("writeconn goroutine down", "err", err)
+ simpleLog(string(buf))
+ }
+ EE.Stop()
+ }()
+ for msg := range chI {
+ /*
+ {
+ "jsonrpc":"2.0",
+ "method":"eth_submitSign",
+ "params":[{
+ "number":11111,
+ "stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000",
+ "sign":"0xb4dd42744e40aa50bc0e747a52dcf8d1196c08a0f9fcfd8fda0aaa413a47bbdda20d1a76298642ee68b1f1009df149680e4e4b14f8c488faa4010ad9f26a81379c93d7f0da5596192da203c31a6301cd50d53734e537d828fe7f9593eb7035d3",
+ "publicKey":"0xa8a236acd9b7ea68c1858a3059e4aad7698842f7de49ace8dc49acc922bd6d543792e98b7b74fa380bf622671792d57a"
+ }],
+ "id":1
+ }
+ */
+ wrappedRequest, err := ws.unwrapJSONRPCRequest(msg)
+ if err != nil {
+ simpleLog("wrapJSONRPCRequest error,err=", err)
+ continue
+ }
+ if err := ws.writeConn.WriteMessage(websocket.TextMessage, wrappedRequest); err != nil {
+ simpleLog("ws producer stopped")
+ return
+ }
+ simpleLog("submit Sign to chain")
+ }
+ }()
+ return chO, chI, nil
+}
+
+type JSONRPCRequest struct {
+ JsonRpc string `json:"jsonrpc"`
+ Method string `json:"method"`
+ ID int `json:"id"`
+ Params []json.RawMessage `json:"params"`
+ Error map[string]interface{} `json:"error"`
+}
+
+func (ws *WebSocketService) unwrapJSONRPCRequest(in []byte) ([]byte, error) {
+ d := &JSONRPCRequest{
+ JsonRpc: "2.0",
+ Method: "eth_submitSign",
+ ID: 1,
+ Params: make([]json.RawMessage, 1),
+ }
+ d.Params[0] = in
+ return json.Marshal(d)
+
+}
diff --git a/cmd/utils/prompt.go b/cmd/utils/prompt.go
new file mode 100644
index 0000000..0d5b7df
--- /dev/null
+++ b/cmd/utils/prompt.go
@@ -0,0 +1,59 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package utils
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/console/prompt"
+)
+
+// GetPassPhrase displays the given text(prompt) to the user and requests some textual
+// data to be entered, but one which must not be echoed out into the terminal.
+// The method returns the input provided by the user.
+func GetPassPhrase(text string, confirmation bool) string {
+ if text != "" {
+ fmt.Println(text)
+ }
+ password, err := prompt.Stdin.PromptPassword("Password: ")
+ if err != nil {
+ Fatalf("Failed to read password: %v", err)
+ }
+ if confirmation {
+ confirm, err := prompt.Stdin.PromptPassword("Repeat password: ")
+ if err != nil {
+ Fatalf("Failed to read password confirmation: %v", err)
+ }
+ if password != confirm {
+ Fatalf("Passwords do not match")
+ }
+ }
+ return password
+}
+
+// GetPassPhraseWithList retrieves the password associated with an account, either fetched
+// from a list of preloaded passphrases, or requested interactively from the user.
+func GetPassPhraseWithList(text string, confirmation bool, index int, passwords []string) string {
+ // If a list of passwords was supplied, retrieve from them
+ if len(passwords) > 0 {
+ if index < len(passwords) {
+ return passwords[index]
+ }
+ return passwords[len(passwords)-1]
+ }
+ // Otherwise prompt the user for the password
+ password := GetPassPhrase(text, confirmation)
+ return password
+}
diff --git a/cmd/utils/tools.go b/cmd/utils/tools.go
new file mode 100644
index 0000000..2fbb021
--- /dev/null
+++ b/cmd/utils/tools.go
@@ -0,0 +1,24 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+//go:build tools
+// +build tools
+
+package tools
+
+import (
+ // Tool imports for go:generate.
+ _ "github.com/fjl/gencodec"
+)
diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go
new file mode 100644
index 0000000..c6cb930
--- /dev/null
+++ b/cmd/utils/utils.go
@@ -0,0 +1,76 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package utils
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "io"
+ "os"
+ "runtime"
+ "strconv"
+
+ "github.com/astranetworld/ast/accounts"
+ "github.com/astranetworld/ast/accounts/keystore"
+ "github.com/astranetworld/ast/internal/avm/common"
+)
+
+// MakeAddress converts an account specified directly as a hex encoded string or
+// a key index in the key store to an internal account representation.
+func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) {
+ // If the specified account is a valid address, return it
+ if common.IsHexAddress(account) {
+ return accounts.Account{Address: types.Address(common.HexToAddress(account))}, nil
+ }
+ // Otherwise try to interpret the account as a keystore index
+ index, err := strconv.Atoi(account)
+ if err != nil || index < 0 {
+ return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account)
+ }
+ log.Warn("-------------------------------------------------------------------")
+ log.Warn("Referring to accounts by order in the keystore folder is dangerous!")
+ log.Warn("This functionality is deprecated and will be removed in the future!")
+ log.Warn("Please use explicit addresses! (can search via `geth account list`)")
+ log.Warn("-------------------------------------------------------------------")
+
+ accs := ks.Accounts()
+ if len(accs) <= index {
+ return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs))
+ }
+ return accs[index], nil
+}
+
+// Fatalf formats a message to standard error and exits the program.
+// The message is also printed to standard output if standard error
+// is redirected to a different file.
+func Fatalf(format string, args ...interface{}) {
+ w := io.MultiWriter(os.Stdout, os.Stderr)
+ if runtime.GOOS == "windows" {
+ // The SameFile check below doesn't work on Windows.
+ // stdout is unlikely to get redirected though, so just print there.
+ w = os.Stdout
+ } else {
+ outf, _ := os.Stdout.Stat()
+ errf, _ := os.Stderr.Stat()
+ if outf != nil && errf != nil && os.SameFile(outf, errf) {
+ w = os.Stderr
+ }
+ }
+ fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
+ os.Exit(1)
+}
diff --git a/cmd/verify/main.go b/cmd/verify/main.go
new file mode 100644
index 0000000..11a45f2
--- /dev/null
+++ b/cmd/verify/main.go
@@ -0,0 +1,200 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "context"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/crypto/bls"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/api"
+ "github.com/astranetworld/ast/log"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/go-kit/kit/transport/http/jsonrpc"
+ "github.com/gorilla/websocket"
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+var privateKey bls.SecretKey
+var addressKey types.Address
+
+func RootContext() (context.Context, context.CancelFunc) {
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ defer cancel()
+
+ ch := make(chan os.Signal, 1)
+ defer close(ch)
+
+ signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
+ defer signal.Stop(ch)
+
+ select {
+ case sig := <-ch:
+ log.Info("Got interrupt, shutting down...", "sig", sig)
+ case <-ctx.Done():
+ }
+ }()
+ return ctx, cancel
+}
+
+func main() {
+ var err error
+ sByte, err := hex.DecodeString("2d09d9f4e166f35a4ab0a2edd599e2a23bbe86b312b2e05b34d9fbe5693b1e48")
+ if nil != err {
+ panic(err)
+ }
+
+ var sb [32]byte
+ copy(sb[:], sByte)
+ privateKey, err = bls.SecretKeyFromRandom32Byte(sb)
+ if nil != err {
+ panic(err)
+ }
+
+ ecdPk, err := crypto.HexToECDSA("2d09d9f4e166f35a4ab0a2edd599e2a23bbe86b312b2e05b34d9fbe5693b1e48")
+ if nil != err {
+ panic(err)
+ }
+ addressKey = crypto.PubkeyToAddress(ecdPk.PublicKey)
+
+ ctx, cancle := RootContext()
+ defer cancle()
+
+ con, _, err := websocket.DefaultDialer.DialContext(ctx, "ws://127.0.0.1:20013", nil)
+ defer con.Close()
+
+ end := make(chan struct{})
+ defer close(end)
+
+ go func() {
+ for {
+ select {
+ case <-ctx.Done():
+ end <- struct{}{}
+ return
+ default:
+ typ, msg, err := con.ReadMessage()
+ if nil != err {
+ log.Errorf("read msg failed: %v", err)
+ continue
+ }
+ if typ == websocket.TextMessage {
+ fmt.Println("read msg: ", string(msg))
+ params, err := unwrapJSONRPC(msg)
+ if nil != err {
+ log.Warn(err.Error())
+ continue
+ }
+
+ bean := new(state.EntireCode)
+ if err := json.Unmarshal(params, bean); err != nil {
+ log.Errorf("unmarshal entire failed, %v", err)
+ continue
+ }
+
+ root := verify(ctx, bean)
+ res := api.AggSign{}
+ res.Number = bean.Entire.Header.Number.Uint64()
+ res.Address = addressKey
+ res.StateRoot = root
+ copy(res.Sign[:], privateKey.Sign(root[:]).Marshal())
+ in, err := json.Marshal(res)
+ if nil != err {
+ panic(err)
+ }
+
+ wrapRequest, _ := wrapJSONRPCRequest(in)
+ if err := con.WriteMessage(websocket.TextMessage, wrapRequest); nil != err {
+ log.Error("write msg failed: ", err)
+ }
+ log.Infof("write msg: %s", wrapRequest)
+ }
+ }
+ }
+ }()
+
+ if err = con.PingHandler()(""); nil != err {
+ panic(err)
+ }
+
+ if err := con.WriteMessage(websocket.TextMessage, []byte(`{
+ "jsonrpc": "2.0",
+ "method": "eth_subscribe",
+ "params": [
+ "minedBlock",
+ "`+addressKey.String()+`"
+ ],
+ "id": 1
+ }`)); err != nil {
+
+ cancle()
+ }
+
+ <-end
+}
+
+func unwrapJSONRPC(in []byte) ([]byte, error) {
+ //"{\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32000,\"message\":\"unauthed address: 0xeB156a42dcaFcf155B07f3638892440C7dE5d564\"}}\n"
+ //ws consumer received msg:%!(EXTRA string=ws consumer received msg:, string={"jsonrpc":"2.0","id":1,"result":"0x96410b68a9f8875bb20fde06823eb861"}
+ req := new(jsonrpc.Request)
+ if err := json.Unmarshal(in, req); err != nil {
+ return nil, err
+ }
+ if len(req.Params) == 0 {
+ return []byte{}, errors.New("empty request params")
+ }
+
+ //type innerProtocolEntire struct {
+ // Entire json.RawMessage `json:"Entire"`
+ //}
+ type innerProtocol struct {
+ Subscription string `json:"subscription"`
+ Result json.RawMessage `json:"result"`
+ }
+
+ innerReq := new(innerProtocol)
+ if err := json.Unmarshal(req.Params, innerReq); err != nil {
+ return nil, err
+ }
+
+ return innerReq.Result, nil
+}
+
+type JSONRPCRequest struct {
+ JsonRpc string `json:"jsonrpc"`
+ Method string `json:"method"`
+ ID int `json:"id"`
+ Params []json.RawMessage `json:"params"`
+}
+
+func wrapJSONRPCRequest(in []byte) ([]byte, error) {
+ d := &JSONRPCRequest{
+ JsonRpc: "2.0",
+ Method: "eth_submitSign",
+ ID: 1,
+ Params: make([]json.RawMessage, 1),
+ }
+ d.Params[0] = in
+ return json.Marshal(d)
+}
diff --git a/cmd/verify/verify.go b/cmd/verify/verify.go
new file mode 100644
index 0000000..b89c518
--- /dev/null
+++ b/cmd/verify/verify.go
@@ -0,0 +1,135 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package main
+
+import (
+ "context"
+ "fmt"
+ common2 "github.com/astranetworld/ast/common"
+ block2 "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal"
+ "github.com/astranetworld/ast/internal/consensus/apos"
+ "github.com/astranetworld/ast/internal/consensus/misc"
+ "github.com/astranetworld/ast/internal/vm"
+ "github.com/astranetworld/ast/modules/ethdb/olddb"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/astranetworld/ast/params"
+ "unsafe"
+)
+
+func verify(ctx context.Context, msg *state.EntireCode) types.Hash {
+ codeMap := make(map[types.Hash][]byte)
+ for _, pair := range msg.Codes {
+ codeMap[pair.Hash] = pair.Code
+ }
+
+ readCodeF := func(hash types.Hash) ([]byte, error) {
+ if code, ok := codeMap[hash]; ok {
+ return code, nil
+ }
+ return nil, nil
+ }
+
+ hashMap := make(map[uint64]types.Hash)
+ for _, h := range msg.Headers {
+ hashMap[h.Number.Uint64()] = h.Hash()
+ }
+ getNumberHash := func(n uint64) types.Hash {
+ if hash, ok := hashMap[n]; ok {
+ return hash
+ }
+ return types.Hash{}
+ }
+
+ var txs transaction.Transactions
+ for _, tByte := range msg.Entire.Transactions {
+ tmp := &transaction.Transaction{}
+ if err := tmp.Unmarshal(tByte); nil != err {
+ panic(err)
+ }
+ txs = append(txs, tmp)
+ }
+
+ body := &block2.Body{
+ Txs: txs,
+ }
+
+ block := block2.NewBlockFromStorage(msg.Entire.Header.Hash(), msg.Entire.Header, body)
+ batch := olddb.NewHashBatch(nil, ctx.Done(), "")
+ defer batch.Rollback()
+ old := make(map[string][]byte, len(msg.Entire.Snap.Items))
+ for _, v := range msg.Entire.Snap.Items {
+ old[*(*string)(unsafe.Pointer(&v.Key))] = v.Value
+ }
+ stateReader := olddb.NewStateReader(old, nil, batch, block.Number64().Uint64())
+ stateReader.SetReadCodeF(readCodeF)
+ ibs := state.New(stateReader)
+ ibs.SetSnapshot(msg.Entire.Snap)
+ ibs.SetHeight(block.Number64().Uint64())
+ ibs.SetGetOneFun(batch.GetOne)
+
+ root, err := checkBlock(getNumberHash, block, ibs, msg.CoinBase, msg.Rewards)
+ if nil != err {
+ panic(err)
+ }
+ return root
+}
+
+func checkBlock(getHashF func(n uint64) types.Hash, block *block2.Block, ibs *state.IntraBlockState, coinbase types.Address, rewards []*block2.Reward) (types.Hash, error) {
+ header := block.Header().(*block2.Header)
+ chainConfig := params.MainnetChainConfig
+ if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number64().ToBig()) == 0 {
+ misc.ApplyDAOHardFork(ibs)
+ }
+ noop := state.NewNoopWriter()
+
+ usedGas := new(uint64)
+ gp := new(common2.GasPool)
+ gp.AddGas(block.GasLimit())
+ cfg := vm.Config{}
+ //cfg := vm.Config{Debug: true, Tracer: logger.NewMarkdownLogger(nil, os.Stdout)}
+
+ engine := apos.NewFaker()
+ for i, tx := range block.Transactions() {
+ ibs.Prepare(tx.Hash(), block.Hash(), i)
+ _, _, err := internal.ApplyTransaction(chainConfig, getHashF, engine, &coinbase, gp, ibs, noop, header, tx, usedGas, cfg)
+ if err != nil {
+
+ return types.Hash{}, err
+ }
+ }
+
+ if !cfg.StatelessExec && *usedGas != header.GasUsed {
+ return types.Hash{}, fmt.Errorf("gas used by execution: %d, in header: %d", *usedGas, header.GasUsed)
+ }
+
+ if len(rewards) > 0 {
+ for _, reward := range rewards {
+ if reward.Amount != nil && !reward.Amount.IsZero() {
+ if !ibs.Exist(reward.Address) {
+ ibs.CreateAccount(reward.Address, false)
+ }
+ ibs.AddBalance(reward.Address, reward.Amount)
+ }
+ }
+ ibs.SoftFinalise()
+ }
+
+ return ibs.IntermediateRoot(), nil
+}
diff --git a/common/account/state_account.go b/common/account/state_account.go
new file mode 100644
index 0000000..29ef3cf
--- /dev/null
+++ b/common/account/state_account.go
@@ -0,0 +1,682 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package account
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/api/protocol/state"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+)
+
+// Account is the Ethereum consensus representation of accounts.
+// These objects are stored in the main account trie.
+// DESCRIBED: docs/programmers_guide/guide.md#ethereum-state
+type StateAccount struct {
+ Initialised bool
+ Nonce uint64
+ Balance uint256.Int
+ Root types.Hash
+ CodeHash types.Hash // hash of the bytecode
+ Incarnation uint16
+}
+
+const (
+ MimetypeDataWithValidator = "data/validator"
+ MimetypeTypedData = "data/typed"
+ MimetypeClique = "application/x-clique-header"
+ MimetypeParlia = "application/x-parlia-header"
+ MimetypeBor = "application/x-bor-header"
+ MimetypeTextPlain = "text/plain"
+)
+
+var emptyCodeHash = crypto.Keccak256Hash(nil)
+
+// var emptyRoot = types.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+var emptyRoot = types.BytesHash(crypto.Keccak256(nil))
+
+// NewAccount creates a new account w/o code nor storage.
+func NewAccount() StateAccount {
+ return StateAccount{
+ Root: emptyRoot,
+ CodeHash: emptyCodeHash,
+ }
+}
+
+func (a *StateAccount) EncodingLengthForStorage() uint {
+ pb := a.ToProtoMessage()
+ return uint(proto.Size(pb))
+}
+
+// var structLength uint = 1 // 1 byte for fieldset
+//
+// if !a.Balance.IsZero() {
+// structLength += uint(a.Balance.ByteLen()) + 1
+// }
+//
+// if a.Nonce > 0 {
+// structLength += uint((bits.Len64(a.Nonce)+7)/8) + 1
+// }
+//
+// if !a.IsEmptyCodeHash() {
+// structLength += 33 // 32-byte array + 1 bytes for length
+// }
+//
+// if a.Incarnation > 0 {
+// structLength += uint((bits.Len64(a.Incarnation)+7)/8) + 1
+// }
+//
+// return structLength
+//}
+
+//func (a *Account) EncodingLengthForHashing() uint {
+// var structLength uint
+//
+// balanceBytes := 0
+// if !a.Balance.LtUint64(128) {
+// balanceBytes = a.Balance.ByteLen()
+// }
+//
+// nonceBytes := rlp.IntLenExcludingHead(a.Nonce)
+//
+// structLength += uint(balanceBytes + nonceBytes + 2)
+//
+// structLength += 66 // Two 32-byte arrays + 2 prefixes
+//
+// if structLength < 56 {
+// return 1 + structLength
+// }
+//
+// lengthBytes := (bits.Len(structLength) + 7) / 8
+//
+// return uint(1+lengthBytes) + structLength
+//}
+
+func (a *StateAccount) EncodeForStorage(buffer []byte) {
+ pb := a.ToProtoMessage()
+ data, _ := proto.Marshal(pb)
+ copy(buffer, data)
+ //var fieldSet = 0 // start with first bit set to 0
+ //var pos = 1
+ //if a.Nonce > 0 {
+ // fieldSet = 1
+ // nonceBytes := (bits.Len64(a.Nonce) + 7) / 8
+ // buffer[pos] = byte(nonceBytes)
+ // var nonce = a.Nonce
+ // for i := nonceBytes; i > 0; i-- {
+ // buffer[pos+i] = byte(nonce)
+ // nonce >>= 8
+ // }
+ // pos += nonceBytes + 1
+ //}
+ //
+ //// Encoding balance
+ //if !a.Balance.IsZero() {
+ // fieldSet |= 2
+ // balanceBytes := a.Balance.ByteLen()
+ // buffer[pos] = byte(balanceBytes)
+ // pos++
+ // a.Balance.WriteToSlice(buffer[pos : pos+balanceBytes])
+ // pos += balanceBytes
+ //}
+ //
+ //if a.Incarnation > 0 {
+ // fieldSet |= 4
+ // incarnationBytes := (bits.Len64(uint64(a.Incarnation)) + 7) / 8
+ // buffer[pos] = byte(incarnationBytes)
+ // var incarnation = a.Incarnation
+ // for i := incarnationBytes; i > 0; i-- {
+ // buffer[pos+i] = byte(incarnation)
+ // incarnation >>= 8
+ // }
+ // pos += incarnationBytes + 1
+ //}
+ //
+ //// Encoding CodeHash
+ //if !a.IsEmptyCodeHash() {
+ // fieldSet |= 8
+ // buffer[pos] = 32
+ // copy(buffer[pos+1:], a.CodeHash.Bytes())
+ // //pos += 33
+ //}
+ //
+ //buffer[0] = byte(fieldSet)
+}
+
+// Decodes length and determines whether it corresponds to a structure of a byte array
+//func decodeLengthForHashing(buffer []byte, pos int) (length int, structure bool, newPos int) {
+// switch firstByte := int(buffer[pos]); {
+// case firstByte < 128:
+// return 0, false, pos
+// case firstByte < 184:
+// return firstByte - 128, false, pos + 1
+// case firstByte < 192:
+// // Next byte is the length of the length + 183
+// lenEnd := pos + 1 + firstByte - 183
+// len := 0
+// for i := pos + 1; i < lenEnd; i++ {
+// len = (len << 8) + int(buffer[i])
+// }
+// return len, false, lenEnd
+// case firstByte < 248:
+// return firstByte - 192, true, pos + 1
+// default:
+// // Next byte is the length of the length + 247
+// lenEnd := pos + 1 + firstByte - 247
+// len := 0
+// for i := pos + 1; i < lenEnd; i++ {
+// len = (len << 8) + int(buffer[i])
+// }
+// return len, true, lenEnd
+// }
+//}
+
+//var rlpEncodingBufPool = sync.Pool{
+// New: func() interface{} {
+// buf := make([]byte, 0, 128)
+// return &buf
+// },
+//}
+
+//func (a *Account) EncodeRLP(w io.Writer) error {
+// var buf []byte
+// l := a.EncodingLengthForHashing()
+// if l > 128 {
+// buf = make([]byte, l)
+// } else {
+// bp := rlpEncodingBufPool.Get().(*[]byte)
+// defer rlpEncodingBufPool.Put(bp)
+// buf = *bp
+// buf = buf[:l]
+// }
+//
+// a.EncodeForHashing(buf)
+// _, err := w.Write(buf)
+// return err
+//}
+
+//func (a *Account) EncodeForHashing(buffer []byte) {
+// balanceBytes := 0
+// if !a.Balance.LtUint64(128) {
+// balanceBytes = a.Balance.ByteLen()
+// }
+//
+// nonceBytes := rlp.IntLenExcludingHead(a.Nonce)
+//
+// var structLength = uint(balanceBytes + nonceBytes + 2)
+// structLength += 66 // Two 32-byte arrays + 2 prefixes
+//
+// var pos int
+// if structLength < 56 {
+// buffer[0] = byte(192 + structLength)
+// pos = 1
+// } else {
+// lengthBytes := (bits.Len(structLength) + 7) / 8
+// buffer[0] = byte(247 + lengthBytes)
+//
+// for i := lengthBytes; i > 0; i-- {
+// buffer[i] = byte(structLength)
+// structLength >>= 8
+// }
+//
+// pos = lengthBytes + 1
+// }
+//
+// // Encoding nonce
+// if a.Nonce < 128 && a.Nonce != 0 {
+// buffer[pos] = byte(a.Nonce)
+// } else {
+// buffer[pos] = byte(128 + nonceBytes)
+// var nonce = a.Nonce
+// for i := nonceBytes; i > 0; i-- {
+// buffer[pos+i] = byte(nonce)
+// nonce >>= 8
+// }
+// }
+// pos += 1 + nonceBytes
+//
+// // Encoding balance
+// if a.Balance.LtUint64(128) && !a.Balance.IsZero() {
+// buffer[pos] = byte(a.Balance.Uint64())
+// pos++
+// } else {
+// buffer[pos] = byte(128 + balanceBytes)
+// pos++
+// a.Balance.WriteToSlice(buffer[pos : pos+balanceBytes])
+// pos += balanceBytes
+// }
+//
+// // Encoding Root and CodeHash
+// buffer[pos] = 128 + 32
+// pos++
+// copy(buffer[pos:], a.Root[:])
+// pos += 32
+// buffer[pos] = 128 + 32
+// pos++
+// copy(buffer[pos:], a.CodeHash[:])
+// //pos += 32
+//}
+
+// Copy makes `a` a full, independent (meaning that if the `image` changes in any way, it does not affect `a`) copy of the account `image`.
+func (a *StateAccount) Copy(image *StateAccount) {
+ a.Initialised = image.Initialised
+ a.Nonce = image.Nonce
+ a.Balance.Set(&image.Balance)
+ copy(a.Root[:], image.Root[:])
+ copy(a.CodeHash[:], image.CodeHash[:])
+ a.Incarnation = image.Incarnation
+}
+
+//func (a *Account) DecodeForHashing(enc []byte) error {
+// length, structure, pos := decodeLengthForHashing(enc, 0)
+// if pos+length != len(enc) {
+// return fmt.Errorf(
+// "malformed RLP for Account(%x): prefixLength(%d) + dataLength(%d) != sliceLength(%d)",
+// enc, pos, length, len(enc))
+// }
+// if !structure {
+// return fmt.Errorf(
+// "encoding of Account should be RLP struct, got byte array: %x",
+// enc,
+// )
+// }
+//
+// a.Initialised = true
+// a.Nonce = 0
+// a.Balance.Clear()
+// a.Root = emptyRoot
+// a.CodeHash = emptyCodeHash
+// if length == 0 && structure {
+// return nil
+// }
+//
+// if pos < len(enc) {
+// nonceBytes, s, newPos := decodeLengthForHashing(enc, pos)
+// if s {
+// return fmt.Errorf(
+// "encoding of Account.Nonce should be byte array, got RLP struct: %x",
+// enc[pos:newPos+nonceBytes],
+// )
+// }
+//
+// if newPos+nonceBytes > len(enc) {
+// return fmt.Errorf(
+// "malformed RLP for Account.Nonce(%x): prefixLength(%d) + dataLength(%d) >= sliceLength(%d)",
+// enc[pos:newPos+nonceBytes],
+// newPos-pos, nonceBytes, len(enc)-pos,
+// )
+// }
+//
+// var nonce uint64
+// if nonceBytes == 0 && newPos == pos {
+// nonce = uint64(enc[newPos])
+// pos = newPos + 1
+// } else {
+// for _, b := range enc[newPos : newPos+nonceBytes] {
+// nonce = (nonce << 8) + uint64(b)
+// }
+// pos = newPos + nonceBytes
+// }
+//
+// a.Nonce = nonce
+// }
+// if pos < len(enc) {
+// balanceBytes, s, newPos := decodeLengthForHashing(enc, pos)
+// if s {
+// return fmt.Errorf(
+// "encoding of Account.Balance should be byte array, got RLP struct: %x",
+// enc[pos:newPos+balanceBytes],
+// )
+// }
+//
+// if newPos+balanceBytes > len(enc) {
+// return fmt.Errorf("malformed RLP for Account.Balance(%x): prefixLength(%d) + dataLength(%d) >= sliceLength(%d)",
+// enc[pos],
+// newPos-pos, balanceBytes, len(enc)-pos,
+// )
+// }
+//
+// if balanceBytes == 0 && newPos == pos {
+// a.Balance.SetUint64(uint64(enc[newPos]))
+// pos = newPos + 1
+// } else {
+// a.Balance.SetBytes(enc[newPos : newPos+balanceBytes])
+// pos = newPos + balanceBytes
+// }
+// }
+// if pos < len(enc) {
+// rootBytes, s, newPos := decodeLengthForHashing(enc, pos)
+// if s {
+// return fmt.Errorf(
+// "encoding of Account.Root should be byte array, got RLP struct: %x",
+// enc[pos:newPos+rootBytes],
+// )
+// }
+//
+// if rootBytes != 32 {
+// return fmt.Errorf(
+// "encoding of Account.Root should have size 32, got %d",
+// rootBytes,
+// )
+// }
+//
+// if newPos+rootBytes > len(enc) {
+// return fmt.Errorf("malformed RLP for Account.Root(%x): prefixLength(%d) + dataLength(%d) >= sliceLength(%d)",
+// enc[pos],
+// newPos-pos, rootBytes, len(enc)-pos,
+// )
+// }
+//
+// copy(a.Root[:], enc[newPos:newPos+rootBytes])
+// pos = newPos + rootBytes
+// }
+//
+// if pos < len(enc) {
+// codeHashBytes, s, newPos := decodeLengthForHashing(enc, pos)
+// if s {
+// return fmt.Errorf(
+// "encoding of Account.CodeHash should be byte array, got RLP struct: %x",
+// enc[pos:newPos+codeHashBytes],
+// )
+// }
+//
+// if codeHashBytes != 32 {
+// return fmt.Errorf(
+// "encoding of Account.CodeHash should have size 32, got %d",
+// codeHashBytes,
+// )
+// }
+//
+// if newPos+codeHashBytes > len(enc) {
+// return fmt.Errorf("malformed RLP for Account.CodeHash(%x): prefixLength(%d) + dataLength(%d) >= sliceLength(%d)",
+// enc[pos:newPos+codeHashBytes],
+// newPos-pos, codeHashBytes, len(enc)-pos,
+// )
+// }
+//
+// copy(a.CodeHash[:], enc[newPos:newPos+codeHashBytes])
+// pos = newPos + codeHashBytes
+// }
+//
+// if pos < len(enc) {
+// storageSizeBytes, s, newPos := decodeLengthForHashing(enc, pos)
+// if s {
+// return fmt.Errorf(
+// "encoding of Account.StorageSize should be byte array, got RLP struct: %x",
+// enc[pos:newPos+storageSizeBytes],
+// )
+// }
+//
+// if newPos+storageSizeBytes > len(enc) {
+// return fmt.Errorf(
+// "malformed RLP for Account.StorageSize(%x): prefixLength(%d) + dataLength(%d) >= sliceLength(%d)",
+// enc[pos:newPos+storageSizeBytes],
+// newPos-pos, storageSizeBytes, len(enc)-pos,
+// )
+// }
+//
+// // Commented out because of the ineffectual assignment - uncomment if adding more fields
+// var storageSize uint64
+// if storageSizeBytes == 0 && newPos == pos {
+// storageSize = uint64(enc[newPos])
+// pos = newPos + 1
+// } else {
+// for _, b := range enc[newPos : newPos+storageSizeBytes] {
+// storageSize = (storageSize << 8) + uint64(b)
+// }
+// pos = newPos + storageSizeBytes
+// }
+// _ = storageSize
+// }
+// _ = pos
+//
+// return nil
+//}
+
+func (a *StateAccount) Reset() {
+ a.Initialised = true
+ a.Nonce = 0
+ a.Incarnation = 0
+ a.Balance.Clear()
+ copy(a.Root[:], emptyRoot[:])
+ copy(a.CodeHash[:], emptyCodeHash[:])
+}
+
+func (a *StateAccount) DecodeForStorage(enc []byte) error {
+ a.Reset()
+ if len(enc) == 0 {
+ return nil
+ }
+ return a.Unmarshal(enc)
+ //pbAccount := new(state.Account)
+ //if err := proto.Unmarshal(enc, pbAccount); nil != err {
+ // return err
+ //}
+ //if err := a.FromProtoMessage(pbAccount); nil != err {
+ // return err
+ //}
+
+ //a.Reset()
+ //
+ //if len(enc) == 0 {
+ // return nil
+ //}
+ //
+ //var fieldSet = enc[0]
+ //var pos = 1
+ //
+ //if fieldSet&1 > 0 {
+ // decodeLength := int(enc[pos])
+ //
+ // if len(enc) < pos+decodeLength+1 {
+ // return fmt.Errorf(
+ // "malformed CBOR for Account.Nonce: %s, Length %d",
+ // enc[pos+1:], decodeLength)
+ // }
+ //
+ // a.Nonce = bytesToUint64(enc[pos+1 : pos+decodeLength+1])
+ // pos += decodeLength + 1
+ //}
+ //
+ //if fieldSet&2 > 0 {
+ // decodeLength := int(enc[pos])
+ //
+ // if len(enc) < pos+decodeLength+1 {
+ // return fmt.Errorf(
+ // "malformed CBOR for Account.Nonce: %s, Length %d",
+ // enc[pos+1:], decodeLength)
+ // }
+ //
+ // a.Balance.SetBytes(enc[pos+1 : pos+decodeLength+1])
+ // pos += decodeLength + 1
+ //}
+ //
+ //if fieldSet&4 > 0 {
+ // decodeLength := int(enc[pos])
+ //
+ // if len(enc) < pos+decodeLength+1 {
+ // return fmt.Errorf(
+ // "malformed CBOR for Account.Incarnation: %s, Length %d",
+ // enc[pos+1:], decodeLength)
+ // }
+ //
+ // a.Incarnation = uint16(bytesToUint64(enc[pos+1 : pos+decodeLength+1]))
+ // pos += decodeLength + 1
+ //}
+ //
+ //if fieldSet&8 > 0 {
+ //
+ // decodeLength := int(enc[pos])
+ //
+ // if decodeLength != 32 {
+ // return fmt.Errorf("codehash should be 32 bytes long, got %d instead",
+ // decodeLength)
+ // }
+ //
+ // if len(enc) < pos+decodeLength+1 {
+ // return fmt.Errorf(
+ // "malformed CBOR for Account.CodeHash: %s, Length %d",
+ // enc[pos+1:], decodeLength)
+ // }
+ //
+ // a.CodeHash.SetBytes(enc[pos+1 : pos+decodeLength+1])
+ // pos += decodeLength + 1
+ //}
+ //
+ //_ = pos
+}
+func bytesToUint64(buf []byte) (x uint64) {
+ for i, b := range buf {
+ x = x<<8 + uint64(b)
+ if i == 7 {
+ return
+ }
+ }
+ return
+}
+
+//func DecodeIncarnationFromStorage(enc []byte) (uint64, error) {
+// if len(enc) == 0 {
+// return 0, nil
+// }
+//
+// var fieldSet = enc[0]
+// var pos = 1
+//
+// //looks for the position incarnation is at
+// if fieldSet&1 > 0 {
+// decodeLength := int(enc[pos])
+// if len(enc) < pos+decodeLength+1 {
+// return 0, fmt.Errorf(
+// "malformed CBOR for Account.Nonce: %s, Length %d",
+// enc[pos+1:], decodeLength)
+// }
+// pos += decodeLength + 1
+// }
+//
+// if fieldSet&2 > 0 {
+// decodeLength := int(enc[pos])
+// if len(enc) < pos+decodeLength+1 {
+// return 0, fmt.Errorf(
+// "malformed CBOR for Account.Nonce: %s, Length %d",
+// enc[pos+1:], decodeLength)
+// }
+// pos += decodeLength + 1
+// }
+//
+// if fieldSet&4 > 0 {
+// decodeLength := int(enc[pos])
+//
+// //checks if the ending position is correct if not returns 0
+// if len(enc) < pos+decodeLength+1 {
+// return 0, fmt.Errorf(
+// "malformed CBOR for Account.Incarnation: %s, Length %d",
+// enc[pos+1:], decodeLength)
+// }
+//
+// incarnation := bytesToUint64(enc[pos+1 : pos+decodeLength+1])
+// return incarnation, nil
+// }
+//
+// return 0, nil
+//
+//}
+
+func (a *StateAccount) SelfCopy() *StateAccount {
+ newAcc := NewAccount()
+ newAcc.Copy(a)
+ return &newAcc
+}
+
+func (a *StateAccount) IsEmptyCodeHash() bool {
+ return IsEmptyCodeHash(a.CodeHash)
+}
+
+func IsEmptyCodeHash(codeHash types.Hash) bool {
+ return codeHash == emptyCodeHash || codeHash == (types.Hash{})
+}
+
+func (a *StateAccount) IsEmptyRoot() bool {
+ return a.Root == emptyRoot || a.Root == types.Hash{}
+}
+
+func (a *StateAccount) GetIncarnation() uint16 {
+ return a.Incarnation
+}
+
+func (a *StateAccount) SetIncarnation(v uint16) {
+ a.Incarnation = v
+}
+
+func (a *StateAccount) Equals(acc *StateAccount) bool {
+ return a.Nonce == acc.Nonce &&
+ a.CodeHash == acc.CodeHash &&
+ a.Balance.Cmp(&acc.Balance) == 0 &&
+ a.Incarnation == acc.Incarnation
+}
+
+func (a *StateAccount) Marshal() ([]byte, error) {
+ protoMsg := a.ToProtoMessage()
+ v, err := proto.Marshal(protoMsg)
+ if nil != err {
+ return nil, err
+ }
+ return v, nil
+}
+
+func (a *StateAccount) Unmarshal(v []byte) error {
+ var pAccount state.Account
+ if err := proto.Unmarshal(v, &pAccount); nil != err {
+ return err
+ }
+ a.Initialised = pAccount.Initialised
+ a.Nonce = pAccount.Nonce
+ a.Balance = *utils.ConvertH256ToUint256Int(pAccount.Balance)
+ a.Root = utils.ConvertH256ToHash(pAccount.Root)
+ a.CodeHash = utils.ConvertH256ToHash(pAccount.CodeHash)
+ a.Incarnation = uint16(pAccount.Incarnation)
+ return nil
+}
+
+func (a *StateAccount) ToProtoMessage() proto.Message {
+ return &state.Account{
+ Initialised: a.Initialised,
+ Nonce: a.Nonce,
+ Balance: utils.ConvertUint256IntToH256(&a.Balance),
+ Root: utils.ConvertHashToH256(a.Root),
+ CodeHash: utils.ConvertHashToH256(a.CodeHash),
+ Incarnation: uint64(a.Incarnation),
+ }
+}
+
+func (a *StateAccount) FromProtoMessage(msg proto.Message) error {
+ pAccount, ok := msg.(*state.Account)
+ if !ok {
+ return fmt.Errorf("impossible type assert ")
+ }
+
+ a.Initialised = pAccount.Initialised
+ a.Nonce = pAccount.Nonce
+ a.Balance = *utils.ConvertH256ToUint256Int(pAccount.Balance)
+ a.Root = utils.ConvertH256ToHash(pAccount.Root)
+ a.CodeHash = utils.ConvertH256ToHash(pAccount.CodeHash)
+ a.Incarnation = uint16(pAccount.Incarnation)
+ return nil
+}
diff --git a/common/big.go b/common/big.go
new file mode 100644
index 0000000..02c0ba5
--- /dev/null
+++ b/common/big.go
@@ -0,0 +1,29 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import "math/big"
+
+var (
+ Big0 = big.NewInt(0)
+ Big1 = big.NewInt(1)
+ Big2 = big.NewInt(2)
+ Big3 = big.NewInt(3)
+ Big32 = big.NewInt(32)
+ Big256 = big.NewInt(256)
+ Big257 = big.NewInt(257)
+)
diff --git a/common/block/block.go b/common/block/block.go
new file mode 100644
index 0000000..b4f6a0f
--- /dev/null
+++ b/common/block/block.go
@@ -0,0 +1,270 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/hash"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+ "strings"
+ "sync/atomic"
+ "time"
+)
+
+//type writeCounter types.StorageSize
+//
+//func (c *writeCounter) Write(b []byte) (int, error) {
+// *c += writeCounter(len(b))
+// return len(b), nil
+//}
+
+type Block struct {
+ header *Header
+ body *Body
+
+ hash atomic.Value
+ size atomic.Value
+
+ ReceiveAt time.Time
+ ReceivedFrom interface{}
+}
+
+type Verify struct {
+ Address types.Address
+ PublicKey types.PublicKey
+}
+
+func (v *Verify) ToProtoMessage() proto.Message {
+ var pbVerifier types_pb.Verifier
+ pbVerifier.Address = utils.ConvertAddressToH160(v.Address)
+ pbVerifier.PublicKey = utils.ConvertPublicKeyToH384(v.PublicKey)
+ return &pbVerifier
+}
+
+func (v *Verify) FromProtoMessage(pbVerifier *types_pb.Verifier) *Verify {
+ v.Address = utils.ConvertH160toAddress(pbVerifier.Address)
+ v.PublicKey = utils.ConvertH384ToPublicKey(pbVerifier.PublicKey)
+ return v
+}
+
+type Reward struct {
+ Address types.Address
+ Amount *uint256.Int
+}
+
+type Rewards []*Reward
+
+func (r Rewards) Len() int {
+ return len(r)
+}
+
+func (r Rewards) Less(i, j int) bool {
+ return strings.Compare(r[i].Address.String(), r[j].Address.String()) > 0
+}
+
+func (r Rewards) Swap(i, j int) {
+ r[i], r[j] = r[j], r[i]
+}
+
+func (r *Reward) ToProtoMessage() proto.Message {
+ var pbReward types_pb.Reward
+ pbReward.Address = utils.ConvertAddressToH160(r.Address)
+ pbReward.Amount = utils.ConvertUint256IntToH256(r.Amount)
+ return &pbReward
+}
+
+func (r *Reward) FromProtoMessage(pbReward *types_pb.Reward) *Reward {
+ r.Address = utils.ConvertH160toAddress(pbReward.Address)
+ r.Amount = utils.ConvertH256ToUint256Int(pbReward.Amount)
+ return r
+}
+
+func (b *Block) Transactions() []*transaction.Transaction {
+ if b.body != nil {
+ return b.body.Transactions()
+ }
+
+ return nil
+}
+
+func (b *Block) StateRoot() types.Hash {
+ return b.header.Root
+}
+
+func (b *Block) Hash() types.Hash {
+ return b.Header().Hash()
+}
+
+func (b *Block) Marshal() ([]byte, error) {
+ bpBlock := b.ToProtoMessage()
+ return proto.Marshal(bpBlock)
+}
+
+func (b *Block) Unmarshal(data []byte) error {
+ var pBlock types_pb.Block
+ if err := proto.Unmarshal(data, &pBlock); err != nil {
+ return err
+ }
+ if err := b.FromProtoMessage(&pBlock); err != nil {
+ return err
+ }
+ return nil
+}
+
+// NewBlock creates a new block. The input data is copied,
+// changes to header and to the field values will not affect the
+// block.
+//
+// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
+// are ignored and set to values derived from the given txs, uncles
+// and receipts.
+func NewBlock(h IHeader, txs []*transaction.Transaction) IBlock {
+
+ block := &Block{
+ header: CopyHeader(h.(*Header)),
+ body: &Body{Txs: txs},
+ ReceiveAt: time.Now(),
+ ReceivedFrom: nil,
+ }
+ return block
+}
+
+func NewBlockFromReceipt(h IHeader, txs []*transaction.Transaction, uncles []IHeader, receipts []*Receipt, reward []*Reward) IBlock {
+
+ block := &Block{
+ header: CopyHeader(h.(*Header)),
+ body: &Body{Txs: txs, Rewards: CopyReward(reward)},
+ ReceiveAt: time.Now(),
+ ReceivedFrom: nil,
+ }
+ //if len(receipts) > 0 {
+ // print(receipts)
+ //}
+
+ block.header.Bloom = CreateBloom(receipts)
+ block.header.TxHash = hash.DeriveSha(transaction.Transactions(txs))
+ block.header.ReceiptHash = hash.DeriveSha(Receipts(receipts))
+
+ return block
+}
+
+func (b *Block) Header() IHeader {
+ return CopyHeader(b.header)
+}
+
+func (b *Block) Body() IBody {
+ return b.body
+}
+
+func (b *Block) Number64() *uint256.Int {
+ return b.header.Number
+}
+
+func (b *Block) BaseFee64() *uint256.Int {
+ return b.header.BaseFee
+}
+
+func (b *Block) Difficulty() *uint256.Int {
+ return b.header.Difficulty
+}
+
+func (b *Block) Time() uint64 {
+ return b.header.Time
+}
+
+func (b *Block) GasLimit() uint64 {
+ return b.header.GasLimit
+}
+
+func (b *Block) GasUsed() uint64 {
+ return b.header.GasUsed
+}
+
+func (b *Block) Nonce() uint64 {
+ return b.header.Nonce.Uint64()
+}
+
+func (b *Block) Coinbase() types.Address {
+ return b.header.Coinbase
+}
+
+func (b *Block) ParentHash() types.Hash {
+ return b.header.ParentHash
+}
+
+func (b *Block) TxHash() types.Hash {
+ return b.header.TxHash
+}
+
+func (b *Block) WithSeal(header IHeader) *Block {
+ b.header = CopyHeader(header.(*Header))
+ return b
+}
+
+func (b *Block) Transaction(hash types.Hash) *transaction.Transaction {
+ return nil
+}
+
+func (b *Block) ToProtoMessage() proto.Message {
+ pbHeader := b.header.ToProtoMessage()
+ pbBody := b.body.ToProtoMessage()
+ pBlock := types_pb.Block{
+ Header: pbHeader.(*types_pb.Header),
+ Body: pbBody.(*types_pb.Body),
+ }
+
+ return &pBlock
+}
+
+func (b *Block) FromProtoMessage(message proto.Message) error {
+ var (
+ pBlock *types_pb.Block
+ header Header
+ body Body
+ ok bool
+ )
+
+ if pBlock, ok = message.(*types_pb.Block); !ok {
+ return fmt.Errorf("type conversion failure")
+ }
+
+ if err := header.FromProtoMessage(pBlock.Header); err != nil {
+ return err
+ }
+
+ if err := body.FromProtoMessage(pBlock.Body); err != nil {
+ return err
+ }
+
+ b.header = &header
+ b.body = &body
+ b.ReceiveAt = time.Now()
+ return nil
+}
+
+func (b *Block) SendersToTxs(senders []types.Address) {
+ //todo
+}
+
+func (b *Block) Uncles() []*Header {
+ return nil
+}
diff --git a/common/block/block_test.go b/common/block/block_test.go
new file mode 100644
index 0000000..bd6346f
--- /dev/null
+++ b/common/block/block_test.go
@@ -0,0 +1,162 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/internal/avm/rlp"
+ "google.golang.org/protobuf/proto"
+ "testing"
+)
+
+func getBlock(tb testing.TB) *Block {
+ //sBlock := "0af5010a200c138c091cfdedd52b8a6f35f664243525de3dba58260788a8e0c1806f3fd0911214f61c62199512e8e85d7744d513b1b74bbe98aea11a200000000000000000000000000000000000000000000000000000000000000000222000000000000000000000000000000000000000000000000000000000000000002a20000000000000000000000000000000000000000000000000000000000000000032033078313a0330783850dfe882950662033078306a40e6bf01023645789ba3cb7e8200eb54289bccbc292b4f4796d2268fee49a7a9a993c539ef50815018014438436b5e5a10c27a4ef2ea4dda9efe5ed5b30ab6fa061200"
+ sBlock := "0af5010a2009876b2d89375c505dd4128e2ea6bc588d8e9beb5ec11af94fc2f9d3e9eae5081214f61c62199512e8e85d7744d513b1b74bbe98aea11a20de7f0cbe30d14da2d6950c0643fe164b9e6ea421550263a02eaaaff257c2f6cc22208ac5c253af5118c7a98eecb957a37bf72a1f5556b8dfd1932322de5e10e32f312a2015647dcb388cef8e815a09408301361aed626048764adb507a06dc08ba593ae332033078323a0330783150858283950662033078306a4080e6d9a605d8be11339047b2541d7f59f3a193a0dad1e27360d016fba82c5449310b485172b8b2a705c8a8111c960149bccdd5c0ffea924c3fe6343aafd258021200"
+ bBlock, err := hex.DecodeString(sBlock)
+ if err != nil {
+ tb.Fatal(err)
+ }
+
+ var block types_pb.Block
+ err = proto.Unmarshal(bBlock, &block)
+ if err != nil {
+ tb.Fatal(err)
+ }
+
+ //tb.Logf("block number: %v", block.Header.Number.String())
+
+ var b Block
+ err = b.FromProtoMessage(&block)
+ if err != nil {
+ tb.Fatal(err)
+ }
+
+ return &b
+}
+
+type exBlock struct {
+ H *Header
+ B *Body
+}
+
+//func TestSize(t *testing.T) {
+// block := getBlock(t)
+// pb := block.ToProtoMessage()
+//
+// buf1, err := proto.Marshal(pb)
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+// exB := exBlock{
+// H: block.header,
+// B: block.body,
+// }
+//
+// buf2, err := rlp.EncodeToBytes(&exB)
+// if err != nil {
+// t.Fatal(err)
+// }
+//
+// t.Logf("proto size: %d, rlp size: %d", len(buf1), len(buf2))
+//}
+
+func BenchmarkProtobuf(b *testing.B) {
+ block := getBlock(b)
+ pb := block.ToProtoMessage()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = proto.Marshal(pb)
+ }
+}
+
+func BenchmarkRlp(b *testing.B) {
+ block := getBlock(b)
+ exB := exBlock{
+ H: block.header,
+ B: block.body,
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = rlp.EncodeToBytes(&exB)
+ }
+}
+
+func BenchmarkJson(b *testing.B) {
+ block := getBlock(b)
+ exB := exBlock{
+ H: block.header,
+ B: block.body,
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, _ = json.Marshal(exB)
+ }
+}
+
+func BenchmarkProtobufUnmarshal(b *testing.B) {
+ //sBlock := "0af5010a200c138c091cfdedd52b8a6f35f664243525de3dba58260788a8e0c1806f3fd0911214f61c62199512e8e85d7744d513b1b74bbe98aea11a200000000000000000000000000000000000000000000000000000000000000000222000000000000000000000000000000000000000000000000000000000000000002a20000000000000000000000000000000000000000000000000000000000000000032033078313a0330783850dfe882950662033078306a40e6bf01023645789ba3cb7e8200eb54289bccbc292b4f4796d2268fee49a7a9a993c539ef50815018014438436b5e5a10c27a4ef2ea4dda9efe5ed5b30ab6fa061200"
+ sBlock := "0af5010a2009876b2d89375c505dd4128e2ea6bc588d8e9beb5ec11af94fc2f9d3e9eae5081214f61c62199512e8e85d7744d513b1b74bbe98aea11a20de7f0cbe30d14da2d6950c0643fe164b9e6ea421550263a02eaaaff257c2f6cc22208ac5c253af5118c7a98eecb957a37bf72a1f5556b8dfd1932322de5e10e32f312a2015647dcb388cef8e815a09408301361aed626048764adb507a06dc08ba593ae332033078323a0330783150858283950662033078306a4080e6d9a605d8be11339047b2541d7f59f3a193a0dad1e27360d016fba82c5449310b485172b8b2a705c8a8111c960149bccdd5c0ffea924c3fe6343aafd258021200"
+ bBlock, err := hex.DecodeString(sBlock)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ var block types_pb.Block
+ _ = proto.Unmarshal(bBlock, &block)
+ }
+}
+
+func BenchmarkRlpDecode(b *testing.B) {
+ block := getBlock(b)
+ exB := exBlock{
+ H: block.header,
+ B: block.body,
+ }
+
+ buf, err := rlp.EncodeToBytes(&exB)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ var exB exBlock
+ _ = rlp.DecodeBytes(buf, &exB)
+ }
+}
+
+func BenchmarkJsonDecode(b *testing.B) {
+ block := getBlock(b)
+ exB := exBlock{
+ H: block.header,
+ B: block.body,
+ }
+ bytes, err := json.Marshal(exB)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ var exB exBlock
+ _ = json.Unmarshal(bytes, &exB)
+ }
+}
diff --git a/common/block/bloom9.go b/common/block/bloom9.go
new file mode 100644
index 0000000..636b415
--- /dev/null
+++ b/common/block/bloom9.go
@@ -0,0 +1,160 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package block
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math/big"
+
+ "github.com/ledgerwatch/erigon-lib/common/hexutility"
+
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/crypto/cryptopool"
+ "github.com/astranetworld/ast/common/hexutil"
+)
+
+type bytesBacked interface {
+ Bytes() []byte
+}
+
+const (
+ // BloomByteLength represents the number of bytes used in a header log bloom.
+ BloomByteLength = 256
+
+ // BloomBitLength represents the number of bits used in a header log bloom.
+ BloomBitLength = 8 * BloomByteLength
+)
+
+// Bloom represents a 2048 bit bloom filter.
+type Bloom [BloomByteLength]byte
+
+// BytesToBloom converts a byte slice to a bloom filter.
+// It panics if b is not of suitable size.
+func BytesToBloom(b []byte) Bloom {
+ var bloom Bloom
+ bloom.SetBytes(b)
+ return bloom
+}
+
+// SetBytes sets the content of b to the given bytes.
+// It panics if d is not of suitable size.
+func (b *Bloom) SetBytes(d []byte) {
+ if len(b) < len(d) {
+ panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
+ }
+ copy(b[BloomByteLength-len(d):], d)
+}
+
+// Add adds d to the filter. Future calls of Test(d) will return true.
+func (b *Bloom) Add(d []byte) {
+ b.add(d, make([]byte, 6))
+}
+
+// add is internal version of Add, which takes a scratch buffer for reuse (needs to be at least 6 bytes)
+func (b *Bloom) add(d []byte, buf []byte) {
+ i1, v1, i2, v2, i3, v3 := bloomValues(d, buf)
+ b[i1] |= v1
+ b[i2] |= v2
+ b[i3] |= v3
+}
+
+// Big converts b to a big integer.
+// Note: Converting a bloom filter to a big.Int and then calling GetBytes
+// does not return the same bytes, since big.Int will trim leading zeroes
+func (b Bloom) Big() *big.Int {
+ return new(big.Int).SetBytes(b[:])
+}
+
+// Bytes returns the backing byte slice of the bloom
+func (b Bloom) Bytes() []byte {
+ return b[:]
+}
+
+// Test checks if the given topic is present in the bloom filter
+func (b Bloom) Test(topic []byte) bool {
+ i1, v1, i2, v2, i3, v3 := bloomValues(topic, make([]byte, 6))
+ return v1 == v1&b[i1] &&
+ v2 == v2&b[i2] &&
+ v3 == v3&b[i3]
+}
+
+// MarshalText encodes b as a hex string with 0x prefix.
+func (b Bloom) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(b[:]).MarshalText()
+}
+
+// UnmarshalText b as a hex string with 0x prefix.
+func (b *Bloom) UnmarshalText(input []byte) error {
+ return hexutility.UnmarshalFixedText("Bloom", input, b[:])
+}
+
+func CreateBloom(receipts Receipts) Bloom {
+ buf := make([]byte, 6)
+ var bin Bloom
+ for _, receipt := range receipts {
+ for _, log := range receipt.Logs {
+ bin.add(log.Address.Bytes(), buf)
+ for _, b := range log.Topics {
+ bin.add(b[:], buf)
+ }
+ }
+ }
+ return bin
+}
+
+// LogsBloom returns the bloom bytes for the given logs
+func LogsBloom(logs []*Log) []byte {
+ buf := make([]byte, 6)
+ var bin Bloom
+ for _, log := range logs {
+ bin.add(log.Address.Bytes(), buf)
+ for _, b := range log.Topics {
+ bin.add(b[:], buf)
+ }
+ }
+ return bin[:]
+}
+
+// Bloom9 returns the bloom filter for the given data
+func Bloom9(data []byte) []byte {
+ var b Bloom
+ b.SetBytes(data)
+ return b.Bytes()
+}
+
+// bloomValues returns the bytes (index-value pairs) to set for the given data
+func bloomValues(data []byte, hashbuf []byte) (uint, byte, uint, byte, uint, byte) {
+ sha := crypto.NewKeccakState()
+ sha.Write(data) //nolint:errcheck
+ sha.Read(hashbuf) //nolint:errcheck
+ cryptopool.ReturnToPoolKeccak256(sha)
+ // The actual bits to flip
+ v1 := byte(1 << (hashbuf[1] & 0x7))
+ v2 := byte(1 << (hashbuf[3] & 0x7))
+ v3 := byte(1 << (hashbuf[5] & 0x7))
+ // The indices for the bytes to OR in
+ i1 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf)&0x7ff)>>3) - 1
+ i2 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[2:])&0x7ff)>>3) - 1
+ i3 := BloomByteLength - uint((binary.BigEndian.Uint16(hashbuf[4:])&0x7ff)>>3) - 1
+
+ return i1, v1, i2, v2, i3, v3
+}
+
+// BloomLookup is a convenience-method to check presence int he bloom filter
+func BloomLookup(bin Bloom, topic bytesBacked) bool {
+ return bin.Test(topic.Bytes())
+}
diff --git a/common/block/bloom9_test.go b/common/block/bloom9_test.go
new file mode 100644
index 0000000..b5a0a6f
--- /dev/null
+++ b/common/block/bloom9_test.go
@@ -0,0 +1,156 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package block
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+ "testing"
+
+ "github.com/astranetworld/ast/common/crypto"
+)
+
+func TestBloom(t *testing.T) {
+ positive := []string{
+ "testtest",
+ "test",
+ "hallo",
+ "other",
+ }
+ negative := []string{
+ "tes",
+ "lo",
+ }
+
+ var bloom Bloom
+ for _, data := range positive {
+ bloom.Add([]byte(data))
+ }
+
+ for _, data := range positive {
+ if !bloom.Test([]byte(data)) {
+ t.Error("expected", data, "to test true")
+ }
+ }
+ for _, data := range negative {
+ if bloom.Test([]byte(data)) {
+ t.Error("did not expect", data, "to test true")
+ }
+ }
+}
+
+// TestBloomExtensively does some more thorough tests
+func TestBloomExtensively(t *testing.T) {
+ var exp = types.HexToHash("c8d3ca65cdb4874300a9e39475508f23ed6da09fdbc487f89a2dcf50b09eb263")
+ var b Bloom
+ // Add 100 "random" things
+ for i := 0; i < 100; i++ {
+ data := fmt.Sprintf("xxxxxxxxxx data %d yyyyyyyyyyyyyy", i)
+ b.Add([]byte(data))
+ //b.Add(new(big.Int).SetBytes([]byte(data)))
+ }
+ got := crypto.Keccak256Hash(b.Bytes())
+ if got != exp {
+ t.Errorf("Got %x, exp %x", got, exp)
+ }
+ var b2 Bloom
+ b2.SetBytes(b.Bytes())
+ got2 := crypto.Keccak256Hash(b2.Bytes())
+ if got != got2 {
+ t.Errorf("Got %x, exp %x", got, got2)
+ }
+}
+
+func BenchmarkBloom9(b *testing.B) {
+ test := []byte("testestestest")
+ for i := 0; i < b.N; i++ {
+ Bloom9(test)
+ }
+}
+
+func BenchmarkBloom9Lookup(b *testing.B) {
+ toTest := []byte("testtest")
+ bloom := new(Bloom)
+ for i := 0; i < b.N; i++ {
+ bloom.Test(toTest)
+ }
+}
+
+func BenchmarkCreateBloom(b *testing.B) {
+ to := types.HexToAddress("0x2")
+ var txs = transaction.Transactions{
+ transaction.NewTx(&transaction.LegacyTx{Nonce: 1, Value: uint256.NewInt(1), Gas: 1, GasPrice: uint256.NewInt(1), Data: nil}),
+ transaction.NewTx(&transaction.LegacyTx{Nonce: 2, To: &to, Value: uint256.NewInt(2), Gas: 2, GasPrice: uint256.NewInt(2), Data: nil}),
+ }
+ var rSmall = Receipts{
+ &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {Address: types.BytesToAddress([]byte{0x11})},
+ {Address: types.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: txs[0].Hash(),
+ ContractAddress: types.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: 1,
+ },
+ &Receipt{
+ PostState: types.Hash{2}.Bytes(),
+ CumulativeGasUsed: 3,
+ Logs: []*Log{
+ {Address: types.BytesToAddress([]byte{0x22})},
+ {Address: types.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: txs[1].Hash(),
+ ContractAddress: types.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: 2,
+ },
+ }
+
+ var rLarge = make(Receipts, 200)
+ // Fill it with 200 receipts x 2 logs
+ for i := 0; i < 200; i += 2 {
+ copy(rLarge[i:], rSmall)
+ }
+ b.Run("small", func(b *testing.B) {
+ b.ReportAllocs()
+ var bl Bloom
+ for i := 0; i < b.N; i++ {
+ bl = CreateBloom(rSmall)
+ }
+ b.StopTimer()
+ var exp = types.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
+ got := crypto.Keccak256Hash(bl.Bytes())
+ if got != exp {
+ b.Errorf("Got %x, exp %x", got, exp)
+ }
+ })
+ b.Run("large", func(b *testing.B) {
+ b.ReportAllocs()
+ var bl Bloom
+ for i := 0; i < b.N; i++ {
+ bl = CreateBloom(rLarge)
+ }
+ b.StopTimer()
+ var exp = types.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949")
+ got := crypto.Keccak256Hash(bl.Bytes())
+ if got != exp {
+ b.Errorf("Got %x, exp %x", got, exp)
+ }
+ })
+}
diff --git a/common/block/body.go b/common/block/body.go
new file mode 100644
index 0000000..7728108
--- /dev/null
+++ b/common/block/body.go
@@ -0,0 +1,171 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/utils"
+ "google.golang.org/protobuf/proto"
+)
+
+type Body struct {
+ Txs []*transaction.Transaction
+ Verifiers []*Verify
+ Rewards []*Reward
+}
+
+func (b *Body) ToProtoMessage() proto.Message {
+ var pbTxs []*types_pb.Transaction
+ var pbVerifiers []*types_pb.Verifier
+ var pbRewards []*types_pb.Reward
+
+ for _, v := range b.Txs {
+ pbTx := v.ToProtoMessage()
+ if pbTx != nil {
+ pbTxs = append(pbTxs, pbTx.(*types_pb.Transaction))
+ }
+ }
+
+ for _, reward := range b.Rewards {
+ pbReward := reward.ToProtoMessage()
+ if pbReward != nil {
+ pbRewards = append(pbRewards, pbReward.(*types_pb.Reward))
+ }
+ }
+
+ for _, verifier := range b.Verifiers {
+ pbVerifier := verifier.ToProtoMessage()
+ if pbVerifier != nil {
+ pbVerifiers = append(pbVerifiers, pbVerifier.(*types_pb.Verifier))
+ }
+ }
+
+ pBody := types_pb.Body{
+ Txs: pbTxs,
+ Verifiers: pbVerifiers,
+ Rewards: pbRewards,
+ }
+
+ return &pBody
+}
+
+func (b *Body) FromProtoMessage(message proto.Message) error {
+ var (
+ pBody *types_pb.Body
+ ok bool
+ )
+
+ if pBody, ok = message.(*types_pb.Body); !ok {
+ return fmt.Errorf("type conversion failure")
+ }
+
+ var txs []*transaction.Transaction
+ //
+ for _, v := range pBody.Txs {
+ tx, err := transaction.FromProtoMessage(v)
+ if err != nil {
+ return err
+ }
+ txs = append(txs, tx)
+ }
+ //
+ b.Txs = txs
+
+ //verifiers
+ var verifiers []*Verify
+ for _, v := range pBody.Verifiers {
+ verify := new(Verify).FromProtoMessage(v)
+ verifiers = append(verifiers, verify)
+ }
+ b.Verifiers = verifiers
+
+ //Reward
+ var rewards []*Reward
+ for _, v := range pBody.Rewards {
+ reward := new(Reward).FromProtoMessage(v)
+ rewards = append(rewards, reward)
+ }
+ b.Rewards = rewards
+
+ return nil
+}
+
+func (b *Body) Transactions() []*transaction.Transaction {
+ return b.Txs
+}
+func (b *Body) Verifier() []*Verify {
+ return b.Verifiers
+}
+
+func (b *Body) Reward() []*Reward {
+ return b.Rewards
+}
+
+func (b *Body) reward() []*types_pb.H256 {
+ var rewardAmount []*types_pb.H256
+ if len(b.Rewards) > 0 {
+ for _, reward := range b.Rewards {
+ rewardAmount = append(rewardAmount, utils.ConvertUint256IntToH256(reward.Amount))
+ }
+ }
+ return rewardAmount
+}
+
+func (b *Body) rewardAddress() []types.Address {
+ var rewardAddress []types.Address
+ for _, reward := range b.Rewards {
+ rewardAddress = append(rewardAddress, reward.Address)
+ }
+ return rewardAddress
+}
+
+func (b *Body) SendersFromTxs() []types.Address {
+ senders := make([]types.Address, len(b.Transactions()))
+ for i, tx := range b.Transactions() {
+ senders[i] = *tx.From()
+ }
+ return senders
+}
+
+func (b *Body) SendersToTxs(senders []types.Address) {
+ if senders == nil {
+ return
+ }
+
+ //todo
+ //for i, tx := range b.Txs {
+ // //tx.SetFrom(senders[i])
+ //}
+}
+
+type BodyForStorage struct {
+ BaseTxId uint64
+ TxAmount uint32
+}
+
+func NewBlockFromStorage(hash types.Hash, header *Header, body *Body) *Block {
+ b := &Block{header: header, body: body}
+ b.hash.Store(hash)
+ return b
+}
+
+type RawBody struct {
+ Transactions [][]byte
+}
diff --git a/common/block/header.go b/common/block/header.go
new file mode 100644
index 0000000..1da45d7
--- /dev/null
+++ b/common/block/header.go
@@ -0,0 +1,221 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+ "sync/atomic"
+
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/types"
+)
+
+type BlockNonce [8]byte
+
+// EncodeNonce converts the given integer to a block nonce.
+func EncodeNonce(i uint64) BlockNonce {
+ var n BlockNonce
+ binary.BigEndian.PutUint64(n[:], i)
+ return n
+}
+
+// Uint64 returns the integer value of a block nonce.
+func (n BlockNonce) Uint64() uint64 {
+ return binary.BigEndian.Uint64(n[:])
+}
+
+// MarshalText todo copy eth
+func (n BlockNonce) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(n[:]).MarshalText()
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (n *BlockNonce) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
+}
+
+type Header struct {
+ ParentHash types.Hash `json:"parentHash" gencodec:"required"`
+ Coinbase types.Address `json:"miner"`
+ Root types.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash types.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash types.Hash `json:"receiptsRoot" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Difficulty *uint256.Int `json:"difficulty" gencodec:"required"`
+ Number *uint256.Int `json:"number" gencodec:"required"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+ Time uint64 `json:"timestamp" gencodec:"required"`
+ MixDigest types.Hash `json:"mixHash"`
+ Nonce BlockNonce `json:"nonce"`
+ Extra []byte `json:"extraData" gencodec:"required"`
+
+ // BaseFee was added by EIP-1559 and is ignored in legacy headers.
+ BaseFee *uint256.Int `json:"baseFeePerGas" rlp:"optional"`
+
+ hash atomic.Value
+
+ Signature types.Signature `json:"signature"`
+}
+
+func (h *Header) Number64() *uint256.Int {
+ return h.Number
+}
+
+func (h *Header) StateRoot() types.Hash {
+ return h.Root
+}
+
+func (h *Header) BaseFee64() *uint256.Int {
+ return h.BaseFee
+}
+
+func (h Header) Hash() types.Hash {
+ if hash := h.hash.Load(); hash != nil {
+ return hash.(types.Hash)
+ }
+
+ if h.BaseFee == nil {
+ h.BaseFee = uint256.NewInt(0)
+ }
+
+ if h.Difficulty == nil {
+ h.Difficulty = uint256.NewInt(0)
+ }
+
+ buf, err := json.Marshal(h)
+ if err != nil {
+ return types.Hash{}
+ }
+
+ if h.Number.Uint64() == 0 {
+ //log.Tracef("genesis header json Marshal: %s", string(buf))
+ }
+
+ hash := types.BytesHash(buf)
+ h.hash.Store(hash)
+ return hash
+}
+
+func (h *Header) ToProtoMessage() proto.Message {
+ return &types_pb.Header{
+ ParentHash: utils.ConvertHashToH256(h.ParentHash),
+ Coinbase: utils.ConvertAddressToH160(h.Coinbase),
+ Root: utils.ConvertHashToH256(h.Root),
+ TxHash: utils.ConvertHashToH256(h.TxHash),
+ ReceiptHash: utils.ConvertHashToH256(h.ReceiptHash),
+ Difficulty: utils.ConvertUint256IntToH256(h.Difficulty),
+ Number: utils.ConvertUint256IntToH256(h.Number),
+ GasLimit: h.GasLimit,
+ GasUsed: h.GasUsed,
+ Time: h.Time,
+ Nonce: h.Nonce.Uint64(),
+ BaseFee: utils.ConvertUint256IntToH256(h.BaseFee),
+ Extra: h.Extra,
+ Signature: utils.ConvertSignatureToH768(h.Signature),
+ Bloom: utils.ConvertBytesToH2048(h.Bloom.Bytes()),
+ MixDigest: utils.ConvertHashToH256(h.MixDigest),
+ }
+}
+
+func (h *Header) FromProtoMessage(message proto.Message) error {
+ var (
+ pbHeader *types_pb.Header
+ ok bool
+ )
+
+ if pbHeader, ok = message.(*types_pb.Header); !ok {
+ return fmt.Errorf("type conversion failure")
+ }
+
+ h.ParentHash = utils.ConvertH256ToHash(pbHeader.ParentHash)
+ h.Coinbase = utils.ConvertH160toAddress(pbHeader.Coinbase)
+ h.Root = utils.ConvertH256ToHash(pbHeader.Root)
+ h.TxHash = utils.ConvertH256ToHash(pbHeader.TxHash)
+ h.ReceiptHash = utils.ConvertH256ToHash(pbHeader.ReceiptHash)
+ h.Difficulty = utils.ConvertH256ToUint256Int(pbHeader.Difficulty)
+ h.Number = utils.ConvertH256ToUint256Int(pbHeader.Number)
+ h.GasLimit = pbHeader.GasLimit
+ h.GasUsed = pbHeader.GasUsed
+ h.Time = pbHeader.Time
+ h.Nonce = EncodeNonce(pbHeader.Nonce)
+ h.BaseFee = utils.ConvertH256ToUint256Int(pbHeader.BaseFee)
+ h.Extra = pbHeader.Extra
+ h.Signature = utils.ConvertH768ToSignature(pbHeader.Signature)
+ h.Bloom = utils.ConvertH2048ToBloom(pbHeader.Bloom)
+ h.MixDigest = utils.ConvertH256ToHash(pbHeader.MixDigest)
+ return nil
+}
+
+func (h *Header) Marshal() ([]byte, error) {
+ pbHeader := h.ToProtoMessage()
+ return proto.Marshal(pbHeader)
+}
+
+func (h *Header) Unmarshal(data []byte) error {
+ var pbHeader types_pb.Header
+ if err := proto.Unmarshal(data, &pbHeader); err != nil {
+ return err
+ }
+ if err := h.FromProtoMessage(&pbHeader); err != nil {
+ return err
+ }
+ return nil
+}
+
+func CopyHeader(h *Header) *Header {
+ cpy := *h
+
+ if cpy.Difficulty = uint256.NewInt(0); h.Difficulty != nil {
+ cpy.Difficulty.SetBytes(h.Difficulty.Bytes())
+ }
+ if cpy.Number = uint256.NewInt(0); h.Number != nil {
+ cpy.Number.SetBytes(h.Number.Bytes())
+ }
+
+ if h.BaseFee != nil {
+ cpy.BaseFee = uint256.NewInt(0).SetBytes(h.BaseFee.Bytes())
+ }
+
+ if len(h.Extra) > 0 {
+ cpy.Extra = make([]byte, len(h.Extra))
+ copy(cpy.Extra, h.Extra)
+ }
+ return &cpy
+}
+
+func CopyReward(rewards []*Reward) []*Reward {
+ var cpyReward []*Reward
+
+ for _, reward := range rewards {
+ addr, amount := types.Address{}, uint256.Int{}
+
+ addr.SetBytes(reward.Address[:])
+ cpyReward = append(cpyReward, &Reward{
+ Address: addr,
+ Amount: amount.SetBytes(reward.Amount.Bytes()),
+ })
+ }
+
+ return cpyReward
+}
diff --git a/common/block/iblock.go b/common/block/iblock.go
new file mode 100644
index 0000000..d930db4
--- /dev/null
+++ b/common/block/iblock.go
@@ -0,0 +1,65 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+)
+
+type IHeader interface {
+ Number64() *uint256.Int
+ BaseFee64() *uint256.Int
+ Hash() types.Hash
+ ToProtoMessage() proto.Message
+ FromProtoMessage(message proto.Message) error
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+ StateRoot() types.Hash
+}
+
+type IBody interface {
+ Verifier() []*Verify
+ Reward() []*Reward
+ Transactions() []*transaction.Transaction
+ ToProtoMessage() proto.Message
+ FromProtoMessage(message proto.Message) error
+}
+
+type IBlock interface {
+ IHeader
+ Header() IHeader
+ Body() IBody
+ Transaction(hash types.Hash) *transaction.Transaction
+ Transactions() []*transaction.Transaction
+ Number64() *uint256.Int
+ Difficulty() *uint256.Int
+ Time() uint64
+ GasLimit() uint64
+ GasUsed() uint64
+ Nonce() uint64
+ Coinbase() types.Address
+ ParentHash() types.Hash
+ TxHash() types.Hash
+ WithSeal(header IHeader) *Block
+ //ToProtoMessage() proto.Message
+ //FromProtoMessage(message proto.Message) error
+}
+
+type Blocks []IBlock
diff --git a/common/block/log.go b/common/block/log.go
new file mode 100644
index 0000000..96444a3
--- /dev/null
+++ b/common/block/log.go
@@ -0,0 +1,118 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/types"
+
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+)
+
+type Log struct {
+ // Consensus fields:
+ // address of the contract that generated the event
+ Address types.Address `json:"address" gencodec:"required"`
+ // list of topics provided by the contract.
+ Topics []types.Hash `json:"topics" gencodec:"required"`
+ // supplied by the contract, usually ABI-encoded
+ Data []byte `json:"data" gencodec:"required"`
+
+ // Derived fields. These fields are filled in by the node
+ // but not secured by consensus.
+ // block in which the transaction was included
+ BlockNumber *uint256.Int `json:"blockNumber"`
+ // hash of the transaction
+ TxHash types.Hash `json:"transactionHash" gencodec:"required"`
+ // index of the transaction in the block
+ TxIndex uint `json:"transactionIndex" gencodec:"required"`
+ // hash of the block in which the transaction was included
+ BlockHash types.Hash `json:"blockHash"`
+ // index of the log in the receipt
+ Index uint `json:"logIndex" gencodec:"required"`
+
+ // The Removed field is true if this log was reverted due to a chain reorganisation.
+ // You must pay attention to this field if you receive logs through a filter query.
+ Removed bool `json:"removed"`
+}
+
+func (l *Log) ToProtoMessage() proto.Message {
+ return &types_pb.Log{
+ Address: utils.ConvertAddressToH160(l.Address),
+ Topics: utils.ConvertHashesToH256(l.Topics),
+ Data: l.Data,
+ BlockNumber: utils.ConvertUint256IntToH256(l.BlockNumber),
+ TxHash: utils.ConvertHashToH256(l.TxHash),
+ TxIndex: uint64(l.TxIndex),
+ BlockHash: utils.ConvertHashToH256(l.BlockHash),
+ Index: uint64(l.Index),
+ Removed: l.Removed,
+ }
+}
+
+func (l *Log) FromProtoMessage(message proto.Message) error {
+ var (
+ pLog *types_pb.Log
+ ok bool
+ )
+
+ if pLog, ok = message.(*types_pb.Log); !ok {
+ return fmt.Errorf("type conversion failure")
+ }
+
+ l.Address = utils.ConvertH160toAddress(pLog.Address)
+ l.Topics = utils.H256sToHashes(pLog.Topics)
+ l.Data = pLog.Data
+ l.BlockNumber = utils.ConvertH256ToUint256Int(pLog.BlockNumber)
+ l.TxHash = utils.ConvertH256ToHash(pLog.TxHash)
+ l.TxIndex = uint(pLog.TxIndex)
+ l.BlockHash = utils.ConvertH256ToHash(pLog.BlockHash)
+ l.Index = uint(pLog.Index)
+ l.Removed = pLog.Removed
+
+ return nil
+}
+
+type Logs []*Log
+
+func (l *Logs) Marshal() ([]byte, error) {
+ pb := new(types_pb.Logs)
+ for _, log := range *l {
+ pb.Logs = append(pb.Logs, log.ToProtoMessage().(*types_pb.Log))
+ }
+
+ return proto.Marshal(pb)
+}
+
+func (l *Logs) Unmarshal(data []byte) error {
+ pb := new(types_pb.Logs)
+ if err := proto.Unmarshal(data, pb); nil != err {
+ return err
+ }
+
+ body := make([]*Log, len(pb.Logs))
+ for i, p := range pb.Logs {
+ if err := body[i].FromProtoMessage(p); nil != err {
+ return err
+ }
+ }
+ *l = body
+ return nil
+}
diff --git a/common/block/receipt.go b/common/block/receipt.go
new file mode 100644
index 0000000..7d03ac5
--- /dev/null
+++ b/common/block/receipt.go
@@ -0,0 +1,214 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package block
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/avm/rlp"
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+)
+
+const (
+ // ReceiptStatusFailed is the status code of a transaction if execution failed.
+ ReceiptStatusFailed = uint64(0)
+
+ // ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
+ ReceiptStatusSuccessful = uint64(1)
+)
+
+type Receipts []*Receipt
+
+func (rs *Receipts) Marshal() ([]byte, error) {
+ pb := rs.ToProtoMessage()
+ return proto.Marshal(pb)
+}
+
+func (rs *Receipts) Unmarshal(data []byte) error {
+ pb := new(types_pb.Receipts)
+ if err := proto.Unmarshal(data, pb); nil != err {
+ return err
+ }
+
+ return rs.FromProtoMessage(pb)
+}
+
+// Len returns the number of receipts in this list.
+func (rs Receipts) Len() int { return len(rs) }
+
+// EncodeIndex encodes the i'th receipt to w.
+func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
+ r := rs[i]
+
+ logs := make([]*storedLog, len(r.Logs))
+
+ for k, log := range r.Logs {
+ logs[k] = &storedLog{
+ Address: log.Address,
+ Topics: log.Topics,
+ Data: log.Data,
+ }
+ }
+ data := &storedReceipt{r.Status, r.CumulativeGasUsed, logs}
+
+ rlp.Encode(w, data)
+ //byte, _ := json.Marshal(data)
+ //w.Write(byte)
+}
+
+func (rs *Receipts) FromProtoMessage(receipts *types_pb.Receipts) error {
+ for _, receipt := range receipts.Receipts {
+ var rec Receipt
+ err := rec.fromProtoMessage(receipt)
+ if err == nil {
+ *rs = append(*rs, &rec)
+ }
+ }
+ return nil
+}
+
+func (rs *Receipts) ToProtoMessage() proto.Message {
+ var receipts []*types_pb.Receipt
+ for _, receipt := range *rs {
+ pReceipt := receipt.toProtoMessage()
+ receipts = append(receipts, pReceipt.(*types_pb.Receipt))
+ }
+ return &types_pb.Receipts{
+ Receipts: receipts,
+ }
+}
+
+type Receipt struct {
+ // Consensus fields: These fields are defined by the Yellow Paper
+ Type uint8 `json:"type,omitempty"`
+ PostState []byte `json:"root"`
+ Status uint64 `json:"status"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
+ Bloom Bloom `json:"logsBloom" gencodec:"required"`
+ Logs []*Log `json:"logs" gencodec:"required"`
+
+ // Implementation fields: These fields are added by geth when processing a transaction.
+ // They are stored in the chain database.
+ TxHash types.Hash `json:"transactionHash" gencodec:"required"`
+ ContractAddress types.Address `json:"contractAddress"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+
+ // Inclusion information: These fields provide information about the inclusion of the
+ // transaction corresponding to this receipt.
+ BlockHash types.Hash `json:"blockHash,omitempty"`
+ BlockNumber *uint256.Int `json:"blockNumber,omitempty"`
+ TransactionIndex uint `json:"transactionIndex"`
+}
+
+func (r *Receipt) Marshal() ([]byte, error) {
+ bpBlock := r.toProtoMessage()
+ return proto.Marshal(bpBlock)
+}
+
+func (r *Receipt) Unmarshal(data []byte) error {
+ var pReceipt types_pb.Receipt
+ if err := proto.Unmarshal(data, &pReceipt); err != nil {
+ return err
+ }
+ if err := r.fromProtoMessage(&pReceipt); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (r *Receipt) toProtoMessage() proto.Message {
+ //bloom, _ := r.Bloom.Marshal()
+
+ var logs []*types_pb.Log
+ for _, log := range r.Logs {
+ logs = append(logs, log.ToProtoMessage().(*types_pb.Log))
+ }
+ pb := &types_pb.Receipt{
+ Type: uint32(r.Type),
+ PostState: r.PostState,
+ Status: r.Status,
+ CumulativeGasUsed: r.CumulativeGasUsed,
+ Logs: logs,
+ TxHash: utils.ConvertHashToH256(r.TxHash),
+ ContractAddress: utils.ConvertAddressToH160(r.ContractAddress),
+ GasUsed: r.GasUsed,
+ BlockHash: utils.ConvertHashToH256(r.BlockHash),
+ BlockNumber: utils.ConvertUint256IntToH256(r.BlockNumber),
+ TransactionIndex: uint64(r.TransactionIndex),
+ Bloom: utils.ConvertBytesToH2048(r.Bloom[:]),
+ }
+ return pb
+}
+
+func (r *Receipt) fromProtoMessage(message proto.Message) error {
+ var (
+ pReceipt *types_pb.Receipt
+ ok bool
+ )
+
+ if pReceipt, ok = message.(*types_pb.Receipt); !ok {
+ return fmt.Errorf("type conversion failure")
+ }
+
+ //bloom := new(types.Bloom)
+ //err := bloom.UnMarshalBloom(pReceipt.Bloom)
+ //if err != nil {
+ // return fmt.Errorf("type conversion failure bloom")
+ //}
+
+ var logs []*Log
+ for _, logMessage := range pReceipt.Logs {
+ log := new(Log)
+
+ if err := log.FromProtoMessage(logMessage); err != nil {
+ return fmt.Errorf("type conversion failure log %s", err)
+ }
+ logs = append(logs, log)
+ }
+
+ r.Type = uint8(pReceipt.Type)
+ r.PostState = pReceipt.PostState
+ r.Status = pReceipt.Status
+ r.CumulativeGasUsed = pReceipt.CumulativeGasUsed
+ r.Bloom = utils.ConvertH2048ToBloom(pReceipt.Bloom)
+ r.Logs = logs
+ r.TxHash = utils.ConvertH256ToHash(pReceipt.TxHash)
+ r.ContractAddress = utils.ConvertH160toAddress(pReceipt.ContractAddress)
+ r.GasUsed = pReceipt.GasUsed
+ r.BlockHash = utils.ConvertH256ToHash(pReceipt.BlockHash)
+ r.BlockNumber = utils.ConvertH256ToUint256Int(pReceipt.BlockNumber)
+ r.TransactionIndex = uint(pReceipt.TransactionIndex)
+
+ return nil
+}
+
+// storedReceipt is the consensus encoding of a receipt.
+type storedReceipt struct {
+ PostStateOrStatus uint64
+ CumulativeGasUsed uint64
+ Logs []*storedLog
+}
+
+type storedLog struct {
+ Address types.Address
+ Topics []types.Hash
+ Data []byte
+}
diff --git a/common/blockchain.go b/common/blockchain.go
new file mode 100644
index 0000000..a22c040
--- /dev/null
+++ b/common/blockchain.go
@@ -0,0 +1,79 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/consensus"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/astranetworld/ast/params"
+ "github.com/holiman/uint256"
+ "github.com/ledgerwatch/erigon-lib/kv"
+ "github.com/libp2p/go-libp2p/core/peer"
+)
+
+type IHeaderChain interface {
+ GetHeaderByNumber(number *uint256.Int) block.IHeader
+ GetHeaderByHash(h types.Hash) (block.IHeader, error)
+ InsertHeader(headers []block.IHeader) (int, error)
+ GetBlockByHash(h types.Hash) (block.IBlock, error)
+ GetBlockByNumber(number *uint256.Int) (block.IBlock, error)
+}
+
+type IBlockChain interface {
+ IHeaderChain
+ Config() *params.ChainConfig
+ CurrentBlock() block.IBlock
+ Blocks() []block.IBlock
+ Start() error
+ GenesisBlock() block.IBlock
+ NewBlockHandler(payload []byte, peer peer.ID) error
+ InsertChain(blocks []block.IBlock) (int, error)
+ InsertBlock(blocks []block.IBlock, isSync bool) (int, error)
+ SetEngine(engine consensus.Engine)
+ GetBlocksFromHash(hash types.Hash, n int) (blocks []block.IBlock)
+ SealedBlock(b block.IBlock) error
+ Engine() consensus.Engine
+ GetReceipts(blockHash types.Hash) (block.Receipts, error)
+ GetLogs(blockHash types.Hash) ([][]*block.Log, error)
+ SetHead(head uint64) error
+ AddFutureBlock(block block.IBlock) error
+
+ GetHeader(types.Hash, *uint256.Int) block.IHeader
+ // alias for GetBlocksFromHash?
+ GetBlock(hash types.Hash, number uint64) block.IBlock
+ StateAt(tx kv.Tx, blockNr uint64) *state.IntraBlockState
+
+ GetTd(hash types.Hash, number *uint256.Int) *uint256.Int
+ HasBlock(hash types.Hash, number uint64) bool
+
+ DB() kv.RwDB
+ Quit() <-chan struct{}
+
+ Close() error
+
+ WriteBlockWithState(block block.IBlock, receipts []*block.Receipt, ibs *state.IntraBlockState, nopay map[types.Address]*uint256.Int) error
+
+ GetDepositInfo(address types.Address) (*uint256.Int, *uint256.Int)
+ GetAccountRewardUnpaid(account types.Address) (*uint256.Int, error)
+}
+
+type IMiner interface {
+ Start()
+ PendingBlockAndReceipts() (block.IBlock, block.Receipts)
+}
diff --git a/common/crypto/blake2b/blake2b.go b/common/crypto/blake2b/blake2b.go
new file mode 100644
index 0000000..8e81a55
--- /dev/null
+++ b/common/crypto/blake2b/blake2b.go
@@ -0,0 +1,308 @@
+// Copyright 2016 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 blake2b implements the BLAKE2b hash algorithm defined by RFC 7693
+// and the extendable output function (XOF) BLAKE2Xb.
+//
+// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf
+// and for BLAKE2Xb see https://blake2.net/blake2x.pdf
+//
+// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512).
+// If you need a secret-key MAC (message authentication code), use the New512
+// function with a non-nil key.
+//
+// BLAKE2X is a construction to compute hash values larger than 64 bytes. It
+// can produce hash values between 0 and 4 GiB.
+package blake2b
+
+import (
+ "encoding/binary"
+ "errors"
+ "hash"
+)
+
+const (
+ // The blocksize of BLAKE2b in bytes.
+ BlockSize = 128
+ // The hash size of BLAKE2b-512 in bytes.
+ Size = 64
+ // The hash size of BLAKE2b-384 in bytes.
+ Size384 = 48
+ // The hash size of BLAKE2b-256 in bytes.
+ Size256 = 32
+)
+
+var (
+ useAVX2 bool
+ useAVX bool
+ useSSE4 bool
+)
+
+var (
+ errKeySize = errors.New("blake2b: invalid key size")
+ errHashSize = errors.New("blake2b: invalid hash size")
+)
+
+var iv = [8]uint64{
+ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
+}
+
+// Sum512 returns the BLAKE2b-512 checksum of the data.
+func Sum512(data []byte) [Size]byte {
+ var sum [Size]byte
+ checkSum(&sum, Size, data)
+ return sum
+}
+
+// Sum384 returns the BLAKE2b-384 checksum of the data.
+func Sum384(data []byte) [Size384]byte {
+ var sum [Size]byte
+ var sum384 [Size384]byte
+ checkSum(&sum, Size384, data)
+ copy(sum384[:], sum[:Size384])
+ return sum384
+}
+
+// Sum256 returns the BLAKE2b-256 checksum of the data.
+func Sum256(data []byte) [Size256]byte {
+ var sum [Size]byte
+ var sum256 [Size256]byte
+ checkSum(&sum, Size256, data)
+ copy(sum256[:], sum[:Size256])
+ return sum256
+}
+
+// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
+// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
+func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
+
+// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
+// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
+func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
+
+// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
+// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
+func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
+
+// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
+// A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long.
+// The hash size can be a value between 1 and 64 but it is highly recommended to use
+// values equal or greater than:
+// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
+// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
+// When the key is nil, the returned hash.Hash implements BinaryMarshaler
+// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
+func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
+
+// F is a compression function for BLAKE2b. It takes as an argument the state
+// vector `h`, message block vector `m`, offset counter `t`, final block indicator
+// flag `f`, and number of rounds `rounds`. The state vector provided as the first
+// parameter is modified by the function.
+func F(h *[8]uint64, m [16]uint64, c [2]uint64, final bool, rounds uint32) {
+ var flag uint64
+ if final {
+ flag = 0xFFFFFFFFFFFFFFFF
+ }
+ f(h, &m, c[0], c[1], flag, uint64(rounds))
+}
+
+func newDigest(hashSize int, key []byte) (*digest, error) {
+ if hashSize < 1 || hashSize > Size {
+ return nil, errHashSize
+ }
+ if len(key) > Size {
+ return nil, errKeySize
+ }
+ d := &digest{
+ size: hashSize,
+ keyLen: len(key),
+ }
+ copy(d.key[:], key)
+ d.Reset()
+ return d, nil
+}
+
+func checkSum(sum *[Size]byte, hashSize int, data []byte) {
+ h := iv
+ h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24)
+ var c [2]uint64
+
+ if length := len(data); length > BlockSize {
+ n := length &^ (BlockSize - 1)
+ if length == n {
+ n -= BlockSize
+ }
+ hashBlocks(&h, &c, 0, data[:n])
+ data = data[n:]
+ }
+
+ var block [BlockSize]byte
+ offset := copy(block[:], data)
+ remaining := uint64(BlockSize - offset)
+ if c[0] < remaining {
+ c[1]--
+ }
+ c[0] -= remaining
+
+ hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
+
+ for i, v := range h[:(hashSize+7)/8] {
+ binary.LittleEndian.PutUint64(sum[8*i:], v)
+ }
+}
+
+func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
+ var m [16]uint64
+ c0, c1 := c[0], c[1]
+
+ for i := 0; i < len(blocks); {
+ c0 += BlockSize
+ if c0 < BlockSize {
+ c1++
+ }
+ for j := range m {
+ m[j] = binary.LittleEndian.Uint64(blocks[i:])
+ i += 8
+ }
+ f(h, &m, c0, c1, flag, 12)
+ }
+ c[0], c[1] = c0, c1
+}
+
+type digest struct {
+ h [8]uint64
+ c [2]uint64
+ size int
+ block [BlockSize]byte
+ offset int
+
+ key [BlockSize]byte
+ keyLen int
+}
+
+const (
+ magic = "b2b"
+ marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1
+)
+
+func (d *digest) MarshalBinary() ([]byte, error) {
+ if d.keyLen != 0 {
+ return nil, errors.New("crypto/blake2b: cannot marshal MACs")
+ }
+ b := make([]byte, 0, marshaledSize)
+ b = append(b, magic...)
+ for i := 0; i < 8; i++ {
+ b = appendUint64(b, d.h[i])
+ }
+ b = appendUint64(b, d.c[0])
+ b = appendUint64(b, d.c[1])
+ // Maximum value for size is 64
+ b = append(b, byte(d.size))
+ b = append(b, d.block[:]...)
+ b = append(b, byte(d.offset))
+ return b, nil
+}
+
+func (d *digest) UnmarshalBinary(b []byte) error {
+ if len(b) < len(magic) || string(b[:len(magic)]) != magic {
+ return errors.New("crypto/blake2b: invalid hash state identifier")
+ }
+ if len(b) != marshaledSize {
+ return errors.New("crypto/blake2b: invalid hash state size")
+ }
+ b = b[len(magic):]
+ for i := 0; i < 8; i++ {
+ b, d.h[i] = consumeUint64(b)
+ }
+ b, d.c[0] = consumeUint64(b)
+ b, d.c[1] = consumeUint64(b)
+ d.size = int(b[0])
+ b = b[1:]
+ copy(d.block[:], b[:BlockSize])
+ b = b[BlockSize:]
+ d.offset = int(b[0])
+ return nil
+}
+
+func (d *digest) BlockSize() int { return BlockSize }
+
+func (d *digest) Size() int { return d.size }
+
+func (d *digest) Reset() {
+ d.h = iv
+ d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24)
+ d.offset, d.c[0], d.c[1] = 0, 0, 0
+ if d.keyLen > 0 {
+ d.block = d.key
+ d.offset = BlockSize
+ }
+}
+
+func (d *digest) Write(p []byte) (n int, err error) {
+ n = len(p)
+
+ if d.offset > 0 {
+ remaining := BlockSize - d.offset
+ if n <= remaining {
+ d.offset += copy(d.block[d.offset:], p)
+ return
+ }
+ copy(d.block[d.offset:], p[:remaining])
+ hashBlocks(&d.h, &d.c, 0, d.block[:])
+ d.offset = 0
+ p = p[remaining:]
+ }
+
+ if length := len(p); length > BlockSize {
+ nn := length &^ (BlockSize - 1)
+ if length == nn {
+ nn -= BlockSize
+ }
+ hashBlocks(&d.h, &d.c, 0, p[:nn])
+ p = p[nn:]
+ }
+
+ if len(p) > 0 {
+ d.offset += copy(d.block[:], p)
+ }
+
+ return
+}
+
+func (d *digest) Sum(sum []byte) []byte {
+ var hash [Size]byte
+ d.finalize(&hash)
+ return append(sum, hash[:d.size]...)
+}
+
+func (d *digest) finalize(hash *[Size]byte) {
+ var block [BlockSize]byte
+ copy(block[:], d.block[:d.offset])
+ remaining := uint64(BlockSize - d.offset)
+
+ c := d.c
+ if c[0] < remaining {
+ c[1]--
+ }
+ c[0] -= remaining
+
+ h := d.h
+ hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
+
+ for i, v := range h {
+ binary.LittleEndian.PutUint64(hash[8*i:], v)
+ }
+}
+
+func appendUint64(b []byte, x uint64) []byte {
+ var a [8]byte
+ binary.BigEndian.PutUint64(a[:], x)
+ return append(b, a[:]...)
+}
+
+func consumeUint64(b []byte) ([]byte, uint64) {
+ x := binary.BigEndian.Uint64(b)
+ return b[8:], x
+}
diff --git a/common/crypto/blake2b/blake2bAVX2_amd64.go b/common/crypto/blake2b/blake2bAVX2_amd64.go
new file mode 100644
index 0000000..bfeee2b
--- /dev/null
+++ b/common/crypto/blake2b/blake2bAVX2_amd64.go
@@ -0,0 +1,37 @@
+// Copyright 2016 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.
+
+//go:build go1.7 && amd64 && !gccgo && !appengine
+
+package blake2b
+
+import "golang.org/x/sys/cpu"
+
+func init() {
+ useAVX2 = cpu.X86.HasAVX2
+ useAVX = cpu.X86.HasAVX
+ useSSE4 = cpu.X86.HasSSE41
+}
+
+//go:noescape
+func fAVX2(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+
+//go:noescape
+func fAVX(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+
+//go:noescape
+func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+
+func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) {
+ switch {
+ case useAVX2:
+ fAVX2(h, m, c0, c1, flag, rounds)
+ case useAVX:
+ fAVX(h, m, c0, c1, flag, rounds)
+ case useSSE4:
+ fSSE4(h, m, c0, c1, flag, rounds)
+ default:
+ fGeneric(h, m, c0, c1, flag, rounds)
+ }
+}
diff --git a/common/crypto/blake2b/blake2bAVX2_amd64.s b/common/crypto/blake2b/blake2bAVX2_amd64.s
new file mode 100644
index 0000000..4998af3
--- /dev/null
+++ b/common/crypto/blake2b/blake2bAVX2_amd64.s
@@ -0,0 +1,717 @@
+// Copyright 2016 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.
+
+// +build go1.7,amd64,!gccgo,!appengine
+
+#include "textflag.h"
+
+DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
+DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
+DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b
+DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1
+GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32
+
+DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1
+DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
+DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b
+DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179
+GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32
+
+DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403
+DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
+DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403
+DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b
+GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32
+
+DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302
+DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
+DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302
+DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a
+GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32
+
+DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
+DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
+GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16
+
+DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
+DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
+GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16
+
+DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1
+DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
+GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16
+
+DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
+DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
+GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16
+
+DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403
+DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
+GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16
+
+DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
+DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
+GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16
+
+#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
+#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
+#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
+#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
+#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39
+
+#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
+ VPADDQ m0, Y0, Y0; \
+ VPADDQ Y1, Y0, Y0; \
+ VPXOR Y0, Y3, Y3; \
+ VPSHUFD $-79, Y3, Y3; \
+ VPADDQ Y3, Y2, Y2; \
+ VPXOR Y2, Y1, Y1; \
+ VPSHUFB c40, Y1, Y1; \
+ VPADDQ m1, Y0, Y0; \
+ VPADDQ Y1, Y0, Y0; \
+ VPXOR Y0, Y3, Y3; \
+ VPSHUFB c48, Y3, Y3; \
+ VPADDQ Y3, Y2, Y2; \
+ VPXOR Y2, Y1, Y1; \
+ VPADDQ Y1, Y1, t; \
+ VPSRLQ $63, Y1, Y1; \
+ VPXOR t, Y1, Y1; \
+ VPERMQ_0x39_Y1_Y1; \
+ VPERMQ_0x4E_Y2_Y2; \
+ VPERMQ_0x93_Y3_Y3; \
+ VPADDQ m2, Y0, Y0; \
+ VPADDQ Y1, Y0, Y0; \
+ VPXOR Y0, Y3, Y3; \
+ VPSHUFD $-79, Y3, Y3; \
+ VPADDQ Y3, Y2, Y2; \
+ VPXOR Y2, Y1, Y1; \
+ VPSHUFB c40, Y1, Y1; \
+ VPADDQ m3, Y0, Y0; \
+ VPADDQ Y1, Y0, Y0; \
+ VPXOR Y0, Y3, Y3; \
+ VPSHUFB c48, Y3, Y3; \
+ VPADDQ Y3, Y2, Y2; \
+ VPXOR Y2, Y1, Y1; \
+ VPADDQ Y1, Y1, t; \
+ VPSRLQ $63, Y1, Y1; \
+ VPXOR t, Y1, Y1; \
+ VPERMQ_0x39_Y3_Y3; \
+ VPERMQ_0x4E_Y2_Y2; \
+ VPERMQ_0x93_Y1_Y1
+
+#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
+#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
+#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
+#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
+#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E
+
+#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
+#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
+#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
+#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
+#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n
+
+#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
+#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
+#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
+#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
+#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01
+
+#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
+#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
+#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
+#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
+#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01
+
+#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
+#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01
+
+// load msg: Y12 = (i0, i1, i2, i3)
+// i0, i1, i2, i3 must not be 0
+#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
+ VMOVQ_SI_X12(i0*8); \
+ VMOVQ_SI_X11(i2*8); \
+ VPINSRQ_1_SI_X12(i1*8); \
+ VPINSRQ_1_SI_X11(i3*8); \
+ VINSERTI128 $1, X11, Y12, Y12
+
+// load msg: Y13 = (i0, i1, i2, i3)
+// i0, i1, i2, i3 must not be 0
+#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
+ VMOVQ_SI_X13(i0*8); \
+ VMOVQ_SI_X11(i2*8); \
+ VPINSRQ_1_SI_X13(i1*8); \
+ VPINSRQ_1_SI_X11(i3*8); \
+ VINSERTI128 $1, X11, Y13, Y13
+
+// load msg: Y14 = (i0, i1, i2, i3)
+// i0, i1, i2, i3 must not be 0
+#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
+ VMOVQ_SI_X14(i0*8); \
+ VMOVQ_SI_X11(i2*8); \
+ VPINSRQ_1_SI_X14(i1*8); \
+ VPINSRQ_1_SI_X11(i3*8); \
+ VINSERTI128 $1, X11, Y14, Y14
+
+// load msg: Y15 = (i0, i1, i2, i3)
+// i0, i1, i2, i3 must not be 0
+#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
+ VMOVQ_SI_X15(i0*8); \
+ VMOVQ_SI_X11(i2*8); \
+ VPINSRQ_1_SI_X15(i1*8); \
+ VPINSRQ_1_SI_X11(i3*8); \
+ VINSERTI128 $1, X11, Y15, Y15
+
+#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
+ VMOVQ_SI_X12_0; \
+ VMOVQ_SI_X11(4*8); \
+ VPINSRQ_1_SI_X12(2*8); \
+ VPINSRQ_1_SI_X11(6*8); \
+ VINSERTI128 $1, X11, Y12, Y12; \
+ LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \
+ LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
+ LOAD_MSG_AVX2_Y15(9, 11, 13, 15)
+
+#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
+ LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
+ LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
+ VMOVQ_SI_X11(11*8); \
+ VPSHUFD $0x4E, 0*8(SI), X14; \
+ VPINSRQ_1_SI_X11(5*8); \
+ VINSERTI128 $1, X11, Y14, Y14; \
+ LOAD_MSG_AVX2_Y15(12, 2, 7, 3)
+
+#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
+ VMOVQ_SI_X11(5*8); \
+ VMOVDQU 11*8(SI), X12; \
+ VPINSRQ_1_SI_X11(15*8); \
+ VINSERTI128 $1, X11, Y12, Y12; \
+ VMOVQ_SI_X13(8*8); \
+ VMOVQ_SI_X11(2*8); \
+ VPINSRQ_1_SI_X13_0; \
+ VPINSRQ_1_SI_X11(13*8); \
+ VINSERTI128 $1, X11, Y13, Y13; \
+ LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
+ LOAD_MSG_AVX2_Y15(14, 6, 1, 4)
+
+#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
+ LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
+ LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
+ LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \
+ VMOVQ_SI_X15(6*8); \
+ VMOVQ_SI_X11_0; \
+ VPINSRQ_1_SI_X15(10*8); \
+ VPINSRQ_1_SI_X11(8*8); \
+ VINSERTI128 $1, X11, Y15, Y15
+
+#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
+ LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \
+ VMOVQ_SI_X13_0; \
+ VMOVQ_SI_X11(4*8); \
+ VPINSRQ_1_SI_X13(7*8); \
+ VPINSRQ_1_SI_X11(15*8); \
+ VINSERTI128 $1, X11, Y13, Y13; \
+ LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
+ LOAD_MSG_AVX2_Y15(1, 12, 8, 13)
+
+#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
+ VMOVQ_SI_X12(2*8); \
+ VMOVQ_SI_X11_0; \
+ VPINSRQ_1_SI_X12(6*8); \
+ VPINSRQ_1_SI_X11(8*8); \
+ VINSERTI128 $1, X11, Y12, Y12; \
+ LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
+ LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \
+ LOAD_MSG_AVX2_Y15(13, 5, 14, 9)
+
+#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
+ LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \
+ LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
+ VMOVQ_SI_X14_0; \
+ VPSHUFD $0x4E, 8*8(SI), X11; \
+ VPINSRQ_1_SI_X14(6*8); \
+ VINSERTI128 $1, X11, Y14, Y14; \
+ LOAD_MSG_AVX2_Y15(7, 3, 2, 11)
+
+#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
+ LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
+ LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
+ LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \
+ VMOVQ_SI_X15_0; \
+ VMOVQ_SI_X11(6*8); \
+ VPINSRQ_1_SI_X15(4*8); \
+ VPINSRQ_1_SI_X11(10*8); \
+ VINSERTI128 $1, X11, Y15, Y15
+
+#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
+ VMOVQ_SI_X12(6*8); \
+ VMOVQ_SI_X11(11*8); \
+ VPINSRQ_1_SI_X12(14*8); \
+ VPINSRQ_1_SI_X11_0; \
+ VINSERTI128 $1, X11, Y12, Y12; \
+ LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
+ VMOVQ_SI_X11(1*8); \
+ VMOVDQU 12*8(SI), X14; \
+ VPINSRQ_1_SI_X11(10*8); \
+ VINSERTI128 $1, X11, Y14, Y14; \
+ VMOVQ_SI_X15(2*8); \
+ VMOVDQU 4*8(SI), X11; \
+ VPINSRQ_1_SI_X15(7*8); \
+ VINSERTI128 $1, X11, Y15, Y15
+
+#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
+ LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \
+ VMOVQ_SI_X13(2*8); \
+ VPSHUFD $0x4E, 5*8(SI), X11; \
+ VPINSRQ_1_SI_X13(4*8); \
+ VINSERTI128 $1, X11, Y13, Y13; \
+ LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
+ VMOVQ_SI_X15(11*8); \
+ VMOVQ_SI_X11(12*8); \
+ VPINSRQ_1_SI_X15(14*8); \
+ VPINSRQ_1_SI_X11_0; \
+ VINSERTI128 $1, X11, Y15, Y15
+
+// func fAVX2(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+TEXT ·fAVX2(SB), 4, $64-48 // frame size = 32 + 32 byte alignment
+ MOVQ h+0(FP), AX
+ MOVQ m+8(FP), SI
+ MOVQ c0+16(FP), R8
+ MOVQ c1+24(FP), R9
+ MOVQ flag+32(FP), CX
+ MOVQ rounds+40(FP), BX
+
+ MOVQ SP, DX
+ MOVQ SP, R10
+ ADDQ $31, R10
+ ANDQ $~31, R10
+ MOVQ R10, SP
+
+ MOVQ CX, 16(SP)
+ XORQ CX, CX
+ MOVQ CX, 24(SP)
+
+ VMOVDQU ·AVX2_c40<>(SB), Y4
+ VMOVDQU ·AVX2_c48<>(SB), Y5
+
+ VMOVDQU 0(AX), Y8
+ VMOVDQU 32(AX), Y9
+ VMOVDQU ·AVX2_iv0<>(SB), Y6
+ VMOVDQU ·AVX2_iv1<>(SB), Y7
+
+ MOVQ R8, 0(SP)
+ MOVQ R9, 8(SP)
+
+ VMOVDQA Y8, Y0
+ VMOVDQA Y9, Y1
+ VMOVDQA Y6, Y2
+ VPXOR 0(SP), Y7, Y3
+
+loop:
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
+ ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
+
+ JMP loop
+
+done:
+ VPXOR Y0, Y8, Y8
+ VPXOR Y1, Y9, Y9
+ VPXOR Y2, Y8, Y8
+ VPXOR Y3, Y9, Y9
+
+ VMOVDQU Y8, 0(AX)
+ VMOVDQU Y9, 32(AX)
+ VZEROUPPER
+
+ MOVQ DX, SP
+ RET
+
+#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
+#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
+#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
+#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
+#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE
+
+#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
+#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
+#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
+#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
+#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
+#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
+#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
+#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF
+
+#define SHUFFLE_AVX() \
+ VMOVDQA X6, X13; \
+ VMOVDQA X2, X14; \
+ VMOVDQA X4, X6; \
+ VPUNPCKLQDQ_X13_X13_X15; \
+ VMOVDQA X5, X4; \
+ VMOVDQA X6, X5; \
+ VPUNPCKHQDQ_X15_X7_X6; \
+ VPUNPCKLQDQ_X7_X7_X15; \
+ VPUNPCKHQDQ_X15_X13_X7; \
+ VPUNPCKLQDQ_X3_X3_X15; \
+ VPUNPCKHQDQ_X15_X2_X2; \
+ VPUNPCKLQDQ_X14_X14_X15; \
+ VPUNPCKHQDQ_X15_X3_X3; \
+
+#define SHUFFLE_AVX_INV() \
+ VMOVDQA X2, X13; \
+ VMOVDQA X4, X14; \
+ VPUNPCKLQDQ_X2_X2_X15; \
+ VMOVDQA X5, X4; \
+ VPUNPCKHQDQ_X15_X3_X2; \
+ VMOVDQA X14, X5; \
+ VPUNPCKLQDQ_X3_X3_X15; \
+ VMOVDQA X6, X14; \
+ VPUNPCKHQDQ_X15_X13_X3; \
+ VPUNPCKLQDQ_X7_X7_X15; \
+ VPUNPCKHQDQ_X15_X6_X6; \
+ VPUNPCKLQDQ_X14_X14_X15; \
+ VPUNPCKHQDQ_X15_X7_X7; \
+
+#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
+ VPADDQ m0, v0, v0; \
+ VPADDQ v2, v0, v0; \
+ VPADDQ m1, v1, v1; \
+ VPADDQ v3, v1, v1; \
+ VPXOR v0, v6, v6; \
+ VPXOR v1, v7, v7; \
+ VPSHUFD $-79, v6, v6; \
+ VPSHUFD $-79, v7, v7; \
+ VPADDQ v6, v4, v4; \
+ VPADDQ v7, v5, v5; \
+ VPXOR v4, v2, v2; \
+ VPXOR v5, v3, v3; \
+ VPSHUFB c40, v2, v2; \
+ VPSHUFB c40, v3, v3; \
+ VPADDQ m2, v0, v0; \
+ VPADDQ v2, v0, v0; \
+ VPADDQ m3, v1, v1; \
+ VPADDQ v3, v1, v1; \
+ VPXOR v0, v6, v6; \
+ VPXOR v1, v7, v7; \
+ VPSHUFB c48, v6, v6; \
+ VPSHUFB c48, v7, v7; \
+ VPADDQ v6, v4, v4; \
+ VPADDQ v7, v5, v5; \
+ VPXOR v4, v2, v2; \
+ VPXOR v5, v3, v3; \
+ VPADDQ v2, v2, t0; \
+ VPSRLQ $63, v2, v2; \
+ VPXOR t0, v2, v2; \
+ VPADDQ v3, v3, t0; \
+ VPSRLQ $63, v3, v3; \
+ VPXOR t0, v3, v3
+
+// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
+// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
+#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
+ VMOVQ_SI_X12(i0*8); \
+ VMOVQ_SI_X13(i2*8); \
+ VMOVQ_SI_X14(i4*8); \
+ VMOVQ_SI_X15(i6*8); \
+ VPINSRQ_1_SI_X12(i1*8); \
+ VPINSRQ_1_SI_X13(i3*8); \
+ VPINSRQ_1_SI_X14(i5*8); \
+ VPINSRQ_1_SI_X15(i7*8)
+
+// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
+#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
+ VMOVQ_SI_X12_0; \
+ VMOVQ_SI_X13(4*8); \
+ VMOVQ_SI_X14(1*8); \
+ VMOVQ_SI_X15(5*8); \
+ VPINSRQ_1_SI_X12(2*8); \
+ VPINSRQ_1_SI_X13(6*8); \
+ VPINSRQ_1_SI_X14(3*8); \
+ VPINSRQ_1_SI_X15(7*8)
+
+// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
+#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
+ VPSHUFD $0x4E, 0*8(SI), X12; \
+ VMOVQ_SI_X13(11*8); \
+ VMOVQ_SI_X14(12*8); \
+ VMOVQ_SI_X15(7*8); \
+ VPINSRQ_1_SI_X13(5*8); \
+ VPINSRQ_1_SI_X14(2*8); \
+ VPINSRQ_1_SI_X15(3*8)
+
+// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
+#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
+ VMOVDQU 11*8(SI), X12; \
+ VMOVQ_SI_X13(5*8); \
+ VMOVQ_SI_X14(8*8); \
+ VMOVQ_SI_X15(2*8); \
+ VPINSRQ_1_SI_X13(15*8); \
+ VPINSRQ_1_SI_X14_0; \
+ VPINSRQ_1_SI_X15(13*8)
+
+// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
+#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
+ VMOVQ_SI_X12(2*8); \
+ VMOVQ_SI_X13(4*8); \
+ VMOVQ_SI_X14(6*8); \
+ VMOVQ_SI_X15_0; \
+ VPINSRQ_1_SI_X12(5*8); \
+ VPINSRQ_1_SI_X13(15*8); \
+ VPINSRQ_1_SI_X14(10*8); \
+ VPINSRQ_1_SI_X15(8*8)
+
+// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
+#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
+ VMOVQ_SI_X12(9*8); \
+ VMOVQ_SI_X13(2*8); \
+ VMOVQ_SI_X14_0; \
+ VMOVQ_SI_X15(4*8); \
+ VPINSRQ_1_SI_X12(5*8); \
+ VPINSRQ_1_SI_X13(10*8); \
+ VPINSRQ_1_SI_X14(7*8); \
+ VPINSRQ_1_SI_X15(15*8)
+
+// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
+#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
+ VMOVQ_SI_X12(2*8); \
+ VMOVQ_SI_X13_0; \
+ VMOVQ_SI_X14(12*8); \
+ VMOVQ_SI_X15(11*8); \
+ VPINSRQ_1_SI_X12(6*8); \
+ VPINSRQ_1_SI_X13(8*8); \
+ VPINSRQ_1_SI_X14(10*8); \
+ VPINSRQ_1_SI_X15(3*8)
+
+// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
+#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
+ MOVQ 0*8(SI), X12; \
+ VPSHUFD $0x4E, 8*8(SI), X13; \
+ MOVQ 7*8(SI), X14; \
+ MOVQ 2*8(SI), X15; \
+ VPINSRQ_1_SI_X12(6*8); \
+ VPINSRQ_1_SI_X14(3*8); \
+ VPINSRQ_1_SI_X15(11*8)
+
+// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
+#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
+ MOVQ 6*8(SI), X12; \
+ MOVQ 11*8(SI), X13; \
+ MOVQ 15*8(SI), X14; \
+ MOVQ 3*8(SI), X15; \
+ VPINSRQ_1_SI_X12(14*8); \
+ VPINSRQ_1_SI_X13_0; \
+ VPINSRQ_1_SI_X14(9*8); \
+ VPINSRQ_1_SI_X15(8*8)
+
+// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
+#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
+ MOVQ 5*8(SI), X12; \
+ MOVQ 8*8(SI), X13; \
+ MOVQ 0*8(SI), X14; \
+ MOVQ 6*8(SI), X15; \
+ VPINSRQ_1_SI_X12(15*8); \
+ VPINSRQ_1_SI_X13(2*8); \
+ VPINSRQ_1_SI_X14(4*8); \
+ VPINSRQ_1_SI_X15(10*8)
+
+// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
+#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
+ VMOVDQU 12*8(SI), X12; \
+ MOVQ 1*8(SI), X13; \
+ MOVQ 2*8(SI), X14; \
+ VPINSRQ_1_SI_X13(10*8); \
+ VPINSRQ_1_SI_X14(7*8); \
+ VMOVDQU 4*8(SI), X15
+
+// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
+#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
+ MOVQ 15*8(SI), X12; \
+ MOVQ 3*8(SI), X13; \
+ MOVQ 11*8(SI), X14; \
+ MOVQ 12*8(SI), X15; \
+ VPINSRQ_1_SI_X12(9*8); \
+ VPINSRQ_1_SI_X13(13*8); \
+ VPINSRQ_1_SI_X14(14*8); \
+ VPINSRQ_1_SI_X15_0
+
+// func fAVX(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+TEXT ·fAVX(SB), 4, $24-48 // frame size = 8 + 16 byte alignment
+ MOVQ h+0(FP), AX
+ MOVQ m+8(FP), SI
+ MOVQ c0+16(FP), R8
+ MOVQ c1+24(FP), R9
+ MOVQ flag+32(FP), CX
+ MOVQ rounds+40(FP), BX
+
+ MOVQ SP, BP
+ MOVQ SP, R10
+ ADDQ $15, R10
+ ANDQ $~15, R10
+ MOVQ R10, SP
+
+ VMOVDQU ·AVX_c40<>(SB), X0
+ VMOVDQU ·AVX_c48<>(SB), X1
+ VMOVDQA X0, X8
+ VMOVDQA X1, X9
+
+ VMOVDQU ·AVX_iv3<>(SB), X0
+ VMOVDQA X0, 0(SP)
+ XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0)
+
+ VMOVDQU 0(AX), X10
+ VMOVDQU 16(AX), X11
+ VMOVDQU 32(AX), X2
+ VMOVDQU 48(AX), X3
+
+ VMOVQ_R8_X15
+ VPINSRQ_1_R9_X15
+
+ VMOVDQA X10, X0
+ VMOVDQA X11, X1
+ VMOVDQU ·AVX_iv0<>(SB), X4
+ VMOVDQU ·AVX_iv1<>(SB), X5
+ VMOVDQU ·AVX_iv2<>(SB), X6
+
+ VPXOR X15, X6, X6
+ VMOVDQA 0(SP), X7
+
+loop:
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX()
+ LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
+ HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
+ SHUFFLE_AVX_INV()
+
+ JMP loop
+
+done:
+ VMOVDQU 32(AX), X14
+ VMOVDQU 48(AX), X15
+ VPXOR X0, X10, X10
+ VPXOR X1, X11, X11
+ VPXOR X2, X14, X14
+ VPXOR X3, X15, X15
+ VPXOR X4, X10, X10
+ VPXOR X5, X11, X11
+ VPXOR X6, X14, X2
+ VPXOR X7, X15, X3
+ VMOVDQU X2, 32(AX)
+ VMOVDQU X3, 48(AX)
+
+ VMOVDQU X10, 0(AX)
+ VMOVDQU X11, 16(AX)
+ VZEROUPPER
+
+ MOVQ BP, SP
+ RET
diff --git a/common/crypto/blake2b/blake2b_amd64.go b/common/crypto/blake2b/blake2b_amd64.go
new file mode 100644
index 0000000..7f933ec
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_amd64.go
@@ -0,0 +1,24 @@
+// Copyright 2016 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.
+
+//go:build !go1.7 && amd64 && !gccgo && !appengine
+
+package blake2b
+
+import "golang.org/x/sys/cpu"
+
+func init() {
+ useSSE4 = cpu.X86.HasSSE41
+}
+
+//go:noescape
+func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+
+func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) {
+ if useSSE4 {
+ fSSE4(h, m, c0, c1, flag, rounds)
+ } else {
+ fGeneric(h, m, c0, c1, flag, rounds)
+ }
+}
diff --git a/common/crypto/blake2b/blake2b_amd64.s b/common/crypto/blake2b/blake2b_amd64.s
new file mode 100644
index 0000000..ce4b56d
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_amd64.s
@@ -0,0 +1,253 @@
+// Copyright 2016 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.
+
+// +build amd64,!gccgo,!appengine
+
+#include "textflag.h"
+
+DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
+DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
+GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16
+
+DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
+DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
+GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16
+
+DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1
+DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
+GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16
+
+DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
+DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
+GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16
+
+DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
+DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
+GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
+
+DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
+DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
+GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
+
+#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
+ MOVO v4, t1; \
+ MOVO v5, v4; \
+ MOVO t1, v5; \
+ MOVO v6, t1; \
+ PUNPCKLQDQ v6, t2; \
+ PUNPCKHQDQ v7, v6; \
+ PUNPCKHQDQ t2, v6; \
+ PUNPCKLQDQ v7, t2; \
+ MOVO t1, v7; \
+ MOVO v2, t1; \
+ PUNPCKHQDQ t2, v7; \
+ PUNPCKLQDQ v3, t2; \
+ PUNPCKHQDQ t2, v2; \
+ PUNPCKLQDQ t1, t2; \
+ PUNPCKHQDQ t2, v3
+
+#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
+ MOVO v4, t1; \
+ MOVO v5, v4; \
+ MOVO t1, v5; \
+ MOVO v2, t1; \
+ PUNPCKLQDQ v2, t2; \
+ PUNPCKHQDQ v3, v2; \
+ PUNPCKHQDQ t2, v2; \
+ PUNPCKLQDQ v3, t2; \
+ MOVO t1, v3; \
+ MOVO v6, t1; \
+ PUNPCKHQDQ t2, v3; \
+ PUNPCKLQDQ v7, t2; \
+ PUNPCKHQDQ t2, v6; \
+ PUNPCKLQDQ t1, t2; \
+ PUNPCKHQDQ t2, v7
+
+#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
+ PADDQ m0, v0; \
+ PADDQ m1, v1; \
+ PADDQ v2, v0; \
+ PADDQ v3, v1; \
+ PXOR v0, v6; \
+ PXOR v1, v7; \
+ PSHUFD $0xB1, v6, v6; \
+ PSHUFD $0xB1, v7, v7; \
+ PADDQ v6, v4; \
+ PADDQ v7, v5; \
+ PXOR v4, v2; \
+ PXOR v5, v3; \
+ PSHUFB c40, v2; \
+ PSHUFB c40, v3; \
+ PADDQ m2, v0; \
+ PADDQ m3, v1; \
+ PADDQ v2, v0; \
+ PADDQ v3, v1; \
+ PXOR v0, v6; \
+ PXOR v1, v7; \
+ PSHUFB c48, v6; \
+ PSHUFB c48, v7; \
+ PADDQ v6, v4; \
+ PADDQ v7, v5; \
+ PXOR v4, v2; \
+ PXOR v5, v3; \
+ MOVOU v2, t0; \
+ PADDQ v2, t0; \
+ PSRLQ $63, v2; \
+ PXOR t0, v2; \
+ MOVOU v3, t0; \
+ PADDQ v3, t0; \
+ PSRLQ $63, v3; \
+ PXOR t0, v3
+
+#define LOAD_MSG(m0, m1, m2, m3, i0, i1, i2, i3, i4, i5, i6, i7) \
+ MOVQ i0*8(SI), m0; \
+ PINSRQ $1, i1*8(SI), m0; \
+ MOVQ i2*8(SI), m1; \
+ PINSRQ $1, i3*8(SI), m1; \
+ MOVQ i4*8(SI), m2; \
+ PINSRQ $1, i5*8(SI), m2; \
+ MOVQ i6*8(SI), m3; \
+ PINSRQ $1, i7*8(SI), m3
+
+// func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64)
+TEXT ·fSSE4(SB), 4, $24-48 // frame size = 8 + 16 byte alignment
+ MOVQ h+0(FP), AX
+ MOVQ m+8(FP), SI
+ MOVQ c0+16(FP), R8
+ MOVQ c1+24(FP), R9
+ MOVQ flag+32(FP), CX
+ MOVQ rounds+40(FP), BX
+
+ MOVQ SP, BP
+ MOVQ SP, R10
+ ADDQ $15, R10
+ ANDQ $~15, R10
+ MOVQ R10, SP
+
+ MOVOU ·iv3<>(SB), X0
+ MOVO X0, 0(SP)
+ XORQ CX, 0(SP) // 0(SP) = ·iv3 ^ (CX || 0)
+
+ MOVOU ·c40<>(SB), X13
+ MOVOU ·c48<>(SB), X14
+
+ MOVOU 0(AX), X12
+ MOVOU 16(AX), X15
+
+ MOVQ R8, X8
+ PINSRQ $1, R9, X8
+
+ MOVO X12, X0
+ MOVO X15, X1
+ MOVOU 32(AX), X2
+ MOVOU 48(AX), X3
+ MOVOU ·iv0<>(SB), X4
+ MOVOU ·iv1<>(SB), X5
+ MOVOU ·iv2<>(SB), X6
+
+ PXOR X8, X6
+ MOVO 0(SP), X7
+
+loop:
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 0, 2, 4, 6, 1, 3, 5, 7)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 8, 10, 12, 14, 9, 11, 13, 15)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 14, 4, 9, 13, 10, 8, 15, 6)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 1, 0, 11, 5, 12, 2, 7, 3)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 11, 12, 5, 15, 8, 0, 2, 13)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 10, 3, 7, 9, 14, 6, 1, 4)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 7, 3, 13, 11, 9, 1, 12, 14)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 2, 5, 4, 15, 6, 10, 0, 8)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 9, 5, 2, 10, 0, 7, 4, 15)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 14, 11, 6, 3, 1, 12, 8, 13)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 2, 6, 0, 8, 12, 10, 11, 3)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 4, 7, 15, 1, 13, 5, 14, 9)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 12, 1, 14, 4, 5, 15, 13, 10)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 0, 6, 9, 8, 7, 3, 2, 11)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 13, 7, 12, 3, 11, 14, 1, 9)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 5, 15, 8, 2, 0, 4, 6, 10)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 6, 14, 11, 0, 15, 9, 3, 8)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 12, 13, 1, 10, 2, 7, 4, 5)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ SUBQ $1, BX; JCS done
+ LOAD_MSG(X8, X9, X10, X11, 10, 8, 7, 1, 2, 4, 6, 5)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
+ LOAD_MSG(X8, X9, X10, X11, 15, 9, 3, 13, 11, 14, 12, 0)
+ HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
+ SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
+
+ JMP loop
+
+done:
+ MOVOU 32(AX), X10
+ MOVOU 48(AX), X11
+ PXOR X0, X12
+ PXOR X1, X15
+ PXOR X2, X10
+ PXOR X3, X11
+ PXOR X4, X12
+ PXOR X5, X15
+ PXOR X6, X10
+ PXOR X7, X11
+ MOVOU X10, 32(AX)
+ MOVOU X11, 48(AX)
+
+ MOVOU X12, 0(AX)
+ MOVOU X15, 16(AX)
+
+ MOVQ BP, SP
+ RET
diff --git a/common/crypto/blake2b/blake2b_f_fuzz.go b/common/crypto/blake2b/blake2b_f_fuzz.go
new file mode 100644
index 0000000..5b7eb67
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_f_fuzz.go
@@ -0,0 +1,57 @@
+//go:build gofuzz
+
+package blake2b
+
+import (
+ "encoding/binary"
+)
+
+func Fuzz(data []byte) int {
+ // Make sure the data confirms to the input model
+ if len(data) != 211 {
+ return 0
+ }
+ // Parse everything and call all the implementations
+ var (
+ rounds = binary.BigEndian.Uint16(data[0:2])
+
+ h [8]uint64
+ m [16]uint64
+ t [2]uint64
+ f uint64
+ )
+ for i := 0; i < 8; i++ {
+ offset := 2 + i*8
+ h[i] = binary.LittleEndian.Uint64(data[offset : offset+8])
+ }
+ for i := 0; i < 16; i++ {
+ offset := 66 + i*8
+ m[i] = binary.LittleEndian.Uint64(data[offset : offset+8])
+ }
+ t[0] = binary.LittleEndian.Uint64(data[194:202])
+ t[1] = binary.LittleEndian.Uint64(data[202:210])
+
+ if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1
+ f = 0xFFFFFFFFFFFFFFFF
+ }
+ // Run the blake2b compression on all instruction sets and cross reference
+ want := h
+ fGeneric(&want, &m, t[0], t[1], f, uint64(rounds))
+
+ have := h
+ fSSE4(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("SSE4 mismatches generic algo")
+ }
+ have = h
+ fAVX(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("AVX mismatches generic algo")
+ }
+ have = h
+ fAVX2(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("AVX2 mismatches generic algo")
+ }
+ return 1
+}
diff --git a/common/crypto/blake2b/blake2b_f_test.go b/common/crypto/blake2b/blake2b_f_test.go
new file mode 100644
index 0000000..4e07d13
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_f_test.go
@@ -0,0 +1,59 @@
+package blake2b
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestF(t *testing.T) {
+ for i, test := range testVectorsF {
+ t.Run(fmt.Sprintf("test vector %v", i), func(t *testing.T) {
+ //toEthereumTestCase(test)
+
+ h := test.hIn
+ F(&h, test.m, test.c, test.f, test.rounds)
+
+ if !reflect.DeepEqual(test.hOut, h) {
+ t.Errorf("Unexpected result\nExpected: [%#x]\nActual: [%#x]\n", test.hOut, h)
+ }
+ })
+ }
+}
+
+type testVector struct {
+ hIn [8]uint64
+ m [16]uint64
+ c [2]uint64
+ f bool
+ rounds uint32
+ hOut [8]uint64
+}
+
+// https://tools.ietf.org/html/rfc7693#appendix-A
+var testVectorsF = []testVector{
+ {
+ hIn: [8]uint64{
+ 0x6a09e667f2bdc948, 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1, 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
+ },
+ m: [16]uint64{
+ 0x0000000000636261, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000,
+ },
+ c: [2]uint64{3, 0},
+ f: true,
+ rounds: 12,
+ hOut: [8]uint64{
+ 0x0D4D1C983FA580BA, 0xE9F6129FB697276A, 0xB7C45A68142F214C,
+ 0xD1A2FFDB6FBB124B, 0x2D79AB2A39C5877D, 0x95CC3345DED552C2,
+ 0x5A92F1DBA88AD318, 0x239900D4ED8623B9,
+ },
+ },
+}
diff --git a/common/crypto/blake2b/blake2b_generic.go b/common/crypto/blake2b/blake2b_generic.go
new file mode 100644
index 0000000..b506e30
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_generic.go
@@ -0,0 +1,161 @@
+// Copyright 2016 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 blake2b
+
+import (
+ "math/bits"
+)
+
+// the precomputed values for BLAKE2b
+// there are 10 16-byte arrays - one for each round
+// the entries are calculated from the sigma constants.
+var precomputed = [10][16]byte{
+ {0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
+ {14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
+ {11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
+ {7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
+ {9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
+ {2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
+ {12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
+ {13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
+ {6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
+ {10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
+}
+
+func fGeneric(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) {
+ v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
+ v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
+ v12 ^= c0
+ v13 ^= c1
+ v14 ^= flag
+
+ for i := 0; i < int(rounds); i++ {
+ s := &(precomputed[i%10])
+
+ v0 += m[s[0]]
+ v0 += v4
+ v12 ^= v0
+ v12 = bits.RotateLeft64(v12, -32)
+ v8 += v12
+ v4 ^= v8
+ v4 = bits.RotateLeft64(v4, -24)
+ v1 += m[s[1]]
+ v1 += v5
+ v13 ^= v1
+ v13 = bits.RotateLeft64(v13, -32)
+ v9 += v13
+ v5 ^= v9
+ v5 = bits.RotateLeft64(v5, -24)
+ v2 += m[s[2]]
+ v2 += v6
+ v14 ^= v2
+ v14 = bits.RotateLeft64(v14, -32)
+ v10 += v14
+ v6 ^= v10
+ v6 = bits.RotateLeft64(v6, -24)
+ v3 += m[s[3]]
+ v3 += v7
+ v15 ^= v3
+ v15 = bits.RotateLeft64(v15, -32)
+ v11 += v15
+ v7 ^= v11
+ v7 = bits.RotateLeft64(v7, -24)
+
+ v0 += m[s[4]]
+ v0 += v4
+ v12 ^= v0
+ v12 = bits.RotateLeft64(v12, -16)
+ v8 += v12
+ v4 ^= v8
+ v4 = bits.RotateLeft64(v4, -63)
+ v1 += m[s[5]]
+ v1 += v5
+ v13 ^= v1
+ v13 = bits.RotateLeft64(v13, -16)
+ v9 += v13
+ v5 ^= v9
+ v5 = bits.RotateLeft64(v5, -63)
+ v2 += m[s[6]]
+ v2 += v6
+ v14 ^= v2
+ v14 = bits.RotateLeft64(v14, -16)
+ v10 += v14
+ v6 ^= v10
+ v6 = bits.RotateLeft64(v6, -63)
+ v3 += m[s[7]]
+ v3 += v7
+ v15 ^= v3
+ v15 = bits.RotateLeft64(v15, -16)
+ v11 += v15
+ v7 ^= v11
+ v7 = bits.RotateLeft64(v7, -63)
+
+ v0 += m[s[8]]
+ v0 += v5
+ v15 ^= v0
+ v15 = bits.RotateLeft64(v15, -32)
+ v10 += v15
+ v5 ^= v10
+ v5 = bits.RotateLeft64(v5, -24)
+ v1 += m[s[9]]
+ v1 += v6
+ v12 ^= v1
+ v12 = bits.RotateLeft64(v12, -32)
+ v11 += v12
+ v6 ^= v11
+ v6 = bits.RotateLeft64(v6, -24)
+ v2 += m[s[10]]
+ v2 += v7
+ v13 ^= v2
+ v13 = bits.RotateLeft64(v13, -32)
+ v8 += v13
+ v7 ^= v8
+ v7 = bits.RotateLeft64(v7, -24)
+ v3 += m[s[11]]
+ v3 += v4
+ v14 ^= v3
+ v14 = bits.RotateLeft64(v14, -32)
+ v9 += v14
+ v4 ^= v9
+ v4 = bits.RotateLeft64(v4, -24)
+
+ v0 += m[s[12]]
+ v0 += v5
+ v15 ^= v0
+ v15 = bits.RotateLeft64(v15, -16)
+ v10 += v15
+ v5 ^= v10
+ v5 = bits.RotateLeft64(v5, -63)
+ v1 += m[s[13]]
+ v1 += v6
+ v12 ^= v1
+ v12 = bits.RotateLeft64(v12, -16)
+ v11 += v12
+ v6 ^= v11
+ v6 = bits.RotateLeft64(v6, -63)
+ v2 += m[s[14]]
+ v2 += v7
+ v13 ^= v2
+ v13 = bits.RotateLeft64(v13, -16)
+ v8 += v13
+ v7 ^= v8
+ v7 = bits.RotateLeft64(v7, -63)
+ v3 += m[s[15]]
+ v3 += v4
+ v14 ^= v3
+ v14 = bits.RotateLeft64(v14, -16)
+ v9 += v14
+ v4 ^= v9
+ v4 = bits.RotateLeft64(v4, -63)
+ }
+ h[0] ^= v0 ^ v8
+ h[1] ^= v1 ^ v9
+ h[2] ^= v2 ^ v10
+ h[3] ^= v3 ^ v11
+ h[4] ^= v4 ^ v12
+ h[5] ^= v5 ^ v13
+ h[6] ^= v6 ^ v14
+ h[7] ^= v7 ^ v15
+}
diff --git a/common/crypto/blake2b/blake2b_ref.go b/common/crypto/blake2b/blake2b_ref.go
new file mode 100644
index 0000000..6825441
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_ref.go
@@ -0,0 +1,11 @@
+// Copyright 2016 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.
+
+//go:build !amd64 || appengine || gccgo
+
+package blake2b
+
+func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) {
+ fGeneric(h, m, c0, c1, flag, rounds)
+}
diff --git a/common/crypto/blake2b/blake2b_test.go b/common/crypto/blake2b/blake2b_test.go
new file mode 100644
index 0000000..14f2e3b
--- /dev/null
+++ b/common/crypto/blake2b/blake2b_test.go
@@ -0,0 +1,868 @@
+// Copyright 2016 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 blake2b
+
+import (
+ "bytes"
+ "encoding"
+ "encoding/hex"
+ "fmt"
+ "github.com/ledgerwatch/log/v3"
+ "hash"
+ "io"
+ "testing"
+)
+
+func TestHashes(t *testing.T) {
+ defer func(sse4, avx, avx2 bool) {
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+ }(useSSE4, useAVX, useAVX2)
+
+ if useAVX2 {
+ t.Log("AVX2 version")
+ testHashes(t)
+ useAVX2 = false
+ }
+ if useAVX {
+ t.Log("AVX version")
+ testHashes(t)
+ useAVX = false
+ }
+ if useSSE4 {
+ t.Log("SSE4 version")
+ testHashes(t)
+ useSSE4 = false
+ }
+ t.Log("generic version")
+ testHashes(t)
+}
+
+func TestHashes2X(t *testing.T) {
+ defer func(sse4, avx, avx2 bool) {
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+ }(useSSE4, useAVX, useAVX2)
+
+ if useAVX2 {
+ t.Log("AVX2 version")
+ testHashes2X(t)
+ useAVX2 = false
+ }
+ if useAVX {
+ t.Log("AVX version")
+ testHashes2X(t)
+ useAVX = false
+ }
+ if useSSE4 {
+ t.Log("SSE4 version")
+ testHashes2X(t)
+ useSSE4 = false
+ }
+ t.Log("generic version")
+ testHashes2X(t)
+}
+
+func TestMarshal(t *testing.T) {
+ input := make([]byte, 255)
+ for i := range input {
+ input[i] = byte(i)
+ }
+ for _, size := range []int{Size, Size256, Size384, 12, 25, 63} {
+ for i := 0; i < 256; i++ {
+ h, err := New(size, nil)
+ if err != nil {
+ t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+ }
+ h2, err := New(size, nil)
+ if err != nil {
+ t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+ }
+
+ h.Write(input[:i/2])
+ halfstate, err := h.(encoding.BinaryMarshaler).MarshalBinary()
+ if err != nil {
+ t.Fatalf("size=%d, len(input)=%d: could not marshal: %v", size, i, err)
+ }
+ binUnmarshaler, ok := h2.(encoding.BinaryUnmarshaler)
+ if !ok {
+ log.Warn("Failed to type convert h2 to encoding.BinaryUnmarshaler")
+ }
+ err = binUnmarshaler.UnmarshalBinary(halfstate)
+ if err != nil {
+ t.Fatalf("size=%d, len(input)=%d: could not unmarshal: %v", size, i, err)
+ }
+
+ h.Write(input[i/2 : i])
+ sum := h.Sum(nil)
+ h2.Write(input[i/2 : i])
+ sum2 := h2.Sum(nil)
+
+ if !bytes.Equal(sum, sum2) {
+ t.Fatalf("size=%d, len(input)=%d: results do not match; sum = %v, sum2 = %v", size, i, sum, sum2)
+ }
+
+ h3, err := New(size, nil)
+ if err != nil {
+ t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
+ }
+ h3.Write(input[:i])
+ sum3 := h3.Sum(nil)
+ if !bytes.Equal(sum, sum3) {
+ t.Fatalf("size=%d, len(input)=%d: sum = %v, want %v", size, i, sum, sum3)
+ }
+ }
+ }
+}
+
+func testHashes(t *testing.T) {
+ key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
+
+ input := make([]byte, 255)
+ for i := range input {
+ input[i] = byte(i)
+ }
+
+ for i, expectedHex := range hashes {
+ h, err := New512(key)
+ if err != nil {
+ t.Fatalf("#%d: error from New512: %v", i, err)
+ }
+
+ h.Write(input[:i])
+ sum := h.Sum(nil)
+
+ if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
+ t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex)
+ }
+
+ h.Reset()
+ for j := 0; j < i; j++ {
+ h.Write(input[j : j+1])
+ }
+
+ sum = h.Sum(sum[:0])
+ if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
+ t.Fatalf("#%d (byte-by-byte): got %s, wanted %s", i, gotHex, expectedHex)
+ }
+ }
+}
+
+func testHashes2X(t *testing.T) {
+ key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
+
+ input := make([]byte, 256)
+ for i := range input {
+ input[i] = byte(i)
+ }
+
+ for i, expectedHex := range hashes2X {
+ length := uint32(len(expectedHex) / 2)
+ sum := make([]byte, int(length))
+
+ h, err := NewXOF(length, key)
+ if err != nil {
+ t.Fatalf("#%d: error from NewXOF: %v", i, err)
+ }
+
+ if _, err := h.Write(input); err != nil {
+ t.Fatalf("#%d (single write): error from Write: %v", i, err)
+ }
+ if _, err := h.Read(sum); err != nil {
+ t.Fatalf("#%d (single write): error from Read: %v", i, err)
+ }
+ if n, err := h.Read(sum); n != 0 || err != io.EOF {
+ t.Fatalf("#%d (single write): Read did not return (0, io.EOF) after exhaustion, got (%v, %v)", i, n, err)
+ }
+ if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
+ t.Fatalf("#%d (single write): got %s, wanted %s", i, gotHex, expectedHex)
+ }
+
+ h.Reset()
+ for j := 0; j < len(input); j++ {
+ h.Write(input[j : j+1])
+ }
+ for j := 0; j < len(sum); j++ {
+ h = h.Clone()
+ if _, err := h.Read(sum[j : j+1]); err != nil {
+ t.Fatalf("#%d (byte-by-byte) - Read %d: error from Read: %v", i, j, err)
+ }
+ }
+ if gotHex := fmt.Sprintf("%x", sum); gotHex != expectedHex {
+ t.Fatalf("#%d (byte-by-byte): got %s, wanted %s", i, gotHex, expectedHex)
+ }
+ }
+
+ h, err := NewXOF(OutputLengthUnknown, key)
+ if err != nil {
+ t.Fatalf("#unknown length: error from NewXOF: %v", err)
+ }
+ if _, err := h.Write(input); err != nil {
+ t.Fatalf("#unknown length: error from Write: %v", err)
+ }
+
+ var result [64]byte
+ if n, err := h.Read(result[:]); err != nil {
+ t.Fatalf("#unknown length: error from Read: %v", err)
+ } else if n != len(result) {
+ t.Fatalf("#unknown length: Read returned %d bytes, want %d", n, len(result))
+ }
+
+ const expected = "3dbba8516da76bf7330055c66ea36cf1005e92714262b24d9710f51d9e126406e1bcd6497059f9331f1091c3634b695428d475ed432f987040575520a1c29f5e"
+ if fmt.Sprintf("%x", result) != expected {
+ t.Fatalf("#unknown length: bad result %x, wanted %s", result, expected)
+ }
+}
+
+func generateSequence(out []byte, seed uint32) {
+ a := 0xDEAD4BAD * seed // prime
+ b := uint32(1)
+
+ for i := range out { // fill the buf
+ a, b = b, a+b
+ out[i] = byte(b >> 24)
+ }
+}
+
+func computeMAC(msg []byte, hashSize int, key []byte) (sum []byte) {
+ var h hash.Hash
+ switch hashSize {
+ case Size:
+ h, _ = New512(key)
+ case Size384:
+ h, _ = New384(key)
+ case Size256:
+ h, _ = New256(key)
+ case 20:
+ h, _ = newDigest(20, key)
+ default:
+ panic("unexpected hashSize")
+ }
+
+ h.Write(msg)
+ return h.Sum(sum)
+}
+
+func computeHash(msg []byte, hashSize int) (sum []byte) {
+ switch hashSize {
+ case Size:
+ hash := Sum512(msg)
+ return hash[:]
+ case Size384:
+ hash := Sum384(msg)
+ return hash[:]
+ case Size256:
+ hash := Sum256(msg)
+ return hash[:]
+ case 20:
+ var hash [64]byte
+ checkSum(&hash, 20, msg)
+ return hash[:20]
+ default:
+ panic("unexpected hashSize")
+ }
+}
+
+// Test function from RFC 7693.
+func TestSelfTest(t *testing.T) {
+ hashLens := [4]int{20, 32, 48, 64}
+ msgLens := [6]int{0, 3, 128, 129, 255, 1024}
+
+ msg := make([]byte, 1024)
+ key := make([]byte, 64)
+
+ h, _ := New256(nil)
+ for _, hashSize := range hashLens {
+ for _, msgLength := range msgLens {
+ generateSequence(msg[:msgLength], uint32(msgLength)) // unkeyed hash
+
+ md := computeHash(msg[:msgLength], hashSize)
+ h.Write(md)
+
+ generateSequence(key[:], uint32(hashSize)) // keyed hash
+ md = computeMAC(msg[:msgLength], hashSize, key[:hashSize])
+ h.Write(md)
+ }
+ }
+
+ sum := h.Sum(nil)
+ expected := [32]byte{
+ 0xc2, 0x3a, 0x78, 0x00, 0xd9, 0x81, 0x23, 0xbd,
+ 0x10, 0xf5, 0x06, 0xc6, 0x1e, 0x29, 0xda, 0x56,
+ 0x03, 0xd7, 0x63, 0xb8, 0xbb, 0xad, 0x2e, 0x73,
+ 0x7f, 0x5e, 0x76, 0x5a, 0x7b, 0xcc, 0xd4, 0x75,
+ }
+ if !bytes.Equal(sum, expected[:]) {
+ t.Fatalf("got %x, wanted %x", sum, expected)
+ }
+}
+
+// Benchmarks
+
+func benchmarkSum(b *testing.B, size int, sse4, avx, avx2 bool) {
+ // Enable the correct set of instructions
+ defer func(sse4, avx, avx2 bool) {
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+ }(useSSE4, useAVX, useAVX2)
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+
+ data := make([]byte, size)
+ b.SetBytes(int64(size))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Sum512(data)
+ }
+}
+
+func benchmarkWrite(b *testing.B, size int, sse4, avx, avx2 bool) {
+ // Enable the correct set of instructions
+ defer func(sse4, avx, avx2 bool) {
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+ }(useSSE4, useAVX, useAVX2)
+ useSSE4, useAVX, useAVX2 = sse4, avx, avx2
+
+ data := make([]byte, size)
+ h, _ := New512(nil)
+ b.SetBytes(int64(size))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ h.Write(data)
+ }
+}
+
+func BenchmarkWrite128Generic(b *testing.B) { benchmarkWrite(b, 128, false, false, false) }
+func BenchmarkWrite1KGeneric(b *testing.B) { benchmarkWrite(b, 1024, false, false, false) }
+func BenchmarkWrite128SSE4(b *testing.B) { benchmarkWrite(b, 128, true, false, false) }
+func BenchmarkWrite1KSSE4(b *testing.B) { benchmarkWrite(b, 1024, true, false, false) }
+func BenchmarkWrite128AVX(b *testing.B) { benchmarkWrite(b, 128, false, true, false) }
+func BenchmarkWrite1KAVX(b *testing.B) { benchmarkWrite(b, 1024, false, true, false) }
+func BenchmarkWrite128AVX2(b *testing.B) { benchmarkWrite(b, 128, false, false, true) }
+func BenchmarkWrite1KAVX2(b *testing.B) { benchmarkWrite(b, 1024, false, false, true) }
+
+func BenchmarkSum128Generic(b *testing.B) { benchmarkSum(b, 128, false, false, false) }
+func BenchmarkSum1KGeneric(b *testing.B) { benchmarkSum(b, 1024, false, false, false) }
+func BenchmarkSum128SSE4(b *testing.B) { benchmarkSum(b, 128, true, false, false) }
+func BenchmarkSum1KSSE4(b *testing.B) { benchmarkSum(b, 1024, true, false, false) }
+func BenchmarkSum128AVX(b *testing.B) { benchmarkSum(b, 128, false, true, false) }
+func BenchmarkSum1KAVX(b *testing.B) { benchmarkSum(b, 1024, false, true, false) }
+func BenchmarkSum128AVX2(b *testing.B) { benchmarkSum(b, 128, false, false, true) }
+func BenchmarkSum1KAVX2(b *testing.B) { benchmarkSum(b, 1024, false, false, true) }
+
+// These values were taken from https://blake2.net/blake2b-test.txt.
+var hashes = []string{
+ "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568",
+ "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd",
+ "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965",
+ "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1",
+ "beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac",
+ "098084b51fd13deae5f4320de94a688ee07baea2800486689a8636117b46c1f4c1f6af7f74ae7c857600456a58a3af251dc4723a64cc7c0a5ab6d9cac91c20bb",
+ "6044540d560853eb1c57df0077dd381094781cdb9073e5b1b3d3f6c7829e12066bbaca96d989a690de72ca3133a83652ba284a6d62942b271ffa2620c9e75b1f",
+ "7a8cfe9b90f75f7ecb3acc053aaed6193112b6f6a4aeeb3f65d3de541942deb9e2228152a3c4bbbe72fc3b12629528cfbb09fe630f0474339f54abf453e2ed52",
+ "380beaf6ea7cc9365e270ef0e6f3a64fb902acae51dd5512f84259ad2c91f4bc4108db73192a5bbfb0cbcf71e46c3e21aee1c5e860dc96e8eb0b7b8426e6abe9",
+ "60fe3c4535e1b59d9a61ea8500bfac41a69dffb1ceadd9aca323e9a625b64da5763bad7226da02b9c8c4f1a5de140ac5a6c1124e4f718ce0b28ea47393aa6637",
+ "4fe181f54ad63a2983feaaf77d1e7235c2beb17fa328b6d9505bda327df19fc37f02c4b6f0368ce23147313a8e5738b5fa2a95b29de1c7f8264eb77b69f585cd",
+ "f228773ce3f3a42b5f144d63237a72d99693adb8837d0e112a8a0f8ffff2c362857ac49c11ec740d1500749dac9b1f4548108bf3155794dcc9e4082849e2b85b",
+ "962452a8455cc56c8511317e3b1f3b2c37df75f588e94325fdd77070359cf63a9ae6e930936fdf8e1e08ffca440cfb72c28f06d89a2151d1c46cd5b268ef8563",
+ "43d44bfa18768c59896bf7ed1765cb2d14af8c260266039099b25a603e4ddc5039d6ef3a91847d1088d401c0c7e847781a8a590d33a3c6cb4df0fab1c2f22355",
+ "dcffa9d58c2a4ca2cdbb0c7aa4c4c1d45165190089f4e983bb1c2cab4aaeff1fa2b5ee516fecd780540240bf37e56c8bcca7fab980e1e61c9400d8a9a5b14ac6",
+ "6fbf31b45ab0c0b8dad1c0f5f4061379912dde5aa922099a030b725c73346c524291adef89d2f6fd8dfcda6d07dad811a9314536c2915ed45da34947e83de34e",
+ "a0c65bddde8adef57282b04b11e7bc8aab105b99231b750c021f4a735cb1bcfab87553bba3abb0c3e64a0b6955285185a0bd35fb8cfde557329bebb1f629ee93",
+ "f99d815550558e81eca2f96718aed10d86f3f1cfb675cce06b0eff02f617c5a42c5aa760270f2679da2677c5aeb94f1142277f21c7f79f3c4f0cce4ed8ee62b1",
+ "95391da8fc7b917a2044b3d6f5374e1ca072b41454d572c7356c05fd4bc1e0f40b8bb8b4a9f6bce9be2c4623c399b0dca0dab05cb7281b71a21b0ebcd9e55670",
+ "04b9cd3d20d221c09ac86913d3dc63041989a9a1e694f1e639a3ba7e451840f750c2fc191d56ad61f2e7936bc0ac8e094b60caeed878c18799045402d61ceaf9",
+ "ec0e0ef707e4ed6c0c66f9e089e4954b058030d2dd86398fe84059631f9ee591d9d77375355149178c0cf8f8e7c49ed2a5e4f95488a2247067c208510fadc44c",
+ "9a37cce273b79c09913677510eaf7688e89b3314d3532fd2764c39de022a2945b5710d13517af8ddc0316624e73bec1ce67df15228302036f330ab0cb4d218dd",
+ "4cf9bb8fb3d4de8b38b2f262d3c40f46dfe747e8fc0a414c193d9fcf753106ce47a18f172f12e8a2f1c26726545358e5ee28c9e2213a8787aafbc516d2343152",
+ "64e0c63af9c808fd893137129867fd91939d53f2af04be4fa268006100069b2d69daa5c5d8ed7fddcb2a70eeecdf2b105dd46a1e3b7311728f639ab489326bc9",
+ "5e9c93158d659b2def06b0c3c7565045542662d6eee8a96a89b78ade09fe8b3dcc096d4fe48815d88d8f82620156602af541955e1f6ca30dce14e254c326b88f",
+ "7775dff889458dd11aef417276853e21335eb88e4dec9cfb4e9edb49820088551a2ca60339f12066101169f0dfe84b098fddb148d9da6b3d613df263889ad64b",
+ "f0d2805afbb91f743951351a6d024f9353a23c7ce1fc2b051b3a8b968c233f46f50f806ecb1568ffaa0b60661e334b21dde04f8fa155ac740eeb42e20b60d764",
+ "86a2af316e7d7754201b942e275364ac12ea8962ab5bd8d7fb276dc5fbffc8f9a28cae4e4867df6780d9b72524160927c855da5b6078e0b554aa91e31cb9ca1d",
+ "10bdf0caa0802705e706369baf8a3f79d72c0a03a80675a7bbb00be3a45e516424d1ee88efb56f6d5777545ae6e27765c3a8f5e493fc308915638933a1dfee55",
+ "b01781092b1748459e2e4ec178696627bf4ebafebba774ecf018b79a68aeb84917bf0b84bb79d17b743151144cd66b7b33a4b9e52c76c4e112050ff5385b7f0b",
+ "c6dbc61dec6eaeac81e3d5f755203c8e220551534a0b2fd105a91889945a638550204f44093dd998c076205dffad703a0e5cd3c7f438a7e634cd59fededb539e",
+ "eba51acffb4cea31db4b8d87e9bf7dd48fe97b0253ae67aa580f9ac4a9d941f2bea518ee286818cc9f633f2a3b9fb68e594b48cdd6d515bf1d52ba6c85a203a7",
+ "86221f3ada52037b72224f105d7999231c5e5534d03da9d9c0a12acb68460cd375daf8e24386286f9668f72326dbf99ba094392437d398e95bb8161d717f8991",
+ "5595e05c13a7ec4dc8f41fb70cb50a71bce17c024ff6de7af618d0cc4e9c32d9570d6d3ea45b86525491030c0d8f2b1836d5778c1ce735c17707df364d054347",
+ "ce0f4f6aca89590a37fe034dd74dd5fa65eb1cbd0a41508aaddc09351a3cea6d18cb2189c54b700c009f4cbf0521c7ea01be61c5ae09cb54f27bc1b44d658c82",
+ "7ee80b06a215a3bca970c77cda8761822bc103d44fa4b33f4d07dcb997e36d55298bceae12241b3fa07fa63be5576068da387b8d5859aeab701369848b176d42",
+ "940a84b6a84d109aab208c024c6ce9647676ba0aaa11f86dbb7018f9fd2220a6d901a9027f9abcf935372727cbf09ebd61a2a2eeb87653e8ecad1bab85dc8327",
+ "2020b78264a82d9f4151141adba8d44bf20c5ec062eee9b595a11f9e84901bf148f298e0c9f8777dcdbc7cc4670aac356cc2ad8ccb1629f16f6a76bcefbee760",
+ "d1b897b0e075ba68ab572adf9d9c436663e43eb3d8e62d92fc49c9be214e6f27873fe215a65170e6bea902408a25b49506f47babd07cecf7113ec10c5dd31252",
+ "b14d0c62abfa469a357177e594c10c194243ed2025ab8aa5ad2fa41ad318e0ff48cd5e60bec07b13634a711d2326e488a985f31e31153399e73088efc86a5c55",
+ "4169c5cc808d2697dc2a82430dc23e3cd356dc70a94566810502b8d655b39abf9e7f902fe717e0389219859e1945df1af6ada42e4ccda55a197b7100a30c30a1",
+ "258a4edb113d66c839c8b1c91f15f35ade609f11cd7f8681a4045b9fef7b0b24c82cda06a5f2067b368825e3914e53d6948ede92efd6e8387fa2e537239b5bee",
+ "79d2d8696d30f30fb34657761171a11e6c3f1e64cbe7bebee159cb95bfaf812b4f411e2f26d9c421dc2c284a3342d823ec293849e42d1e46b0a4ac1e3c86abaa",
+ "8b9436010dc5dee992ae38aea97f2cd63b946d94fedd2ec9671dcde3bd4ce9564d555c66c15bb2b900df72edb6b891ebcadfeff63c9ea4036a998be7973981e7",
+ "c8f68e696ed28242bf997f5b3b34959508e42d613810f1e2a435c96ed2ff560c7022f361a9234b9837feee90bf47922ee0fd5f8ddf823718d86d1e16c6090071",
+ "b02d3eee4860d5868b2c39ce39bfe81011290564dd678c85e8783f29302dfc1399ba95b6b53cd9ebbf400cca1db0ab67e19a325f2d115812d25d00978ad1bca4",
+ "7693ea73af3ac4dad21ca0d8da85b3118a7d1c6024cfaf557699868217bc0c2f44a199bc6c0edd519798ba05bd5b1b4484346a47c2cadf6bf30b785cc88b2baf",
+ "a0e5c1c0031c02e48b7f09a5e896ee9aef2f17fc9e18e997d7f6cac7ae316422c2b1e77984e5f3a73cb45deed5d3f84600105e6ee38f2d090c7d0442ea34c46d",
+ "41daa6adcfdb69f1440c37b596440165c15ada596813e2e22f060fcd551f24dee8e04ba6890387886ceec4a7a0d7fc6b44506392ec3822c0d8c1acfc7d5aebe8",
+ "14d4d40d5984d84c5cf7523b7798b254e275a3a8cc0a1bd06ebc0bee726856acc3cbf516ff667cda2058ad5c3412254460a82c92187041363cc77a4dc215e487",
+ "d0e7a1e2b9a447fee83e2277e9ff8010c2f375ae12fa7aaa8ca5a6317868a26a367a0b69fbc1cf32a55d34eb370663016f3d2110230eba754028a56f54acf57c",
+ "e771aa8db5a3e043e8178f39a0857ba04a3f18e4aa05743cf8d222b0b095825350ba422f63382a23d92e4149074e816a36c1cd28284d146267940b31f8818ea2",
+ "feb4fd6f9e87a56bef398b3284d2bda5b5b0e166583a66b61e538457ff0584872c21a32962b9928ffab58de4af2edd4e15d8b35570523207ff4e2a5aa7754caa",
+ "462f17bf005fb1c1b9e671779f665209ec2873e3e411f98dabf240a1d5ec3f95ce6796b6fc23fe171903b502023467dec7273ff74879b92967a2a43a5a183d33",
+ "d3338193b64553dbd38d144bea71c5915bb110e2d88180dbc5db364fd6171df317fc7268831b5aef75e4342b2fad8797ba39eddcef80e6ec08159350b1ad696d",
+ "e1590d585a3d39f7cb599abd479070966409a6846d4377acf4471d065d5db94129cc9be92573b05ed226be1e9b7cb0cabe87918589f80dadd4ef5ef25a93d28e",
+ "f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e76842d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2",
+ "30186055c07949948183c850e9a756cc09937e247d9d928e869e20bafc3cd9721719d34e04a0899b92c736084550186886efba2e790d8be6ebf040b209c439a4",
+ "f3c4276cb863637712c241c444c5cc1e3554e0fddb174d035819dd83eb700b4ce88df3ab3841ba02085e1a99b4e17310c5341075c0458ba376c95a6818fbb3e2",
+ "0aa007c4dd9d5832393040a1583c930bca7dc5e77ea53add7e2b3f7c8e231368043520d4a3ef53c969b6bbfd025946f632bd7f765d53c21003b8f983f75e2a6a",
+ "08e9464720533b23a04ec24f7ae8c103145f765387d738777d3d343477fd1c58db052142cab754ea674378e18766c53542f71970171cc4f81694246b717d7564",
+ "d37ff7ad297993e7ec21e0f1b4b5ae719cdc83c5db687527f27516cbffa822888a6810ee5c1ca7bfe3321119be1ab7bfa0a502671c8329494df7ad6f522d440f",
+ "dd9042f6e464dcf86b1262f6accfafbd8cfd902ed3ed89abf78ffa482dbdeeb6969842394c9a1168ae3d481a017842f660002d42447c6b22f7b72f21aae021c9",
+ "bd965bf31e87d70327536f2a341cebc4768eca275fa05ef98f7f1b71a0351298de006fba73fe6733ed01d75801b4a928e54231b38e38c562b2e33ea1284992fa",
+ "65676d800617972fbd87e4b9514e1c67402b7a331096d3bfac22f1abb95374abc942f16e9ab0ead33b87c91968a6e509e119ff07787b3ef483e1dcdccf6e3022",
+ "939fa189699c5d2c81ddd1ffc1fa207c970b6a3685bb29ce1d3e99d42f2f7442da53e95a72907314f4588399a3ff5b0a92beb3f6be2694f9f86ecf2952d5b41c",
+ "c516541701863f91005f314108ceece3c643e04fc8c42fd2ff556220e616aaa6a48aeb97a84bad74782e8dff96a1a2fa949339d722edcaa32b57067041df88cc",
+ "987fd6e0d6857c553eaebb3d34970a2c2f6e89a3548f492521722b80a1c21a153892346d2cba6444212d56da9a26e324dccbc0dcde85d4d2ee4399eec5a64e8f",
+ "ae56deb1c2328d9c4017706bce6e99d41349053ba9d336d677c4c27d9fd50ae6aee17e853154e1f4fe7672346da2eaa31eea53fcf24a22804f11d03da6abfc2b",
+ "49d6a608c9bde4491870498572ac31aac3fa40938b38a7818f72383eb040ad39532bc06571e13d767e6945ab77c0bdc3b0284253343f9f6c1244ebf2ff0df866",
+ "da582ad8c5370b4469af862aa6467a2293b2b28bd80ae0e91f425ad3d47249fdf98825cc86f14028c3308c9804c78bfeeeee461444ce243687e1a50522456a1d",
+ "d5266aa3331194aef852eed86d7b5b2633a0af1c735906f2e13279f14931a9fc3b0eac5ce9245273bd1aa92905abe16278ef7efd47694789a7283b77da3c70f8",
+ "2962734c28252186a9a1111c732ad4de4506d4b4480916303eb7991d659ccda07a9911914bc75c418ab7a4541757ad054796e26797feaf36e9f6ad43f14b35a4",
+ "e8b79ec5d06e111bdfafd71e9f5760f00ac8ac5d8bf768f9ff6f08b8f026096b1cc3a4c973333019f1e3553e77da3f98cb9f542e0a90e5f8a940cc58e59844b3",
+ "dfb320c44f9d41d1efdcc015f08dd5539e526e39c87d509ae6812a969e5431bf4fa7d91ffd03b981e0d544cf72d7b1c0374f8801482e6dea2ef903877eba675e",
+ "d88675118fdb55a5fb365ac2af1d217bf526ce1ee9c94b2f0090b2c58a06ca58187d7fe57c7bed9d26fca067b4110eefcd9a0a345de872abe20de368001b0745",
+ "b893f2fc41f7b0dd6e2f6aa2e0370c0cff7df09e3acfcc0e920b6e6fad0ef747c40668417d342b80d2351e8c175f20897a062e9765e6c67b539b6ba8b9170545",
+ "6c67ec5697accd235c59b486d7b70baeedcbd4aa64ebd4eef3c7eac189561a726250aec4d48cadcafbbe2ce3c16ce2d691a8cce06e8879556d4483ed7165c063",
+ "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd",
+ "cbaa259572d4aebfc1917acddc582b9f8dfaa928a198ca7acd0f2aa76a134a90252e6298a65b08186a350d5b7626699f8cb721a3ea5921b753ae3a2dce24ba3a",
+ "fa1549c9796cd4d303dcf452c1fbd5744fd9b9b47003d920b92de34839d07ef2a29ded68f6fc9e6c45e071a2e48bd50c5084e96b657dd0404045a1ddefe282ed",
+ "5cf2ac897ab444dcb5c8d87c495dbdb34e1838b6b629427caa51702ad0f9688525f13bec503a3c3a2c80a65e0b5715e8afab00ffa56ec455a49a1ad30aa24fcd",
+ "9aaf80207bace17bb7ab145757d5696bde32406ef22b44292ef65d4519c3bb2ad41a59b62cc3e94b6fa96d32a7faadae28af7d35097219aa3fd8cda31e40c275",
+ "af88b163402c86745cb650c2988fb95211b94b03ef290eed9662034241fd51cf398f8073e369354c43eae1052f9b63b08191caa138aa54fea889cc7024236897",
+ "48fa7d64e1ceee27b9864db5ada4b53d00c9bc7626555813d3cd6730ab3cc06ff342d727905e33171bde6e8476e77fb1720861e94b73a2c538d254746285f430",
+ "0e6fd97a85e904f87bfe85bbeb34f69e1f18105cf4ed4f87aec36c6e8b5f68bd2a6f3dc8a9ecb2b61db4eedb6b2ea10bf9cb0251fb0f8b344abf7f366b6de5ab",
+ "06622da5787176287fdc8fed440bad187d830099c94e6d04c8e9c954cda70c8bb9e1fc4a6d0baa831b9b78ef6648681a4867a11da93ee36e5e6a37d87fc63f6f",
+ "1da6772b58fabf9c61f68d412c82f182c0236d7d575ef0b58dd22458d643cd1dfc93b03871c316d8430d312995d4197f0874c99172ba004a01ee295abac24e46",
+ "3cd2d9320b7b1d5fb9aab951a76023fa667be14a9124e394513918a3f44096ae4904ba0ffc150b63bc7ab1eeb9a6e257e5c8f000a70394a5afd842715de15f29",
+ "04cdc14f7434e0b4be70cb41db4c779a88eaef6accebcb41f2d42fffe7f32a8e281b5c103a27021d0d08362250753cdf70292195a53a48728ceb5844c2d98bab",
+ "9071b7a8a075d0095b8fb3ae5113785735ab98e2b52faf91d5b89e44aac5b5d4ebbf91223b0ff4c71905da55342e64655d6ef8c89a4768c3f93a6dc0366b5bc8",
+ "ebb30240dd96c7bc8d0abe49aa4edcbb4afdc51ff9aaf720d3f9e7fbb0f9c6d6571350501769fc4ebd0b2141247ff400d4fd4be414edf37757bb90a32ac5c65a",
+ "8532c58bf3c8015d9d1cbe00eef1f5082f8f3632fbe9f1ed4f9dfb1fa79e8283066d77c44c4af943d76b300364aecbd0648c8a8939bd204123f4b56260422dec",
+ "fe9846d64f7c7708696f840e2d76cb4408b6595c2f81ec6a28a7f2f20cb88cfe6ac0b9e9b8244f08bd7095c350c1d0842f64fb01bb7f532dfcd47371b0aeeb79",
+ "28f17ea6fb6c42092dc264257e29746321fb5bdaea9873c2a7fa9d8f53818e899e161bc77dfe8090afd82bf2266c5c1bc930a8d1547624439e662ef695f26f24",
+ "ec6b7d7f030d4850acae3cb615c21dd25206d63e84d1db8d957370737ba0e98467ea0ce274c66199901eaec18a08525715f53bfdb0aacb613d342ebdceeddc3b",
+ "b403d3691c03b0d3418df327d5860d34bbfcc4519bfbce36bf33b208385fadb9186bc78a76c489d89fd57e7dc75412d23bcd1dae8470ce9274754bb8585b13c5",
+ "31fc79738b8772b3f55cd8178813b3b52d0db5a419d30ba9495c4b9da0219fac6df8e7c23a811551a62b827f256ecdb8124ac8a6792ccfecc3b3012722e94463",
+ "bb2039ec287091bcc9642fc90049e73732e02e577e2862b32216ae9bedcd730c4c284ef3968c368b7d37584f97bd4b4dc6ef6127acfe2e6ae2509124e66c8af4",
+ "f53d68d13f45edfcb9bd415e2831e938350d5380d3432278fc1c0c381fcb7c65c82dafe051d8c8b0d44e0974a0e59ec7bf7ed0459f86e96f329fc79752510fd3",
+ "8d568c7984f0ecdf7640fbc483b5d8c9f86634f6f43291841b309a350ab9c1137d24066b09da9944bac54d5bb6580d836047aac74ab724b887ebf93d4b32eca9",
+ "c0b65ce5a96ff774c456cac3b5f2c4cd359b4ff53ef93a3da0778be4900d1e8da1601e769e8f1b02d2a2f8c5b9fa10b44f1c186985468feeb008730283a6657d",
+ "4900bba6f5fb103ece8ec96ada13a5c3c85488e05551da6b6b33d988e611ec0fe2e3c2aa48ea6ae8986a3a231b223c5d27cec2eadde91ce07981ee652862d1e4",
+ "c7f5c37c7285f927f76443414d4357ff789647d7a005a5a787e03c346b57f49f21b64fa9cf4b7e45573e23049017567121a9c3d4b2b73ec5e9413577525db45a",
+ "ec7096330736fdb2d64b5653e7475da746c23a4613a82687a28062d3236364284ac01720ffb406cfe265c0df626a188c9e5963ace5d3d5bb363e32c38c2190a6",
+ "82e744c75f4649ec52b80771a77d475a3bc091989556960e276a5f9ead92a03f718742cdcfeaee5cb85c44af198adc43a4a428f5f0c2ddb0be36059f06d7df73",
+ "2834b7a7170f1f5b68559ab78c1050ec21c919740b784a9072f6e5d69f828d70c919c5039fb148e39e2c8a52118378b064ca8d5001cd10a5478387b966715ed6",
+ "16b4ada883f72f853bb7ef253efcab0c3e2161687ad61543a0d2824f91c1f81347d86be709b16996e17f2dd486927b0288ad38d13063c4a9672c39397d3789b6",
+ "78d048f3a69d8b54ae0ed63a573ae350d89f7c6cf1f3688930de899afa037697629b314e5cd303aa62feea72a25bf42b304b6c6bcb27fae21c16d925e1fbdac3",
+ "0f746a48749287ada77a82961f05a4da4abdb7d77b1220f836d09ec814359c0ec0239b8c7b9ff9e02f569d1b301ef67c4612d1de4f730f81c12c40cc063c5caa",
+ "f0fc859d3bd195fbdc2d591e4cdac15179ec0f1dc821c11df1f0c1d26e6260aaa65b79fafacafd7d3ad61e600f250905f5878c87452897647a35b995bcadc3a3",
+ "2620f687e8625f6a412460b42e2cef67634208ce10a0cbd4dff7044a41b7880077e9f8dc3b8d1216d3376a21e015b58fb279b521d83f9388c7382c8505590b9b",
+ "227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c528fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f",
+ "1a929901b09c25f27d6b35be7b2f1c4745131fdebca7f3e2451926720434e0db6e74fd693ad29b777dc3355c592a361c4873b01133a57c2e3b7075cbdb86f4fc",
+ "5fd7968bc2fe34f220b5e3dc5af9571742d73b7d60819f2888b629072b96a9d8ab2d91b82d0a9aaba61bbd39958132fcc4257023d1eca591b3054e2dc81c8200",
+ "dfcce8cf32870cc6a503eadafc87fd6f78918b9b4d0737db6810be996b5497e7e5cc80e312f61e71ff3e9624436073156403f735f56b0b01845c18f6caf772e6",
+ "02f7ef3a9ce0fff960f67032b296efca3061f4934d690749f2d01c35c81c14f39a67fa350bc8a0359bf1724bffc3bca6d7c7bba4791fd522a3ad353c02ec5aa8",
+ "64be5c6aba65d594844ae78bb022e5bebe127fd6b6ffa5a13703855ab63b624dcd1a363f99203f632ec386f3ea767fc992e8ed9686586aa27555a8599d5b808f",
+ "f78585505c4eaa54a8b5be70a61e735e0ff97af944ddb3001e35d86c4e2199d976104b6ae31750a36a726ed285064f5981b503889fef822fcdc2898dddb7889a",
+ "e4b5566033869572edfd87479a5bb73c80e8759b91232879d96b1dda36c012076ee5a2ed7ae2de63ef8406a06aea82c188031b560beafb583fb3de9e57952a7e",
+ "e1b3e7ed867f6c9484a2a97f7715f25e25294e992e41f6a7c161ffc2adc6daaeb7113102d5e6090287fe6ad94ce5d6b739c6ca240b05c76fb73f25dd024bf935",
+ "85fd085fdc12a080983df07bd7012b0d402a0f4043fcb2775adf0bad174f9b08d1676e476985785c0a5dcc41dbff6d95ef4d66a3fbdc4a74b82ba52da0512b74",
+ "aed8fa764b0fbff821e05233d2f7b0900ec44d826f95e93c343c1bc3ba5a24374b1d616e7e7aba453a0ada5e4fab5382409e0d42ce9c2bc7fb39a99c340c20f0",
+ "7ba3b2e297233522eeb343bd3ebcfd835a04007735e87f0ca300cbee6d416565162171581e4020ff4cf176450f1291ea2285cb9ebffe4c56660627685145051c",
+ "de748bcf89ec88084721e16b85f30adb1a6134d664b5843569babc5bbd1a15ca9b61803c901a4fef32965a1749c9f3a4e243e173939dc5a8dc495c671ab52145",
+ "aaf4d2bdf200a919706d9842dce16c98140d34bc433df320aba9bd429e549aa7a3397652a4d768277786cf993cde2338673ed2e6b66c961fefb82cd20c93338f",
+ "c408218968b788bf864f0997e6bc4c3dba68b276e2125a4843296052ff93bf5767b8cdce7131f0876430c1165fec6c4f47adaa4fd8bcfacef463b5d3d0fa61a0",
+ "76d2d819c92bce55fa8e092ab1bf9b9eab237a25267986cacf2b8ee14d214d730dc9a5aa2d7b596e86a1fd8fa0804c77402d2fcd45083688b218b1cdfa0dcbcb",
+ "72065ee4dd91c2d8509fa1fc28a37c7fc9fa7d5b3f8ad3d0d7a25626b57b1b44788d4caf806290425f9890a3a2a35a905ab4b37acfd0da6e4517b2525c9651e4",
+ "64475dfe7600d7171bea0b394e27c9b00d8e74dd1e416a79473682ad3dfdbb706631558055cfc8a40e07bd015a4540dcdea15883cbbf31412df1de1cd4152b91",
+ "12cd1674a4488a5d7c2b3160d2e2c4b58371bedad793418d6f19c6ee385d70b3e06739369d4df910edb0b0a54cbff43d54544cd37ab3a06cfa0a3ddac8b66c89",
+ "60756966479dedc6dd4bcff8ea7d1d4ce4d4af2e7b097e32e3763518441147cc12b3c0ee6d2ecabf1198cec92e86a3616fba4f4e872f5825330adbb4c1dee444",
+ "a7803bcb71bc1d0f4383dde1e0612e04f872b715ad30815c2249cf34abb8b024915cb2fc9f4e7cc4c8cfd45be2d5a91eab0941c7d270e2da4ca4a9f7ac68663a",
+ "b84ef6a7229a34a750d9a98ee2529871816b87fbe3bc45b45fa5ae82d5141540211165c3c5d7a7476ba5a4aa06d66476f0d9dc49a3f1ee72c3acabd498967414",
+ "fae4b6d8efc3f8c8e64d001dabec3a21f544e82714745251b2b4b393f2f43e0da3d403c64db95a2cb6e23ebb7b9e94cdd5ddac54f07c4a61bd3cb10aa6f93b49",
+ "34f7286605a122369540141ded79b8957255da2d4155abbf5a8dbb89c8eb7ede8eeef1daa46dc29d751d045dc3b1d658bb64b80ff8589eddb3824b13da235a6b",
+ "3b3b48434be27b9eababba43bf6b35f14b30f6a88dc2e750c358470d6b3aa3c18e47db4017fa55106d8252f016371a00f5f8b070b74ba5f23cffc5511c9f09f0",
+ "ba289ebd6562c48c3e10a8ad6ce02e73433d1e93d7c9279d4d60a7e879ee11f441a000f48ed9f7c4ed87a45136d7dccdca482109c78a51062b3ba4044ada2469",
+ "022939e2386c5a37049856c850a2bb10a13dfea4212b4c732a8840a9ffa5faf54875c5448816b2785a007da8a8d2bc7d71a54e4e6571f10b600cbdb25d13ede3",
+ "e6fec19d89ce8717b1a087024670fe026f6c7cbda11caef959bb2d351bf856f8055d1c0ebdaaa9d1b17886fc2c562b5e99642fc064710c0d3488a02b5ed7f6fd",
+ "94c96f02a8f576aca32ba61c2b206f907285d9299b83ac175c209a8d43d53bfe683dd1d83e7549cb906c28f59ab7c46f8751366a28c39dd5fe2693c9019666c8",
+ "31a0cd215ebd2cb61de5b9edc91e6195e31c59a5648d5c9f737e125b2605708f2e325ab3381c8dce1a3e958886f1ecdc60318f882cfe20a24191352e617b0f21",
+ "91ab504a522dce78779f4c6c6ba2e6b6db5565c76d3e7e7c920caf7f757ef9db7c8fcf10e57f03379ea9bf75eb59895d96e149800b6aae01db778bb90afbc989",
+ "d85cabc6bd5b1a01a5afd8c6734740da9fd1c1acc6db29bfc8a2e5b668b028b6b3154bfb8703fa3180251d589ad38040ceb707c4bad1b5343cb426b61eaa49c1",
+ "d62efbec2ca9c1f8bd66ce8b3f6a898cb3f7566ba6568c618ad1feb2b65b76c3ce1dd20f7395372faf28427f61c9278049cf0140df434f5633048c86b81e0399",
+ "7c8fdc6175439e2c3db15bafa7fb06143a6a23bc90f449e79deef73c3d492a671715c193b6fea9f036050b946069856b897e08c00768f5ee5ddcf70b7cd6d0e0",
+ "58602ee7468e6bc9df21bd51b23c005f72d6cb013f0a1b48cbec5eca299299f97f09f54a9a01483eaeb315a6478bad37ba47ca1347c7c8fc9e6695592c91d723",
+ "27f5b79ed256b050993d793496edf4807c1d85a7b0a67c9c4fa99860750b0ae66989670a8ffd7856d7ce411599e58c4d77b232a62bef64d15275be46a68235ff",
+ "3957a976b9f1887bf004a8dca942c92d2b37ea52600f25e0c9bc5707d0279c00c6e85a839b0d2d8eb59c51d94788ebe62474a791cadf52cccf20f5070b6573fc",
+ "eaa2376d55380bf772ecca9cb0aa4668c95c707162fa86d518c8ce0ca9bf7362b9f2a0adc3ff59922df921b94567e81e452f6c1a07fc817cebe99604b3505d38",
+ "c1e2c78b6b2734e2480ec550434cb5d613111adcc21d475545c3b1b7e6ff12444476e5c055132e2229dc0f807044bb919b1a5662dd38a9ee65e243a3911aed1a",
+ "8ab48713389dd0fcf9f965d3ce66b1e559a1f8c58741d67683cd971354f452e62d0207a65e436c5d5d8f8ee71c6abfe50e669004c302b31a7ea8311d4a916051",
+ "24ce0addaa4c65038bd1b1c0f1452a0b128777aabc94a29df2fd6c7e2f85f8ab9ac7eff516b0e0a825c84a24cfe492eaad0a6308e46dd42fe8333ab971bb30ca",
+ "5154f929ee03045b6b0c0004fa778edee1d139893267cc84825ad7b36c63de32798e4a166d24686561354f63b00709a1364b3c241de3febf0754045897467cd4",
+ "e74e907920fd87bd5ad636dd11085e50ee70459c443e1ce5809af2bc2eba39f9e6d7128e0e3712c316da06f4705d78a4838e28121d4344a2c79c5e0db307a677",
+ "bf91a22334bac20f3fd80663b3cd06c4e8802f30e6b59f90d3035cc9798a217ed5a31abbda7fa6842827bdf2a7a1c21f6fcfccbb54c6c52926f32da816269be1",
+ "d9d5c74be5121b0bd742f26bffb8c89f89171f3f934913492b0903c271bbe2b3395ef259669bef43b57f7fcc3027db01823f6baee66e4f9fead4d6726c741fce",
+ "50c8b8cf34cd879f80e2faab3230b0c0e1cc3e9dcadeb1b9d97ab923415dd9a1fe38addd5c11756c67990b256e95ad6d8f9fedce10bf1c90679cde0ecf1be347",
+ "0a386e7cd5dd9b77a035e09fe6fee2c8ce61b5383c87ea43205059c5e4cd4f4408319bb0a82360f6a58e6c9ce3f487c446063bf813bc6ba535e17fc1826cfc91",
+ "1f1459cb6b61cbac5f0efe8fc487538f42548987fcd56221cfa7beb22504769e792c45adfb1d6b3d60d7b749c8a75b0bdf14e8ea721b95dca538ca6e25711209",
+ "e58b3836b7d8fedbb50ca5725c6571e74c0785e97821dab8b6298c10e4c079d4a6cdf22f0fedb55032925c16748115f01a105e77e00cee3d07924dc0d8f90659",
+ "b929cc6505f020158672deda56d0db081a2ee34c00c1100029bdf8ea98034fa4bf3e8655ec697fe36f40553c5bb46801644a627d3342f4fc92b61f03290fb381",
+ "72d353994b49d3e03153929a1e4d4f188ee58ab9e72ee8e512f29bc773913819ce057ddd7002c0433ee0a16114e3d156dd2c4a7e80ee53378b8670f23e33ef56",
+ "c70ef9bfd775d408176737a0736d68517ce1aaad7e81a93c8c1ed967ea214f56c8a377b1763e676615b60f3988241eae6eab9685a5124929d28188f29eab06f7",
+ "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f",
+ "6f43094cafb5ebf1f7a4937ec50f56a4c9da303cbb55ac1f27f1f1976cd96beda9464f0e7b9c54620b8a9fba983164b8be3578425a024f5fe199c36356b88972",
+ "3745273f4c38225db2337381871a0c6aafd3af9b018c88aa02025850a5dc3a42a1a3e03e56cbf1b0876d63a441f1d2856a39b8801eb5af325201c415d65e97fe",
+ "c50c44cca3ec3edaae779a7e179450ebdda2f97067c690aa6c5a4ac7c30139bb27c0df4db3220e63cb110d64f37ffe078db72653e2daacf93ae3f0a2d1a7eb2e",
+ "8aef263e385cbc61e19b28914243262af5afe8726af3ce39a79c27028cf3ecd3f8d2dfd9cfc9ad91b58f6f20778fd5f02894a3d91c7d57d1e4b866a7f364b6be",
+ "28696141de6e2d9bcb3235578a66166c1448d3e905a1b482d423be4bc5369bc8c74dae0acc9cc123e1d8ddce9f97917e8c019c552da32d39d2219b9abf0fa8c8",
+ "2fb9eb2085830181903a9dafe3db428ee15be7662224efd643371fb25646aee716e531eca69b2bdc8233f1a8081fa43da1500302975a77f42fa592136710e9dc",
+ "66f9a7143f7a3314a669bf2e24bbb35014261d639f495b6c9c1f104fe8e320aca60d4550d69d52edbd5a3cdeb4014ae65b1d87aa770b69ae5c15f4330b0b0ad8",
+ "f4c4dd1d594c3565e3e25ca43dad82f62abea4835ed4cd811bcd975e46279828d44d4c62c3679f1b7f7b9dd4571d7b49557347b8c5460cbdc1bef690fb2a08c0",
+ "8f1dc9649c3a84551f8f6e91cac68242a43b1f8f328ee92280257387fa7559aa6db12e4aeadc2d26099178749c6864b357f3f83b2fb3efa8d2a8db056bed6bcc",
+ "3139c1a7f97afd1675d460ebbc07f2728aa150df849624511ee04b743ba0a833092f18c12dc91b4dd243f333402f59fe28abdbbbae301e7b659c7a26d5c0f979",
+ "06f94a2996158a819fe34c40de3cf0379fd9fb85b3e363ba3926a0e7d960e3f4c2e0c70c7ce0ccb2a64fc29869f6e7ab12bd4d3f14fce943279027e785fb5c29",
+ "c29c399ef3eee8961e87565c1ce263925fc3d0ce267d13e48dd9e732ee67b0f69fad56401b0f10fcaac119201046cca28c5b14abdea3212ae65562f7f138db3d",
+ "4cec4c9df52eef05c3f6faaa9791bc7445937183224ecc37a1e58d0132d35617531d7e795f52af7b1eb9d147de1292d345fe341823f8e6bc1e5badca5c656108",
+ "898bfbae93b3e18d00697eab7d9704fa36ec339d076131cefdf30edbe8d9cc81c3a80b129659b163a323bab9793d4feed92d54dae966c77529764a09be88db45",
+ "ee9bd0469d3aaf4f14035be48a2c3b84d9b4b1fff1d945e1f1c1d38980a951be197b25fe22c731f20aeacc930ba9c4a1f4762227617ad350fdabb4e80273a0f4",
+ "3d4d3113300581cd96acbf091c3d0f3c310138cd6979e6026cde623e2dd1b24d4a8638bed1073344783ad0649cc6305ccec04beb49f31c633088a99b65130267",
+ "95c0591ad91f921ac7be6d9ce37e0663ed8011c1cfd6d0162a5572e94368bac02024485e6a39854aa46fe38e97d6c6b1947cd272d86b06bb5b2f78b9b68d559d",
+ "227b79ded368153bf46c0a3ca978bfdbef31f3024a5665842468490b0ff748ae04e7832ed4c9f49de9b1706709d623e5c8c15e3caecae8d5e433430ff72f20eb",
+ "5d34f3952f0105eef88ae8b64c6ce95ebfade0e02c69b08762a8712d2e4911ad3f941fc4034dc9b2e479fdbcd279b902faf5d838bb2e0c6495d372b5b7029813",
+ "7f939bf8353abce49e77f14f3750af20b7b03902e1a1e7fb6aaf76d0259cd401a83190f15640e74f3e6c5a90e839c7821f6474757f75c7bf9002084ddc7a62dc",
+ "062b61a2f9a33a71d7d0a06119644c70b0716a504de7e5e1be49bd7b86e7ed6817714f9f0fc313d06129597e9a2235ec8521de36f7290a90ccfc1ffa6d0aee29",
+ "f29e01eeae64311eb7f1c6422f946bf7bea36379523e7b2bbaba7d1d34a22d5ea5f1c5a09d5ce1fe682cced9a4798d1a05b46cd72dff5c1b355440b2a2d476bc",
+ "ec38cd3bbab3ef35d7cb6d5c914298351d8a9dc97fcee051a8a02f58e3ed6184d0b7810a5615411ab1b95209c3c810114fdeb22452084e77f3f847c6dbaafe16",
+ "c2aef5e0ca43e82641565b8cb943aa8ba53550caef793b6532fafad94b816082f0113a3ea2f63608ab40437ecc0f0229cb8fa224dcf1c478a67d9b64162b92d1",
+ "15f534efff7105cd1c254d074e27d5898b89313b7d366dc2d7d87113fa7d53aae13f6dba487ad8103d5e854c91fdb6e1e74b2ef6d1431769c30767dde067a35c",
+ "89acbca0b169897a0a2714c2df8c95b5b79cb69390142b7d6018bb3e3076b099b79a964152a9d912b1b86412b7e372e9cecad7f25d4cbab8a317be36492a67d7",
+ "e3c0739190ed849c9c962fd9dbb55e207e624fcac1eb417691515499eea8d8267b7e8f1287a63633af5011fde8c4ddf55bfdf722edf88831414f2cfaed59cb9a",
+ "8d6cf87c08380d2d1506eee46fd4222d21d8c04e585fbfd08269c98f702833a156326a0724656400ee09351d57b440175e2a5de93cc5f80db6daf83576cf75fa",
+ "da24bede383666d563eeed37f6319baf20d5c75d1635a6ba5ef4cfa1ac95487e96f8c08af600aab87c986ebad49fc70a58b4890b9c876e091016daf49e1d322e",
+ "f9d1d1b1e87ea7ae753a029750cc1cf3d0157d41805e245c5617bb934e732f0ae3180b78e05bfe76c7c3051e3e3ac78b9b50c05142657e1e03215d6ec7bfd0fc",
+ "11b7bc1668032048aa43343de476395e814bbbc223678db951a1b03a021efac948cfbe215f97fe9a72a2f6bc039e3956bfa417c1a9f10d6d7ba5d3d32ff323e5",
+ "b8d9000e4fc2b066edb91afee8e7eb0f24e3a201db8b6793c0608581e628ed0bcc4e5aa6787992a4bcc44e288093e63ee83abd0bc3ec6d0934a674a4da13838a",
+ "ce325e294f9b6719d6b61278276ae06a2564c03bb0b783fafe785bdf89c7d5acd83e78756d301b445699024eaeb77b54d477336ec2a4f332f2b3f88765ddb0c3",
+ "29acc30e9603ae2fccf90bf97e6cc463ebe28c1b2f9b4b765e70537c25c702a29dcbfbf14c99c54345ba2b51f17b77b5f15db92bbad8fa95c471f5d070a137cc",
+ "3379cbaae562a87b4c0425550ffdd6bfe1203f0d666cc7ea095be407a5dfe61ee91441cd5154b3e53b4f5fb31ad4c7a9ad5c7af4ae679aa51a54003a54ca6b2d",
+ "3095a349d245708c7cf550118703d7302c27b60af5d4e67fc978f8a4e60953c7a04f92fcf41aee64321ccb707a895851552b1e37b00bc5e6b72fa5bcef9e3fff",
+ "07262d738b09321f4dbccec4bb26f48cb0f0ed246ce0b31b9a6e7bc683049f1f3e5545f28ce932dd985c5ab0f43bd6de0770560af329065ed2e49d34624c2cbb",
+ "b6405eca8ee3316c87061cc6ec18dba53e6c250c63ba1f3bae9e55dd3498036af08cd272aa24d713c6020d77ab2f3919af1a32f307420618ab97e73953994fb4",
+ "7ee682f63148ee45f6e5315da81e5c6e557c2c34641fc509c7a5701088c38a74756168e2cd8d351e88fd1a451f360a01f5b2580f9b5a2e8cfc138f3dd59a3ffc",
+ "1d263c179d6b268f6fa016f3a4f29e943891125ed8593c81256059f5a7b44af2dcb2030d175c00e62ecaf7ee96682aa07ab20a611024a28532b1c25b86657902",
+ "106d132cbdb4cd2597812846e2bc1bf732fec5f0a5f65dbb39ec4e6dc64ab2ce6d24630d0f15a805c3540025d84afa98e36703c3dbee713e72dde8465bc1be7e",
+ "0e79968226650667a8d862ea8da4891af56a4e3a8b6d1750e394f0dea76d640d85077bcec2cc86886e506751b4f6a5838f7f0b5fef765d9dc90dcdcbaf079f08",
+ "521156a82ab0c4e566e5844d5e31ad9aaf144bbd5a464fdca34dbd5717e8ff711d3ffebbfa085d67fe996a34f6d3e4e60b1396bf4b1610c263bdbb834d560816",
+ "1aba88befc55bc25efbce02db8b9933e46f57661baeabeb21cc2574d2a518a3cba5dc5a38e49713440b25f9c744e75f6b85c9d8f4681f676160f6105357b8406",
+ "5a9949fcb2c473cda968ac1b5d08566dc2d816d960f57e63b898fa701cf8ebd3f59b124d95bfbbedc5f1cf0e17d5eaed0c02c50b69d8a402cabcca4433b51fd4",
+ "b0cead09807c672af2eb2b0f06dde46cf5370e15a4096b1a7d7cbb36ec31c205fbefca00b7a4162fa89fb4fb3eb78d79770c23f44e7206664ce3cd931c291e5d",
+ "bb6664931ec97044e45b2ae420ae1c551a8874bc937d08e969399c3964ebdba8346cdd5d09caafe4c28ba7ec788191ceca65ddd6f95f18583e040d0f30d0364d",
+ "65bc770a5faa3792369803683e844b0be7ee96f29f6d6a35568006bd5590f9a4ef639b7a8061c7b0424b66b60ac34af3119905f33a9d8c3ae18382ca9b689900",
+ "ea9b4dca333336aaf839a45c6eaa48b8cb4c7ddabffea4f643d6357ea6628a480a5b45f2b052c1b07d1fedca918b6f1139d80f74c24510dcbaa4be70eacc1b06",
+ "e6342fb4a780ad975d0e24bce149989b91d360557e87994f6b457b895575cc02d0c15bad3ce7577f4c63927ff13f3e381ff7e72bdbe745324844a9d27e3f1c01",
+ "3e209c9b33e8e461178ab46b1c64b49a07fb745f1c8bc95fbfb94c6b87c69516651b264ef980937fad41238b91ddc011a5dd777c7efd4494b4b6ecd3a9c22ac0",
+ "fd6a3d5b1875d80486d6e69694a56dbb04a99a4d051f15db2689776ba1c4882e6d462a603b7015dc9f4b7450f05394303b8652cfb404a266962c41bae6e18a94",
+ "951e27517e6bad9e4195fc8671dee3e7e9be69cee1422cb9fecfce0dba875f7b310b93ee3a3d558f941f635f668ff832d2c1d033c5e2f0997e4c66f147344e02",
+ "8eba2f874f1ae84041903c7c4253c82292530fc8509550bfdc34c95c7e2889d5650b0ad8cb988e5c4894cb87fbfbb19612ea93ccc4c5cad17158b9763464b492",
+ "16f712eaa1b7c6354719a8e7dbdfaf55e4063a4d277d947550019b38dfb564830911057d50506136e2394c3b28945cc964967d54e3000c2181626cfb9b73efd2",
+ "c39639e7d5c7fb8cdd0fd3e6a52096039437122f21c78f1679cea9d78a734c56ecbeb28654b4f18e342c331f6f7229ec4b4bc281b2d80a6eb50043f31796c88c",
+ "72d081af99f8a173dcc9a0ac4eb3557405639a29084b54a40172912a2f8a395129d5536f0918e902f9e8fa6000995f4168ddc5f893011be6a0dbc9b8a1a3f5bb",
+ "c11aa81e5efd24d5fc27ee586cfd8847fbb0e27601ccece5ecca0198e3c7765393bb74457c7e7a27eb9170350e1fb53857177506be3e762cc0f14d8c3afe9077",
+ "c28f2150b452e6c0c424bcde6f8d72007f9310fed7f2f87de0dbb64f4479d6c1441ba66f44b2accee61609177ed340128b407ecec7c64bbe50d63d22d8627727",
+ "f63d88122877ec30b8c8b00d22e89000a966426112bd44166e2f525b769ccbe9b286d437a0129130dde1a86c43e04bedb594e671d98283afe64ce331de9828fd",
+ "348b0532880b88a6614a8d7408c3f913357fbb60e995c60205be9139e74998aede7f4581e42f6b52698f7fa1219708c14498067fd1e09502de83a77dd281150c",
+ "5133dc8bef725359dff59792d85eaf75b7e1dcd1978b01c35b1b85fcebc63388ad99a17b6346a217dc1a9622ebd122ecf6913c4d31a6b52a695b86af00d741a0",
+ "2753c4c0e98ecad806e88780ec27fccd0f5c1ab547f9e4bf1659d192c23aa2cc971b58b6802580baef8adc3b776ef7086b2545c2987f348ee3719cdef258c403",
+ "b1663573ce4b9d8caefc865012f3e39714b9898a5da6ce17c25a6a47931a9ddb9bbe98adaa553beed436e89578455416c2a52a525cf2862b8d1d49a2531b7391",
+ "64f58bd6bfc856f5e873b2a2956ea0eda0d6db0da39c8c7fc67c9f9feefcff3072cdf9e6ea37f69a44f0c61aa0da3693c2db5b54960c0281a088151db42b11e8",
+ "0764c7be28125d9065c4b98a69d60aede703547c66a12e17e1c618994132f5ef82482c1e3fe3146cc65376cc109f0138ed9a80e49f1f3c7d610d2f2432f20605",
+ "f748784398a2ff03ebeb07e155e66116a839741a336e32da71ec696001f0ad1b25cd48c69cfca7265eca1dd71904a0ce748ac4124f3571076dfa7116a9cf00e9",
+ "3f0dbc0186bceb6b785ba78d2a2a013c910be157bdaffae81bb6663b1a73722f7f1228795f3ecada87cf6ef0078474af73f31eca0cc200ed975b6893f761cb6d",
+ "d4762cd4599876ca75b2b8fe249944dbd27ace741fdab93616cbc6e425460feb51d4e7adcc38180e7fc47c89024a7f56191adb878dfde4ead62223f5a2610efe",
+ "cd36b3d5b4c91b90fcbba79513cfee1907d8645a162afd0cd4cf4192d4a5f4c892183a8eacdb2b6b6a9d9aa8c11ac1b261b380dbee24ca468f1bfd043c58eefe",
+ "98593452281661a53c48a9d8cd790826c1a1ce567738053d0bee4a91a3d5bd92eefdbabebe3204f2031ca5f781bda99ef5d8ae56e5b04a9e1ecd21b0eb05d3e1",
+ "771f57dd2775ccdab55921d3e8e30ccf484d61fe1c1b9c2ae819d0fb2a12fab9be70c4a7a138da84e8280435daade5bbe66af0836a154f817fb17f3397e725a3",
+ "c60897c6f828e21f16fbb5f15b323f87b6c8955eabf1d38061f707f608abdd993fac3070633e286cf8339ce295dd352df4b4b40b2f29da1dd50b3a05d079e6bb",
+ "8210cd2c2d3b135c2cf07fa0d1433cd771f325d075c6469d9c7f1ba0943cd4ab09808cabf4acb9ce5bb88b498929b4b847f681ad2c490d042db2aec94214b06b",
+ "1d4edfffd8fd80f7e4107840fa3aa31e32598491e4af7013c197a65b7f36dd3ac4b478456111cd4309d9243510782fa31b7c4c95fa951520d020eb7e5c36e4ef",
+ "af8e6e91fab46ce4873e1a50a8ef448cc29121f7f74deef34a71ef89cc00d9274bc6c2454bbb3230d8b2ec94c62b1dec85f3593bfa30ea6f7a44d7c09465a253",
+ "29fd384ed4906f2d13aa9fe7af905990938bed807f1832454a372ab412eea1f5625a1fcc9ac8343b7c67c5aba6e0b1cc4644654913692c6b39eb9187ceacd3ec",
+ "a268c7885d9874a51c44dffed8ea53e94f78456e0b2ed99ff5a3924760813826d960a15edbedbb5de5226ba4b074e71b05c55b9756bb79e55c02754c2c7b6c8a",
+ "0cf8545488d56a86817cd7ecb10f7116b7ea530a45b6ea497b6c72c997e09e3d0da8698f46bb006fc977c2cd3d1177463ac9057fdd1662c85d0c126443c10473",
+ "b39614268fdd8781515e2cfebf89b4d5402bab10c226e6344e6b9ae000fb0d6c79cb2f3ec80e80eaeb1980d2f8698916bd2e9f747236655116649cd3ca23a837",
+ "74bef092fc6f1e5dba3663a3fb003b2a5ba257496536d99f62b9d73f8f9eb3ce9ff3eec709eb883655ec9eb896b9128f2afc89cf7d1ab58a72f4a3bf034d2b4a",
+ "3a988d38d75611f3ef38b8774980b33e573b6c57bee0469ba5eed9b44f29945e7347967fba2c162e1c3be7f310f2f75ee2381e7bfd6b3f0baea8d95dfb1dafb1",
+ "58aedfce6f67ddc85a28c992f1c0bd0969f041e66f1ee88020a125cbfcfebcd61709c9c4eba192c15e69f020d462486019fa8dea0cd7a42921a19d2fe546d43d",
+ "9347bd291473e6b4e368437b8e561e065f649a6d8ada479ad09b1999a8f26b91cf6120fd3bfe014e83f23acfa4c0ad7b3712b2c3c0733270663112ccd9285cd9",
+ "b32163e7c5dbb5f51fdc11d2eac875efbbcb7e7699090a7e7ff8a8d50795af5d74d9ff98543ef8cdf89ac13d0485278756e0ef00c817745661e1d59fe38e7537",
+ "1085d78307b1c4b008c57a2e7e5b234658a0a82e4ff1e4aaac72b312fda0fe27d233bc5b10e9cc17fdc7697b540c7d95eb215a19a1a0e20e1abfa126efd568c7",
+ "4e5c734c7dde011d83eac2b7347b373594f92d7091b9ca34cb9c6f39bdf5a8d2f134379e16d822f6522170ccf2ddd55c84b9e6c64fc927ac4cf8dfb2a17701f2",
+ "695d83bd990a1117b3d0ce06cc888027d12a054c2677fd82f0d4fbfc93575523e7991a5e35a3752e9b70ce62992e268a877744cdd435f5f130869c9a2074b338",
+ "a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15",
+ "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9",
+ "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461",
+}
+
+var hashes2X = []string{
+ "64",
+ "f457",
+ "e8c045",
+ "a74c6d0d",
+ "eb02ae482a",
+ "be65b981275e",
+ "8540ccd083a455",
+ "074a02fa58d7c7c0",
+ "da6da05e10db3022b6",
+ "542a5aae2f28f2c3b68c",
+ "ca3af2afc4afe891da78b1",
+ "e0f66b8dcebf4edc85f12c85",
+ "744224d383733b3fa2c53bfcf5",
+ "b09b653e85b72ef5cdf8fcfa95f3",
+ "dd51877f31f1cf7b9f68bbb09064a3",
+ "f5ebf68e7ebed6ad445ffc0c47e82650",
+ "ebdcfe03bcb7e21a9091202c5938c0a1bb",
+ "860fa5a72ff92efafc48a89df1632a4e2809",
+ "0d6d49daa26ae2818041108df3ce0a4db48c8d",
+ "e5d7e1bc5715f5ae991e4043e39533af5d53e47f",
+ "5232028a43b9d4dfa7f37439b49495926481ab8a29",
+ "c118803c922f9ae2397fb676a2ab7603dd9c29c21fe4",
+ "2af924f48b9bd7076bfd68794bba6402e2a7ae048de3ea",
+ "61255ac38231087c79ea1a0fa14538c26be1c851b6f318c0",
+ "f9712b8e42f0532162822f142cb946c40369f2f0e77b6b186e",
+ "76da0b89558df66f9b1e66a61d1e795b178ce77a359087793ff2",
+ "9036fd1eb32061bdecebc4a32aa524b343b8098a16768ee774d93c",
+ "f4ce5a05934e125d159678bea521f585574bcf9572629f155f63efcc",
+ "5e1c0d9fae56393445d3024d6b82692d1339f7b5936f68b062c691d3bf",
+ "538e35f3e11111d7c4bab69f83b30ade4f67addf1f45cdd2ac74bf299509",
+ "17572c4dcbb17faf8785f3bba9f6903895394352eae79b01ebd758377694cc",
+ "29f6bb55de7f8868e053176c878c9fe6c2055c4c5413b51ab0386c277fdbac75",
+ "bad026c8b2bd3d294907f2280a7145253ec2117d76e3800357be6d431b16366e41",
+ "386b7cb6e0fd4b27783125cbe80065af8eb9981fafc3ed18d8120863d972fa7427d9",
+ "06e8e6e26e756fff0b83b226dce974c21f970e44fb5b3e5bbada6e4b12f81cca666f48",
+ "2f9bd300244f5bc093ba6dcdb4a89fa29da22b1de9d2c9762af919b5fedf6998fbda305b",
+ "cf6bdcc46d788074511f9e8f0a4b86704365b2d3f98340b8db53920c385b959a38c8869ae7",
+ "1171e603e5cdeb4cda8fd7890222dd8390ede87b6f3284cac0f0d832d8250c9200715af7913d",
+ "bda7b2ad5d02bd35ffb009bdd72b7d7bc9c28b3a32f32b0ba31d6cbd3ee87c60b7b98c03404621",
+ "2001455324e748503aa08eff2fb2e52ae0170e81a6e9368ada054a36ca340fb779393fb045ac72b3",
+ "45f0761aefafbf87a68f9f1f801148d9bba52616ad5ee8e8ac9207e9846a782f487d5cca8b20355a18",
+ "3a7e05708be62f087f17b41ac9f20e4ef8115c5ab6d08e84d46af8c273fb46d3ce1aabebae5eea14e018",
+ "ea318da9d042ca337ccdfb2bee3e96ecb8f907876c8d143e8e44569178353c2e593e4a82c265931ba1dd79",
+ "e0f7c08f5bd712f87094b04528fadb283d83c9ceb82a3e39ec31c19a42a1a1c3bee5613b5640abe069b0d690",
+ "d35e63fb1f3f52ab8f7c6cd7c8247e9799042e53922fbaea808ab979fa0c096588cfea3009181d2f93002dfc11",
+ "b8b0ab69e3ae55a8699eb481dd665b6a2424c89bc6b7cca02d15fdf1b9854139cab49d34de498b50b2c7e8b910cf",
+ "fb65e3222a2950eae1701d4cdd4736266f65bf2c0d2e77968996eadb60ef74fb786f6234973a2524bdfe32d100aa0e",
+ "f28b4bb3a2e2c4d5c01a23ff134558559a2d3d704b75402983ee4e0f71d273ae056842c4153b18ee5c47e2bfa54313d4",
+ "7bb78794e58a53c3e4b1aeb161e756af051583d14e0a5a3205e094b7c9a8cf62d098fa9ea1db12f330a51ab9852c17f983",
+ "a879a8ebae4d0987789bcc58ec3448e35ba1fa1ee58c668d8295aba4eaeaf2762b053a677e25404f635a53037996974d418a",
+ "695865b353ec701ecc1cb38f3154489eed0d39829fc192bb68db286d20fa0a64235cde5639137819f7e99f86bd89afcef84a0f",
+ "a6ec25f369f71176952fb9b33305dc768589a6070463ee4c35996e1ced4964a865a5c3dc8f0d809eab71366450de702318e4834d",
+ "604749f7bfadb069a036409ffac5ba291fa05be8cba2f141554132f56d9bcb88d1ce12f2004cd3ade1aa66a26e6ef64e327514096d",
+ "daf9fa7dc2464a899533594e7916fc9bc585bd29dd60c930f3bfa78bc47f6c8439448043a45119fc9228c15bce5fd24f46baf9de736b",
+ "943ea5647a8666763084da6a6f15dcf0e8dc24f27fd0d9194805d25180fe3a6d98f4b2b5e0d6a04e9b41869817030f16ae975dd41fc35c",
+ "af4f73cbfc093760dfeb52d57ef45207bbd1a515f5523404e5d95a73c237d97ae65bd195b472de6d514c2c448b12fafc282166da132258e9",
+ "605f4ed72ed7f5046a342fe4cf6808100d4632e610d59f7ebb016e367d0ff0a95cf45b02c727ba71f147e95212f52046804d376c918cadd260",
+ "3750d8ab0a6b13f78e51d321dfd1aa801680e958de45b7b977d05732ee39f856b27cb2bcce8fbf3db6666d35e21244c2881fdcc27fbfea6b1672",
+ "8f1b929e80ab752b58abe9731b7b34eb61369536995abef1c0980d93903c1880da3637d367456895f0cb4769d6de3a979e38ed6f5f6ac4d48e9b32",
+ "d8469b7aa538b36cdc711a591d60dafecca22bd421973a70e2deef72f69d8014a6f0064eabfbebf5383cbb90f452c6e113d2110e4b1092c54a38b857",
+ "7d1f1ad2029f4880e1898af8289c23bc933a40863cc4ab697fead79c58b6b8e25b68cf5324579b0fe879fe7a12e6d03907f0140dfe7b29d33d6109ecf1",
+ "87a77aca6d551642288a0dff66078225ae39d288801607429d6725ca949eed7a6f199dd8a65523b4ee7cfa4187400e96597bfffc3e38ade0ae0ab88536a9",
+ "e101f43179d8e8546e5ce6a96d7556b7e6b9d4a7d00e7aade5579d085d527ce34a9329551ebcaf6ba946949bbe38e30a62ae344c1950b4bde55306b3bac432",
+ "4324561d76c370ef35ac36a4adf8f3773a50d86504bd284f71f7ce9e2bc4c1f1d34a7fb2d67561d101955d448b67577eb30dfee96a95c7f921ef53e20be8bc44",
+ "78f0ed6e220b3da3cc9381563b2f72c8dc830cb0f39a48c6ae479a6a78dcfa94002631dec467e9e9b47cc8f0887eb680e340aec3ec009d4a33d241533c76c8ca8c",
+ "9f6589c31a472e0a736f4eb22b6c70a9d332cc15304ccb66a6b97cd051b6ed82f8990e1d9bee2e4bb1c3c45e550ae0e7b96e93ae23f2fb8f63b309131e72b36cba6a",
+ "c138077ee4ed3d7ffa85ba851dfdf6e9843fc1dc00889d117237bfaad9aa757192f73556b959f98e6d24886ce48869f2a01a48c371785f12b6484eb2078f08c22066e1",
+ "f83e7c9e0954a500576ea1fc90a3db2cbd7994eaef647dab5b34e88ab9dc0b47addbc807b21c8e6dd3d0bd357f008471d4f3e0abb18450e1d4919e03a34545b9643f870e",
+ "3277a11f2628544fc66f50428f1ad56bcba6ee36ba2ca6ecdf7e255effc0c30235c039d13e01f04cf1efe95b5c2033ab72adda30994b62f2851d17c9920eadca9a251752dc",
+ "c2a834281a06fe7b730d3a03f90761daf02714c066e33fc07e1f59ac801ec2f4433486b5a2da8faa51a0cf3c34e29b2960cd0013378938dbd47c3a3d12d70db01d7d06c3e91e",
+ "47680182924a51cabe142a6175c9253e8ba7ea579ece8d9bcb78b1e9ca00db844fa08abcf41702bd758ee2c608d9612fed50e85854469cb4ef3038acf1e35b6ba4390561d8ae82",
+ "cec45830cd71869e83b109a99a3cd7d935f83a95de7c582f3adbd34e4938fa2f3f922f52f14f169c38cc6618d3f306a8a4d607b345b8a9c48017136fbf825aecf7b620e85f837fae",
+ "46fb53c70ab105079d5d78dc60eaa30d938f26e4d0b9df122e21ec85deda94744c1daf8038b8a6652d1ff3e7e15376f5abd30e564784a999f665078340d66b0e939e0c2ef03f9c08bb",
+ "7b0dcb52791a170cc52f2e8b95d8956f325c3751d3ef3b2b83b41d82d4496b46228a750d02b71a96012e56b0720949ca77dc68be9b1ef1ad6d6a5ceb86bf565cb972279039e209dddcdc",
+ "7153fd43e6b05f5e1a4401e0fef954a737ed142ec2f60bc4daeef9ce73ea1b40a0fcaf1a1e03a3513f930dd5335723632f59f7297fe3a98b68e125eadf478eb045ed9fc4ee566d13f537f5",
+ "c7f569c79c801dab50e9d9ca6542f25774b3841e49c83efe0b89109f569509ce7887bc0d2b57b50320eb81fab9017f16c4c870e59edb6c26620d93748500231d70a36f48a7c60747ca2d5986",
+ "0a81e0c547648595adca65623ce783411aac7f7d30c3ad269efafab288e7186f6895261972f5137877669c550f34f5128850ebb50e1884814ea1055ee29a866afd04b2087abed02d9592573428",
+ "6a7b6769e1f1c95314b0c7fe77013567891bd23416374f23e4f43e27bc4c55cfada13b53b1581948e07fb96a50676baa2756db0988077b0f27d36ac088e0ff0fe72eda1e8eb4b8facff3218d9af0",
+ "a399474595cb1ccab6107f18e80f03b1707745c7bf769fc9f260094dc9f8bc6fe09271cb0b131ebb2acd073de4a6521c8368e664278be86be216d1622393f23435fae4fbc6a2e7c961282a777c2d75",
+ "4f0fc590b2755a515ae6b46e9628092369d9c8e589e3239320639aa8f7aa44f8111c7c4b3fdbe6e55e036fbf5ebc9c0aa87a4e66851c11e86f6cbf0bd9eb1c98a378c7a7d3af900f55ee108b59bc9e5c",
+ "ed96a046f08dd675107331d267379c6fce3c352a9f8d7b243008a74cb4e9410836afaabe871dab6038ca94ce5f6d41fa922ce08aba58169f94cfc86d9f688f396abd24c11a6a9b0830572105a477c33e92",
+ "379955f539abf0eb2972ee99ed9546c4bbee363403991833005dc27904c271ef22a799bc32cb39f08d2e4ba6717d55153feb692d7c5efae70890bf29d96df02333c7b05ccc314e4835b018fec9141a82c745",
+ "e16cc8d41b96547ede0d0cf4d908c5fa393399daa4a9696e76a4c1f6a2a9fef70f17fb53551a8145ed88f18db8fe780a079d94732437023f7c1d1849ef69ad536a76204239e8ba5d97e507c36c7d042f87fe0e",
+ "a81de50750ece3f84536728f227208bf01ec5b7721579d007de72c88ee20663318332efe5bc7c09ad1fa8342be51f0609046ccf760a7957a7d8dc88941adb93666a4521ebe76618e5ddc2dd3261493d400b50073",
+ "b72c5fb7c7f60d243928fa41a2d711157b96aef290185c64b4de3dcfa3d644da67a8f37c2ac55caad79ec695a473e8b481f658c497edb8a191526592b11a412282d2a4010c90ef4647bd6ce745ebc9244a71d4876b",
+ "9550703877079c90e200e830f277b605624954c549e729c359ee01ee2b07741ecc4255cb37f96682dafcdbaade1063e2c5ccbd1918fb669926a67744101fb6de3ac016be4c74165a1e5a696b704ba2ebf4a953d44b95",
+ "a17eb44d4de502dc04a80d5a5e9507d17f27c96467f24c79b06bc98a4c410741d4ac2db98ec02c2a976d788531f1a4451b6c6204cef6dae1b6ebbcd0bde23e6fffb02754043c8fd3c783d90a670b16879ce68b5554fe1c",
+ "41d3ea1eaba5be4a206732dbb5b70b79b66a6e5908795ad4fb7cf9e67efb13f06fef8f90acb080ce082aadec6a1b543af759ab63fa6f1d3941186482b0c2b312f1151ea8386253a13ed3708093279b8eb04185636488b226",
+ "5e7cdd8373dc42a243c96013cd29df9283b5f28bb50453a903c85e2ce57f35861bf93f03029072b70dac0804e7d51fd0c578c8d9fa619f1e9ce3d8044f65d55634dba611280c1d5cfb59c836a595c803124f696b07ddfac718",
+ "26a14c4aa168907cb5de0d12a82e1373a128fb21f2ed11feba108b1bebce934ad63ed89f4ed7ea5e0bc8846e4fc10142f82de0bebd39d68f7874f615c3a9c896bab34190e85df05aaa316e14820b5e478d838fa89dfc94a7fc1e",
+ "0211dfc3c35881adc170e4ba6daab1b702dff88933db9a6829a76b8f4a7c2a6d658117132a974f0a0b3a38ceea1efc2488da21905345909e1d859921dc2b5054f09bce8eeb91fa2fc6d048ce00b9cd655e6aafbdaa3a2f19270a16",
+ "ddf015b01b68c4f5f72c3145d54049867d99ee6bef24282abf0eecdb506e295bacf8f23ffa65a4cd891f76a046b9dd82cae43a8d01e18a8dff3b50aeb92672be69d7c087ec1fa2d3b2a39196ea5b49b7baede37a586fea71aded587f",
+ "6ee721f71ca4dd5c9ce7873c5c04c6ce76a2c824b984251c15535afc96adc9a4d48ca314bfeb6b8ee65092f14cf2a7ca9614e1dcf24c2a7f0f0c11207d3d8aed4af92873b56e8b9ba2fbd659c3f4ca90fa24f113f74a37181bf0fdf758",
+ "689bd150e65ac123612524f720f54def78c095eaab8a87b8bcc72b443408e3227f5c8e2bd5af9bcac684d497bc3e41b7a022c28fb5458b95e8dfa2e8caccde0492936ff1902476bb7b4ef2125b19aca2cd3384d922d9f36dddbcd96ae0d6",
+ "3a3c0ef066fa4390ec76ad6be1dc9c31ddf45fef43fbfa1f49b439caa2eb9f3042253a9853e96a9cf86b4f873785a5d2c5d3b05f6501bc876e09031188e05f48937bf3c9b667d14800db62437590b84ce96aa70bb5141ee2ea41b55a6fd944",
+ "741ce384e5e0edaebb136701ce38b3d33215415197758ae81235307a4115777d4dab23891db530c6d28f63a957428391421f742789a0e04c99c828373d9903b64dd57f26b3a38b67df829ae243feef731ead0abfca049924667fdec49d40f665",
+ "a513f450d66cd5a48a115aee862c65b26e836f35a5eb6894a80519e2cd96cc4cad8ed7eb922b4fc9bbc55c973089d627b1da9c3a95f6c019ef1d47143cc545b15e4244424be28199c51a5efc7234dcd94e72d229897c392af85f523c2633427825",
+ "71f1554d2d49bb7bd9e62e71fa049fb54a2c097032f61ebda669b3e1d4593962e47fc62a0ab5d85706aebd6a2f9a192c88aa1ee2f6a46710cf4af6d3c25b7e68ad5c3db23ac009c8f13625ff85dc8e50a9a1b2682d3329330b973ec8cbb7bb73b2bd",
+ "167cc1067bc08a8d2c1a0c10041ebe1fc327b37043f6bd8f1c63569e9d36ded58519e66b162f34b6d8f1107ef1e3de199d97b36b44141a1fc4f49b883f40507ff11f909a017869dc8a2357fc7336ae68703d25f75710b0ff5f9765321c0fa53a51675c",
+ "cb859b35dc70e264efaad2a809fea1e71cd4a3f924be3b5a13f8687a1166b538c40b2ad51d5c3e47b0de482497382673140f547068ff0b3b0fb7501209e1bf36082509ae85f60bb98fd02ac50d883a1a8daa704952d83c1f6da60c9624bc7c99912930bf",
+ "afb1f0c6b7125b04fa2578dd40f60cb411b35ebc7026c702e25b3f0ae3d4695d44cfdf37cb755691dd9c365edadf21ee44245620e6a24d4c2497135b37cd7ac67e3bd0aaee9f63f107746f9b88859ea902bc7d6895406aa2161f480cad56327d0a5bba2836",
+ "13e9c0522587460d90c7cb354604de8f1bf850e75b4b176bda92862d35ec810861f7d5e7ff6ba9302f2c2c8642ff8b7776a2f53665790f570fcef3cac069a90d50db42227331c4affb33d6c040d75b9aeafc9086eb83ced38bb02c759e95ba08c92b17031288",
+ "0549812d62d3ed497307673a4806a21060987a4dbbf43d352b9b170a29240954cf04bc3e1e250476e6800b79e843a8bd8253b7d743de01ab336e978d4bea384eaff700ce020691647411b10a60acacb6f8837fb08ad666b8dcc9eaa87ccb42aef6914a3f3bc30a",
+ "3a263efbe1f2d463f20526e1d0fd735035fd3f808925f058b32c4d8788aeeab9b8ce233b3c34894731cd73361f465bd350395aebcabd2fb63010298ca025d849c1fa3cd573309b74d7f824bbfe383f09db24bcc565f636b877333206a6ad70815c3bef5574c5fc1c",
+ "3c6a7d8a84ef7e3eaa812fc1eb8e85105467230d2c9e4562edbfd808f4d1ac15d16b786cc6a02959c2bc17149c2ce74c6f85ee5ef22a8a96b9be1f197cffd214c1ab02a06a9227f37cd432579f8c28ff2b5ac91cca8ffe6240932739d56788c354e92c591e1dd76499",
+ "b571859294b02af17541a0b5e899a5f67d6f5e36d38255bc417486e69240db56b09cf2607fbf4f95d085a779358a8a8b41f36503438c1860c8f361ce0f2783a08b21bd7232b50ca6d35428335272a5c05b436b2631d8d5c84d60e8040083768ce56a250727fb0579dd5c",
+ "98ee1b7269d2a0dd490ca38d447279870ea55326571a1b430adbb2cf65c492131136f504145df3ab113a13abfb72c33663266b8bc9c458db4bf5d7ef03e1d3b8a99d5de0c024be8fabc8dc4f5dac82a0342d8ed65c329e7018d6997e69e29a01350516c86beaf153da65ac",
+ "41c5c95f088df320d35269e5bf86d10248f17aec6776f0fe653f1c356aae409788c938befeb67c86d1c8870e8099ca0ce61a80fbb5a6654c44529368f70fc9b9c2f912f5092047d0ffc339577d24142300e34948e086f62e23ecaca410d24f8a36b5c8c5a80e0926bc8aa16a",
+ "9f93c41f533b2a82a4df893c78faaaa793c1506974ba2a604cd33101713ca4adfd30819ffd8403402b8d40aff78106f3357f3e2c24312c0d3603a17184d7b999fc9908d14d50192aebabd90d05073da7af4be37dd3d81c90acc80e8333df546f17ab6874f1ec204392d1c0571e",
+ "3da5207245ac270a915fc91cdb314e5a2577c4f8e269c4e701f0d7493ba716de79935918b917a2bd5db98050dbd1eb3894b65fac5abf13e075abebc011e651c03cafb6127147771a5c8418223e1548137a89206635c26ca9c235ccc108dc25cf846e4732444bd0c2782b197b262b",
+ "96011af3965bb941dc8f749932ea484eccb9ba94e34b39f24c1e80410f96ce1d4f6e0aa5be606def4f54301e930493d4b55d484d93ab9dd4dc2c9cfb79345363af31ad42f4bd1aa6c77b8afc9f0d551bef7570b13b927afe3e7ac4de7603a0876d5edb1ad9be05e9ee8b53941e8f59",
+ "51dbbf2a7ca224e524e3454fe82ddc901fafd2120fa8603bc343f129484e9600f688586e040566de0351d1693829045232d04ff31aa6b80125c763faab2a9b233313d931903dcfaba490538b06e4688a35886dc24cdd32a13875e6acf45454a8eb8a315ab95e608ad8b6a49aef0e299a",
+ "5a6a422529e22104681e8b18d64bc0463a45df19ae2633751c7aae412c250f8fb2cd5e1270d3d0cf009c8aa69688ccd4e2b6536f5747a5bc479b20c135bf4e89d33a26118705a614c6be7ecfe766932471ad4ba01c4f045b1abb5070f90ec78439a27a1788db9327d1c32f939e5fb1d5ba",
+ "5d26c983642093cb12ff0afabd87b7c56e211d01844ad6da3f623b9f20a0c968034299f2a65e6673530c5980a532beb831c7d0697d12760445986681076dfb6fae5f3a4d8f17a0db5008ce8619f566d2cfe4cf2a6d6f9c3664e3a48564a351c0b3c945c5ee24587521e4112c57e318be1b6a",
+ "52641dbc6e36be4d905d8d60311e303e8e859cc47901ce30d6f67f152343e3c4030e3a33463793c19effd81fb7c4d631a9479a7505a983a052b1e948ce093b30efa595fab3a00f4cef9a2f664ceeb07ec61719212d58966bca9f00a7d7a8cb4024cf6476bab7fbccee5fd4e7c3f5e2b2975aa2",
+ "a34ce135b37bf3db1c4aaa4878b4499bd2ee17b85578fcaf605d41e1826b45fdaa1b083d8235dc642787f11469a5493e36806504fe2a2063905e821475e2d5ee217057950370492f5024995e77b82aa51b4f5bd8ea24dc71e0a8a640b0592c0d80c24a726169cf0a10b40944747113d03b52708c",
+ "46b3cdf4946e15a5334fc3244d6680f5fc132afa67bf43bfade23d0c9e0ec64e7dab76faaeca1870c05f96b7d019411d8b0873d9fed04fa5057c039d5949a4d592827f619471359d6171691cfa8a5d7cb07ef2804f6ccad4821c56d4988bea7765f660f09ef87405f0a80bcf8559efa111f2a0b419",
+ "8b9fc21691477f11252fca050b121c5334eb4280aa11659e267297de1fec2b2294c7ccee9b59a149b9930b08bd320d3943130930a7d931b71d2f10234f4480c67f1de883d9894ada5ed5071660e221d78ae402f1f05af47761e13fec979f2671e3c63fb0ae7aa1327cf9b8313adab90794a52686bbc4",
+ "cd6598924ce847de7ff45b20ac940aa6292a8a99b56a74eddc24f2cfb45797188614a21d4e8867e23ff75afd7cd324248d58fcf1ddc73fbd115dfa8c09e62022fab540a59f87c989c12a86ded05130939f00cd2f3b512963dfe0289f0e54acad881c1027d2a0292138fdee902d67d9669c0ca1034a9456",
+ "594e1cd7337248704e691854af0fdb021067ddf7832b049ba7b684438c32b029eded2df2c89a6ff5f2f2c311522ae2dc6db5a815afc60637b15ec24ef9541f1550409db2a006da3affffe548a1eaee7bd114e9b805d0756c8e90c4dc33cb05226bc2b393b18d953f8730d4c7ae693159cdba758ad28964e2",
+ "1f0d292453f04406ada8be4c161b82e3cdd69099a8637659e0ee40b8f6da46005cfc6085db9804852decfbe9f7b4dda019a7112612895a144ed430a960c8b2f5458d3d56b7f427cee6358915aee7146278aed2a0296cdd929e4d21ef95a3adf8b7a6beba673cdccdbdcfb2474711732d972ad054b2dc64f38d",
+ "b65a72d4e1f9f9f75911cc46ad0806b9b18c87d105332a3fe183f45f063a746c892dc6c4b9181b1485b3e3a2cc3b453eba2d4c39d6905a774ed3fb755468beb190925ecd8e57ecb0d985125741650c6b6a1b2a3a50e93e3892c21d47ed5884eed83aa94e1602288f2f49fe286624de9d01fcb54433a0dc4ad70b",
+ "705ce0ffa469250782aff725248fc88fe98eb76659e8407edc1c4842c9867d61fe64fb86f74e980598b92bc213d06f337bd5654fc28643c7ba769a4c31563427543c00808b627a19c90d86c322f33566ce020121cc322229c3337943d46f68ef939d613dcef0077269f88151d6398b6b009abb763410b154ad76a3",
+ "7fa881ce87498440ab6af13854f0d851a7e0404de33896999a9b3292a5d2f5b3ad033530c558168fe5d2fdb9b89a2354c46cf32a0e612afc6c6485d789511bfef26800c74bf1a4cfbe30bda310d5f6029c3dccdedb6149e4971274e276dccfabd63bc4b9955e8303feb57f8a688db55ecb4b33d1f9fe1b3a8ba7ac32",
+ "23a98f71c01c0408ae16843dc03be7db0aeaf055f951709d4e0dfdf64fffbffaf900ee592ee10929648e56f6c1e9f5be5793f7df66453eb56502c7c56c0f0c88da77abc8fa371e434104627ef7c663c49f40998dbad63fa6c7aa4fac17ae138d8bbe081f9bd168cd33c1fbc92fa35ed687679f48a64b87db1fe5bae675",
+ "7b8970b6a33237e5a7bcb39272703edb92285c55842b30b9a48834b1b507cc02a6764739f2f7ee6ae02a7b715a1c455e59e8c77a1ae98abb10161853f1234d20da99016588cd8602d6b7ec7e177d4011edfa61e6b3766a3c6f8d6e9eac893c568903eb6e6aba9c4725774f6b4343b7acaa6c031593a36eef6c72806ff309",
+ "f7f4d328ba108b7b1de4443e889a985ed52f485f3ca4e0c246aa5526590cbed344e9f4fe53e4eea0e761c82324649206ca8c2b45152157d4115e68c818644b03b65bb47ad79f94d37cb03c1d953b74c2b8adfa0e1c418bda9c518ddcd7050e0f149044740a2b16479413b63fc13c36144f80c73687513dca761ba8642a8ae0",
+ "2d7dc80c19a1d12d5fe3963569547a5d1d3e821e6f06c5d5e2c09401f946c9f7e13cd019f2f9a878b62dd850453b6294b99ccaa068e542993524b0f63832d48e865be31e8ec1ee103c718340c904b32efb69170b67f038d50a3252794b1b4076c0620621ab3d91215d55ffea99f23d54e161a90d8d4902fda5931d9f6a27146a",
+ "77dff4c7ad30c954338c4b23639dae4b275086cbe654d401a2343528065e4c9f1f2eca22aa025d49ca823e76fdbb35df78b1e5075ff2c82b680bca385c6d57f7ea7d1030bb392527b25dd73e9eeff97bea397cf3b9dda0c817a9c870ed12c006cc054968c64000e0da874e9b7d7d621b0679866912243ea096c7b38a1344e98f74",
+ "83bed0d556798f2b419f7056e6d3ffada06e939b95a688d0ec8c6ac5ea45ab73a4cf01043e0a170766e21395f27ab4b78c435f5f0dfe6e93ab80df38610e41158429ddf20296f53a06a017723359fe22dc08b5da33f0800a4fe50118e8d7eab2f83a85cd764bf8a166903bd0e9dcfeeceba44ff4ca4439846458d31ea2bb564645d1",
+ "ea12cf5a113543e39504123036f15a5bafa9c555562469f99cd29996a4dfaaab2a34b00557ccf15f37fc0cc1b3be427e725f2cd952e50af7970dda9200cd5ce252b1f29c40067fea3027ed686190803b59d834179d1b8f5b55abe55ad174b2a1188f7753ec0ae2fc01316e7d498b68ee3598a0e9baaaa664a60f7fb4f90edbed494ad7",
+ "55266358332d8d9e68bd13432088beadf95833aab67a0eb3b10650414255f299e2670c3e1a5b2976159a46c72a7ce57d59b7be14c15798e09ed50fa312a431b0264d7a1396aa6168bde897e208ece53d2cfc83786113b1e6eac5e9bb98984abb6c8d64eebb991903254abc650c999bb9958a5d7937434b869bc940e21b9dc1cc8982f2ba",
+ "4d6104ded730aefe02873f4c741232c8234a6d66d85393aff57fbf56ba6347666988dfc4d58f3cc895a0da598822edeee4533d24ec0ee292fd5e1ad04898ffbc1ff4bef14dec220babcb0f28fffe32a6e2c28aaaac16442bf4feb02917d18bb3a415d84fa9358d5a9852688d846c92271911f934181c30f82434d915f93f155a1ffbf0b125",
+ "eb5f579a4c476af554aac11e5719d378549497e613b35a929d6f36bb8831d7a466aa76de9be24ebb55543f1c13924f64cfd648a5b3fa90387315c16174dbf1e9a183c196d9bb8f84af65f1f8212429aadc11ef2426d07d4716062b85c8d5d2dff8e21b9e62b7fa7dbd57d72633054b464fb28583a56ca13ccc5ddc74dae942492f31731e7046",
+ "ebddec3dcaf18063e45a76ebeac39af85a1adc2818881ccce48c106288f5988365cca2b4b1d7f037322da46840f42bebdcbc7193838d426e101087d8cea03aaff743d573eb4f4e9a71a2c884390769a6503874125d194bee8d46a3a0d5e4fcf28ff8465887d8e9df771d70157e75df3642b331d2778ceb32ceba868640171ab7a5d22eede1ee44",
+ "26d87ec70b57691e3bb359633d3ddba17f029d62cdfe977f5fd42274d79b444a32494d1c01e9f72d03cce78c806df96e93ea78da3a054209924ed765edc4d570f66168dc25ee3114e4017e387440349c8f0a94804761c3055f88e4fda2a49b860b1486a9609095f6250f268b6a4d1aecc03a505632ebf0b9dc22d0755a736faf7ad7000858b5864b",
+ "3880f5cc2d08fa70ef44b1f263fcf534d062a298c1bd5ee2eee8c3265806c4ce50b004f3a1fc1fa5b024aaac7f528c023c8181f67c6e1c357425dc4d573bd46b93a542afa3a19bdb140a2ce666e1a01f5c4d2dcd681fa9f5839b797813c394738d5ee4971386c12c7c117d17c7bec324b760aa30cda9ab2aa850284ba6fa97946f710f02449d1883c6",
+ "3317d2f452105dd3f4a96f9257af8285a80be58066b50f6f54bd633749b49f6ab9d57d45652d2ae852a2f6940cd5ec3159dd7f333358b12f502325df38843508faf7e246352d201280babd90b14fbf7722641c3601d0e458474439973c611bb5502fd0eb3078f87124ca7e1a016fcb6cfeff65f6a565985aca7122cfa8c5a11da0cb47797c5132333179",
+ "f2c5c955d0224e784a46b9125f8fef8a5e1271e145eb08bbbd07ca8e1cfc848cef14fa3b36221ac62006403dbb7f7d77958ccc54a8566c837858b809f3e310ace8ca682515bc655d2a397cab238a663b464d511f02dc5d033dad4cb5e0e519e94a54b62a3896e460ec70e5716b5921bf8396aa86a60123e6287e34570bb01bdc602e113670bf498af2ff10",
+ "180e275205691a83630cf4b0c7b80e6df8fad6ef1c23ba8013d2f09aef7abade1827f23af230de90676240b4b3b0673f8afdea0327330055041741f65560d90348de696d34ca80dfe8afae582fe4879d4594b80e9408fb53e800e01ca58552b905c365e7f1416e51c080f517d6bbd30e64ae1535d59decdc76c6624d737868f49f2f719da39ba1344d59eab9",
+ "c517a84e4631a7f65ace170d1e5c2fdb259841535d88da323e68c0883e6af7b041cfe05908815a5a9d1b14fa712c2c16fadcf1ca54d3aa954d411240df331b2aebdfb65aced84d0b8aace56ec0aa7c13ec7d75ca883b6bcf6db74c9e98463c484a8262684f29910373430651f90ecffe18b072170e61ee58de20e2a6ff67b3ab00fccbb80af943f20b56b98107",
+ "d1a56a5ee990e02b84b5862fde62f69ec07567be2d7ccb769a461c4989d11fdda6c945d942fb8b2da795ed97e43a5b7dbdde7f8fd2ff7154544336d5c50fb7380341e660d4898c7fbc39b2b782f28defac6873523c7c1de8e52c65e4395c686ba483c35a220b0416d46357a063fa4c33fa9c52d5c207a1304ae141c791e62ba6a7374ed922b8dd94079b72b69302",
+ "4720b88d6bfb1ab43958e26827730d852d9ec30173ebd0fe0d273edcece2e788558984cd9306fe5978086a5cb6d37975755d2a3daeb16f99a8a11544b8247a8b7ed5587afc5bea1daf85dcea5703c5905cf56ae7cc76408ccabb8fcc25cacc5ff456db3f62fa559c45b9c71505eb5073df1f10fc4c9060843f0cd68bbb4e8edfb48d0fd81d9c21e53b28a2aae4f7ba",
+ "f4639b511db9e092823d47d2947efacbaae0e5b912dec3b284d2350b9262f3a51796a0cd9f8bc5a65879d6578ec24a060e293100c2e12ad82d5b2a0e9d22965858030e7cdf2ab3562bfa8ac084c6e8237aa22f54b94c4e92d69f22169ced6c85a293f5e16bfc326153bf629cdd6393675c6627cd949cd367eef02e0f54779f4d5210197698e4754a5fe490a3a7521c1c",
+ "3d9e7a860a718565e3670c29079ce80e381969fea91017cfd5952e0d8a4a79bb08e2cd1e26161f30ee03a24891d1bfa8c212861b51618d07429fb48000ff87ef09c6fca526567777e9c076d58a642d5c521b1caa5fb0fb3a4b8982dc14a444732b72b239b8f01fc8ba8ee86b3013b5d3e98a92b2aeaecd4879fca5d5e9e0bd880dbfffa6f96f94f3998812aac6a714f331",
+ "4d9bf551d7fd531e7482e2ec875c0651b0bcc6caa738f7497befd11e67ae0e036c9d7ae4301cc3c7906f0d0e1ed4738753f414f9b3cd9b8a71176e325c4c74ce020680ecbfb146889597f5b40487e93f974cd866817fb9fb24c7c7c16177e6e120bfe349e83aa82ba40e59e917565788658a2b254f25cf99bc65070b3794cea2259eb10e42bb54852cba3110baa773dcd70c",
+ "b91f65ab5bc059bfa5b43b6ebae243b1c46826f3da061338b5af02b2da76bb5ebad2b426de3c3134a633499c7c36a120369727cb48a0c6cbab0acecdda137057159aa117a5d687c4286868f561a272e0c18966b2fec3e55d75abea818ce2d339e26adc005c2658493fe06271ad0cc33fcb25065e6a2a286af45a518aee5e2532f81ec9256f93ff2d0d41c9b9a2efdb1a2af899",
+ "736f6e387acb9acbee026a6080f8a9eb8dbb5d7c54ac7053ce75dd184b2cb7b942e22a3497419ddb3a04cf9e4eb9340a1a6f9474c06ee1dcfc8513979fee1fc4768087617fd424f4d65f54782c787a1d2de6efc81534343e855f20b3f3589027a5436201eee747d45b9b8375e4294d72ab6a52e04dfbb2914db92ee58f134b026527ed52d4f794459e02a43a17b0d51ea69bd7f3",
+ "9242d3eb31d26d923b99d66954cfade94f25a18912e6356810b63b971ae74bb53bc58b3c01424208ea1e0b1499936daea27e63d904f9ed65fdf69de40780a3027b2e89d94bdf214f585472613ce328f628f4f0d56217dfb53db5f7a07f54c8d71db16e27de7cdb8d23988837b49b65c12f1771d979e8b192c9f4a16b8d9fba917bcf74ce5a82aac2075608ba6c2d485fa59864b9de",
+ "5da68704f4b592d41f08aca08f62d85e2e2466e5f3be010315d11d113db674c4b98764a509a2f5aacc7ae72c9deff2bcc42810b47f64d429b35745b9efff0b18c58653461e968aaa3c2c7fc455bc5771a8f10cd184be831040df767201ab8d32cb9a58c89afbebecb524502c9b940c1b838f8361bbcde90d272715017f67609ea39b20fac985332d82daaa023999e3f8bfa5f3758bb8",
+ "71ea2af9c8ac2e5ae44a176662882e01027ca3cdb41ec2c6785606a07d7231cd4a2bded7155c2feef3d44d8fd42afa73265cef826f6e03aa761c5c51d5b1f129ddc27503ff50d9c2d748322df4b13dd5cdc7d46381528ab22b79b0049011e4d2e57fe2735e0d58d8d56e92c75dbeac8c76c4239d7f3f24fb56697593b3e4afa6671d5bbc96c079a1c154fe20212ade67b05d49ceaa7a84",
+ "1d133170582fa4bff59a21953ebbc01bc202d43cd79c083d1f5c02fa15a43a0f519e36acb710bdabac880f04bc003800641c2487930de9c03c0e0deb347fa815efca0a38c6c5de694db698743bc955581f6a945deec4ae988ef7cdf40498b77796ddea3fae0ea844891ab751c7ee20917c5a4af53cd4ebd82170078f41ada2795e6eea17593fa90cbf5290a1095e299fc7f507f360f187cd",
+ "5ec4ac45d48fc15c72471d795066bdf8e99a483d5fdd599511b9cdc408de7c0616491b73924d0266da34a495331a935c4b8884f57d7ad8cce4cbe586875aa52482215ed39d7626cce55d50349c7767981c8bd6890f132a196184247343566fc972b86fe3c5369d6a6519e9f07942f0522b77ad01c751dcf7defe31e471a0ec00963765dd8518144a3b8c3c978ad108056516a25dbe3092e73c",
+ "0d5e74b78290c689f2b3cfea45fc9b6a84c822639cd438a7f05c07c374adced42cdc12d2a9233a4ffe80307efc1ac13cb04300e165f8d90dd01c0ea955e7657332c6e86ad6b43e78ba4c13c675aed83192d8427866fb6484e6a3071b2369a46fba9005f31232da7ffec7952f831aaaddf63e225263531c2cf387f8cc14fa856c8795137142c3a52ffa69b8e30ebc88ce3bbc227597bcc8dddd89",
+ "a0fe36f983259921dc2fa7d89002b3066241d63bfc2448caf7e10522a35562be0bfedc3dce49cfce2e614a04d4c64cfc0ab898873a7fc26928dc1927c009d12f6f9b7a278205d3d0057604f4ac746f8b9287c3bc6b929832bf253b6586192ac43fdd29ba585dbd9059aab9c6ff6000a7867c67fec1457b733f6b620881166b8fed92bc8d84f0426002e7be7fcd6ee0abf3755e2babfe5636ca0b37",
+ "1d29b6d8eca793bb801becf90b7d7de215b17618ec32340da4bac707cdbb58b951d5036ec02e105d83b5960e2a72002d19b7fa8e1128cc7c5049ed1f76b82a59eac6ed09e56eb73d9ade38a6739f0e07155afa6ec0d9f5cf13c4b30f5f9a465b162a9c3ba04b5a0b3363c2a63f13f2a3b57c590ec6aa7f64f4dcf7f1582d0ca157eb3b3e53b20e306b1f24e9bda87397d413f01b453ceffeca1fb1e7",
+ "6a2860c110cd0fc5a19bcaafcd30762ee10242d34739638e716bd89fd537ea4dc630e6f85d1bd88a25ad3892ca554c232c9830bd56980c9f08d378d28f7fa6fa7df4fcbf6ad98b1adfff3ec1f63310e50f920c99a5200b8e64c2c2ca249399a149942261f737d5d72da949e914c024d57c4b639cb89990fed2b38a37e5bcd24d17ca12dfcd36ce04691fd03c32f6ed5de2a2191ed7c826375ba81f78d0",
+ "7132aa291ddc9210c60dbe7eb3c19f9053f2dd74742cf57fdc5df98312adbf4710a73245de4a0c3b24e21ab8b466a77ae29d15500d5142555ef3088cbccbe685ed9119a10755148f0b9f0dbcf02b2b9bcadc8517c88346ea4e78285e9cbab122f824cc18faf53b742a87c008bb6aa47eed8e1c8709b8c2b9adb4cc4f07fb423e5830a8e503ab4f7945a2a02ab0a019b65d4fd71dc364d07bdc6e637990e3",
+ "3e664da330f2c6007bff0d5101d88288aaacd3c07913c09e871cce16e55a39fde1ce4db6b8379977c46cce08983ca686778afe0a77a41baf447854b9aa286c398c2b83c95a127b053101b6799c1638e5efd67273b2618df6ec0b96d8d040e8c1ee01a99b9b5c8fe63fea2f749e6c90d31f6fae4e1469ac09884c4fe1a8539acb313f42c941224a0e79c059e18affc2bcb6724975c436f7bf949ebdd8aef51c",
+ "7a6ea63a271eb49470f5ce77519ed61ae9b2f1be07a96855726bc3df1d0723af3a703fdfc2e739c9d31d25814daf661a23558b50982e66ee37ad880f5c8f11c8130fac8a5d0250583700d5a324894fae6d61993f6bf9327214f8674649f355b23fd634940b2c467973a839e659169c773119919f5b81ee171edb2e5f6940d7551f9e5a70625d9ea88711ad0ed8ab2da720ad358bef954456cb2d5636425717c2",
+ "c5106bbda114168c449172e49590c7eeb827fa4e1a2a7a87a3c1f721a9047d0c0a50fbf244731be1b7eb1a2ef30f5ae846a9f38f0df44f32af61b68dbdcd0226e741dfb6ef81a2503691af5e4b3171f48c59ba4ef91eba344b5b697f261df7bbbb734ca6e6daebaa4a179feb17002823281b8534d55a6531c59305f6e3fd3fa63b747bcf0deb654c392a02fe687a269effb1238f38bcaea6b208b221c45fe7fbe7",
+ "597716a5ebeebc4bf524c15518816f0b5dcda39cc833c3d66b6368ce39f3fd02ceba8d12072bfe6137c68d3acd50c849873150928b320b4fbc31c1456679ea1d0acaeeabf666d1f1bad3e6b9312c5cbdecf9b799d3e30b0316bed5f41245107b693366accc8b2bcef2a6be54209ffabc0bb6f93377abdcd57d1b25a89e046f16d8fd00f99d1c0cd247aafa72234386ae484510c084ee609f08aad32a005a0a5710cb",
+ "0771ffe789f4135704b6970b617bae41666bc9a6939d47bd04282e140d5a861c44cf05e0aa57190f5b02e298f1431265a365d29e3127d6fccd86ec0df600e26bcdda2d8f487d2e4b38fbb20f1667591f9b5730930788f2691b9ee1564829d1ada15fffc53e785e0c5e5dd11705a5a71e390ca66f4a592785be188fefe89b4bd085b2024b22a210cb7f4a71c2ad215f082ec63746c7367c22aedb5601f513d9f1ffc1f3",
+ "be6556c94313739c115895a7bad2b620c0708e24f0390daa55521c31d2c6782acf41156271238885c367a57c72b4fe999c160e804ad58d8e565edbce14a2dd90e443eb80626b3eab9d7ab75d6f8a062d7ca89b7af8eb292c98eaf87ad1dfd0db103d1bb6188bd7e7a63502153cf3ce23d43b60c5782602bac8ad92fb2324f5a79453898c5de18415639ecc5c7974d3077f76fc1df5b956723bb19a624d7ea3ec13ba3d86",
+ "4bc33729f14cd2f1dc2ff459abee8f6860dda1062845e4adab78b53c835d106bdfa35dd9e77219eaef403d4e80488ca6bd1c93dd76ef9d543fbb7c8904dccc5f71509a6214f73d0f4e467c3e038ea639b29e7fc442ee29f57117740576188ada15a739827c647a46b0271817ab235c023c30c90f2115e5c90cd8501e7b286962fc66ffc3fe7e8978746168314908a41998bd83a1eeffda9d714b864f4d490fdeb9c7a6edfa",
+ "ab12faea205b3d3a803cf6cb32b9698c32301a1e7f7c6c23a20174c95e98b7c3cfe93fffb3c970face8f5751312a261741141b948d777b8a2ea286fe69fc8ac84d34116a4674bb09a1a0b6af90a748e511749de4697908f4acb22be08e96ebc58ab1690acf73914286c198a2b57f1dd70ea8a52325d3045b8bdfe9a09792521526b7564a2a5fcd01e291f1f8894017ce7d3e8a5dba15332fb410fcfc8d62195a48a9e7c86fc4",
+ "7d421e59a567af70594757a49809a9c22e07fe14061090b9a041875bb77933deae36c823a9b47044fa0599187c75426b6b5ed94982ab1af7882d9e952eca399ee80a8903c4bc8ebe7a0fb035b6b26a2a013536e57fa9c94b16f8c2753c9dd79fb568f638966b06da81ce87cd77ac0793b7a36c45b8687c995bf4414d28289dbee977e77bf05d931b4feaa359a397ca41be529910077c8d498e0e8fb06e8e660cc6ebf07b77a02f",
+ "0c18ab727725d62fd3a2714b7185c09faca130438eff1675b38beca7f93a6962d7b98cb300ea33067a2035cdd694348784aa2eda2f16c731eca119a050d3b3ce7d5c0fd6c234354a1da98c0642451922f670984d035f8c6f35031d6188bbeb31a95e99e21b26f6eb5e2af3c7f8eea426357b3b5f83e0029f4c4732bca366c9aa625748297f039327c276cd8d9c9bf692a47af098aa50ca97b99961bef8bc2a7a802e0b8cfdb84319",
+ "92d5909d18a8b2b9971cd1627b461e98a74ba377186a6a9df5bd133635250b300abccb2254cacb775df6d99f7c7d0952653c28e6909b9f9a45adce691f7adc1afffcd9b06e49f775364cc2c62825b9c1a86089080e26b57e732aac98d80d009bfe50df01b95205aa07ed8ec5c873da3b92d00d53af825aa64b3c634c5ece40bff152c331222d3453fd92e0ca17cef19ecb96a6eed4961b627aca48b12fecd091754f770d52ba861546",
+ "802f22e4a388e874927fef24c797408254e03910bab5bf372320207f8067f2b1ea543917d4a27df89f5bf936ba12e04302bde23119533d0976beca9e20cc16b4dbf17a2ddc44b66aba76c61ad59d5e90de02a88327ead0a8b75463a1a68e307a6e2e53ecc1986274b9ee80bc9f3140671d5285bc5fb57b281042a8978a1175900c6073fd7bd740122956602c1aa773dd2896674d0a6beab24454b107f7c847acb31a0d332b4dfc5e3f2f",
+ "3844fe65db11c92fb90bf15e2e0cd216b5b5be91604baf3b84a0ca480e41ecfaca3709b32f8c6e8761406a635b88eec91e075c48799a16ca08f295d9766d74475c47f3f2a274eae8a6ee1d191a7f37ee413a4bf42cad52acd5564a651715ae42ac2cddd52f819c692ecdef52ecb763270322cdca7bd5aef71428fa73e844568b96b43c89bf1ed42a0abf209ffad0eeec286c6f141e8af073ba4adfbbdeda253752ae36c9957dfc905b4c49",
+ "329377f7bf3c8d74991a7d61b0cf39baff5d485d79751b0d5ad017d23bec570fb19810105bab79ab5acb102ab972165224d4ec888ec7de5148077fa9c1bb6820e0d91ae4e2591a21fec2f820606ce4bafc1e377f8dc3a5bd1a9e2772a57abccd0b757164d768872c91d02789545ab5b203f688d71dd08522a3fd2f5bcd7df507aebf1ca27ddff0a82afb7aa9c180008f49d1325adf97d047e77238fc75f56356de4e87d8c961575c9f6362c9",
+ "f7f269929b0d71ea8eef7120e55ccba691c582dd534692abef35c0fe9dec7dae973cd9702e5ad420d278fe0e653fdcb22fdcb63148109ec7e94f2d0750b28157dd1764376ae10fdb0a4aef3b304bd82793e0595f941226a2d72abbc929f53134dc495b0d65ced409914f94c2523f3dfbbdeeac84ae247ab5d1b9ea33dce1a808885a55be1f3683b46f4be73d9b62eec2585f690056858dfc427aabf591cd276724885bcd4c00b93bb51fb7484d",
+ "ac022309aa2c4d7fb628255b8b7fb4c3e3ae64b1cb65e0de711a6def1653d95d8088871cb8905fe8ae76423604988a8f77589f3f776dc1e4b30dbe9dd262b2187db02518a132d219bd1a06ebac13132b5164b6c420b37dd2ccee7d69b3b7fa12e54f0a53b853d490a68379ea1fa2d79762830ffb71bf86aab506b51f85c4b6a41b69325c7d0c7aa85b93b7144489d213e8f33dbb879fce22849865337b620b155cb2d2d36a68832889e30194d36d",
+ "d009c2b78a8f02e5e5dbb586ef71fc324b375092e15913ca1a5bfd22d516baadb96867bee3562e77c4a4852344a1a76c30728be5e22400b4cc41711f66754c246a520498d8c24f0205b9c873748dbeb67fe1ad099ad04cf89f4b517f0aa481136d9f6de2d727df01c6aa4099da59d4382b51e25fd47c33d9842c32b62331e50794bfe8b61b3ba9de1b8b704779c6d65edff3af00f121ab4a7ea384edabe47c6d0098a48991f387ca4444135ec59d46",
+ "c00bab36cce69899817d1425016d222d7303197ed3e3fdcac744705e7f178a1ac745968900f69299163e19b3161f3e0a4cc55aa2e4e71e0ee6ac427d1f4d14e063f68d303ddfbb18118335cfa7a6a90d99c38319ee76f7a884846a9e0b68030bf28e78bfbd56359b9368842814da42b04cb0e307d5d846dc22f049147bae31b9a956d17676a8cc348dafa3cabc2007a30e730e3894dddf9999fb8819086311f0703e141613ed6dcd7af8510e2dc435b0",
+ "c9789152a9fc29698d49ed95f09bd11b75f18a8c5615a73dbe54ae5e550027fd0ae6a8b60667040c1b12de3d1ee3f6bf061c78c951a3210effc912e19f482dd4de152063c588c44903bc11761706fd935afa040df085b08144d83d0dde32b46ab52f4fae98ac116c7ff11d7f553450c2e37b9c5f0b1dd9e0b8640a24cba6f2a5246c41f197f46e3dc8a29131c79bef3351c6e277a0a34442274d546ccd058891277473d668420f121750d19cd684267405",
+ "06a15a0731ce52557e368bcbaa11ef3399299e36fb9f2eda6e5726907c1d29c5c6fc581405ba48c7e2e522206a8f128d7c1c939d1132a00bd7d6366aa82724e968964eb2e373563f607dfa649590dcf5589114df69da5547fef8d1604cc4c6de1ed5783c8746918a4dd31168d6bc8784cd0c769206bd803d6ca8557b66748770402b075ef44b38157d4c0da7c6281725a2065d087b1f7b23455fa673bdeeba45b983311c44eabe9ef4b7bde3420ae9881863",
+ "d08aacef2d7a41aec09473bd8a44f628e15addb7b9e5b77a1e09c8ab4942f379a0bfcb324d580b774666f18ae78dd36710824ff12393f059068fe4b559c53662c2b0e6c69e23785c8f32554e837ec1714bee902e60737b639dd933af4f68cb9d7de77e1f3b28e5b122891afce62b79acd5b1ab4ba411662cc77d806449e69c5a45a143b742d98ac84a0826d68433b9b700ace6cd472ba2d58a90847f42ce9c43f38ffc017db4bf40450b2eee1f4594dc740c0f",
+ "6a6058b0a498b7ea76a93c646eb9b8629f0cba4a0c726420c5f67ba9b0412cade356abdf0a4fb94384bad32ce0d5dd9e23dcaae1d6f28ff8683616b30f1392890c67b3a2c04b360893b801f127e527e4da82e239f4c878da13f4a4f1c76db07190e77ec123995168102fb274434a2d1e12913b9b5cbab4aacaad2bd89d88b3ca2b8e60dacf7c22c9379097ff60880f552e320ca3b571994f52534470feee2b39e0dadb5cd88257a3e459a4cc6f12f17b8d54e1bb",
+ "adeced01fc5671531cbb45679f5ddd42b3a95151677b6125aaf6f5e8f82fbabaa5ecf7c3552c2458587224f0042870f178f5fca5465250e75d71352e652eeed23cdb7f915f5ebb44099b6db116ca1be45530ac8ed32b7f161d60ed4397ad3d7d649ae6bf75ca5bec891d8e595605be9764f3a03965e1fe0eaffbf212e3df4f0fa35e08ff9d0091e6d4ac4748edfe43b611085a6ffec163014655fdd839fd9e81b63b1fa8cae4ec335ec343289758e389a79ceedfae",
+ "d014592f3a83ba40af366f137c674724916c3cdd3f6cf9d4c5c7c8d6d51ebf26e315e2c12b3546be56fb52382904046ecbd2f5b883aa4ff473de6f0c26ab862c3fa34bf3d880cc1911ce39a4088c6617c179dc5faf68a2c488bbde12d67b50f73abcfab0e3b062e68c95363e11f5f1de8ec36ed01ea21442518089045df67d346135283ad5b3fff80cf57f20876849f6db9fa139728358415a90610f69ec720fc92d8234e3e122551e9df2c644c4a2c4e3734d07de8e",
+ "c0d0c37838873ba8757d6e41b409605043bc1635edcd731219587676d94217e9f0ab44b71de25000661ce7303b7015f45e6eaa7b7ebef92b8f4a34c902c908d2172185505fa33aca5a41be83079316cdfdd430fc2c45f505f85d867e6d516f7e1bf19c001d9f43018968aab65ec031b3801399231c83ec9e622dab5629922a6b424cab938c135ff7310501c2c02971bfd2f577e25904d1a618baf0859f77f4e8b1d0cde9544e95ec52ff710c0672fdb3d891feeea2b017",
+ "7022e7f00902219ba97baa0e940e8ac7727f58955aa068c29680fac4a16bcd812c03eeb5adbcfe867a7f7c6b5d89f4641adb9173b76a1a8438866f9b4f640ce2aedf5f1080c890bcf515b4be4e3e512352f1e5323c62ec46cb73f3d71be8235fee55a154763f7c3f9aeb61ffd28f4cd93d3310f608e2133586bf1ab3f102de96f64c68a4668de8acb2a76a7ce0cddddc8fa3df5e9d230823da16ed9ebb402d36e38e6e018795e5a71517ecab5f9ca472b9ced8ff69d2d195",
+ "acaf4baf3681ab865ab9abfae41697141ead9d5e98523c2e0e1eeb6373dd15405242a3393611e19b693cabaa4e45ac866cc66663a6e898dc73095a4132d43fb78ff7166724f06562fc6c546c78f2d5087467fcfb780478ec871ac38d9516c2f62bdb66c00218747e959b24f1f1795fafe39ee4109a1f84e3f82e96436a3f8e2c74ef1a665b0daaa459c7a80757b52c905e2fb4e30c4a3f882e87bce35d70e2925a1671205c28c89886a49e045e31434abaab4a7aed077ff22c",
+ "84cb6ec8a2da4f6c3b15edf77f9af9e44e13d67acc17b24bd4c7a33980f37050c0301ba3aa15ad92efe842cd3ebd3636cf945bb1f199fe0682037b9dacf86f162dadabfa625239c37f8b8db9901df0e618ff56fa62a57499f7ba83baebc085eaf3dda850835520344a67e09419368d81012168e5de5ea45158397af9a5c6a1657b26f319b66f816cd2c28996547d697e8df2bb163ccb9dda4d6691dffd102a13667ab9cde60ffbfb872187d9c425a7f67c1d9fffff9276ed0aeb",
+ "6a52c9bbbba454c14540b2be58230d78ecbeb391646a0c6fcce2f789086a78364b81ae85d5396d7cfa8b46bda41e3083ec5cf7b4c47dc601c8a697df52f557defca248506dbebab25657f5a561d09625b7f4b2f0119a12beeac087efc9d350a735c35d2431c1da7dda99befb17f41a3dc4da0f00bb95366be128538ce27763d81f832fe3c1d4efc07b5b08ad8dc9e65fb5e48546664e18cb2d3bb3fe1f56fa7aae718c5e3bbdeaf70e15023f6a25b72a2d177fcfd04211d40664fe",
+ "c3c4d3b31f1f5f9538923df3478c84fffaef411520a542da9a220ee4132eabb9d718b5076fb2f985485e8ba058330aed27ddfd3afa3db34aa60301088caec3d0053828c0c2bc87e2e61db5ea5a29f62fdad9c8b5fc5063ec4ee865e5b2e35fac0c7a835d5f57a1b1079833c25fc38fcb14311c54f8a3bd251bca19342d69e5785f9c2e43cf189d421c76c8e8db925d70fa0fae5ee3a28c4047c23a2b8a167ce53f35ced33bec822b88b06f41558c47d4fed1bfa3e21eb060df4d8ba1",
+ "8d55e92136992ba23856c1aea109766fc44772477efc932b3194af2265e433ed77d63b44d2a1cff2e8680eff120a430fe012f0f09c6201d546e13ad46fc4ce910eab27bb1569879abed2d9c37fae9f1267c2216ec5debcb20d4de58461a621e6ce8946899de81c0add44d35e27b7982a97f2a5e6314901caebe41dbba35f48bc9244ca6dca2bdde7306435892f287036df088633a070c2e385815ab3e2bfc1a47c05a5b9fe0e80dd6e38e4713a70c8f82bd32475eea8400c7bc67f59cf",
+ "5016284e20362610fa05ca9d789cad25f6d43263787e7e085476764ce4a8908ce99b262b375e9d106170b1bec1f473d5e777e0c1896533040e39c8c1465e07907ef5860e14e4d8310013e35f12090e0bfc687474b1f15f3dd2033a0edac5246102da4deec7e188c3517d84d9c2a0a4497a4c5f82a30f1ba009e45ee6eb3ab4368c720ea6feee428ffd2c4cc52debb8d634a64176572c72368f94a66689f23f8a01218f532117af5a8060d140e7ca435a92882fcb5630ebe14a4805f1dc83",
+ "05456ec59b8d41bbd736727976b96b38c43827f9e16169be673ff37870c2ecd5f0d1ea1a136be4cc7b047a02a4421d484fd2a12ece418e42ee391a13a0b1df5a0162b29ab70d3fe3e04ba6ab26b37d62b7cf05a5e2f033611bf970b8e1f30e198e483e740fa9618c1e8677e07b61296b94a9787a68fba622d7653b5568f4a8628025939b0f74389ea8fced6098c065bf2a869fd8e07d705eadb53006be2abb716a3114ceb0236d7e916f037cb954cf977720855d12be76d900ca124a2a66bb",
+ "eb6f60b83fcee77060ff346aaf6ec34d82a8af469947d3b5074cde8eb26566eb1fa039bcc707738df1e95869bd827c246e88436f0614d9834ead5392ef376105c4a9f370071cdeaaff6ca0f18b74c3a48d19a717253c49bd9009ccbfdd5728a08b7d112a2ed8dbafbbb46d7a75dc9a05e09bfde1a0a92d74a51887f9d123d7896e9f9d0057b660ed7d55454c069d3c5260411db4cdc67e7b74f680d7ac4b9dcc2f8baf72e15e6b3cafebcdf449a6436ed2c398b675f79c644747c57553bf7ea2",
+ "187a88e88514f6c4157c1ba40b442baae1ae563a6c989277443b12a219aa484cb9fa8adbb9a29d429f50155321b15664926317477079c7060dfdaa84c1d74bba78892c34e6f21ad35208d2ae622012401696bff5cd57b6485944b3db7b9071fa5f57fbfb1085d91bb9cff5808d662cdc6c8157249478262c44b7fbc397ed42a4977b202e817717bfccc9f0467294062313f7705251ed09573f16d23429361fada259dfb300369c4198f07341b38e84d02cdb74af5de6aab1fc2026208ea7c418c0",
+ "be31bc96606d0fab007e5caeded2f1c9f747c759777e9b6eef962bed49e45a1d4fc993e279d024915e600865ecb087b960584be18c41114d3c43f92169b9e0e1f85a0ebcd4e196376ccdc920e66103cd3b1c58407d0aafd0e003c4e341a1daddb9f4faba974362a32f35db83384b05ae8e3322d728893861afd8b1c940de5a17f691e763ce4969b6d94f67fb4a0235d100225bd8602f291388f0ca4a568748ad0d6040f1262eac2aede6cd27419bb78a394c1ffad72c262be8c3f9d9619d633e51d0",
+ "4d83d85ca838b4518588f2a90228a4dd18f14dd5b4c012d26298a97d848abbd825d221d02cceb6e8c701b4ad00e1dee4889b5c533e4bb60f1f41a4a61ee5478be2c1b1016c30345afd7a5253668260515e70751f22c8b4022d7fe4877d7bbce90b46531507dd3e89549e7fd58ea28f4cb23d33662bd003c1345ba94cc4b06867f778957901a8c441bee0f3b12e16463a51f7e50690356971dd73a686a49fda1eae46c9d54fba262811d698025d0ee053f1c58591c3bb3cbde69de0b31549ef5b69cf10",
+ "cdeb07d36dc5f9a1cd717a9e9cca37a2ce93caa298eee63571f7d6c5fde2a11c666cf53cf2dcb41ca2ea2319e7230ca68e38c647905928713a13982bf47fe33d7095ebd50b2df976208920a43eb2e29b942f32467403c45cea18bf44e0f6aeb155b48a8e5c471fec972a9d62f7ae093d2758f0aaec7ca50cb4725bfa219f1a3a46ad6bde7361f445f86b94d66b8ece080e56c510250693a5d0ea0ae87b4421860b853bcf0381eae4f1bf7c5c0472a93ad18407bc88475ab8560d344a921d3e86a02da397",
+ "a598fad52852c5d51ae3b10528fc1f722e21d44fbd42ae5acdf20e85a28532e646a223d27fd907bfd38eb8bb75175636892f8242877aab89e8c0824d368f3339ce7a82aa4e5af6db1f3b588a4d667a00f67bee37cfd2724dde06d2909fb9e58d892f4cfd2c4ca85acdf8256f5458b030a6bda151154ff2e6d7a8da90b54a2884c8a99fab5a4ac211ff23dc0975f4f592fd1b6b9dc7783bdcd2d4ca4e68d2902f2013e122cb62e2bff6b0a98ec55ba25837e21f1cfe67739b568d43e6413dab2bd1dc471e5a",
+ "17b68c74c9fe4926e8102070916a4e381b9fe25f5973c9bd4b04ce25749fc18931f37a65a356d3f5e5a1ef125d546f4f0ea797c15fb2efea6fbfcc5739c564693d47adeb12dcb3d98a2830719b13247792cb2491dca159a28138c6cff925aca42f4fdb02e73fbd508ec49b25c60703a7595a3e8f44b155b371d525e48e7e5dc84ac7b17c52bf5e526a67e7187234a2f19f57c548c70fc0b27183df73ffa53fa58b658034c896fa791ae9a7fd2620f5e46ce84c842a6e60e9324ae4db224ffc87d9617cb85ca2",
+ "b9e4267ea39e1de1fed0579f93bb351007c9f8fcdd811053fae33f09e2753d7428f04e1a9efcd45ea701a5d87a35b3afb2e6b65365dee6ead0bbb611b7797b212ac688653f542e604a39df277f12514ddfee3b4e27b98395c2cd97a203f1f1153c50327965770802ec2c9783edc428271762b275471e7ac65ac36523df28b0d7e6e6ccc7674268a132a63411fc82c0738dbb68af003b769a0bf9e6587b36476cb465350fee13f88ea355d47ffac7b0f964f4139db11b7642cb8d75fe1bc74d859b6d9e884f75ac",
+ "8ca704fe7208fe5f9c23110c0b3b4eee0ef632cae82bda68d8db2436ad409aa05cf159223586e1e6d8bdae9f316ea786809fbe7fe81ec61c61552d3a83cd6beaf652d1263862664df6aae321d0323440430f400f291c3efbe5d5c690b0cc6b0bf871b3933befb40bc870e2ee1ebb68025a2dcc11b68daadef6be29b5f21e440374301bde1e80dcfade4c9d681480e65ec494a6af48df232c3d51447b9d06be714949249c44c43cf73ed13ef0d533e770284e51369d94ae241a5fb2f163893071b2b4c118aeaf9eae",
+ "4fd8dd01012bb4df82bf42e0683f998e6f52dd9c5617bae33f867d6c0b69798cead8179346d70acc941abbbdd26e3229d5651361d2252c72ff22db2938d06ff6fc29a42fdf800ae967d06479bc7bbb8e71f40b1190a4b7189ffc9a7096cdb76d40aec424e1388e1eb7ef4ac3b34f3f089da8fda7d1927f5d775c0b2801d22dd1265c973158f640cec93edfed06dc80b20ef8c496b98289d54d46ccd205951cbb0f4e7daeb866b60bacb483411e4382b6f04d472843186bd0e31fbaa93e5c901ec028efafeb45fc551a",
+ "e9ee1b22b04b321a5fdd8301627011f583887d77560fb0f35552e207561f81e38ac58a0d0aeaf832d1ee72d913720d01f75574e9a321864fe95f4d0d8f0b8db97649a53e71e940aede5c40b4b9105daa42a6fb2811b61209247534cbaf830b07abe338d75d2f5f4eb1c3cf151e9edabe2c8d5f6fff08fac1495ef48160b100d30dcb0676700bcceb28723a29980ab0766a93abb8cb3d1963007db8458ed99b689d2a7c28c788743c80e8c1239b20982c81dadd0eed6740c65fbc4ef15c7b5569cb9fc997c6550a34b3b2",
+ "ec01e3a60964360f7f23ab0b22e021815765ad706f242265ebc19a2bb9e4eac94393952dcf61aae47682671a10f9165f0b20adf83a6706bfbdcf04c6faba6114653a35584267267873291c6fe7ff5f7695243143421509502c8875aafa9e9afe5be5ef2c851c7f35d69be5d3896000ccdbbfab5c238bb34d607cfe2d55d748880545b4aa7ca61137992925189025c62654b1f20d49c3ccd75aa73ce99cd7258dabedd6480a9f5185531fc0118beb68cc0a9cd182f6973287cf9252e12be5b619f15c25b65c71b7a316ebfd",
+ "db51a2f84704b78414093aa93708ec5e78573595c6e3a16c9e15744fa0f98ec78a1b3ed1e16f9717c01f6cab1bff0d56367ffc516c2e33261074935e0735ccf0d018744b4d28450f9a4db0dcf7ff504d3183aa967f76a507357948da9018fc38f150db53e2df6cea14466f03792f8bc11bdb5266dd6d508cde9e12ff04305c0295de29de19d491ad86e766774bb517e7e65befb1c5e2c267f013e235d8483e177214f89978b4cdc81aa7eff8b39f2825ad3a1b6ac1424e30edd49b067d770f16e74dd7a9c3af2ad74289a676",
+ "00e40f30ae3746edad0f5dd03d0e640933cf3d1694804c1e1ed6399ac36611d405196ee48f129344a8512feda16a354517871322bd5d9c6a1b592933eab531923efb393ffb23d9109cbe1075cebfa5fb917b40df028a621460ff6783c798792cb1d9635b5a6f84ec13918fa302924649b5c7fcb1f7007f0d2f06e9cfd7c27491e565a96c68a0c3644f92cd8f38857258c33801c5d537a83dfe583cba59d7eec7e394199c0a2660a62fabe3ed2099d57f315a6cd8de1a4ade29d977f15d65759cff433e5ac0c182aef3761163e1",
+ "3c5ea24d0d9b618294a263f062b2414a722be4eb10dfc346a6ec3b821d7396eba61cd6ef33618b04cd087a811f299d4606820227f16000d7c839062b96d3e3f59cd1a082448d13fc8f56b3fa7fb5f66d0350aa3b72dd7c165d590282f7da2e12cfe9e60e1796122bb8c2d40fdc2997af634b9c6b127a893dfb3467909378300db3da911be1d7b616bb8e0572433e65527e15d936500a2c60e9f9909dcf22ab5e4b6700f0238c205b4a813626fac3d945bab2637fb08203044a73d20c9a3fcf7c3fc4eb7807c3276dd5f73ce89597",
+ "9271aeeebfac46f4de85df78f1bfd36136aa8905e15835c9e1941176f71e3aa5b1b131843d40479735e23e182a2bd71f66f6149dccb7ed8c16469079dc8590bbf165374951785f4531f7e7361de62f936cfb23a2b5bdf186632e7042a0dd451fdc9b7208f923f3a5f250ae590ec348c63a16c3aacaf7379f53b5dd4152dcd40d23e683e2156e64c592ffc07e2cd6bbeebef4dd590b2f6b2bcbf08fcd111c079f5c4033adb6c17574f8756ecd87be27eff1d7c8e8d0324438d59ae171d5a17128fbcb5533d921bd044a2038a5046b33",
+ "4e3e533d5bcb15793d1b9d0468aaee801f32fdb486b11027183553a09ddbee8213924296f2815dc61577297459e834bf1c7a53f87d43782209e589b8295219ba7073a8fff18ad647fdb474fa39e1faa69911bf83438d5f64fe52f38ce6a991f25812c8f548de7bf2fdea7e9b4782beb4011d3567184c817521a2ba0ebad75b892f7f8e35d68b099827a1b08a84ec5e8125651d6f260295684d0ab1011a9209d2bdeb75128bf5364774d7df91e0746b7b08bda9185035f4f226e7d0a1946fcaa9c607a66b185d8546aac2800e85b74e67",
+ "b5d89fa2d94531093365d1259cc6fe8827fea48e6374c8b9a8c4d2209c280fa5c44958a1847222a692a59e6aa2696e6cdc8a543dd89b0ce03bc293b4e78d6ef48e1839694ccd5c65661143095c705b07e3ced84a0f5959114dd89deb956ab3fac8130eb4a878278205b801ae41a29e34146192308c4e759b374757b0c3b00319bce92a1b95a4d2ee179fd6714ff96155d26f693a5bc973f84ac8b3b91e3926276297532d98b46992a3f104c08100bf1671c43134bac280c617da711e90a0100137525375ebb12802a428885ae7fce6514a",
+ "40e3d8048fc10650cb8a7fc2e7113e26dec34f9ca2d5129cd10a8e8e44d113d61ee48c7d003e19fd307fc6debd70feb30243f298c510ccc4418355ce143066f067ad7c6de7288c3080e7ad46a23c8d34deb55a43e652fe90444ad3c57d3ec1e1c489d63ef915a24bc74a7925a0a7b1e1523f21ca8fee78df24e3d0a68d0013423db97c280799a0618229c0f2c167289a891e5c8d6661ab21285951c31710e3b5fe55f6347fe16d9b40507948a59252efeb616df83e5c098b07d0a7247cd371daff0e50491c582503fd89f79ba94d6af9ed76",
+ "1fa444de01dd3901e2b4684e3d7a799ffa02d85afd35fb30fe4c9d672837bee6dd8a3b8608b4bb5e589220ad5a854f46b46e41c6d57ad124a46beab4169ff69fee7e3838a6165e19dad8eb5d7bf53d4edd3cd2769daf219510a02fdd2afe0c0e1da3cd30fcd1aa88b68965586f07a25a1720fbd90a096ea30fc8e945e3637d7857c8a9c0ab4154ffb2000e57b5f9adfa4e4eaf8065bc3c2b2e75f495963325588785a6ce417dcddffd299873b15dcccca128d63cd4eeeadb64cda28099a9ad7c80d34844901f26b88b00b9aafeb2f90286d29d",
+ "fde0a0d9d813983bd1f55cf778a003a2023b34a555322ab280584537bc6bdd844d22a7d6066c18da83ec09f3d8d5a1aab4be0d5ce19b436052f6e259a4b49017a1f47f1fe2bf115d5bc8599fb216351c60dd6b1bedb2e6f4dcadf424b833501b6f099cbfad9e2290680fb69c25032b42a6274f7cb9b5c5950401354838a45f7cb77b95bf54718e2f3d3d9fb91eb2311903980277396398d9736d8e92fd838594ac8a537c6c529db5a8a4f89290e6ba6f20ac0e5ed6fef40901d0e0e8e3e502990811f9acaae555dd54eb1bcd96b513e2fe751bec",
+ "9f8e0caec87858599f5ab29bff86da78a841a918a023a111098687ecdf2747612d3f3809d9ca400b878bd4f92c43a1004f1c17c7f19a3cd1ce449bd2b23aff551623c37dd8c0be56bf3fd857b500c2b9f9ccea62481944090a3cf3b6ee81d9af8eeb60f65ef150f9fa4d3ed6ce4762d3d4f174ee8ccd460c25cafac0ea5ec8a6a4b2f9e8c0520cb7061155e532cb65f188b01e4b9086db951f504b060c296b326b3fc1c590498ecce594f828f4a10ea416675720ae505295d38a791bd0e93f428448a8f4c1fc0af53604a9e8255384d29ae5c334e2",
+ "33d1e683a4c97ee6bbaa5f9df1a88cb53b7f3c157b6045d70a56fda0ccbd3a1fa1f049cd564da072b53f415bf5fb843771c1d2551fd075d33377362b2f7c0645f9723123d11975991db8a2b518f02e2c7c30342a044754290bae2c77496d755e5981f12e6b0a0174280b958bf11ed628a9062775993ced04bf752ea8d165e3ac2177d7cd1b9371c44efa98f0b3e68602a839d384eec007979f46429dafb138cbc231ad928a9f65f7d66fac77416395e8f1debaaf76ec2e4e03e8674102cd26f614739f3ec9f949033df1fb97e87c2326d65aef94ed5f",
+ "180048f09d0b480887af7fd548a85abf605440c1ddde6afe4c30c30670233f7bf928f43b4681f59279ebbda5e8f8f2a1abefdee129e18ac60f9224e90b38b0aabd01308e0a27f41b6fb2ee07ee176ec9048c5fe33c3f7c791469c81f30e28170585b9f3e7e3c8c2e9d74370cb4518f13bf2dee048cbd98ffa32d85e43bcc64a626b40efb51ce712925fdd6fee006dc68b88004a81549d2121986dd1966084cd654a7c6686b3bae32afbd9625e09344e85cf9611ea08dfce835a2e5b3726e69ae8a76a97db60fcc539944ba4b1e8449e4d9802ae99fae86",
+ "13c0bc2f5eb887cd90eae426143764cf82b3545998c386007cca871890912217aa143ac4ed4ddb5a7495b704aa4de18419b8664b15bc26cfc6596a4d2ae408f98b47a566476d5802d594ba84c2f538def9d016661f6404bb2337a3932a24f6e30073a6c9c274b940c62c727242e24466084a3ea336365d71ea8fa6499c0ea8d59eea505f1126b99c795023c4963aa0d99323d0391e8701110edf551b2d3799e1063ca443f1add162156e445502ca1a052fe70c289838593b58839fc63de128a03e2bbf389e22ae0cf957fd03315ee407b096cc1cfd92dee6",
+ "6f1eb607d679efef065df08987a1174aab41bdac8aece7726dfa65805d6fff5b3d17a672d96b770dc32165f144f0f7324822a5c87563b7cd9e37a742ae83ef245d09006d91576f435a03476f509ea2936636232f66aa7f6cdf1ac187bbd1fcb8e20f8791866e60ed96c73374c12ac16795e999b891c64507d2dbd97e5fc29fac750ad27f2937cbcd29fdafccf27ab22453834d475f6186eaf975a36fad5c8bd61c21da554e1ded46c4c39765dcf5c8f5ccfb49b6a4dc562c919d0c7d8940ec536ab2448ec3c9a9c8b0e8fd4870cad9de2577c7b0c38563f355",
+ "dcdd993c94d3acbc555f464871a32c5da6f13b3d5bbc3e34429705e8ad2e76393fdd96a69a94acb652f5dc3c120d41187e9aa919669f727c4868013b0cb6acc165c1b7706c52248e15c3bf81eb6c147619467945c7c48fa14a73e7c3d5bec91706c567145342a026c9d97eff97ec672c5debb9df1a998083b0b0081d65c517b3e5634c95e347e781aa30ca1c8af815e2e494d844e847fdcb41622894a518dc36571123a40bfdbe8c4f4cff44d83c61dd9dcd24c464c53b395edb31efee9f3aa080e87cdc3d22d613ae84a53c9249c32c96f9a3bc4629bb126a70",
+ "49971f9823e63c3a72574d977953329e813b22a8387cd13f56d8ea77a5d1a8a20012632d1d8732bbcb9f756b9675aab5db927beacab7ca263e5718b8dfa7b2eed9a91bf5ed163b16139d45f7b8cc7e3f7bdda6202106f67dfb23b7c315ee3e17a09d466b1e6b13e7c7428184a979f5358667b4fa8bd40bcc8ea46058db44587a85377ac46bf155136c09ac58cb6c27f28e17028c91e7e8f74d5b500e56293b316974f02b9d9ea205d9b6ac4cfb74eb8eb0c944577fd2f41316368307beab3e327bf7dbaa0a4428836ec4e895dea635234abeaf113ceeadac33c7a3",
+ "c57a9cc958cee983599b04fe694f15fb470fcbc53e4bfcc00a27351b12d5d2434444253ad4184e87b81b738922ffd7ff1dc1e54f39c5518b49fb8fe50d63e3935f99e4bd125e8dc0ba8a17fd62de709339a43fabe15cf86d96a54010112170c340cfac4132182eed7301402bc7c8276089dec38488af145cb6222525894658f03501204b7a66aba0be1b557b28a2f652d66f7313ed825ecc4d8596c1be7420d4425b86a1a90a5b7f30d0f24e0d1aae0eb619ca457a71699e44be612a4011c597ee80b94d5507e429d7fc6af22579cd6ad642723b05ef169fade526fb",
+ "0568a672cd1ecbaa947045b712e2ac27995392fbef8f9488f79803cbee561c212287f080eca95adb5ba42739d78e3ba667f06045d87850d3a0499358649caa257ad29f1a9c511e7054db20554d15cbb55ff854afa45cae475c729cea72ede953522031865bc02b95589ed4d9841c552a8cc94904a93ed09ed77222f6c178195056be59bc4e96a815adf534e6b466fb47e262ff79c803c157a21b6e2269c2e0abeb494113cd868d8466e82d4b2f6a28b73645853d96bc9242515d803e33294848d3fe42fdff68da53c03491636beede47ff1399dd3d54a5e914d55d7adf",
+ "3f19f61a4cd085796731ac9f85a75a8bce77031932c31762d87d8b8d07b8bd19ff78d6b7d1bd1e87f3a4f41aad03b6c4d17a6cbc86be55f7c8b88ada047bb04f8d49f1c34bcf81cc0f3389ad01a758fc7eeb0072aa9ad1481992bfdde82e438e75590a4423832dfbe3756e2229ea873bc3606e6d72174cb2163bf40b5d49c81009dab85ecc03e311351bbf96e32c030a2b276a7698cb25bc2c967acb3213161a1fdde7d912cd6a804490f8056c47da1333f6e35c41e749c2c23919cb9af5eec5652e6e072b034fb1682e9aaa194a9c0bd456ea0b008d14dbce37967a7a8e",
+ "705f98f632d99d3651793825c38dc4deda56c59eac539da6a0159c83131cf8ab6f2ee0c3b74111fde351f7aa1a8c500a0cecab17c212d2c58ca09eae608c8eefc922b9902ef8d6832f799ba48c3c28aa702b3242107edeba01daafe424406a3822965056cfe8783455a671e93b1e2eae2321364f1871471c82124df33bc09e1b52882bd7e1c4c7d0b2f3dd4a28c2a002a43246768af0700f9659de99d62167be93177aabf19d678e79e9c726ac510d94e74873eda99620a3961930cd91937c88a06d8153d64fd60da7ca38cf26d1d4f04a0df273f52127c53fdc593f0f8df9",
+ "ea6f8e977c954657b45f25480ff42c36c7a10c77caa26eb1c907062e24fbca5aebc65cacca0de10abea8c78322f08672e13d8ac16996eca1aa17402eaea4c1cc6c800b22dc18cb8d620192d74bac02c07b5cfa61e513c7f28b7e29b9700e0e442720bf4c669d4995da19d19f841d9eb68cc74153592591e3bf059ef616b95305aa453b32fe99a91afb35bd482cf2b7aa42702837a53be3c38883d2963020e347556f841254ec6b85854485fe8c520b05f2ea67a9bf3981555c20991e2bacd4db5b418228b6002d8d41c025cb472bf5443aaa885974a408ea7f2e3f932c600deb",
+ "408190134ed06556811b1af808ab2d986aff152a28de2c41a2207c0ccc18125ac20f48384de89ea7c80cda1da14e60cc1599943646b4c0082bbcda2d9fa55a13e9df2934edf15eb4fd41f25fa3dd706ab6de522ed351b106321e494e7a27d5f7caf44ec6fadf1122d227eefc0f57aefc140d2c63d07dcbfd65790b1099745ed042cfd1548242076b98e616b76ff0d53db5179df8dd62c06a36a8b9e95a671e2a9b9dd3fb187a31ae5828d218ec5851913e0b52e2532bd4bf9e7b349f32de2b6d5d3cdf9f372d49617b6220c93c05962327e99a0480488443349f0fd54c1860f7c8",
+ "5f9e5c6f38573a85010a9d84d33f29c057003b2645e3ea6f72cbc7af95d197ce6a06b13fea81722853e6991791b8b15091cd066f5ed913592ed3d3af5370d39ba22beeb2a582a414b16824b77e194a094c2afdcc09aa73ce36f4943cca5ae32c5017dc398801dd92a47382d9327c9f6cffd38ca4167cd836f7855fc5ff048d8efba378cdde224905a0425e6b1de061fc951c5e624a5153b008ad41160a710b3ff2081748d5e02deb9f841f4fc6cf4a15153dd4fe874fd447482696283e79ee0e6bc8c1c0409baa5ab02c5209c319e3169b2476149c0c6e541c6197ca46e004eef533",
+ "218c6b3508aec69574f2b5039b30b942b72a8349d05f48ff945bbbe5c8957d5a6199492a6bf54bab821c9377e2edfa4c908384664d2c80112d5e805d66e0a551b941021be17dd20bd825bea9a3b6afb1b8c605805b3bda58750f03ea5c953a698494b425d8980c69f34d1c3f6b5866e8717031152a127215c256e08873c21b0f5cc85875d0f7c94601659150c04cd5fe5d381ba29983a2d94fcd3a65a94c53c7279cd000dddd4253d8cff8d7f6ace10247fe3bc30d63ba4bb54f557b3d22a3924369430d71ab37b701e9500bda70b5a643704858beed4726a889b6c9c91584194c68f1",
+ "dac26aa7273fc25d6e044c79fc2bfa46e59892a42bbca59a86826c91e76ab03e4bd9f7c0b5f08d1931d88b36ea77d94f7ba67cd4f1d3086e529427201119096ae066ae6f170940830ed7900de7bb9d66e09788287403a4ecc93c6da975d2fb08e918840a236c15f5d3a8f7375c2eeebbf6f01a6e7f29ca2b8d42df158414c320777433663c59fdcd1f39ca68e3473db721be7ce8c6dba5fddc024f94fedb286b0477581d451313ca8c737484daf60d67f9b2d56d4bcc271f7e9ae958c7f258efbc74d25753e0516f28282461941bf2dcc7dd8c7df6173b89760cefcac07190243ff863fb",
+ "c46e6512e6797cc7a54254a1b26b2de29aa83d6c4b1ea5a2786fbcec388270625b12635eae39e1fba013f8a65219421bca8b52a8ddfd431cda60299bdf160734d5a7450ec79620058522702174ae451b9bfa7c4a455fbbee3e1d048c7d4bac5131018228f137c8e130440c7059b4f15eaa34ce872a851a16ce86f982df78a00be4d564da2003a450ddee9ab43ea876b8b4b65c84f0b39265fd5456417afb5bc54997c986e66fc222f2123ba5e719c4d6b9a177b188277df384f1125821cf19d5248cef0be183ccdc84ac194506f740ed2188b2689ea4c9236a9e9e3a2fff85b6af4e9b49a3",
+ "1ccd4d278d67b65cf2564ecd4de1b55fe07adc80e1f735fe2f08ea53fd3977323689122c29c798957abaff6aba09bdcbf661d77f4dc8913ab1fe2bef38846166e3834785e7105d746484eff8c656af5d8c7854abc1c62b7fadb65521dc6f793d978bda9838eb3800417d32e8a24d8c8cb1d18a5de6ca79d9e1b0ff9aa25e6218fe944cf18666fecc1e31334b390260dbe0997539e1b02f6366b2aea4f4a21efe04f4b97568fcb39e59919d5ebac6543d5d0f48fc66b923c34aac377dc95c20329b837b6ed5e8d9a3d2089cd0d8f025658006ff41cbdaccca618822ca590ab155253f8bc1c7f5",
+ "9875209588395ee3c9fdd793fd48717cc84c8c3ea622b2ccc4a1be4448e6034b7810569855255031f10be5ffd714b05f9ce01972d712d40abf03d4d0ce175813a7a668f761324996093fc2aa5912f7fc2abdadd8775d2b4d9ad492216293381460ed8f6db3d641d1525f4242c348bbfe504c704f215dc461de51b5c75c1aae967936963848f16c673eca5e78dfd47eb19001d52d1bcf96c98956dad5ddf594a5da757e7ca35f2f69803b784e66ac5a58b75c228b8266ec592505e5d1ca87d81225738855f15bc0914677e81593fd409e77d159f8a908f67788de9eb06c5561547aada96c47c535",
+ "40c90e375e366f3756d89091eb3eed9fe0fbfc5638700af4617d358812bac53124a2205dd6756456787d49cd6a35e302479a0992288f47532e4ea7ab62fc5ad5adc690a5d9a446f7e035ad4641bd8dae83946aee3338ec984ccb5cc633e1409f2531eeffe05532a8b0062ba99454c9aeabf8ecb94db195af7032bfebc22912f49d39330add47ff8fa5720612d697f0b602738930e060a1bb214efc5e292224cf34e29deaea6b1b1ff847e94ecc997325ac38df61db45d82bf0e74a664d2fe085c20b04c39e90d6a170b68d2f1d373f00c731c524456ada73d659aaac9df3191a7a3865083343fc13",
+ "e8800d82e072210ca6d7fa2472028974780b76aad4bcb9ad362422dd05ae3232668251d164daa375a43b26a38cce28dbeb3dee1a4a579f70d0fe7febb29b5ece8aa836e050fb3d188c63aa9c3c0da6c717d86458a6096b5effceb964efdec7035960c09ccd10dea3c5f1c7f9f478d5887ebbe2e15c5ff85dbacbc444bb951c4eec7abecb89ed80187e409e2972ffe1a5f01562af109f2cf09471cf72cf83a3bb8f4e2ef38ed0e326b698296394e5b2718a5000c01425708e8ad0461e62462d8819c2377f13ab1be2c7c9f33dc06fe23cad27b87569f2ce2e56e4b2c60c7b1b3d370841d89ebdc1f192",
+ "796d6d1447d5b7e8c55cd8b2f8b7010db39f27565f907e3fc0e464ea2d4bb52b37f10e7c6dcfc59231b9cdee12c32aeb4adbc42b86e86eb6defb5b69e6ca75e1f4d0dae3e124e5a1b8b6697f7e10b0403f1f0a5ff848eef3752837a9ba17780f16a9a709188a8d5b89a2fa74adb2e651163b1c2b3d261e225c9158dcd9eb7ac3d6704cee290cdff6bcb3cb90cee030aa0d19d4693655c3c30ac6fc06d2ae37787c47126d57ed9a6bef5f8a6c56859aefc08755739a95aac57a4dd916a92ba9f3afbf969df8085949615033365c751a9a3e1a18cee98a69d22e64009bebf8307169b6c61de0617ecfafdf",
+ "4f9057183566153cf337b07c3f5556006de54c56b2a1e5326c07aaeabd1886ec6f1641358925db232b2f0dbf75229c796a7395b2f934c1f99090bec1123f3c841b1cb3c5b1ec42ed5408f2940f0c48a9470b852c46d6557853d459cecd2c32bbcd8ee21fa11e385eef0857cba4d8545a61b52a484cdd779db4739fbc7aa9860dcabe0488b98fa0b60c3f7d6153db279000a52ffb573dab37d2ab1896a90e5deb7ac6bbe56239085c325d83a917dc6e8a448425b718c2356b9f3066163555ec444f372e184e02c8c4c69b1c1c2ae2b51e45b98f73d933d18750968945ca85d6bbb22014b4c4015262e3c40d",
+ "79dcca7d8b81a61359e4aece21f3df7b99518ce70bd2f57a18bab5e7114af2add0a0cea7f319d69f231f060e0a539d9a23fb3e95451ce8c6340cfb09edf931df84203a39226dd9eb278f11b691ef612585b973daab373e65d11325898badf6732100371fd759960fa8fec373268421d28bffdb9b12a430b92fe4b07566ca0c89e616e49f8fc75ccd9cdc66db820d7c02e109aa5ed86b89770262918a518f90a2292f6b68d68ae03992e4259a17a23c84ec2a417f082b5abf3a26e44d2278ecb8ba9456965303a75f25394d1aaf5544590e74b14d8a4cc4050be2b0ebcfe4d2db6b12a02c68a3bcdda70301f3",
+ "848755dc31e25e9a42f9ec12d847d19f292c14c162c9aba49e972cb123b58b8e57bb263a923929833373858594ff52dbc298dbbc078599194e4c07b0e5fc1e10808bbacdb6e93c72b333685cf961f28eb0d5a395c63266b01f130d25db384b356e5da6d01042fc2359581b89c63b3bb2d1ce897fbc9e83fe85d9666cb60e6a8c657f70caad5387b8a045bf91095606802c8424ea8ac52ef29386dc46183378a5fcb2cb927428b8c070f1c42aafd3bc70ca25437807696a46873cfeb7b80ba2ebc3c4272443d445e46343a1465253a9eebd532a0d1d2c18264b91ff45159f245404ae9335f2af55c802772426b4",
+ "ecaa6e999ef355a0768730edb835db411829a3764f79d764bb5682af6d00f51b313e017b83fffe2e332cd4a3de0a81d6a52084d5748346a1f81eb9b183ff6d93d05edc00e938d001c90872dfe234e8dd085f639af168af4a07e18f1c56ca6c7c1addffc4a70eb4660666dda0321636c3f83479ad3b64e23d749620413a2ecdcc52ad4e6e63f2b817ce99c15b5d2da3792721d7158297cce65e0c04fe810d7e2434b969e4c7892b3840623e153576356e9a696fd9e7a801c25de621a7849da3f99158d3d09bf039f43c510c8ffb00fa3e9a3c12d2c8062dd25b8dabe53d8581e30427e81c3dfc2d455352487e1255",
+ "23a3fe80e3636313fdf922a1359514d9f31775e1adf24285e8001c04dbce866df055edf25b506e18953492a173ba5aa0c1ec758123406a97025ba9b6b7a97eb14734424d1a7841ec0eaeba0051d6e9734263bea1af9895a3b8c83d8c854da2ae7832bdd7c285b73f8113c3821cced38b3656b4e6369a9f8327cd368f04128f1d78b6b4260f55995277feffa15e34532cd0306c1f47354667c17018ee012a791af2dbbc7afc92c388008c601740cccbbe66f1eb06ea657e9d478066c2bd2093ab62cd94abadc002722f50968e8acf361658fc64f50685a5b1b004888b3b4f64a4ddb67bec7e4ac64c9ee8deeda896b9",
+ "758f3567cd992228386a1c01930f7c52a9dcce28fdc1aaa54b0fed97d9a54f1df805f31bac12d559e90a2063cd7df8311a148f6904f78c5440f75e49877c0c0855d59c7f7ee52837e6ef3e54a568a7b38a0d5b896e298c8e46a56d24d8cabda8aeff85a622a3e7c87483ba921f34156defd185f608e2241224286e38121a162c2ba7604f68484717196f6628861a948180e8f06c6cc1ec66d032cf8d16da039cd74277cde31e535bc1692a44046e16881c954af3cd91dc49b443a3680e4bc42a954a46ebd1368b1398edd7580f935514b15c7fbfa9b40048a35122283af731f5e460aa85b66e65f49a9d158699bd2870",
+ "fe511e86971cea2b6af91b2afa898d9b067fa71780790bb409189f5debe719f405e16acf7c4306a6e6ac5cd535290efe088943b9e6c5d25bfc508023c1b105d20d57252fee8cdbddb4d34a6ec2f72e8d55be55afcafd2e922ab8c31888bec4e816d04f0b2cd23df6e04720969c5152b3563c6da37e4608554cc7b8715bc10aba6a2e3b6fbcd35408df0dd73a9076bfad32b741fcdb0edfb563b3f753508b9b26f0a91673255f9bcda2b9a120f6bfa0632b6551ca517d846a747b66ebda1b2170891ece94c19ce8bf682cc94afdf0053fba4e4f0530935c07cdd6f879c999a8c4328ef6d3e0a37974a230ada83910604337",
+ "a6024f5b959698c0de45f4f29e1803f99dc8112989c536e5a1337e281bc856ff721e986de183d7b0ea9eb61166830ae5d6d6bc857dc833ff189b52889b8e2bd3f35b4937624d9b36dc5f19db44f0772508029784c7dac9568d28609058bc437e2f79f95b12307d8a8fb042d7fd6ee910a9e8df609ede3283f958ba918a9925a0b1d0f9f9f232062315f28a52cbd60e71c09d83e0f6600f508f0ae8ad7642c080ffc618fcd2314e26f67f1529342569f6df37017f7e3b2dac32ad88d56d175ab22205ee7e3ee94720d76933a21132e110fefbb0689a3adbaa4c685f43652136d09b3a359b5c671e38f11915cb5612db2ae294",
+ "af6de0e227bd78494acb559ddf34d8a7d55a03912384831be21c38376f39cda8a864aff7a48aed758f6bdf777779a669068a75ce82a06f6b3325c855ed83daf5513a078a61f7dc6c1622a633367e5f3a33e765c8ec5d8d54f48494006fdbf8922063e5340013e312871b7f8f8e5ea439c0d4cb78e2f19dd11f010729b692c65dd0d347f0ce53de9d849224666ea2f6487f1c6f953e8f9dbfd3d6de291c3e9d045e633cfd83c89d2f2327d0b2f31f72ac1604a3db1febc5f22cad08153278047210cc2894582c251a014c652e3951593e70e52a5d7451be8924b64f85c8247dab6268d24710b39fc1c07b4ac829fbda34ed79b5",
+ "d7314e8b1ff82100b8f5870da62b61c31ab37ace9e6a7b6f7d294571523783c1fdedcbc00dd487dd6f848c34aab493507d07071b5eb59d1a2346068c7f356755fbde3d2cab67514f8c3a12d6ff9f96a977a9ac9263491bd33122a904da5386b943d35a6ba383932df07f259b6b45f69e9b27b4ca124fb3ae143d709853eed86690bc2754d5f8865c355a44b5279d8eb31cdc00f7407fb5f5b34edc57fc7ace943565da2222dc80632ccf42f2f125ceb19714ea964c2e50603c9f8960c3f27c2ed0e18a559931c4352bd7422109a28c5e145003f55c9b7c664fdc985168868950396eaf6fefc7b73d815c1aca721d7c67da632925",
+ "2928b55c0e4d0f5cb4b60af59e9a702e3d616a8cf427c8bb03981fb8c29026d8f7d89161f36c11654f9a5e8ccb703595a58d671ecdc22c6a784abe363158682be4643002a7da5c9d268a30ea9a8d4cc24f562ab59f55c2b43af7dbcecc7e5ebe7494e82d74145a1e7d442125eb0431c5ea0939b27afa47f8ca97849f341f707660c7fbe49b7a0712fbcb6f7562ae2961425f27c7779c7534ecdeb8047ff3cb89a25159f3e1cefe42f9ef16426241f2c4d62c11d7ac43c4500dfcd184436bb4ef33260366f875230f26d81613c334dbda4736ba9d1d2966502914ec01bbe72d885606ec11da7a2cb01b29d35eebedbb0ecc73ed6c35",
+ "fd993f50e8a68c7b2c7f87511ce65b93c0aa94dcbdf2c9cca93816f0f3b2ab34c62c586fc507b4900a34cf9d0517e0fe10a89d154c5419c1f5e38de00e8834fe3dc1032abdeb10729a81655a69a12856a78ca6e12110580de879b086fd6608726541cfa9616326bdd36064bc0d1e5f9c93b41278bff6a13b2494b81e238c0c45aea1b07d855e8f3fe1478e373bd9d3957cf8a5e5b9003386793d994c7c575cff2322e2428cbbaa4f47560316ae3354a7478842ff7cc5dcbacb6e871e72b36f06d63a9aaeb9044cfb7974afdc238a5816f537dcf33ee40b4e1a5eb3cff2402b46d548264e133008d284f11b7e4e450bc3c5ff9f79b9c4",
+ "8df21892f5fc303b0de4adef1970186db6fe71bb3ea3094922e13afcfabf1d0be009f36d6f6310c5f9fda51f1a946507a055b645c296370440e5e83d8e906a2fb51f2b42de8856a81a4f28a73a8825c68ea08e5e366730bce8047011cb7d6d9be8c6f4211308fad21856284d5bc47d199988e0abf5badf8693ceeed0a2d98e8ae94b7775a42925edb1f697ffbd8e806af23145054a85e071819cca4cd48875290ca65e5ee72a9a54ff9f19c10ef4adaf8d04c9a9afcc73853fc128bbebc61f78702787c966ca6e1b1a0e4dab646acdfcd3c6bf3e5cfbec5ebe3e06c8abaa1de56e48421d87c46b5c78030afcafd91f27e7d7c85eb4872b",
+ "48ec6ec520f8e593d7b3f653eb15553de246723b81a6d0c3221aaa42a37420fba98a23796338dff5f845dce6d5a449be5ecc1887356619270461087e08d05fb60433a83d7bd00c002b09ea210b428965124b9b27d9105a71c826c1a2491cfd60e4cfa86c2da0c7100a8dc1c3f2f94b280d54e01e043acf0e966200d9fa8a41daf3b9382820786c75cadbb8841a1b2be5b6cbeb64878e4a231ae063a99b4e2308960ef0c8e2a16bb3545cc43bdf171493fb89a84f47e7973dc60cf75aeeca71e0a7ebe17d161d4fb9fe009941cc438f16a5bae6c99fcad08cac486eb2a48060b023d8730bf1d82fe60a2f036e6f52a5bff95f43bbe088933f",
+ "f4d84ed3e564c102600a795eaa9b1eaf4ad12f1a4deca1d042a0a2750ddf6201db03073d8bf553cb9dde48a1b0083827a609f7242b86584cc180964ae794b12ce55661e00e36a6ba4dbc389e6a5a85f1b45df9af7ead1b0a54db56e68639b9d438a91504e82c35d40c7bc7e048a53ac0b04accd0dadf4ac9884b0ca0e3cb5ba4336e3581be4c4760a553823ffa283a1120d4e145af56a59f2533903650f0b9e9ad9fe2e8a3c3c3dd03a1fcb709032c8835324839c735b0c051d0cbd8b5d867617c11023432e4bd275d3d0eb98a0b6cf58071a5b712922f2bc751ac7c2588c447444cde2f37a8ea5ec126425bf517e0d17c9e2999f52fee14b3",
+ "2ccea21bac9c2b70d3923309cbf2d7cb7abd1fcc8b8b002688870a80029c62397350c3c898194e5deea360bb963d26d485cb7963f8167586976ec0556950b2e86135f4a2800991ce8473bfd44a3c5e937a48b5e355ba5141bccf2131a83988d9d2a9e8e7635a956105b3512c05ef708139ced51d7a4e204c12d8a49a21e8dc6de2629a2fd092326885d9f218745fe09f6d91fb6afce250a30a63689534b6be1f26899ffa3767d835cf586aa47776700f94241bc999b1e3deefe188f37ff734f5f16ee6a00914323dc7b8a143c9137cdcc5cd08ae9566f04bb2941532674c97dff6ffa5ce3405ef8e5d27ec403114253dd6394c0167d72a0044c5",
+ "2b681c6398aee63bf862770341648bbcd31d7de7903c5903fe3d9469311320bb24d914f2af0cdca199c97214c7c679dc32a2800ba484a03c010ea6be3bb9f2c87e30a98b606050b8a3f297f12b8f92caaeceb3e844652115934874e0a1ab093a73d759b53f6a6c3096940dd22c2bb96ce6820a7b9c6d71a208de9892aa6a7209b0fff56a0cafea52b952cdd6f5752cff3309d448800b4e4c878aa595595b56b12b83fcd6ca89520c7da664e449d7b4438fc455888aad5de0fad9a06eed14afd3513b5ebbffe01775549b701181bd26370764f56eba52fdb24286ad1ac0f5418a7c429f7dfc7f3168437fa8eed7a2ed7c723a485e4c3ed14dea2e07",
+ "aadfd505a89f4aade2c3018258a7e039401b1fc6a7f3d87910dddbb880d372ec8a13c70d92245de5b8e5f9a285c33b99dc82fa2b22decee72b93a72211656ad7a52696c8e570f78be28c0e427a371dafde856e8d5ed24f83b0660b51e7fac05d93a8666dfde6def59af863f80f3e5f6801182c87422203df390dcb736b8f830052a8832eeeb0b4e27e732aaf793d166b5a3ec7745aeef3766937c2b75a276bddd145f6010c29d035e343e267cb2d828436876ec3a7ebe3b6347d4172f7a99d6821ce152e039e53deb33340b324c7f068ffb94b3cde35a8eaa12d15c3806a7ad0acec3e8c7078c1d32a28fd3eec9f32cb86e4c22166ff69e83785e851",
+ "1605b8cce529a9d6262fd4390d9e4ae5e14e0adc0ec89b028ef68dd0f373ea259aaa96f2967091dd0874c0105385e9e6da9ca68297c31afa44ef834535fb302ce5b4e49edacbbdf359fe1228a8172495b3e57014c27edd58b685110980056c50c398a64f4923f2d720b4df16d75cb36b4233660694182099c35028a972519c24764fc94e18e582b24deb3491535fc06b83837c7958522800e822201d694af0bd0aa3834e17d4b1ba36f470905ae5f8bbeeb6c4c8604d8af02baa347b07086d6989867ddd5e8e8ed7740c3469bfa2810519c55c6add1332c4c54ee9097961d6741cb12a09713a0d07645f784f42f5ad94b48b836b34263130b0483f15e3",
+ "ff9c6125b2f60bfd6c2427b279df070e430075096647599bdc68c531152c58e13858b82385d78c856092d6c74106e87ccf51ac7e673936332d9b223444eaa0e762ee258d8a733d3a515ec68ed73285e5ca183ae3278b4820b0ab2797feb1e7d8cc864df585dfb5ebe02a993325a9ad5e2d7d49d3132cf66013898351d044e0fe908ccdfeeebf651983601e3673a1f92d36510c0cc19b2e75856db8e4a41f92a51efa66d6cc22e414944c2c34a5a89ccde0be76f51410824e330d8e7c613194338c93732e8aea651fca18bcf1ac1824340c5553aff1e58d4ab8d7c8842b4712021e517cd6c140f6743c69c7bee05b10a8f24050a8caa4f96d1664909c5a06",
+ "6e85c2f8e1fdc3aaeb969da1258cb504bbf0070cd03d23b3fb5ee08feea5ee2e0ee1c71a5d0f4f701b351f4e4b4d74cb1e2ae6184814f77b62d2f08134b7236ebf6b67d8a6c9f01b4248b30667c555f5d8646dbfe291151b23c9c9857e33a4d5c847be29a5ee7b402e03bac02d1a4319acc0dd8f25e9c7a266f5e5c896cc11b5b238df96a0963ae806cb277abc515c298a3e61a3036b177acf87a56ca4478c4c6d0d468913de602ec891318bbaf52c97a77c35c5b7d164816cf24e4c4b0b5f45853882f716d61eb947a45ce2efa78f1c70a918512af1ad536cbe6148083385b34e207f5f690d7a954021e4b5f4258a385fd8a87809a481f34202af4caccb82",
+ "1e9b2c454e9de3a2d723d850331037dbf54133dbe27488ff757dd255833a27d8eb8a128ad12d0978b6884e25737086a704fb289aaaccf930d5b582ab4df1f55f0c429b6875edec3fe45464fa74164be056a55e243c4222c586bec5b18f39036aa903d98180f24f83d09a454dfa1e03a60e6a3ba4613e99c35f874d790174ee48a557f4f021ade4d1b278d7997ef094569b37b3db0505951e9ee8400adaea275c6db51b325ee730c69df97745b556ae41cd98741e28aa3a49544541eeb3da1b1e8fa4e8e9100d66dd0c7f5e2c271b1ecc077de79c462b9fe4c273543ecd82a5bea63c5acc01eca5fb780c7d7c8c9fe208ae8bd50cad1769693d92c6c8649d20d8",
+}
diff --git a/common/crypto/blake2b/blake2x.go b/common/crypto/blake2b/blake2x.go
new file mode 100644
index 0000000..52c414d
--- /dev/null
+++ b/common/crypto/blake2b/blake2x.go
@@ -0,0 +1,177 @@
+// 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.
+
+package blake2b
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+// XOF defines the interface to hash functions that
+// support arbitrary-length output.
+type XOF interface {
+ // Write absorbs more data into the hash's state. It panics if called
+ // after Read.
+ io.Writer
+
+ // Read reads more output from the hash. It returns io.EOF if the limit
+ // has been reached.
+ io.Reader
+
+ // Clone returns a copy of the XOF in its current state.
+ Clone() XOF
+
+ // Reset resets the XOF to its initial state.
+ Reset()
+}
+
+// OutputLengthUnknown can be used as the size argument to NewXOF to indicate
+// the length of the output is not known in advance.
+const OutputLengthUnknown = 0
+
+// magicUnknownOutputLength is a magic value for the output size that indicates
+// an unknown number of output bytes.
+const magicUnknownOutputLength = (1 << 32) - 1
+
+// maxOutputLength is the absolute maximum number of bytes to produce when the
+// number of output bytes is unknown.
+const maxOutputLength = (1 << 32) * 64
+
+// NewXOF creates a new variable-output-length hash. The hash either produce a
+// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
+// (size == OutputLengthUnknown). In the latter case, an absolute limit of
+// 256GiB applies.
+//
+// A non-nil key turns the hash into a MAC. The key must between
+// zero and 32 bytes long.
+func NewXOF(size uint32, key []byte) (XOF, error) {
+ if len(key) > Size {
+ return nil, errKeySize
+ }
+ if size == magicUnknownOutputLength {
+ // 2^32-1 indicates an unknown number of bytes and thus isn't a
+ // valid length.
+ return nil, errors.New("blake2b: XOF length too large")
+ }
+ if size == OutputLengthUnknown {
+ size = magicUnknownOutputLength
+ }
+ x := &xof{
+ d: digest{
+ size: Size,
+ keyLen: len(key),
+ },
+ length: size,
+ }
+ copy(x.d.key[:], key)
+ x.Reset()
+ return x, nil
+}
+
+type xof struct {
+ d digest
+ length uint32
+ remaining uint64
+ cfg, root, block [Size]byte
+ offset int
+ nodeOffset uint32
+ readMode bool
+}
+
+func (x *xof) Write(p []byte) (n int, err error) {
+ if x.readMode {
+ panic("blake2b: write to XOF after read")
+ }
+ return x.d.Write(p)
+}
+
+func (x *xof) Clone() XOF {
+ clone := *x
+ return &clone
+}
+
+func (x *xof) Reset() {
+ x.cfg[0] = byte(Size)
+ binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
+ binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length
+ x.cfg[17] = byte(Size) // inner hash size
+
+ x.d.Reset()
+ x.d.h[1] ^= uint64(x.length) << 32
+
+ x.remaining = uint64(x.length)
+ if x.remaining == magicUnknownOutputLength {
+ x.remaining = maxOutputLength
+ }
+ x.offset, x.nodeOffset = 0, 0
+ x.readMode = false
+}
+
+func (x *xof) Read(p []byte) (n int, err error) {
+ if !x.readMode {
+ x.d.finalize(&x.root)
+ x.readMode = true
+ }
+
+ if x.remaining == 0 {
+ return 0, io.EOF
+ }
+
+ n = len(p)
+ if uint64(n) > x.remaining {
+ n = int(x.remaining)
+ p = p[:n]
+ }
+
+ if x.offset > 0 {
+ blockRemaining := Size - x.offset
+ if n < blockRemaining {
+ x.offset += copy(p, x.block[x.offset:])
+ x.remaining -= uint64(n)
+ return
+ }
+ copy(p, x.block[x.offset:])
+ p = p[blockRemaining:]
+ x.offset = 0
+ x.remaining -= uint64(blockRemaining)
+ }
+
+ for len(p) >= Size {
+ binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
+ x.nodeOffset++
+
+ x.d.initConfig(&x.cfg)
+ x.d.Write(x.root[:])
+ x.d.finalize(&x.block)
+
+ copy(p, x.block[:])
+ p = p[Size:]
+ x.remaining -= uint64(Size)
+ }
+
+ if todo := len(p); todo > 0 {
+ if x.remaining < uint64(Size) {
+ x.cfg[0] = byte(x.remaining)
+ }
+ binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
+ x.nodeOffset++
+
+ x.d.initConfig(&x.cfg)
+ x.d.Write(x.root[:])
+ x.d.finalize(&x.block)
+
+ x.offset = copy(p, x.block[:todo])
+ x.remaining -= uint64(todo)
+ }
+ return
+}
+
+func (d *digest) initConfig(cfg *[Size]byte) {
+ d.offset, d.c[0], d.c[1] = 0, 0, 0
+ for i := range d.h {
+ d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
+ }
+}
diff --git a/common/crypto/blake2b/register.go b/common/crypto/blake2b/register.go
new file mode 100644
index 0000000..d9fcac3
--- /dev/null
+++ b/common/crypto/blake2b/register.go
@@ -0,0 +1,32 @@
+// 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.
+
+//go:build go1.9
+
+package blake2b
+
+import (
+ "crypto"
+ "hash"
+)
+
+func init() {
+ newHash256 := func() hash.Hash {
+ h, _ := New256(nil)
+ return h
+ }
+ newHash384 := func() hash.Hash {
+ h, _ := New384(nil)
+ return h
+ }
+
+ newHash512 := func() hash.Hash {
+ h, _ := New512(nil)
+ return h
+ }
+
+ crypto.RegisterHash(crypto.BLAKE2b_256, newHash256)
+ crypto.RegisterHash(crypto.BLAKE2b_384, newHash384)
+ crypto.RegisterHash(crypto.BLAKE2b_512, newHash512)
+}
diff --git a/common/crypto/bls/bls.go b/common/crypto/bls/bls.go
new file mode 100644
index 0000000..2c0b471
--- /dev/null
+++ b/common/crypto/bls/bls.go
@@ -0,0 +1,73 @@
+// Package bls implements a go-wrapper around a library implementing the
+// the BLS12-381 curve and signature scheme. This package exposes a public API for
+// verifying and aggregating BLS signatures used by Ethereum.
+package bls
+
+import (
+ "github.com/astranetworld/ast/common/crypto/bls/blst"
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+)
+
+// Initialize herumi temporarily while we transition to blst for ethdo.
+func init() {
+}
+
+// SecretKeyFromBytes creates a BLS private key from a BigEndian byte slice.
+func SecretKeyFromBytes(privKey []byte) (SecretKey, error) {
+ return blst.SecretKeyFromBytes(privKey)
+}
+
+// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
+func PublicKeyFromBytes(pubKey []byte) (PublicKey, error) {
+ return blst.PublicKeyFromBytes(pubKey)
+}
+
+// SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
+func SignatureFromBytes(sig []byte) (Signature, error) {
+ return blst.SignatureFromBytes(sig)
+}
+
+// MultipleSignaturesFromBytes creates a slice of BLS signatures from a LittleEndian 2d-byte slice.
+func MultipleSignaturesFromBytes(sigs [][]byte) ([]Signature, error) {
+ return blst.MultipleSignaturesFromBytes(sigs)
+}
+
+// AggregatePublicKeys aggregates the provided raw public keys into a single key.
+func AggregatePublicKeys(pubs [][]byte) (PublicKey, error) {
+ return blst.AggregatePublicKeys(pubs)
+}
+
+// AggregateMultiplePubkeys aggregates the provided decompressed keys into a single key.
+func AggregateMultiplePubkeys(pubs []PublicKey) PublicKey {
+ return blst.AggregateMultiplePubkeys(pubs)
+}
+
+// AggregateSignatures converts a list of signatures into a single, aggregated sig.
+func AggregateSignatures(sigs []common.Signature) common.Signature {
+ return blst.AggregateSignatures(sigs)
+}
+
+// AggregateCompressedSignatures converts a list of compressed signatures into a single, aggregated sig.
+func AggregateCompressedSignatures(multiSigs [][]byte) (common.Signature, error) {
+ return blst.AggregateCompressedSignatures(multiSigs)
+}
+
+// VerifyMultipleSignatures verifies multiple signatures for distinct messages securely.
+func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []common.PublicKey) (bool, error) {
+ return blst.VerifyMultipleSignatures(sigs, msgs, pubKeys)
+}
+
+// NewAggregateSignature creates a blank aggregate signature.
+func NewAggregateSignature() common.Signature {
+ return blst.NewAggregateSignature()
+}
+
+// RandKey creates a new private key using a random input.
+func RandKey() (common.SecretKey, error) {
+ return blst.RandKey()
+}
+
+// SecretKeyFromRandom32Byte creates a new private key using random 32 bytes
+func SecretKeyFromRandom32Byte(ikm [32]byte) (common.SecretKey, error) {
+ return blst.SecretKeyFromRandom32Byte(ikm)
+}
diff --git a/common/crypto/bls/blst/aliases.go b/common/crypto/bls/blst/aliases.go
new file mode 100644
index 0000000..e1e529b
--- /dev/null
+++ b/common/crypto/bls/blst/aliases.go
@@ -0,0 +1,11 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package blst
+
+import blst "github.com/supranational/blst/bindings/go"
+
+// Internal types for blst.
+type blstPublicKey = blst.P1Affine
+type blstSignature = blst.P2Affine
+type blstAggregateSignature = blst.P2Aggregate
+type blstAggregatePublicKey = blst.P1Aggregate
diff --git a/common/crypto/bls/blst/doc.go b/common/crypto/bls/blst/doc.go
new file mode 100644
index 0000000..76a2e9a
--- /dev/null
+++ b/common/crypto/bls/blst/doc.go
@@ -0,0 +1,6 @@
+// Package blst implements a go-wrapper around a library implementing the
+// the BLS12-381 curve and signature scheme. This package exposes a public API for
+// verifying and aggregating BLS signatures used by Ethereum.
+//
+// This implementation uses the library written by Supranational, blst.
+package blst
diff --git a/common/crypto/bls/blst/init.go b/common/crypto/bls/blst/init.go
new file mode 100644
index 0000000..02d1ac9
--- /dev/null
+++ b/common/crypto/bls/blst/init.go
@@ -0,0 +1,18 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package blst
+
+import (
+ "runtime"
+
+ blst "github.com/supranational/blst/bindings/go"
+)
+
+func init() {
+ // Reserve 1 core for general application work
+ maxProcs := runtime.GOMAXPROCS(0) - 1
+ if maxProcs <= 0 {
+ maxProcs = 1
+ }
+ blst.SetMaxProcs(maxProcs)
+}
diff --git a/common/crypto/bls/blst/public_key.go b/common/crypto/bls/blst/public_key.go
new file mode 100644
index 0000000..4c2ec95
--- /dev/null
+++ b/common/crypto/bls/blst/public_key.go
@@ -0,0 +1,155 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package blst
+
+import (
+ "fmt"
+
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/log"
+ lru "github.com/hashicorp/golang-lru"
+ "github.com/pkg/errors"
+)
+
+var maxKeys = 1000000
+var pubkeyCache, _ = lru.New(maxKeys)
+
+const (
+ BLSPubkeyLength = 48
+)
+
+// PublicKey used in the BLS signature scheme.
+type PublicKey struct {
+ p *blstPublicKey
+}
+
+// PublicKeyFromBytes creates a BLS public key from a BigEndian byte slice.
+func PublicKeyFromBytes(pubKey []byte) (common.PublicKey, error) {
+ if len(pubKey) != BLSPubkeyLength {
+ return nil, fmt.Errorf("public key must be %d bytes", BLSPubkeyLength)
+ }
+ newKey := (*[BLSPubkeyLength]byte)(pubKey)
+ if cv, ok := pubkeyCache.Get(*newKey); ok {
+ return cv.(*PublicKey).Copy(), nil
+ }
+ // Subgroup check NOT done when decompressing pubkey.
+ p := new(blstPublicKey).Uncompress(pubKey)
+ if p == nil {
+ return nil, errors.New("could not unmarshal bytes into public key")
+ }
+ // Subgroup and infinity check
+ if !p.KeyValidate() {
+ // NOTE: the error is not quite accurate since it includes group check
+ return nil, common.ErrInfinitePubKey
+ }
+ pubKeyObj := &PublicKey{p: p}
+ copiedKey := pubKeyObj.Copy()
+ cacheKey := *newKey
+ pubkeyCache.Add(cacheKey, copiedKey)
+ return pubKeyObj, nil
+}
+
+// AggregatePublicKeys aggregates the provided raw public keys into a single key.
+func AggregatePublicKeys(pubs [][]byte) (common.PublicKey, error) {
+ if len(pubs) == 0 {
+ return nil, errors.New("nil or empty public keys")
+ }
+ agg := new(blstAggregatePublicKey)
+ mulP1 := make([]*blstPublicKey, 0, len(pubs))
+ for _, pubkey := range pubs {
+ pubKeyObj, err := PublicKeyFromBytes(pubkey)
+ if err != nil {
+ return nil, err
+ }
+ mulP1 = append(mulP1, pubKeyObj.(*PublicKey).p)
+ }
+ // No group check needed here since it is done in PublicKeyFromBytes
+ // Note the checks could be moved from PublicKeyFromBytes into Aggregate
+ // and take advantage of multi-threading.
+ agg.Aggregate(mulP1, false)
+ return &PublicKey{p: agg.ToAffine()}, nil
+}
+
+// Marshal a public key into a LittleEndian byte slice.
+func (p *PublicKey) Marshal() []byte {
+ return p.p.Compress()
+}
+
+// Copy the public key to a new pointer reference.
+func (p *PublicKey) Copy() common.PublicKey {
+ np := *p.p
+ return &PublicKey{p: &np}
+}
+
+// IsInfinite checks if the public key is infinite.
+func (p *PublicKey) IsInfinite() bool {
+ zeroKey := new(blstPublicKey)
+ return p.p.Equals(zeroKey)
+}
+
+// Equals checks if the provided public key is equal to
+// the current one.
+func (p *PublicKey) Equals(p2 common.PublicKey) bool {
+ return p.p.Equals(p2.(*PublicKey).p)
+}
+
+// Aggregate two public keys.
+func (p *PublicKey) Aggregate(p2 common.PublicKey) common.PublicKey {
+
+ agg := new(blstAggregatePublicKey)
+ // No group check here since it is checked at decompression time
+ agg.Add(p.p, false)
+ agg.Add(p2.(*PublicKey).p, false)
+ p.p = agg.ToAffine()
+
+ return p
+}
+
+func (p *PublicKey) UnmarshalJSON(input []byte) error {
+ log.Infof(string(input))
+ log.Info("1111")
+ b := make([]byte, 0)
+ err := hexutil.UnmarshalFixedText("PublicKey", input, b[:])
+ if err != nil {
+ return err
+ }
+ pubkey, err := PublicKeyFromBytes(b)
+ if err != nil {
+ p = pubkey.(*PublicKey)
+ }
+ return err
+}
+
+func (p *PublicKey) UnmarshalText(input []byte) error {
+ log.Infof(string(input))
+ log.Info("1111")
+ b := make([]byte, 0)
+ err := hexutil.UnmarshalFixedText("PublicKey", input, b[:])
+ if err != nil {
+ return err
+ }
+ pubkey, err := PublicKeyFromBytes(b)
+ if err != nil {
+ p = pubkey.(*PublicKey)
+ }
+ return err
+}
+
+func (p *PublicKey) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(p.Marshal()).MarshalText()
+}
+
+// AggregateMultiplePubkeys aggregates the provided decompressed keys into a single key.
+func AggregateMultiplePubkeys(pubkeys []common.PublicKey) common.PublicKey {
+ mulP1 := make([]*blstPublicKey, 0, len(pubkeys))
+ for _, pubkey := range pubkeys {
+ mulP1 = append(mulP1, pubkey.(*PublicKey).p)
+ }
+ agg := new(blstAggregatePublicKey)
+ // No group check needed here since it is done in PublicKeyFromBytes
+ // Note the checks could be moved from PublicKeyFromBytes into Aggregate
+ // and take advantage of multi-threading.
+ agg.Aggregate(mulP1, false)
+ return &PublicKey{p: agg.ToAffine()}
+}
diff --git a/common/crypto/bls/blst/secret_key.go b/common/crypto/bls/blst/secret_key.go
new file mode 100644
index 0000000..92fd700
--- /dev/null
+++ b/common/crypto/bls/blst/secret_key.go
@@ -0,0 +1,96 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package blst
+
+import (
+ "crypto/subtle"
+ "fmt"
+
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+ "github.com/astranetworld/ast/common/crypto/rand"
+
+ blst "github.com/supranational/blst/bindings/go"
+)
+
+const BLSSecretKeyLength = 32
+
+// bls12SecretKey used in the BLS signature scheme.
+type bls12SecretKey struct {
+ p *blst.SecretKey
+}
+
+// RandKey creates a new private key using a random method provided as an io.Reader.
+func RandKey() (common.SecretKey, error) {
+ // Generate 32 bytes of randomness
+ var ikm [32]byte
+ _, err := rand.NewGenerator().Read(ikm[:])
+ if err != nil {
+ return nil, err
+ }
+ // Defensive check, that we have not generated a secret key,
+ secKey := &bls12SecretKey{blst.KeyGen(ikm[:])}
+ if IsZero(secKey.Marshal()) {
+ return nil, common.ErrZeroKey
+ }
+ return secKey, nil
+}
+
+// SecretKeyFromRandom32Byte creates a new private key using random 32 bytes
+func SecretKeyFromRandom32Byte(ikm [32]byte) (common.SecretKey, error) {
+ // Defensive check, that we have not generated a secret key,
+ secKey := &bls12SecretKey{blst.KeyGen(ikm[:])}
+ if IsZero(secKey.Marshal()) {
+ return nil, common.ErrZeroKey
+ }
+ return secKey, nil
+}
+
+// SecretKeyFromBytes creates a BLS private key from a BigEndian byte slice.
+func SecretKeyFromBytes(privKey []byte) (common.SecretKey, error) {
+ if len(privKey) != BLSSecretKeyLength {
+ return nil, fmt.Errorf("secret key must be %d bytes", BLSSecretKeyLength)
+ }
+ secKey := new(blst.SecretKey).Deserialize(privKey)
+ if secKey == nil {
+ return nil, common.ErrSecretUnmarshal
+ }
+ wrappedKey := &bls12SecretKey{p: secKey}
+ if IsZero(privKey) {
+ return nil, common.ErrZeroKey
+ }
+ return wrappedKey, nil
+}
+
+// PublicKey obtains the public key corresponding to the BLS secret key.
+func (s *bls12SecretKey) PublicKey() common.PublicKey {
+ return &PublicKey{p: new(blstPublicKey).From(s.p)}
+}
+
+// IsZero checks if the secret key is a zero key.
+func IsZero(sKey []byte) bool {
+ b := byte(0)
+ for _, s := range sKey {
+ b |= s
+ }
+ return subtle.ConstantTimeByteEq(b, 0) == 1
+}
+
+// Sign a message using a secret key - in a beacon/validator client.
+//
+// In IETF draft BLS specification:
+// Sign(SK, message) -> signature: a signing algorithm that generates
+//
+// a deterministic signature given a secret key SK and a message.
+//
+// In Ethereum proof of stake specification:
+// def Sign(SK: int, message: Bytes) -> BLSSignature
+func (s *bls12SecretKey) Sign(msg []byte) common.Signature {
+ signature := new(blstSignature).Sign(s.p, msg, dst)
+ return &Signature{s: signature}
+}
+
+// Marshal a secret key into a LittleEndian byte slice.
+func (s *bls12SecretKey) Marshal() []byte {
+ keyBytes := s.p.Serialize()
+ return keyBytes
+}
diff --git a/common/crypto/bls/blst/signature.go b/common/crypto/bls/blst/signature.go
new file mode 100644
index 0000000..3973aef
--- /dev/null
+++ b/common/crypto/bls/blst/signature.go
@@ -0,0 +1,262 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package blst
+
+import (
+ "bytes"
+ "fmt"
+ "sync"
+
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+ "github.com/astranetworld/ast/common/crypto/rand"
+
+ "github.com/pkg/errors"
+ blst "github.com/supranational/blst/bindings/go"
+)
+
+var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
+
+const scalarBytes = 32
+const randBitsEntropy = 64
+
+const BLSSignatureLength = 96
+
+// Signature used in the BLS signature scheme.
+type Signature struct {
+ s *blstSignature
+}
+
+// SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
+func SignatureFromBytes(sig []byte) (common.Signature, error) {
+ if len(sig) != BLSSignatureLength {
+ return nil, fmt.Errorf("signature must be %d bytes", BLSSignatureLength)
+ }
+ signature := new(blstSignature).Uncompress(sig)
+ if signature == nil {
+ return nil, errors.New("could not unmarshal bytes into signature")
+ }
+ // Group check signature. Do not check for infinity since an aggregated signature
+ // could be infinite.
+ if !signature.SigValidate(false) {
+ return nil, errors.New("signature not in group")
+ }
+ return &Signature{s: signature}, nil
+}
+
+// AggregateCompressedSignatures converts a list of compressed signatures into a single, aggregated sig.
+func AggregateCompressedSignatures(multiSigs [][]byte) (common.Signature, error) {
+ signature := new(blstAggregateSignature)
+ valid := signature.AggregateCompressed(multiSigs, true)
+ if !valid {
+ return nil, errors.New("provided signatures fail the group check and cannot be compressed")
+ }
+ return &Signature{s: signature.ToAffine()}, nil
+}
+
+// MultipleSignaturesFromBytes creates a group of BLS signatures from a LittleEndian 2d-byte slice.
+func MultipleSignaturesFromBytes(multiSigs [][]byte) ([]common.Signature, error) {
+ if len(multiSigs) == 0 {
+ return nil, fmt.Errorf("0 signatures provided to the method")
+ }
+ for _, s := range multiSigs {
+ if len(s) != BLSSignatureLength {
+ return nil, fmt.Errorf("signature must be %d bytes", BLSSignatureLength)
+ }
+ }
+ multiSignatures := new(blstSignature).BatchUncompress(multiSigs)
+ if len(multiSignatures) == 0 {
+ return nil, errors.New("could not unmarshal bytes into signature")
+ }
+ if len(multiSignatures) != len(multiSigs) {
+ return nil, errors.Errorf("wanted %d decompressed signatures but got %d", len(multiSigs), len(multiSignatures))
+ }
+ wrappedSigs := make([]common.Signature, len(multiSignatures))
+ for i, signature := range multiSignatures {
+ // Group check signature. Do not check for infinity since an aggregated signature
+ // could be infinite.
+ if !signature.SigValidate(false) {
+ return nil, errors.New("signature not in group")
+ }
+ copiedSig := signature
+ wrappedSigs[i] = &Signature{s: copiedSig}
+ }
+ return wrappedSigs, nil
+}
+
+// Verify a bls signature given a public key, a message.
+//
+// In IETF draft BLS specification:
+// Verify(PK, message, signature) -> VALID or INVALID: a verification
+//
+// algorithm that outputs VALID if signature is a valid signature of
+// message under public key PK, and INVALID otherwise.
+//
+// In the Ethereum proof of stake specification:
+// def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool
+func (s *Signature) Verify(pubKey common.PublicKey, msg []byte) bool {
+ // Signature and PKs are assumed to have been validated upon decompression!
+ return s.s.Verify(false, pubKey.(*PublicKey).p, false, msg, dst)
+}
+
+// AggregateVerify verifies each public key against its respective message. This is vulnerable to
+// rogue public-key attack. Each user must provide a proof-of-knowledge of the public key.
+//
+// Note: The msgs must be distinct. For maximum performance, this method does not ensure distinct
+// messages.
+//
+// In IETF draft BLS specification:
+// AggregateVerify((PK_1, message_1), ..., (PK_n, message_n),
+//
+// signature) -> VALID or INVALID: an aggregate verification
+// algorithm that outputs VALID if signature is a valid aggregated
+// signature for a collection of public keys and messages, and
+// outputs INVALID otherwise.
+//
+// In the Ethereum proof of stake specification:
+// def AggregateVerify(pairs: Sequence[PK: BLSPubkey, message: Bytes], signature: BLSSignature) -> bool
+//
+// Deprecated: Use FastAggregateVerify or use this method in spectests only.
+func (s *Signature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool {
+ size := len(pubKeys)
+ if size == 0 {
+ return false
+ }
+ if size != len(msgs) {
+ return false
+ }
+ msgSlices := make([][]byte, len(msgs))
+ rawKeys := make([]*blstPublicKey, len(msgs))
+ for i := 0; i < size; i++ {
+ msgSlices[i] = msgs[i][:]
+ rawKeys[i] = pubKeys[i].(*PublicKey).p
+ }
+ // Signature and PKs are assumed to have been validated upon decompression!
+ return s.s.AggregateVerify(false, rawKeys, false, msgSlices, dst)
+}
+
+// FastAggregateVerify verifies all the provided public keys with their aggregated signature.
+//
+// In IETF draft BLS specification:
+// FastAggregateVerify(PK_1, ..., PK_n, message, signature) -> VALID
+//
+// or INVALID: a verification algorithm for the aggregate of multiple
+// signatures on the same message. This function is faster than
+// AggregateVerify.
+//
+// In the Ethereum proof of stake specification:
+// def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Bytes, signature: BLSSignature) -> bool
+func (s *Signature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool {
+ if len(pubKeys) == 0 {
+ return false
+ }
+ rawKeys := make([]*blstPublicKey, len(pubKeys))
+ for i := 0; i < len(pubKeys); i++ {
+ rawKeys[i] = pubKeys[i].(*PublicKey).p
+ }
+ return s.s.FastAggregateVerify(true, rawKeys, msg[:], dst)
+}
+
+// Eth2FastAggregateVerify implements a wrapper on top of bls's FastAggregateVerify. It accepts G2_POINT_AT_INFINITY signature
+// when pubkeys empty.
+//
+// Spec code:
+// def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, signature: BLSSignature) -> bool:
+//
+// """
+// Wrapper to ``bls.FastAggregateVerify`` accepting the ``G2_POINT_AT_INFINITY`` signature when ``pubkeys`` is empty.
+// """
+// if len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY:
+// return True
+// return bls.FastAggregateVerify(pubkeys, message, signature)
+func (s *Signature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool {
+ if len(pubKeys) == 0 && bytes.Equal(s.Marshal(), common.InfiniteSignature[:]) {
+ return true
+ }
+ return s.FastAggregateVerify(pubKeys, msg)
+}
+
+// NewAggregateSignature creates a blank aggregate signature.
+func NewAggregateSignature() common.Signature {
+ sig := blst.HashToG2([]byte{'m', 'o', 'c', 'k'}, dst).ToAffine()
+ return &Signature{s: sig}
+}
+
+// AggregateSignatures converts a list of signatures into a single, aggregated sig.
+func AggregateSignatures(sigs []common.Signature) common.Signature {
+ if len(sigs) == 0 {
+ return nil
+ }
+
+ rawSigs := make([]*blstSignature, len(sigs))
+ for i := 0; i < len(sigs); i++ {
+ rawSigs[i] = sigs[i].(*Signature).s
+ }
+
+ // Signature and PKs are assumed to have been validated upon decompression!
+ signature := new(blstAggregateSignature)
+ signature.Aggregate(rawSigs, false)
+ return &Signature{s: signature.ToAffine()}
+}
+
+// VerifyMultipleSignatures verifies a non-singular set of signatures and its respective pubkeys and messages.
+// This method provides a safe way to verify multiple signatures at once. We pick a number randomly from 1 to max
+// uint64 and then multiply the signature by it. We continue doing this for all signatures and its respective pubkeys.
+// S* = S_1 * r_1 + S_2 * r_2 + ... + S_n * r_n
+// P'_{i,j} = P_{i,j} * r_i
+// e(S*, G) = \prod_{i=1}^n \prod_{j=1}^{m_i} e(P'_{i,j}, M_{i,j})
+// Using this we can verify multiple signatures safely.
+func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []common.PublicKey) (bool, error) {
+ if len(sigs) == 0 || len(pubKeys) == 0 {
+ return false, nil
+ }
+ rawSigs := new(blstSignature).BatchUncompress(sigs)
+
+ length := len(sigs)
+ if length != len(pubKeys) || length != len(msgs) {
+ return false, errors.Errorf("provided signatures, pubkeys and messages have differing lengths. S: %d, P: %d,M %d",
+ length, len(pubKeys), len(msgs))
+ }
+ mulP1Aff := make([]*blstPublicKey, length)
+ rawMsgs := make([]blst.Message, length)
+
+ for i := 0; i < length; i++ {
+ mulP1Aff[i] = pubKeys[i].(*PublicKey).p
+ rawMsgs[i] = msgs[i][:]
+ }
+ // Secure source of RNG
+ randGen := rand.NewGenerator()
+ randLock := new(sync.Mutex)
+
+ randFunc := func(scalar *blst.Scalar) {
+ var rbytes [scalarBytes]byte
+ randLock.Lock()
+ randGen.Read(rbytes[:]) // #nosec G104 -- Error will always be nil in `read` in math/rand
+ randLock.Unlock()
+ // Protect against the generator returning 0. Since the scalar value is
+ // derived from a big endian byte slice, we take the last byte.
+ rbytes[len(rbytes)-1] |= 0x01
+ scalar.FromBEndian(rbytes[:])
+ }
+ dummySig := new(blstSignature)
+
+ // Validate signatures since we uncompress them here. Public keys should already be validated.
+ return dummySig.MultipleAggregateVerify(rawSigs, true, mulP1Aff, false, rawMsgs, dst, randFunc, randBitsEntropy), nil
+}
+
+// Marshal a signature into a LittleEndian byte slice.
+func (s *Signature) Marshal() []byte {
+ return s.s.Compress()
+}
+
+// Copy returns a full deep copy of a signature.
+func (s *Signature) Copy() common.Signature {
+ sign := *s.s
+ return &Signature{s: &sign}
+}
+
+// VerifyCompressed verifies that the compressed signature and pubkey
+// are valid from the message provided.
+func VerifyCompressed(signature, pub, msg []byte) bool {
+ // Validate signature and PKs since we will uncompress them here
+ return new(blstSignature).VerifyCompressed(signature, true, pub, true, msg, dst)
+}
diff --git a/common/crypto/bls/blst/stub.go b/common/crypto/bls/blst/stub.go
new file mode 100644
index 0000000..5e14ea0
--- /dev/null
+++ b/common/crypto/bls/blst/stub.go
@@ -0,0 +1,150 @@
+//go:build blst_disabled
+
+package blst
+
+// This stub file exists until build issues can be resolved for libfuzz.
+const err = "blst is only supported on linux,darwin,windows"
+
+// SecretKey -- stub
+type SecretKey struct{}
+
+// PublicKey -- stub
+func (s SecretKey) PublicKey() common.PublicKey {
+ panic(err)
+}
+
+// Sign -- stub
+func (s SecretKey) Sign(_ []byte) common.Signature {
+ panic(err)
+}
+
+// Marshal -- stub
+func (s SecretKey) Marshal() []byte {
+ panic(err)
+}
+
+// IsZero -- stub
+func (s SecretKey) IsZero() bool {
+ panic(err)
+}
+
+// PublicKey -- stub
+type PublicKey struct{}
+
+// Marshal -- stub
+func (p PublicKey) Marshal() []byte {
+ panic(err)
+}
+
+// Copy -- stub
+func (p PublicKey) Copy() common.PublicKey {
+ panic(err)
+}
+
+// Aggregate -- stub
+func (p PublicKey) Aggregate(_ common.PublicKey) common.PublicKey {
+ panic(err)
+}
+
+// IsInfinite -- stub
+func (p PublicKey) IsInfinite() bool {
+ panic(err)
+}
+
+// Equals -- stub
+func (p PublicKey) Equals(_ common.PublicKey) bool {
+ panic(err)
+}
+
+// Signature -- stub
+type Signature struct{}
+
+// Verify -- stub
+func (s Signature) Verify(_ common.PublicKey, _ []byte) bool {
+ panic(err)
+}
+
+// AggregateVerify -- stub
+func (s Signature) AggregateVerify(_ []common.PublicKey, _ [][32]byte) bool {
+ panic(err)
+}
+
+// FastAggregateVerify -- stub
+func (s Signature) FastAggregateVerify(_ []common.PublicKey, _ [32]byte) bool {
+ panic(err)
+}
+
+// Eth2FastAggregateVerify -- stub
+func (s Signature) Eth2FastAggregateVerify(_ []common.PublicKey, _ [32]byte) bool {
+ panic(err)
+}
+
+// Marshal -- stub
+func (s Signature) Marshal() []byte {
+ panic(err)
+}
+
+// Copy -- stub
+func (s Signature) Copy() common.Signature {
+ panic(err)
+}
+
+// SecretKeyFromBytes -- stub
+func SecretKeyFromBytes(_ []byte) (SecretKey, error) {
+ panic(err)
+}
+
+// PublicKeyFromBytes -- stub
+func PublicKeyFromBytes(_ []byte) (PublicKey, error) {
+ panic(err)
+}
+
+// SignatureFromBytes -- stub
+func SignatureFromBytes(_ []byte) (Signature, error) {
+ panic(err)
+}
+
+// MultipleSignaturesFromBytes -- stub
+func MultipleSignaturesFromBytes(multiSigs [][]byte) ([]common.Signature, error) {
+ panic(err)
+}
+
+// AggregatePublicKeys -- stub
+func AggregatePublicKeys(_ [][]byte) (PublicKey, error) {
+ panic(err)
+}
+
+// AggregateSignatures -- stub
+func AggregateSignatures(_ []common.Signature) common.Signature {
+ panic(err)
+}
+
+// AggregateMultiplePubkeys -- stub
+func AggregateMultiplePubkeys(pubs []common.PublicKey) common.PublicKey {
+ panic(err)
+}
+
+// AggregateCompressedSignatures -- stub
+func AggregateCompressedSignatures(multiSigs [][]byte) (common.Signature, error) {
+ panic(err)
+}
+
+// VerifyMultipleSignatures -- stub
+func VerifyMultipleSignatures(_ [][]byte, _ [][32]byte, _ []common.PublicKey) (bool, error) {
+ panic(err)
+}
+
+// NewAggregateSignature -- stub
+func NewAggregateSignature() common.Signature {
+ panic(err)
+}
+
+// RandKey -- stub
+func RandKey() (common.SecretKey, error) {
+ panic(err)
+}
+
+// VerifyCompressed -- stub
+func VerifyCompressed(_, _, _ []byte) bool {
+ panic(err)
+}
diff --git a/common/crypto/bls/common/constants.go b/common/crypto/bls/common/constants.go
new file mode 100644
index 0000000..0959632
--- /dev/null
+++ b/common/crypto/bls/common/constants.go
@@ -0,0 +1,14 @@
+package common
+
+const (
+ BLSPubkeyLength = 48
+)
+
+// ZeroSecretKey represents a zero secret key.
+var ZeroSecretKey = [32]byte{}
+
+// InfinitePublicKey represents an infinite public key (G1 Point at Infinity).
+var InfinitePublicKey = [BLSPubkeyLength]byte{0xC0}
+
+// InfiniteSignature represents an infinite signature (G2 Point at Infinity).
+var InfiniteSignature = [96]byte{0xC0}
diff --git a/common/crypto/bls/common/error.go b/common/crypto/bls/common/error.go
new file mode 100644
index 0000000..13cc48e
--- /dev/null
+++ b/common/crypto/bls/common/error.go
@@ -0,0 +1,13 @@
+package common
+
+import "errors"
+
+// ErrZeroKey describes an error due to a zero secret key.
+var ErrZeroKey = errors.New("received secret key is zero")
+
+// ErrSecretUnmarshal describes an error which happens during unmarshalling
+// a secret key.
+var ErrSecretUnmarshal = errors.New("could not unmarshal bytes into secret key")
+
+// ErrInfinitePubKey describes an error due to an infinite public key.
+var ErrInfinitePubKey = errors.New("received an infinite public key")
diff --git a/common/crypto/bls/common/interface.go b/common/crypto/bls/common/interface.go
new file mode 100644
index 0000000..64bba43
--- /dev/null
+++ b/common/crypto/bls/common/interface.go
@@ -0,0 +1,37 @@
+// Package common provides the BLS interfaces that are implemented by the various BLS wrappers.
+//
+// This package should not be used by downstream consumers. These interfaces are re-exporter by
+// github.com/prysmaticlabs/prysm/crypto/bls. This package exists to prevent an import circular
+// dependency.
+package common
+
+// SecretKey represents a BLS secret or private key.
+type SecretKey interface {
+ PublicKey() PublicKey
+ Sign(msg []byte) Signature
+ Marshal() []byte
+}
+
+// PublicKey represents a BLS public key.
+type PublicKey interface {
+ Marshal() []byte
+ Copy() PublicKey
+ Aggregate(p2 PublicKey) PublicKey
+ IsInfinite() bool
+ Equals(p2 PublicKey) bool
+ //json
+ UnmarshalJSON(input []byte) error
+ //UnmarshalText(input []byte) error
+ MarshalText() ([]byte, error)
+}
+
+// Signature represents a BLS signature.
+type Signature interface {
+ Verify(pubKey PublicKey, msg []byte) bool
+ // Deprecated: Use FastAggregateVerify or use this method in spectests only.
+ AggregateVerify(pubKeys []PublicKey, msgs [][32]byte) bool
+ FastAggregateVerify(pubKeys []PublicKey, msg [32]byte) bool
+ Eth2FastAggregateVerify(pubKeys []PublicKey, msg [32]byte) bool
+ Marshal() []byte
+ Copy() Signature
+}
diff --git a/common/crypto/bls/constants.go b/common/crypto/bls/constants.go
new file mode 100644
index 0000000..f66aa55
--- /dev/null
+++ b/common/crypto/bls/constants.go
@@ -0,0 +1,7 @@
+package bls
+
+// DomainByteLength length of domain byte array.
+const DomainByteLength = 4
+
+// CurveOrder for the BLS12-381 curve.
+const CurveOrder = "52435875175126190479447740508185965837690552500527637822603658699938581184513"
diff --git a/common/crypto/bls/error.go b/common/crypto/bls/error.go
new file mode 100644
index 0000000..4875668
--- /dev/null
+++ b/common/crypto/bls/error.go
@@ -0,0 +1 @@
+package bls
diff --git a/common/crypto/bls/interface.go b/common/crypto/bls/interface.go
new file mode 100644
index 0000000..aa4725e
--- /dev/null
+++ b/common/crypto/bls/interface.go
@@ -0,0 +1,12 @@
+package bls
+
+import "github.com/astranetworld/ast/common/crypto/bls/common"
+
+// PublicKey represents a BLS public key.
+type PublicKey = common.PublicKey
+
+// SecretKey represents a BLS secret or private key.
+type SecretKey = common.SecretKey
+
+// Signature represents a BLS signature.
+type Signature = common.Signature
diff --git a/common/crypto/bls/lbs_ntest.go b/common/crypto/bls/lbs_ntest.go
new file mode 100644
index 0000000..68b154d
--- /dev/null
+++ b/common/crypto/bls/lbs_ntest.go
@@ -0,0 +1,416 @@
+//go:build (android || (linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package bls
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "strings"
+
+ "github.com/astranetworld/ast/common/crypto/bls/blst"
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+ "github.com/stretchr/testify/assert"
+ //"github.com/stretchr/testify/assert"
+)
+
+func E2P(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func TestSignVerify2() error {
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ msg := []byte("hello")
+ sig := priv.Sign(msg)
+ if !sig.Verify(pub, msg) {
+ return errors.New("Signature did not verify")
+ }
+ return nil
+}
+
+func TestAggregateVerify2() error {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ msgs = append(msgs, msg)
+ }
+ aggSig := AggregateSignatures(sigs)
+ if !aggSig.AggregateVerify(pubkeys, msgs) {
+ return errors.New("Signature did not verify")
+ }
+ return nil
+}
+
+func TestAggregateVerify_CompressedSignatures2() error {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ var sigBytes [][]byte
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ sigBytes = append(sigBytes, sig.Marshal())
+ msgs = append(msgs, msg)
+ }
+ aggSig := AggregateSignatures(sigs)
+ if !aggSig.AggregateVerify(pubkeys, msgs) {
+ return errors.New("Signature did not verify")
+ }
+
+ aggSig2, err := AggregateCompressedSignatures(sigBytes)
+ if err != nil {
+ return err
+ }
+ if bytes.Compare(aggSig.Marshal(), aggSig2.Marshal()) != 0 {
+ return errors.New("Signature did not match up")
+ }
+ return nil
+}
+
+func TestFastAggregateVerify2() error {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+ for i := 0; i < 100; i++ {
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ }
+ aggSig := AggregateSignatures(sigs)
+ if !aggSig.FastAggregateVerify(pubkeys, msg) {
+ return errors.New("Signature did not verify")
+ }
+ return nil
+}
+
+func TestVerifyCompressed2() error {
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ msg := []byte("hello")
+ sig := priv.Sign(msg)
+ if !sig.Verify(pub, msg) {
+ return errors.New("Non compressed signature did not verify")
+ }
+
+ if !blst.VerifyCompressed(sig.Marshal(), pub.Marshal(), msg) {
+ return errors.New("Compressed signatures and pubkeys did not verify")
+ }
+ return nil
+}
+
+func TestMultipleSignatureVerification2() error {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([][]byte, 0, 100)
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:]).Marshal()
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ msgs = append(msgs, msg)
+ }
+ verify, err := VerifyMultipleSignatures(sigs, msgs, pubkeys)
+ if err != nil {
+ return err
+ }
+ if !verify {
+ return errors.New("Signature did not verify")
+ }
+ return nil
+}
+
+func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList2() error {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ aggSig := NewAggregateSignature()
+ if aggSig.FastAggregateVerify(pubkeys, msg) {
+ return errors.New("Expected FastAggregateVerify to return false with empty input")
+ }
+ return nil
+}
+
+func TestEth2FastAggregateVerify2() error {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+ for i := 0; i < 100; i++ {
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ }
+ aggSig := AggregateSignatures(sigs)
+ if !aggSig.Eth2FastAggregateVerify(pubkeys, msg) {
+ return errors.New("Signature did not verify")
+ }
+ return nil
+}
+
+func TestEth2FastAggregateVerify_ReturnsFalseOnEmptyPubKeyList2() error {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ aggSig := NewAggregateSignature()
+ if aggSig.Eth2FastAggregateVerify(pubkeys, msg) {
+ return errors.New("Expected Eth2FastAggregateVerify to return false with empty input")
+ }
+ return nil
+}
+
+func TestEth2FastAggregateVerify_ReturnsTrueOnG2PointAtInfinity2() error {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ g2PointAtInfinity := append([]byte{0xC0}, make([]byte, 95)...)
+ aggSig, err := SignatureFromBytes(g2PointAtInfinity)
+ if err != nil {
+ return err
+ }
+ if !aggSig.Eth2FastAggregateVerify(pubkeys, msg) {
+ return errors.New("Eth2FastAggregateVerify error")
+ }
+ return nil
+}
+
+func TestSignatureFromBytes2() error {
+ tests := []struct {
+ name string
+ input []byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Empty",
+ input: []byte{},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Short",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Long",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Bad",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("could not unmarshal bytes into signature"),
+ },
+ {
+ name: "Good",
+ input: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
+ },
+ }
+
+ for _, test := range tests {
+ res, err := SignatureFromBytes(test.input)
+ if test.err != nil {
+ if err == nil {
+ return errors.New("No error returned")
+ }
+ //assert.ErrorContains(t, test.err, err.Error(), "Unexpected error returned")
+ } else {
+ if err != nil {
+ return err
+ }
+ if bytes.Compare(res.Marshal(), test.input) != 0 {
+ return errors.New("bytes.Compare(res.Marshal(), test.input)!=0")
+ }
+ }
+ }
+ return nil
+}
+
+func TestMultipleSignatureFromBytes2() error {
+ tests := []struct {
+ name string
+ input [][]byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("0 signatures provided to the method"),
+ },
+ {
+ name: "Empty",
+ input: [][]byte{},
+ err: errors.New("0 signatures provided to the method"),
+ },
+ {
+ name: "Short",
+ input: [][]byte{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Long",
+ input: [][]byte{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Bad",
+ input: [][]byte{{0x8f, 0xc0, 0xb4, 0x9e, 0x2e, 0xac, 0x50, 0x86, 0xe2, 0xe2, 0xaa, 0xf, 0xdc, 0x54, 0x23, 0x51, 0x6, 0xd8, 0x29, 0xf5, 0xae, 0x3, 0x5d, 0xb8, 0x31, 0x4d, 0x26, 0x3, 0x48, 0x18, 0xb9, 0x1f, 0x6b, 0xd7, 0x86, 0xb4, 0xa2, 0x69, 0xc7, 0xe7, 0xf5, 0xc0, 0x93, 0x19, 0x6e, 0xfd, 0x33, 0xb8, 0x1, 0xe1, 0x1f, 0x4e, 0xb4, 0xb1, 0xa0, 0x1, 0x30, 0x48, 0x8a, 0x6c, 0x97, 0x29, 0xd6, 0xcb, 0x1c, 0x45, 0xef, 0x87, 0xba, 0x4f, 0xce, 0x22, 0x84, 0x48, 0xad, 0x16, 0xf7, 0x5c, 0xb2, 0xa8, 0x34, 0xb9, 0xee, 0xb8, 0xbf, 0xe5, 0x58, 0x2c, 0x44, 0x7b, 0x1f, 0x9c, 0x22, 0x26, 0x3a, 0x22},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("could not unmarshal bytes into signature"),
+ },
+ {
+ name: "Good",
+ input: [][]byte{
+ {0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
+ {0xb7, 0x86, 0xe5, 0x7, 0x43, 0xe2, 0x53, 0x6c, 0x15, 0x51, 0x9c, 0x6, 0x2a, 0xa7, 0xe5, 0x12, 0xf9, 0xb7, 0x77, 0x93, 0x3f, 0x55, 0xb3, 0xaf, 0x38, 0xf7, 0x39, 0xe4, 0x84, 0x6d, 0x88, 0x44, 0x52, 0x77, 0x65, 0x42, 0x95, 0xd9, 0x79, 0x93, 0x7e, 0xc8, 0x12, 0x60, 0xe3, 0x24, 0xea, 0x8, 0x10, 0x52, 0xcd, 0xd2, 0x7f, 0x5d, 0x25, 0x3a, 0xa8, 0x9b, 0xb7, 0x65, 0xa9, 0x31, 0xea, 0x7c, 0x85, 0x13, 0x53, 0xc0, 0xa3, 0x88, 0xd1, 0xa5, 0x54, 0x85, 0x2, 0x2d, 0xf8, 0xa1, 0xd7, 0xc1, 0x60, 0x58, 0x93, 0xec, 0x7c, 0xf9, 0x33, 0x43, 0x4, 0x48, 0x40, 0x97, 0xef, 0x67, 0x2a, 0x27},
+ {0xb2, 0x12, 0xd0, 0xec, 0x46, 0x76, 0x6b, 0x24, 0x71, 0x91, 0x2e, 0xa8, 0x53, 0x9a, 0x48, 0xa3, 0x78, 0x30, 0xc, 0xe8, 0xf0, 0x86, 0xa3, 0x68, 0xec, 0xe8, 0x96, 0x43, 0x34, 0xda, 0xf, 0xf4, 0x65, 0x48, 0xbb, 0xe0, 0x92, 0xa1, 0x8, 0x12, 0x18, 0x46, 0xe6, 0x4a, 0xd6, 0x92, 0x88, 0xe, 0x2, 0xf5, 0xf3, 0x2a, 0x96, 0xb1, 0x4, 0xf1, 0x11, 0xa9, 0x92, 0x79, 0x52, 0x0, 0x64, 0x34, 0xeb, 0x25, 0xe, 0xf4, 0x29, 0x6b, 0x39, 0x4e, 0x28, 0x78, 0xfe, 0x25, 0xa3, 0xc0, 0x88, 0x5a, 0x40, 0xfd, 0x71, 0x37, 0x63, 0x79, 0xcd, 0x6b, 0x56, 0xda, 0xee, 0x91, 0x26, 0x72, 0xfc, 0xbc},
+ {0x8f, 0xc0, 0xb4, 0x9e, 0x2e, 0xac, 0x50, 0x86, 0xe2, 0xe2, 0xaa, 0xf, 0xdc, 0x54, 0x23, 0x51, 0x6, 0xd8, 0x29, 0xf5, 0xae, 0x3, 0x5d, 0xb8, 0x31, 0x4d, 0x26, 0x3, 0x48, 0x18, 0xb9, 0x1f, 0x6b, 0xd7, 0x86, 0xb4, 0xa2, 0x69, 0xc7, 0xe7, 0xf5, 0xc0, 0x93, 0x19, 0x6e, 0xfd, 0x33, 0xb8, 0x1, 0xe1, 0x1f, 0x4e, 0xb4, 0xb1, 0xa0, 0x1, 0x30, 0x48, 0x8a, 0x6c, 0x97, 0x29, 0xd6, 0xcb, 0x1c, 0x45, 0xef, 0x87, 0xba, 0x4f, 0xce, 0x22, 0x84, 0x48, 0xad, 0x16, 0xf7, 0x5c, 0xb2, 0xa8, 0x34, 0xb9, 0xee, 0xb8, 0xbf, 0xe5, 0x58, 0x2c, 0x44, 0x7b, 0x1f, 0x9c, 0x22, 0x26, 0x3a, 0x22},
+ },
+ },
+ }
+
+ for _, test := range tests {
+ res, err := MultipleSignaturesFromBytes(test.input)
+ if test.err != nil {
+ if err == nil {
+ return errors.New("No error returned")
+ }
+ if !strings.Contains(test.err.Error(), err.Error()) {
+ return errors.New("Unexpected error returned")
+ }
+ } else {
+ if err != nil {
+ return err
+ }
+ for i, s := range res {
+ if bytes.Compare(s.Marshal(), test.input[i]) != 0 {
+ return errors.New("329:bytes.Compare(s.Marshal(), test.input[i])!=0")
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func TestCopy2() error {
+ priv, err := RandKey()
+ if err != nil {
+ return err
+ }
+
+ signatureA := priv.Sign([]byte("foo"))
+ signatureB := signatureA.Copy()
+
+ if signatureA == signatureB {
+ return errors.New("signatureA equal to signatureB")
+ }
+
+ signatureC := priv.Sign([]byte("bar"))
+ if assert.ObjectsAreEqual(signatureC, signatureB) {
+ return errors.New("assert.ObjectsAreEqual(signatureC, signatureB){")
+ }
+ return nil
+}
+
+func TestSecretKeyFromBytes2() error {
+ b, _ := hex.DecodeString("5de474bbf3fee5dfda9047287f596c6e6c0271876305357e399fffba6a5f9a9f")
+ tests := []struct {
+ name string
+ input []byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Empty",
+ input: []byte{},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Short",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Long",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Bad",
+ input: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ err: common.ErrSecretUnmarshal,
+ },
+ {
+ name: "Good",
+ input: []byte{0x25, 0x29, 0x5f, 0x0d, 0x1d, 0x59, 0x2a, 0x90, 0xb3, 0x33, 0xe2, 0x6e, 0x85, 0x14, 0x97, 0x08, 0x20, 0x8e, 0x9f, 0x8e, 0x8b, 0xc1, 0x8f, 0x6c, 0x77, 0xbd, 0x62, 0xf8, 0xad, 0x7a, 0x68, 0x66},
+ },
+ {
+ name: "myTest",
+ input: b,
+ },
+ }
+
+ for _, test := range tests {
+ res, err := SecretKeyFromBytes(test.input)
+ if test.err != nil {
+ if err == nil {
+ return errors.New("No error returned")
+ }
+ if !strings.Contains(test.err.Error(), err.Error()) {
+ return errors.New("Unexpected error returned")
+ }
+ } else {
+ if err != nil {
+ return err
+ }
+ if bytes.Compare(res.Marshal(), test.input) != 0 {
+ return errors.New("bytes.Compare(res.Marshal(), test.input)!=0")
+ }
+ }
+ }
+ return nil
+}
diff --git a/common/crypto/bls/lbs_test.go b/common/crypto/bls/lbs_test.go
new file mode 100644
index 0000000..3da3cc2
--- /dev/null
+++ b/common/crypto/bls/lbs_test.go
@@ -0,0 +1,332 @@
+//go:build ((linux && amd64) || (linux && arm64) || (darwin && amd64) || (darwin && arm64) || (windows && amd64)) && !blst_disabled
+
+package bls
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "github.com/astranetworld/ast/common/crypto/bls/blst"
+ "github.com/astranetworld/ast/common/crypto/bls/common"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "testing"
+)
+
+func TestSignVerify(t *testing.T) {
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ msg := []byte("hello")
+ sig := priv.Sign(msg)
+ assert.Equal(t, true, sig.Verify(pub, msg), "Signature did not verify")
+}
+
+func TestAggregateVerify(t *testing.T) {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ msgs = append(msgs, msg)
+ }
+ aggSig := AggregateSignatures(sigs)
+ assert.Equal(t, true, aggSig.AggregateVerify(pubkeys, msgs), "Signature did not verify")
+}
+
+func TestAggregateVerify_CompressedSignatures(t *testing.T) {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ var sigBytes [][]byte
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ sigBytes = append(sigBytes, sig.Marshal())
+ msgs = append(msgs, msg)
+ }
+ aggSig := AggregateSignatures(sigs)
+ assert.Equal(t, true, aggSig.AggregateVerify(pubkeys, msgs), "Signature did not verify")
+
+ aggSig2, err := AggregateCompressedSignatures(sigBytes)
+ assert.NoError(t, err)
+ assert.Equal(t, aggSig.Marshal(), aggSig2.Marshal(), "Signature did not match up")
+}
+
+func TestFastAggregateVerify(t *testing.T) {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+ for i := 0; i < 100; i++ {
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ }
+ aggSig := AggregateSignatures(sigs)
+ assert.Equal(t, true, aggSig.FastAggregateVerify(pubkeys, msg), "Signature did not verify")
+
+}
+
+func TestVerifyCompressed(t *testing.T) {
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ msg := []byte("hello")
+ sig := priv.Sign(msg)
+ assert.Equal(t, true, sig.Verify(pub, msg), "Non compressed signature did not verify")
+ assert.Equal(t, true, blst.VerifyCompressed(sig.Marshal(), pub.Marshal(), msg), "Compressed signatures and pubkeys did not verify")
+}
+
+func TestMultipleSignatureVerification(t *testing.T) {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([][]byte, 0, 100)
+ var msgs [][32]byte
+ for i := 0; i < 100; i++ {
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o', byte(i)}
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:]).Marshal()
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ msgs = append(msgs, msg)
+ }
+ verify, err := VerifyMultipleSignatures(sigs, msgs, pubkeys)
+ assert.NoError(t, err, "Signature did not verify")
+ assert.Equal(t, true, verify, "Signature did not verify")
+}
+
+func TestFastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ aggSig := NewAggregateSignature()
+ assert.Equal(t, false, aggSig.FastAggregateVerify(pubkeys, msg), "Expected FastAggregateVerify to return false with empty input ")
+}
+
+func TestEth2FastAggregateVerify(t *testing.T) {
+ pubkeys := make([]common.PublicKey, 0, 100)
+ sigs := make([]common.Signature, 0, 100)
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+ for i := 0; i < 100; i++ {
+ priv, err := RandKey()
+ require.NoError(t, err)
+ pub := priv.PublicKey()
+ sig := priv.Sign(msg[:])
+ pubkeys = append(pubkeys, pub)
+ sigs = append(sigs, sig)
+ }
+ aggSig := AggregateSignatures(sigs)
+ assert.Equal(t, true, aggSig.Eth2FastAggregateVerify(pubkeys, msg), "Signature did not verify")
+
+}
+
+func TestEth2FastAggregateVerify_ReturnsFalseOnEmptyPubKeyList(t *testing.T) {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ aggSig := NewAggregateSignature()
+ assert.Equal(t, false, aggSig.Eth2FastAggregateVerify(pubkeys, msg), "Expected Eth2FastAggregateVerify to return false with empty input ")
+}
+
+func TestEth2FastAggregateVerify_ReturnsTrueOnG2PointAtInfinity(t *testing.T) {
+ var pubkeys []common.PublicKey
+ msg := [32]byte{'h', 'e', 'l', 'l', 'o'}
+
+ g2PointAtInfinity := append([]byte{0xC0}, make([]byte, 95)...)
+ aggSig, err := SignatureFromBytes(g2PointAtInfinity)
+ require.NoError(t, err)
+ assert.Equal(t, true, aggSig.Eth2FastAggregateVerify(pubkeys, msg))
+}
+
+func TestSignatureFromBytes(t *testing.T) {
+ tests := []struct {
+ name string
+ input []byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Empty",
+ input: []byte{},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Short",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Long",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Bad",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("could not unmarshal bytes into signature"),
+ },
+ {
+ name: "Good",
+ input: []byte{0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ res, err := SignatureFromBytes(test.input)
+ if test.err != nil {
+ assert.NotEqual(t, nil, err, "No error returned")
+ assert.ErrorContains(t, test.err, err.Error(), "Unexpected error returned")
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, 0, bytes.Compare(res.Marshal(), test.input))
+ }
+ })
+ }
+}
+
+func TestMultipleSignatureFromBytes(t *testing.T) {
+ tests := []struct {
+ name string
+ input [][]byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("0 signatures provided to the method"),
+ },
+ {
+ name: "Empty",
+ input: [][]byte{},
+ err: errors.New("0 signatures provided to the method"),
+ },
+ {
+ name: "Short",
+ input: [][]byte{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Long",
+ input: [][]byte{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("signature must be 96 bytes"),
+ },
+ {
+ name: "Bad",
+ input: [][]byte{{0x8f, 0xc0, 0xb4, 0x9e, 0x2e, 0xac, 0x50, 0x86, 0xe2, 0xe2, 0xaa, 0xf, 0xdc, 0x54, 0x23, 0x51, 0x6, 0xd8, 0x29, 0xf5, 0xae, 0x3, 0x5d, 0xb8, 0x31, 0x4d, 0x26, 0x3, 0x48, 0x18, 0xb9, 0x1f, 0x6b, 0xd7, 0x86, 0xb4, 0xa2, 0x69, 0xc7, 0xe7, 0xf5, 0xc0, 0x93, 0x19, 0x6e, 0xfd, 0x33, 0xb8, 0x1, 0xe1, 0x1f, 0x4e, 0xb4, 0xb1, 0xa0, 0x1, 0x30, 0x48, 0x8a, 0x6c, 0x97, 0x29, 0xd6, 0xcb, 0x1c, 0x45, 0xef, 0x87, 0xba, 0x4f, 0xce, 0x22, 0x84, 0x48, 0xad, 0x16, 0xf7, 0x5c, 0xb2, 0xa8, 0x34, 0xb9, 0xee, 0xb8, 0xbf, 0xe5, 0x58, 0x2c, 0x44, 0x7b, 0x1f, 0x9c, 0x22, 0x26, 0x3a, 0x22},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ err: errors.New("could not unmarshal bytes into signature"),
+ },
+ {
+ name: "Good",
+ input: [][]byte{
+ {0xab, 0xb0, 0x12, 0x4c, 0x75, 0x74, 0xf2, 0x81, 0xa2, 0x93, 0xf4, 0x18, 0x5c, 0xad, 0x3c, 0xb2, 0x26, 0x81, 0xd5, 0x20, 0x91, 0x7c, 0xe4, 0x66, 0x65, 0x24, 0x3e, 0xac, 0xb0, 0x51, 0x00, 0x0d, 0x8b, 0xac, 0xf7, 0x5e, 0x14, 0x51, 0x87, 0x0c, 0xa6, 0xb3, 0xb9, 0xe6, 0xc9, 0xd4, 0x1a, 0x7b, 0x02, 0xea, 0xd2, 0x68, 0x5a, 0x84, 0x18, 0x8a, 0x4f, 0xaf, 0xd3, 0x82, 0x5d, 0xaf, 0x6a, 0x98, 0x96, 0x25, 0xd7, 0x19, 0xcc, 0xd2, 0xd8, 0x3a, 0x40, 0x10, 0x1f, 0x4a, 0x45, 0x3f, 0xca, 0x62, 0x87, 0x8c, 0x89, 0x0e, 0xca, 0x62, 0x23, 0x63, 0xf9, 0xdd, 0xb8, 0xf3, 0x67, 0xa9, 0x1e, 0x84},
+ {0xb7, 0x86, 0xe5, 0x7, 0x43, 0xe2, 0x53, 0x6c, 0x15, 0x51, 0x9c, 0x6, 0x2a, 0xa7, 0xe5, 0x12, 0xf9, 0xb7, 0x77, 0x93, 0x3f, 0x55, 0xb3, 0xaf, 0x38, 0xf7, 0x39, 0xe4, 0x84, 0x6d, 0x88, 0x44, 0x52, 0x77, 0x65, 0x42, 0x95, 0xd9, 0x79, 0x93, 0x7e, 0xc8, 0x12, 0x60, 0xe3, 0x24, 0xea, 0x8, 0x10, 0x52, 0xcd, 0xd2, 0x7f, 0x5d, 0x25, 0x3a, 0xa8, 0x9b, 0xb7, 0x65, 0xa9, 0x31, 0xea, 0x7c, 0x85, 0x13, 0x53, 0xc0, 0xa3, 0x88, 0xd1, 0xa5, 0x54, 0x85, 0x2, 0x2d, 0xf8, 0xa1, 0xd7, 0xc1, 0x60, 0x58, 0x93, 0xec, 0x7c, 0xf9, 0x33, 0x43, 0x4, 0x48, 0x40, 0x97, 0xef, 0x67, 0x2a, 0x27},
+ {0xb2, 0x12, 0xd0, 0xec, 0x46, 0x76, 0x6b, 0x24, 0x71, 0x91, 0x2e, 0xa8, 0x53, 0x9a, 0x48, 0xa3, 0x78, 0x30, 0xc, 0xe8, 0xf0, 0x86, 0xa3, 0x68, 0xec, 0xe8, 0x96, 0x43, 0x34, 0xda, 0xf, 0xf4, 0x65, 0x48, 0xbb, 0xe0, 0x92, 0xa1, 0x8, 0x12, 0x18, 0x46, 0xe6, 0x4a, 0xd6, 0x92, 0x88, 0xe, 0x2, 0xf5, 0xf3, 0x2a, 0x96, 0xb1, 0x4, 0xf1, 0x11, 0xa9, 0x92, 0x79, 0x52, 0x0, 0x64, 0x34, 0xeb, 0x25, 0xe, 0xf4, 0x29, 0x6b, 0x39, 0x4e, 0x28, 0x78, 0xfe, 0x25, 0xa3, 0xc0, 0x88, 0x5a, 0x40, 0xfd, 0x71, 0x37, 0x63, 0x79, 0xcd, 0x6b, 0x56, 0xda, 0xee, 0x91, 0x26, 0x72, 0xfc, 0xbc},
+ {0x8f, 0xc0, 0xb4, 0x9e, 0x2e, 0xac, 0x50, 0x86, 0xe2, 0xe2, 0xaa, 0xf, 0xdc, 0x54, 0x23, 0x51, 0x6, 0xd8, 0x29, 0xf5, 0xae, 0x3, 0x5d, 0xb8, 0x31, 0x4d, 0x26, 0x3, 0x48, 0x18, 0xb9, 0x1f, 0x6b, 0xd7, 0x86, 0xb4, 0xa2, 0x69, 0xc7, 0xe7, 0xf5, 0xc0, 0x93, 0x19, 0x6e, 0xfd, 0x33, 0xb8, 0x1, 0xe1, 0x1f, 0x4e, 0xb4, 0xb1, 0xa0, 0x1, 0x30, 0x48, 0x8a, 0x6c, 0x97, 0x29, 0xd6, 0xcb, 0x1c, 0x45, 0xef, 0x87, 0xba, 0x4f, 0xce, 0x22, 0x84, 0x48, 0xad, 0x16, 0xf7, 0x5c, 0xb2, 0xa8, 0x34, 0xb9, 0xee, 0xb8, 0xbf, 0xe5, 0x58, 0x2c, 0x44, 0x7b, 0x1f, 0x9c, 0x22, 0x26, 0x3a, 0x22},
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ res, err := MultipleSignaturesFromBytes(test.input)
+ if test.err != nil {
+ assert.NotEqual(t, nil, err, "No error returned")
+ assert.ErrorContains(t, test.err, err.Error(), "Unexpected error returned")
+ } else {
+ assert.NoError(t, err)
+ for i, s := range res {
+ assert.Equal(t, 0, bytes.Compare(s.Marshal(), test.input[i]))
+ }
+ }
+ })
+ }
+}
+
+func TestCopy(t *testing.T) {
+ priv, err := RandKey()
+ require.NoError(t, err)
+
+ signatureA := priv.Sign([]byte("foo"))
+ signatureB := signatureA.Copy()
+
+ if signatureA == signatureB {
+ t.Fatal("signatureA equal to signatureB")
+ }
+
+ signatureC := priv.Sign([]byte("bar"))
+ assert.NotEqual(t, signatureC, signatureB)
+}
+
+func TestSecretKeyFromBytes(t *testing.T) {
+ b, _ := hex.DecodeString("5de474bbf3fee5dfda9047287f596c6e6c0271876305357e399fffba6a5f9a9f")
+ tests := []struct {
+ name string
+ input []byte
+ err error
+ }{
+ {
+ name: "Nil",
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Empty",
+ input: []byte{},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Short",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Long",
+ input: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ err: errors.New("secret key must be 32 bytes"),
+ },
+ {
+ name: "Bad",
+ input: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ err: common.ErrSecretUnmarshal,
+ },
+ {
+ name: "Good",
+ input: []byte{0x25, 0x29, 0x5f, 0x0d, 0x1d, 0x59, 0x2a, 0x90, 0xb3, 0x33, 0xe2, 0x6e, 0x85, 0x14, 0x97, 0x08, 0x20, 0x8e, 0x9f, 0x8e, 0x8b, 0xc1, 0x8f, 0x6c, 0x77, 0xbd, 0x62, 0xf8, 0xad, 0x7a, 0x68, 0x66},
+ },
+ {
+ name: "myTest",
+ input: b,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ res, err := SecretKeyFromBytes(test.input)
+ if test.err != nil {
+ assert.NotEqual(t, nil, err, "No error returned")
+ assert.ErrorContains(t, test.err, err.Error(), "Unexpected error returned")
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, 0, bytes.Compare(res.Marshal(), test.input))
+ }
+ })
+ }
+}
diff --git a/common/crypto/bls/signature_batch.go b/common/crypto/bls/signature_batch.go
new file mode 100644
index 0000000..be4d046
--- /dev/null
+++ b/common/crypto/bls/signature_batch.go
@@ -0,0 +1,148 @@
+package bls
+
+import "github.com/pkg/errors"
+
+// SignatureBatch refers to the defined set of
+// signatures and its respective public keys and
+// messages required to verify it.
+type SignatureBatch struct {
+ Signatures [][]byte
+ PublicKeys []PublicKey
+ Messages [][32]byte
+}
+
+// NewSet constructs an empty signature batch object.
+func NewSet() *SignatureBatch {
+ return &SignatureBatch{
+ Signatures: [][]byte{},
+ PublicKeys: []PublicKey{},
+ Messages: [][32]byte{},
+ }
+}
+
+// Join merges the provided signature batch to out current one.
+func (s *SignatureBatch) Join(set *SignatureBatch) *SignatureBatch {
+ s.Signatures = append(s.Signatures, set.Signatures...)
+ s.PublicKeys = append(s.PublicKeys, set.PublicKeys...)
+ s.Messages = append(s.Messages, set.Messages...)
+ return s
+}
+
+// Verify the current signature batch using the batch verify algorithm.
+func (s *SignatureBatch) Verify() (bool, error) {
+ return VerifyMultipleSignatures(s.Signatures, s.Messages, s.PublicKeys)
+}
+
+// Copy the attached signature batch and return it
+// to the caller.
+func (s *SignatureBatch) Copy() *SignatureBatch {
+ signatures := make([][]byte, len(s.Signatures))
+ pubkeys := make([]PublicKey, len(s.PublicKeys))
+ messages := make([][32]byte, len(s.Messages))
+ for i := range s.Signatures {
+ sig := make([]byte, len(s.Signatures[i]))
+ copy(sig, s.Signatures[i])
+ signatures[i] = sig
+ }
+ for i := range s.PublicKeys {
+ pubkeys[i] = s.PublicKeys[i].Copy()
+ }
+ for i := range s.Messages {
+ copy(messages[i][:], s.Messages[i][:])
+ }
+ return &SignatureBatch{
+ Signatures: signatures,
+ PublicKeys: pubkeys,
+ Messages: messages,
+ }
+}
+
+// RemoveDuplicates removes duplicate signature sets from the signature batch.
+func (s *SignatureBatch) RemoveDuplicates() (int, *SignatureBatch, error) {
+ if len(s.Signatures) == 0 || len(s.PublicKeys) == 0 || len(s.Messages) == 0 {
+ return 0, s, nil
+ }
+ if len(s.Signatures) != len(s.PublicKeys) || len(s.Signatures) != len(s.Messages) {
+ return 0, s, errors.Errorf("mismatch number of signatures, publickeys and messages in signature batch. "+
+ "Signatures %d, Public Keys %d , Messages %d", s.Signatures, s.PublicKeys, s.Messages)
+ }
+ sigMap := make(map[string]int)
+ duplicateSet := make(map[int]bool)
+ for i := 0; i < len(s.Signatures); i++ {
+ if sigIdx, ok := sigMap[string(s.Signatures[i])]; ok {
+ if s.PublicKeys[sigIdx].Equals(s.PublicKeys[i]) &&
+ s.Messages[sigIdx] == s.Messages[i] {
+ duplicateSet[i] = true
+ continue
+ }
+ }
+ sigMap[string(s.Signatures[i])] = i
+ }
+
+ sigs := s.Signatures[:0]
+ pubs := s.PublicKeys[:0]
+ msgs := s.Messages[:0]
+
+ for i := 0; i < len(s.Signatures); i++ {
+ if duplicateSet[i] {
+ continue
+ }
+ sigs = append(sigs, s.Signatures[i])
+ pubs = append(pubs, s.PublicKeys[i])
+ msgs = append(msgs, s.Messages[i])
+ }
+
+ s.Signatures = sigs
+ s.PublicKeys = pubs
+ s.Messages = msgs
+
+ return len(duplicateSet), s, nil
+}
+
+// AggregateBatch aggregates common messages in the provided batch to
+// reduce the number of pairings required when we finally verify the
+// whole batch.
+func (s *SignatureBatch) AggregateBatch() (*SignatureBatch, error) {
+ if len(s.Signatures) == 0 || len(s.PublicKeys) == 0 || len(s.Messages) == 0 {
+ return s, nil
+ }
+ if len(s.Signatures) != len(s.PublicKeys) || len(s.Signatures) != len(s.Messages) {
+ return s, errors.Errorf("mismatch number of signatures, publickeys and messages in signature batch. "+
+ "Signatures %d, Public Keys %d , Messages %d", s.Signatures, s.PublicKeys, s.Messages)
+ }
+ msgMap := make(map[[32]byte]*SignatureBatch)
+
+ for i := 0; i < len(s.Messages); i++ {
+ currMsg := s.Messages[i]
+ currBatch, ok := msgMap[currMsg]
+ if ok {
+ currBatch.Signatures = append(currBatch.Signatures, s.Signatures[i])
+ currBatch.Messages = append(currBatch.Messages, s.Messages[i])
+ currBatch.PublicKeys = append(currBatch.PublicKeys, s.PublicKeys[i])
+ continue
+ }
+ currBatch = &SignatureBatch{
+ Signatures: [][]byte{s.Signatures[i]},
+ Messages: [][32]byte{s.Messages[i]},
+ PublicKeys: []PublicKey{s.PublicKeys[i]},
+ }
+ msgMap[currMsg] = currBatch
+ }
+ newSt := NewSet()
+ for rt, b := range msgMap {
+ if len(b.PublicKeys) > 1 {
+ aggPub := AggregateMultiplePubkeys(b.PublicKeys)
+ aggSig, err := AggregateCompressedSignatures(b.Signatures)
+ if err != nil {
+ return nil, err
+ }
+ copiedRt := rt
+ b.PublicKeys = []PublicKey{aggPub}
+ b.Signatures = [][]byte{aggSig.Marshal()}
+ b.Messages = [][32]byte{copiedRt}
+ }
+ newObj := *b
+ newSt = newSt.Join(&newObj)
+ }
+ return newSt, nil
+}
diff --git a/common/crypto/bls12381/arithmetic_decl.go b/common/crypto/bls12381/arithmetic_decl.go
new file mode 100644
index 0000000..09e2ec6
--- /dev/null
+++ b/common/crypto/bls12381/arithmetic_decl.go
@@ -0,0 +1,83 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build (amd64 && blsasm) || (amd64 && blsadx)
+
+package bls12381
+
+import (
+ "golang.org/x/sys/cpu"
+)
+
+func init() {
+ if !enableADX || !cpu.X86.HasADX || !cpu.X86.HasBMI2 {
+ mul = mulNoADX
+ }
+}
+
+// Use ADX backend for default
+var mul func(c, a, b *fe) = mulADX
+
+func square(c, a *fe) {
+ mul(c, a, a)
+}
+
+func neg(c, a *fe) {
+ if a.isZero() {
+ c.set(a)
+ } else {
+ _neg(c, a)
+ }
+}
+
+//go:noescape
+func add(c, a, b *fe)
+
+//go:noescape
+func addAssign(a, b *fe)
+
+//go:noescape
+func ladd(c, a, b *fe)
+
+//go:noescape
+func laddAssign(a, b *fe)
+
+//go:noescape
+func double(c, a *fe)
+
+//go:noescape
+func doubleAssign(a *fe)
+
+//go:noescape
+func ldouble(c, a *fe)
+
+//go:noescape
+func sub(c, a, b *fe)
+
+//go:noescape
+func subAssign(a, b *fe)
+
+//go:noescape
+func lsubAssign(a, b *fe)
+
+//go:noescape
+func _neg(c, a *fe)
+
+//go:noescape
+func mulNoADX(c, a, b *fe)
+
+//go:noescape
+func mulADX(c, a, b *fe)
diff --git a/common/crypto/bls12381/arithmetic_fallback.go b/common/crypto/bls12381/arithmetic_fallback.go
new file mode 100644
index 0000000..b7774be
--- /dev/null
+++ b/common/crypto/bls12381/arithmetic_fallback.go
@@ -0,0 +1,566 @@
+// Native go field arithmetic code is generated with 'goff'
+// https://github.com/ConsenSys/goff
+// Many function signature of field operations are renamed.
+
+// Copyright 2020 ConsenSys AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// field modulus q =
+//
+// 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
+// Code generated by goff DO NOT EDIT
+// goff version: v0.1.0 - build: 790f1f56eac432441e043abff8819eacddd1d668
+// fe are assumed to be in Montgomery form in all methods
+
+// /!\ WARNING /!\
+// this code has not been audited and is provided as-is. In particular,
+// there is no security guarantees such as constant time implementation
+// or side-channel attack resistance
+// /!\ WARNING /!\
+
+// Package bls (generated by goff) contains field arithmetics operations
+
+//go:build !amd64 || (!blsasm && !blsadx)
+
+package bls12381
+
+import (
+ "math/bits"
+)
+
+func add(z, x, y *fe) {
+ var carry uint64
+
+ z[0], carry = bits.Add64(x[0], y[0], 0)
+ z[1], carry = bits.Add64(x[1], y[1], carry)
+ z[2], carry = bits.Add64(x[2], y[2], carry)
+ z[3], carry = bits.Add64(x[3], y[3], carry)
+ z[4], carry = bits.Add64(x[4], y[4], carry)
+ z[5], _ = bits.Add64(x[5], y[5], carry)
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], 13402431016077863595, 0)
+ z[1], b = bits.Sub64(z[1], 2210141511517208575, b)
+ z[2], b = bits.Sub64(z[2], 7435674573564081700, b)
+ z[3], b = bits.Sub64(z[3], 7239337960414712511, b)
+ z[4], b = bits.Sub64(z[4], 5412103778470702295, b)
+ z[5], _ = bits.Sub64(z[5], 1873798617647539866, b)
+ }
+}
+
+func addAssign(x, y *fe) {
+ var carry uint64
+
+ x[0], carry = bits.Add64(x[0], y[0], 0)
+ x[1], carry = bits.Add64(x[1], y[1], carry)
+ x[2], carry = bits.Add64(x[2], y[2], carry)
+ x[3], carry = bits.Add64(x[3], y[3], carry)
+ x[4], carry = bits.Add64(x[4], y[4], carry)
+ x[5], _ = bits.Add64(x[5], y[5], carry)
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(x[5] < 1873798617647539866 || (x[5] == 1873798617647539866 && (x[4] < 5412103778470702295 || (x[4] == 5412103778470702295 && (x[3] < 7239337960414712511 || (x[3] == 7239337960414712511 && (x[2] < 7435674573564081700 || (x[2] == 7435674573564081700 && (x[1] < 2210141511517208575 || (x[1] == 2210141511517208575 && (x[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ x[0], b = bits.Sub64(x[0], 13402431016077863595, 0)
+ x[1], b = bits.Sub64(x[1], 2210141511517208575, b)
+ x[2], b = bits.Sub64(x[2], 7435674573564081700, b)
+ x[3], b = bits.Sub64(x[3], 7239337960414712511, b)
+ x[4], b = bits.Sub64(x[4], 5412103778470702295, b)
+ x[5], _ = bits.Sub64(x[5], 1873798617647539866, b)
+ }
+}
+
+func ladd(z, x, y *fe) {
+ var carry uint64
+ z[0], carry = bits.Add64(x[0], y[0], 0)
+ z[1], carry = bits.Add64(x[1], y[1], carry)
+ z[2], carry = bits.Add64(x[2], y[2], carry)
+ z[3], carry = bits.Add64(x[3], y[3], carry)
+ z[4], carry = bits.Add64(x[4], y[4], carry)
+ z[5], _ = bits.Add64(x[5], y[5], carry)
+}
+
+func laddAssign(x, y *fe) {
+ var carry uint64
+ x[0], carry = bits.Add64(x[0], y[0], 0)
+ x[1], carry = bits.Add64(x[1], y[1], carry)
+ x[2], carry = bits.Add64(x[2], y[2], carry)
+ x[3], carry = bits.Add64(x[3], y[3], carry)
+ x[4], carry = bits.Add64(x[4], y[4], carry)
+ x[5], _ = bits.Add64(x[5], y[5], carry)
+}
+
+func double(z, x *fe) {
+ var carry uint64
+
+ z[0], carry = bits.Add64(x[0], x[0], 0)
+ z[1], carry = bits.Add64(x[1], x[1], carry)
+ z[2], carry = bits.Add64(x[2], x[2], carry)
+ z[3], carry = bits.Add64(x[3], x[3], carry)
+ z[4], carry = bits.Add64(x[4], x[4], carry)
+ z[5], _ = bits.Add64(x[5], x[5], carry)
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], 13402431016077863595, 0)
+ z[1], b = bits.Sub64(z[1], 2210141511517208575, b)
+ z[2], b = bits.Sub64(z[2], 7435674573564081700, b)
+ z[3], b = bits.Sub64(z[3], 7239337960414712511, b)
+ z[4], b = bits.Sub64(z[4], 5412103778470702295, b)
+ z[5], _ = bits.Sub64(z[5], 1873798617647539866, b)
+ }
+}
+
+func doubleAssign(z *fe) {
+ var carry uint64
+
+ z[0], carry = bits.Add64(z[0], z[0], 0)
+ z[1], carry = bits.Add64(z[1], z[1], carry)
+ z[2], carry = bits.Add64(z[2], z[2], carry)
+ z[3], carry = bits.Add64(z[3], z[3], carry)
+ z[4], carry = bits.Add64(z[4], z[4], carry)
+ z[5], _ = bits.Add64(z[5], z[5], carry)
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], 13402431016077863595, 0)
+ z[1], b = bits.Sub64(z[1], 2210141511517208575, b)
+ z[2], b = bits.Sub64(z[2], 7435674573564081700, b)
+ z[3], b = bits.Sub64(z[3], 7239337960414712511, b)
+ z[4], b = bits.Sub64(z[4], 5412103778470702295, b)
+ z[5], _ = bits.Sub64(z[5], 1873798617647539866, b)
+ }
+}
+
+func ldouble(z, x *fe) {
+ var carry uint64
+
+ z[0], carry = bits.Add64(x[0], x[0], 0)
+ z[1], carry = bits.Add64(x[1], x[1], carry)
+ z[2], carry = bits.Add64(x[2], x[2], carry)
+ z[3], carry = bits.Add64(x[3], x[3], carry)
+ z[4], carry = bits.Add64(x[4], x[4], carry)
+ z[5], _ = bits.Add64(x[5], x[5], carry)
+}
+
+func sub(z, x, y *fe) {
+ var b uint64
+ z[0], b = bits.Sub64(x[0], y[0], 0)
+ z[1], b = bits.Sub64(x[1], y[1], b)
+ z[2], b = bits.Sub64(x[2], y[2], b)
+ z[3], b = bits.Sub64(x[3], y[3], b)
+ z[4], b = bits.Sub64(x[4], y[4], b)
+ z[5], b = bits.Sub64(x[5], y[5], b)
+ if b != 0 {
+ var c uint64
+ z[0], c = bits.Add64(z[0], 13402431016077863595, 0)
+ z[1], c = bits.Add64(z[1], 2210141511517208575, c)
+ z[2], c = bits.Add64(z[2], 7435674573564081700, c)
+ z[3], c = bits.Add64(z[3], 7239337960414712511, c)
+ z[4], c = bits.Add64(z[4], 5412103778470702295, c)
+ z[5], _ = bits.Add64(z[5], 1873798617647539866, c)
+ }
+}
+
+func subAssign(z, x *fe) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], x[0], 0)
+ z[1], b = bits.Sub64(z[1], x[1], b)
+ z[2], b = bits.Sub64(z[2], x[2], b)
+ z[3], b = bits.Sub64(z[3], x[3], b)
+ z[4], b = bits.Sub64(z[4], x[4], b)
+ z[5], b = bits.Sub64(z[5], x[5], b)
+ if b != 0 {
+ var c uint64
+ z[0], c = bits.Add64(z[0], 13402431016077863595, 0)
+ z[1], c = bits.Add64(z[1], 2210141511517208575, c)
+ z[2], c = bits.Add64(z[2], 7435674573564081700, c)
+ z[3], c = bits.Add64(z[3], 7239337960414712511, c)
+ z[4], c = bits.Add64(z[4], 5412103778470702295, c)
+ z[5], _ = bits.Add64(z[5], 1873798617647539866, c)
+ }
+}
+
+func lsubAssign(z, x *fe) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], x[0], 0)
+ z[1], b = bits.Sub64(z[1], x[1], b)
+ z[2], b = bits.Sub64(z[2], x[2], b)
+ z[3], b = bits.Sub64(z[3], x[3], b)
+ z[4], b = bits.Sub64(z[4], x[4], b)
+ z[5], _ = bits.Sub64(z[5], x[5], b)
+}
+
+func neg(z *fe, x *fe) {
+ if x.isZero() {
+ z.zero()
+ return
+ }
+ var borrow uint64
+ z[0], borrow = bits.Sub64(13402431016077863595, x[0], 0)
+ z[1], borrow = bits.Sub64(2210141511517208575, x[1], borrow)
+ z[2], borrow = bits.Sub64(7435674573564081700, x[2], borrow)
+ z[3], borrow = bits.Sub64(7239337960414712511, x[3], borrow)
+ z[4], borrow = bits.Sub64(5412103778470702295, x[4], borrow)
+ z[5], _ = bits.Sub64(1873798617647539866, x[5], borrow)
+}
+
+func mul(z, x, y *fe) {
+ var t [6]uint64
+ var c [3]uint64
+ {
+ // round 0
+ v := x[0]
+ c[1], c[0] = bits.Mul64(v, y[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd1(v, y[1], c[1])
+ c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd1(v, y[2], c[1])
+ c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd1(v, y[3], c[1])
+ c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd1(v, y[4], c[1])
+ c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd1(v, y[5], c[1])
+ t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+ {
+ // round 1
+ v := x[1]
+ c[1], c[0] = madd1(v, y[0], t[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd2(v, y[1], c[1], t[1])
+ c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd2(v, y[2], c[1], t[2])
+ c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd2(v, y[3], c[1], t[3])
+ c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd2(v, y[4], c[1], t[4])
+ c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd2(v, y[5], c[1], t[5])
+ t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+ {
+ // round 2
+ v := x[2]
+ c[1], c[0] = madd1(v, y[0], t[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd2(v, y[1], c[1], t[1])
+ c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd2(v, y[2], c[1], t[2])
+ c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd2(v, y[3], c[1], t[3])
+ c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd2(v, y[4], c[1], t[4])
+ c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd2(v, y[5], c[1], t[5])
+ t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+ {
+ // round 3
+ v := x[3]
+ c[1], c[0] = madd1(v, y[0], t[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd2(v, y[1], c[1], t[1])
+ c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd2(v, y[2], c[1], t[2])
+ c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd2(v, y[3], c[1], t[3])
+ c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd2(v, y[4], c[1], t[4])
+ c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd2(v, y[5], c[1], t[5])
+ t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+ {
+ // round 4
+ v := x[4]
+ c[1], c[0] = madd1(v, y[0], t[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd2(v, y[1], c[1], t[1])
+ c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd2(v, y[2], c[1], t[2])
+ c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd2(v, y[3], c[1], t[3])
+ c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd2(v, y[4], c[1], t[4])
+ c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd2(v, y[5], c[1], t[5])
+ t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+ {
+ // round 5
+ v := x[5]
+ c[1], c[0] = madd1(v, y[0], t[0])
+ m := c[0] * 9940570264628428797
+ c[2] = madd0(m, 13402431016077863595, c[0])
+ c[1], c[0] = madd2(v, y[1], c[1], t[1])
+ c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0])
+ c[1], c[0] = madd2(v, y[2], c[1], t[2])
+ c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0])
+ c[1], c[0] = madd2(v, y[3], c[1], t[3])
+ c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0])
+ c[1], c[0] = madd2(v, y[4], c[1], t[4])
+ c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0])
+ c[1], c[0] = madd2(v, y[5], c[1], t[5])
+ z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1])
+ }
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], 13402431016077863595, 0)
+ z[1], b = bits.Sub64(z[1], 2210141511517208575, b)
+ z[2], b = bits.Sub64(z[2], 7435674573564081700, b)
+ z[3], b = bits.Sub64(z[3], 7239337960414712511, b)
+ z[4], b = bits.Sub64(z[4], 5412103778470702295, b)
+ z[5], _ = bits.Sub64(z[5], 1873798617647539866, b)
+ }
+}
+
+func square(z, x *fe) {
+
+ var p [6]uint64
+
+ var u, v uint64
+ {
+ // round 0
+ u, p[0] = bits.Mul64(x[0], x[0])
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ var t uint64
+ t, u, v = madd1sb(x[0], x[1], u)
+ C, p[0] = madd2(m, 2210141511517208575, v, C)
+ t, u, v = madd1s(x[0], x[2], t, u)
+ C, p[1] = madd2(m, 7435674573564081700, v, C)
+ t, u, v = madd1s(x[0], x[3], t, u)
+ C, p[2] = madd2(m, 7239337960414712511, v, C)
+ t, u, v = madd1s(x[0], x[4], t, u)
+ C, p[3] = madd2(m, 5412103778470702295, v, C)
+ _, u, v = madd1s(x[0], x[5], t, u)
+ p[5], p[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+ {
+ // round 1
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ u, v = madd1(x[1], x[1], p[1])
+ C, p[0] = madd2(m, 2210141511517208575, v, C)
+ var t uint64
+ t, u, v = madd2sb(x[1], x[2], p[2], u)
+ C, p[1] = madd2(m, 7435674573564081700, v, C)
+ t, u, v = madd2s(x[1], x[3], p[3], t, u)
+ C, p[2] = madd2(m, 7239337960414712511, v, C)
+ t, u, v = madd2s(x[1], x[4], p[4], t, u)
+ C, p[3] = madd2(m, 5412103778470702295, v, C)
+ _, u, v = madd2s(x[1], x[5], p[5], t, u)
+ p[5], p[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+ {
+ // round 2
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ C, p[0] = madd2(m, 2210141511517208575, p[1], C)
+ u, v = madd1(x[2], x[2], p[2])
+ C, p[1] = madd2(m, 7435674573564081700, v, C)
+ var t uint64
+ t, u, v = madd2sb(x[2], x[3], p[3], u)
+ C, p[2] = madd2(m, 7239337960414712511, v, C)
+ t, u, v = madd2s(x[2], x[4], p[4], t, u)
+ C, p[3] = madd2(m, 5412103778470702295, v, C)
+ _, u, v = madd2s(x[2], x[5], p[5], t, u)
+ p[5], p[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+ {
+ // round 3
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ C, p[0] = madd2(m, 2210141511517208575, p[1], C)
+ C, p[1] = madd2(m, 7435674573564081700, p[2], C)
+ u, v = madd1(x[3], x[3], p[3])
+ C, p[2] = madd2(m, 7239337960414712511, v, C)
+ var t uint64
+ t, u, v = madd2sb(x[3], x[4], p[4], u)
+ C, p[3] = madd2(m, 5412103778470702295, v, C)
+ _, u, v = madd2s(x[3], x[5], p[5], t, u)
+ p[5], p[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+ {
+ // round 4
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ C, p[0] = madd2(m, 2210141511517208575, p[1], C)
+ C, p[1] = madd2(m, 7435674573564081700, p[2], C)
+ C, p[2] = madd2(m, 7239337960414712511, p[3], C)
+ u, v = madd1(x[4], x[4], p[4])
+ C, p[3] = madd2(m, 5412103778470702295, v, C)
+ _, u, v = madd2sb(x[4], x[5], p[5], u)
+ p[5], p[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+ {
+ // round 5
+ m := p[0] * 9940570264628428797
+ C := madd0(m, 13402431016077863595, p[0])
+ C, z[0] = madd2(m, 2210141511517208575, p[1], C)
+ C, z[1] = madd2(m, 7435674573564081700, p[2], C)
+ C, z[2] = madd2(m, 7239337960414712511, p[3], C)
+ C, z[3] = madd2(m, 5412103778470702295, p[4], C)
+ u, v = madd1(x[5], x[5], p[5])
+ z[5], z[4] = madd3(m, 1873798617647539866, v, C, u)
+ }
+
+ // if z > q --> z -= q
+ // note: this is NOT constant time
+ if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) {
+ var b uint64
+ z[0], b = bits.Sub64(z[0], 13402431016077863595, 0)
+ z[1], b = bits.Sub64(z[1], 2210141511517208575, b)
+ z[2], b = bits.Sub64(z[2], 7435674573564081700, b)
+ z[3], b = bits.Sub64(z[3], 7239337960414712511, b)
+ z[4], b = bits.Sub64(z[4], 5412103778470702295, b)
+ z[5], _ = bits.Sub64(z[5], 1873798617647539866, b)
+ }
+}
+
+// arith.go
+// Copyright 2020 ConsenSys AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by goff DO NOT EDIT
+
+func madd(a, b, t, u, v uint64) (uint64, uint64, uint64) {
+ var carry uint64
+ hi, lo := bits.Mul64(a, b)
+ v, carry = bits.Add64(lo, v, 0)
+ u, carry = bits.Add64(hi, u, carry)
+ t, _ = bits.Add64(t, 0, carry)
+ return t, u, v
+}
+
+// madd0 hi = a*b + c (discards lo bits)
+func madd0(a, b, c uint64) (hi uint64) {
+ var carry, lo uint64
+ hi, lo = bits.Mul64(a, b)
+ _, carry = bits.Add64(lo, c, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ return
+}
+
+// madd1 hi, lo = a*b + c
+func madd1(a, b, c uint64) (hi uint64, lo uint64) {
+ var carry uint64
+ hi, lo = bits.Mul64(a, b)
+ lo, carry = bits.Add64(lo, c, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ return
+}
+
+// madd2 hi, lo = a*b + c + d
+func madd2(a, b, c, d uint64) (hi uint64, lo uint64) {
+ var carry uint64
+ hi, lo = bits.Mul64(a, b)
+ c, carry = bits.Add64(c, d, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ lo, carry = bits.Add64(lo, c, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ return
+}
+
+// madd2s superhi, hi, lo = 2*a*b + c + d + e
+func madd2s(a, b, c, d, e uint64) (superhi, hi, lo uint64) {
+ var carry, sum uint64
+
+ hi, lo = bits.Mul64(a, b)
+ lo, carry = bits.Add64(lo, lo, 0)
+ hi, superhi = bits.Add64(hi, hi, carry)
+
+ sum, carry = bits.Add64(c, e, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ lo, carry = bits.Add64(lo, sum, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ hi, _ = bits.Add64(hi, 0, d)
+ return
+}
+
+func madd1s(a, b, d, e uint64) (superhi, hi, lo uint64) {
+ var carry uint64
+
+ hi, lo = bits.Mul64(a, b)
+ lo, carry = bits.Add64(lo, lo, 0)
+ hi, superhi = bits.Add64(hi, hi, carry)
+ lo, carry = bits.Add64(lo, e, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ hi, _ = bits.Add64(hi, 0, d)
+ return
+}
+
+func madd2sb(a, b, c, e uint64) (superhi, hi, lo uint64) {
+ var carry, sum uint64
+
+ hi, lo = bits.Mul64(a, b)
+ lo, carry = bits.Add64(lo, lo, 0)
+ hi, superhi = bits.Add64(hi, hi, carry)
+
+ sum, carry = bits.Add64(c, e, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ lo, carry = bits.Add64(lo, sum, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ return
+}
+
+func madd1sb(a, b, e uint64) (superhi, hi, lo uint64) {
+ var carry uint64
+
+ hi, lo = bits.Mul64(a, b)
+ lo, carry = bits.Add64(lo, lo, 0)
+ hi, superhi = bits.Add64(hi, hi, carry)
+ lo, carry = bits.Add64(lo, e, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ return
+}
+
+func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) {
+ var carry uint64
+ hi, lo = bits.Mul64(a, b)
+ c, carry = bits.Add64(c, d, 0)
+ hi, _ = bits.Add64(hi, 0, carry)
+ lo, carry = bits.Add64(lo, c, 0)
+ hi, _ = bits.Add64(hi, e, carry)
+ return
+}
diff --git a/common/crypto/bls12381/arithmetic_x86.s b/common/crypto/bls12381/arithmetic_x86.s
new file mode 100644
index 0000000..2cebbc4
--- /dev/null
+++ b/common/crypto/bls12381/arithmetic_x86.s
@@ -0,0 +1,2150 @@
+// +build amd64,blsasm amd64,blsadx
+
+#include "textflag.h"
+
+// addition w/ modular reduction
+// a = (a + b) % p
+TEXT ·addAssign(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ b+8(FP), SI
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+
+ // |
+ ADDQ (SI), R8
+ ADCQ 8(SI), R9
+ ADCQ 16(SI), R10
+ ADCQ 24(SI), R11
+ ADCQ 32(SI), R12
+ ADCQ 40(SI), R13
+
+ // |
+ MOVQ R8, R14
+ MOVQ R9, R15
+ MOVQ R10, CX
+ MOVQ R11, DX
+ MOVQ R12, SI
+ MOVQ R13, BX
+ MOVQ $0xb9feffffffffaaab, AX
+ SUBQ AX, R14
+ MOVQ $0x1eabfffeb153ffff, AX
+ SBBQ AX, R15
+ MOVQ $0x6730d2a0f6b0f624, AX
+ SBBQ AX, CX
+ MOVQ $0x64774b84f38512bf, AX
+ SBBQ AX, DX
+ MOVQ $0x4b1ba7b6434bacd7, AX
+ SBBQ AX, SI
+ MOVQ $0x1a0111ea397fe69a, AX
+ SBBQ AX, BX
+ CMOVQCC R14, R8
+ CMOVQCC R15, R9
+ CMOVQCC CX, R10
+ CMOVQCC DX, R11
+ CMOVQCC SI, R12
+ CMOVQCC BX, R13
+
+ // |
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+
+/* | end */
+
+
+// addition w/ modular reduction
+// c = (a + b) % p
+TEXT ·add(SB), NOSPLIT, $0-24
+ // |
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+
+ // |
+ ADDQ (SI), R8
+ ADCQ 8(SI), R9
+ ADCQ 16(SI), R10
+ ADCQ 24(SI), R11
+ ADCQ 32(SI), R12
+ ADCQ 40(SI), R13
+
+ // |
+ MOVQ R8, R14
+ MOVQ R9, R15
+ MOVQ R10, CX
+ MOVQ R11, DX
+ MOVQ R12, SI
+ MOVQ R13, BX
+ MOVQ $0xb9feffffffffaaab, DI
+ SUBQ DI, R14
+ MOVQ $0x1eabfffeb153ffff, DI
+ SBBQ DI, R15
+ MOVQ $0x6730d2a0f6b0f624, DI
+ SBBQ DI, CX
+ MOVQ $0x64774b84f38512bf, DI
+ SBBQ DI, DX
+ MOVQ $0x4b1ba7b6434bacd7, DI
+ SBBQ DI, SI
+ MOVQ $0x1a0111ea397fe69a, DI
+ SBBQ DI, BX
+ CMOVQCC R14, R8
+ CMOVQCC R15, R9
+ CMOVQCC CX, R10
+ CMOVQCC DX, R11
+ CMOVQCC SI, R12
+ CMOVQCC BX, R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// addition w/o reduction check
+// c = (a + b)
+TEXT ·ladd(SB), NOSPLIT, $0-24
+ // |
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+
+ // |
+ ADDQ (SI), R8
+ ADCQ 8(SI), R9
+ ADCQ 16(SI), R10
+ ADCQ 24(SI), R11
+ ADCQ 32(SI), R12
+ ADCQ 40(SI), R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// addition w/o reduction check
+// a = a + b
+TEXT ·laddAssign(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ b+8(FP), SI
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+
+ // |
+ ADDQ (SI), R8
+ ADCQ 8(SI), R9
+ ADCQ 16(SI), R10
+ ADCQ 24(SI), R11
+ ADCQ 32(SI), R12
+ ADCQ 40(SI), R13
+
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// subtraction w/ modular reduction
+// c = (a - b) % p
+TEXT ·sub(SB), NOSPLIT, $0-24
+ // |
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+ XORQ AX, AX
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+ SUBQ (SI), R8
+ SBBQ 8(SI), R9
+ SBBQ 16(SI), R10
+ SBBQ 24(SI), R11
+ SBBQ 32(SI), R12
+ SBBQ 40(SI), R13
+
+ // |
+ MOVQ $0xb9feffffffffaaab, R14
+ MOVQ $0x1eabfffeb153ffff, R15
+ MOVQ $0x6730d2a0f6b0f624, CX
+ MOVQ $0x64774b84f38512bf, DX
+ MOVQ $0x4b1ba7b6434bacd7, SI
+ MOVQ $0x1a0111ea397fe69a, BX
+ CMOVQCC AX, R14
+ CMOVQCC AX, R15
+ CMOVQCC AX, CX
+ CMOVQCC AX, DX
+ CMOVQCC AX, SI
+ CMOVQCC AX, BX
+ ADDQ R14, R8
+ ADCQ R15, R9
+ ADCQ CX, R10
+ ADCQ DX, R11
+ ADCQ SI, R12
+ ADCQ BX, R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// subtraction w/ modular reduction
+// a = (a - b) % p
+TEXT ·subAssign(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ b+8(FP), SI
+ XORQ AX, AX
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+ SUBQ (SI), R8
+ SBBQ 8(SI), R9
+ SBBQ 16(SI), R10
+ SBBQ 24(SI), R11
+ SBBQ 32(SI), R12
+ SBBQ 40(SI), R13
+
+ // |
+ MOVQ $0xb9feffffffffaaab, R14
+ MOVQ $0x1eabfffeb153ffff, R15
+ MOVQ $0x6730d2a0f6b0f624, CX
+ MOVQ $0x64774b84f38512bf, DX
+ MOVQ $0x4b1ba7b6434bacd7, SI
+ MOVQ $0x1a0111ea397fe69a, BX
+ CMOVQCC AX, R14
+ CMOVQCC AX, R15
+ CMOVQCC AX, CX
+ CMOVQCC AX, DX
+ CMOVQCC AX, SI
+ CMOVQCC AX, BX
+ ADDQ R14, R8
+ ADCQ R15, R9
+ ADCQ CX, R10
+ ADCQ DX, R11
+ ADCQ SI, R12
+ ADCQ BX, R13
+
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// subtraction w/o reduction check
+// a = (a - b)
+TEXT ·lsubAssign(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ b+8(FP), SI
+
+ // |
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+ SUBQ (SI), R8
+ SBBQ 8(SI), R9
+ SBBQ 16(SI), R10
+ SBBQ 24(SI), R11
+ SBBQ 32(SI), R12
+ SBBQ 40(SI), R13
+
+ // |
+ MOVQ a+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+// doubling w/ reduction
+// c = (2 * a) % p
+TEXT ·double(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+8(FP), DI
+
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+ ADDQ R8, R8
+ ADCQ R9, R9
+ ADCQ R10, R10
+ ADCQ R11, R11
+ ADCQ R12, R12
+ ADCQ R13, R13
+
+ // |
+ MOVQ R8, R14
+ MOVQ R9, R15
+ MOVQ R10, CX
+ MOVQ R11, DX
+ MOVQ R12, SI
+ MOVQ R13, BX
+ MOVQ $0xb9feffffffffaaab, DI
+ SUBQ DI, R14
+ MOVQ $0x1eabfffeb153ffff, DI
+ SBBQ DI, R15
+ MOVQ $0x6730d2a0f6b0f624, DI
+ SBBQ DI, CX
+ MOVQ $0x64774b84f38512bf, DI
+ SBBQ DI, DX
+ MOVQ $0x4b1ba7b6434bacd7, DI
+ SBBQ DI, SI
+ MOVQ $0x1a0111ea397fe69a, DI
+ SBBQ DI, BX
+ CMOVQCC R14, R8
+ CMOVQCC R15, R9
+ CMOVQCC CX, R10
+ CMOVQCC DX, R11
+ CMOVQCC SI, R12
+ CMOVQCC BX, R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// doubling w/ reduction
+// a = (2 * a) % p
+TEXT ·doubleAssign(SB), NOSPLIT, $0-8
+ // |
+ MOVQ a+0(FP), DI
+
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+ ADDQ R8, R8
+ ADCQ R9, R9
+ ADCQ R10, R10
+ ADCQ R11, R11
+ ADCQ R12, R12
+ ADCQ R13, R13
+
+ // |
+ MOVQ R8, R14
+ MOVQ R9, R15
+ MOVQ R10, CX
+ MOVQ R11, DX
+ MOVQ R12, SI
+ MOVQ R13, BX
+ MOVQ $0xb9feffffffffaaab, AX
+ SUBQ AX, R14
+ MOVQ $0x1eabfffeb153ffff, AX
+ SBBQ AX, R15
+ MOVQ $0x6730d2a0f6b0f624, AX
+ SBBQ AX, CX
+ MOVQ $0x64774b84f38512bf, AX
+ SBBQ AX, DX
+ MOVQ $0x4b1ba7b6434bacd7, AX
+ SBBQ AX, SI
+ MOVQ $0x1a0111ea397fe69a, AX
+ SBBQ AX, BX
+ CMOVQCC R14, R8
+ CMOVQCC R15, R9
+ CMOVQCC CX, R10
+ CMOVQCC DX, R11
+ CMOVQCC SI, R12
+ CMOVQCC BX, R13
+
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// doubling w/o reduction
+// c = 2 * a
+TEXT ·ldouble(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+8(FP), DI
+
+ MOVQ (DI), R8
+ MOVQ 8(DI), R9
+ MOVQ 16(DI), R10
+ MOVQ 24(DI), R11
+ MOVQ 32(DI), R12
+ MOVQ 40(DI), R13
+
+ // |
+ ADDQ R8, R8
+ ADCQ R9, R9
+ ADCQ R10, R10
+ ADCQ R11, R11
+ ADCQ R12, R12
+ ADCQ R13, R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+
+ RET
+/* | end */
+
+
+TEXT ·_neg(SB), NOSPLIT, $0-16
+ // |
+ MOVQ a+8(FP), DI
+
+ // |
+ MOVQ $0xb9feffffffffaaab, R8
+ MOVQ $0x1eabfffeb153ffff, R9
+ MOVQ $0x6730d2a0f6b0f624, R10
+ MOVQ $0x64774b84f38512bf, R11
+ MOVQ $0x4b1ba7b6434bacd7, R12
+ MOVQ $0x1a0111ea397fe69a, R13
+ SUBQ (DI), R8
+ SBBQ 8(DI), R9
+ SBBQ 16(DI), R10
+ SBBQ 24(DI), R11
+ SBBQ 32(DI), R12
+ SBBQ 40(DI), R13
+
+ // |
+ MOVQ c+0(FP), DI
+ MOVQ R8, (DI)
+ MOVQ R9, 8(DI)
+ MOVQ R10, 16(DI)
+ MOVQ R11, 24(DI)
+ MOVQ R12, 32(DI)
+ MOVQ R13, 40(DI)
+ RET
+/* | end */
+
+
+// multiplication without using MULX/ADX
+// c = a * b % p
+TEXT ·mulNoADX(SB), NOSPLIT, $24-24
+ // |
+
+/* inputs */
+
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+ MOVQ $0x00, R9
+ MOVQ $0x00, R10
+ MOVQ $0x00, R11
+ MOVQ $0x00, R12
+ MOVQ $0x00, R13
+ MOVQ $0x00, R14
+ MOVQ $0x00, R15
+
+ // |
+
+/* i0 */
+
+ // | a0 @ CX
+ MOVQ (DI), CX
+
+ // | a0 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ MOVQ AX, (SP)
+ MOVQ DX, R8
+
+ // | a0 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R8
+ ADCQ DX, R9
+
+ // | a0 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R9
+ ADCQ DX, R10
+
+ // | a0 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R10
+ ADCQ DX, R11
+
+ // | a0 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R11
+ ADCQ DX, R12
+
+ // | a0 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+
+ // |
+
+/* i1 */
+
+ // | a1 @ CX
+ MOVQ 8(DI), CX
+ MOVQ $0x00, BX
+
+ // | a1 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ ADDQ AX, R8
+ ADCQ DX, R9
+ ADCQ $0x00, R10
+ ADCQ $0x00, BX
+ MOVQ R8, 8(SP)
+ MOVQ $0x00, R8
+
+ // | a1 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R9
+ ADCQ DX, R10
+ ADCQ BX, R11
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a1 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R10
+ ADCQ DX, R11
+ ADCQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a1 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R11
+ ADCQ DX, R12
+ ADCQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a1 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+ ADCQ BX, R14
+
+ // | a1 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R13
+ ADCQ DX, R14
+
+ // |
+
+/* i2 */
+
+ // | a2 @ CX
+ MOVQ 16(DI), CX
+ MOVQ $0x00, BX
+
+ // | a2 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ ADDQ AX, R9
+ ADCQ DX, R10
+ ADCQ $0x00, R11
+ ADCQ $0x00, BX
+ MOVQ R9, 16(SP)
+ MOVQ $0x00, R9
+
+ // | a2 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R10
+ ADCQ DX, R11
+ ADCQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a2 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R11
+ ADCQ DX, R12
+ ADCQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a2 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+ ADCQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a2 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R13
+ ADCQ DX, R14
+ ADCQ BX, R15
+
+ // | a2 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R14
+ ADCQ DX, R15
+
+ // |
+
+/* i3 */
+
+ // | a3 @ CX
+ MOVQ 24(DI), CX
+ MOVQ $0x00, BX
+
+ // | a3 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ ADDQ AX, R10
+ ADCQ DX, R11
+ ADCQ $0x00, R12
+ ADCQ $0x00, BX
+
+ // | a3 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R11
+ ADCQ DX, R12
+ ADCQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a3 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+ ADCQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a3 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R13
+ ADCQ DX, R14
+ ADCQ BX, R15
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a3 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R14
+ ADCQ DX, R15
+ ADCQ BX, R8
+
+ // | a3 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R15
+ ADCQ DX, R8
+
+ // |
+
+/* i4 */
+
+ // | a4 @ CX
+ MOVQ 32(DI), CX
+ MOVQ $0x00, BX
+
+ // | a4 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ ADDQ AX, R11
+ ADCQ DX, R12
+ ADCQ $0x00, R13
+ ADCQ $0x00, BX
+
+ // | a4 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+ ADCQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a4 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R13
+ ADCQ DX, R14
+ ADCQ BX, R15
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a4 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R14
+ ADCQ DX, R15
+ ADCQ BX, R8
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a4 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R15
+ ADCQ DX, R8
+ ADCQ BX, R9
+
+ // | a4 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R8
+ ADCQ DX, R9
+
+ // |
+
+/* i5 */
+
+ // | a5 @ CX
+ MOVQ 40(DI), CX
+ MOVQ $0x00, BX
+
+ // | a5 * b0
+ MOVQ (SI), AX
+ MULQ CX
+ ADDQ AX, R12
+ ADCQ DX, R13
+ ADCQ $0x00, R14
+ ADCQ $0x00, BX
+
+ // | a5 * b1
+ MOVQ 8(SI), AX
+ MULQ CX
+ ADDQ AX, R13
+ ADCQ DX, R14
+ ADCQ BX, R15
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a5 * b2
+ MOVQ 16(SI), AX
+ MULQ CX
+ ADDQ AX, R14
+ ADCQ DX, R15
+ ADCQ BX, R8
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a5 * b3
+ MOVQ 24(SI), AX
+ MULQ CX
+ ADDQ AX, R15
+ ADCQ DX, R8
+ ADCQ BX, R9
+ MOVQ $0x00, BX
+ ADCQ $0x00, BX
+
+ // | a5 * b4
+ MOVQ 32(SI), AX
+ MULQ CX
+ ADDQ AX, R8
+ ADCQ DX, R9
+ ADCQ $0x00, BX
+
+ // | a5 * b5
+ MOVQ 40(SI), AX
+ MULQ CX
+ ADDQ AX, R9
+ ADCQ DX, BX
+
+ // |
+
+/* */
+
+ // |
+ // | W
+ // | 0 (SP) | 1 8(SP) | 2 16(SP) | 3 R10 | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 R9 | 11 BX
+
+
+ MOVQ (SP), CX
+ MOVQ 8(SP), DI
+ MOVQ 16(SP), SI
+ MOVQ BX, (SP)
+ MOVQ R9, 8(SP)
+
+ // |
+
+/* montgomery reduction */
+
+ // |
+
+/* i0 */
+
+ // |
+ // | W
+ // | 0 CX | 1 DI | 2 SI | 3 R10 | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP)
+
+
+ // | | u0 = w0 * inp
+ MOVQ CX, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w0 @ CX
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, CX
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w1 @ DI
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, DI
+ ADCQ $0x00, DX
+ ADDQ BX, DI
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w2 @ SI
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, SI
+ ADCQ $0x00, DX
+ ADDQ BX, SI
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w3 @ R10
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R10
+ ADCQ $0x00, DX
+ ADDQ BX, R10
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w4 @ R11
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R11
+ ADCQ $0x00, DX
+ ADDQ BX, R11
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w5 @ R12
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ $0x00, DX
+ ADDQ BX, R12
+
+ // | w6 @ R13
+ ADCQ DX, R13
+ ADCQ $0x00, CX
+
+ // |
+
+/* i1 */
+
+ // |
+ // | W
+ // | 0 - | 1 DI | 2 SI | 3 R10 | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP)
+
+
+ // | | u1 = w1 * inp
+ MOVQ DI, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w1 @ DI
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, DI
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w2 @ SI
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, SI
+ ADCQ $0x00, DX
+ ADDQ BX, SI
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w3 @ R10
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, R10
+ ADCQ $0x00, DX
+ ADDQ BX, R10
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w4 @ R11
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R11
+ ADCQ $0x00, DX
+ ADDQ BX, R11
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w5 @ R12
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ $0x00, DX
+ ADDQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w6 @ R13
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, R13
+ ADCQ DX, CX
+ ADDQ BX, R13
+
+ // | w7 @ R14
+ ADCQ CX, R14
+ MOVQ $0x00, CX
+ ADCQ $0x00, CX
+
+ // |
+
+/* i2 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 SI | 3 R10 | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP)
+
+
+ // | | u2 = w2 * inp
+ MOVQ SI, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w2 @ SI
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, SI
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w3 @ R10
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, R10
+ ADCQ $0x00, DX
+ ADDQ BX, R10
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w4 @ R11
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, R11
+ ADCQ $0x00, DX
+ ADDQ BX, R11
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w5 @ R12
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ $0x00, DX
+ ADDQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w6 @ R13
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R13
+ ADCQ $0x00, DX
+ ADDQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w7 @ R14
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, R14
+ ADCQ DX, CX
+ ADDQ BX, R14
+
+ // | w8 @ R15
+ ADCQ CX, R15
+ MOVQ $0x00, CX
+ ADCQ $0x00, CX
+
+ // |
+
+/* i3 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 R10 | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP)
+
+
+ // | | u3 = w3 * inp
+ MOVQ R10, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w3 @ R10
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, R10
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w4 @ R11
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, R11
+ ADCQ $0x00, DX
+ ADDQ BX, R11
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w5 @ R12
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ $0x00, DX
+ ADDQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w6 @ R13
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R13
+ ADCQ $0x00, DX
+ ADDQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w7 @ R14
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R14
+ ADCQ $0x00, DX
+ ADDQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w8 @ R15
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, R15
+ ADCQ DX, CX
+ ADDQ BX, R15
+
+ // | w9 @ R8
+ ADCQ CX, R8
+ MOVQ $0x00, CX
+ ADCQ $0x00, CX
+
+ // |
+
+/* i4 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 - | 4 R11 | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP)
+
+
+ // | | u4 = w4 * inp
+ MOVQ R11, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w4 @ R11
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, R11
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w5 @ R12
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ $0x00, DX
+ ADDQ BX, R12
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w6 @ R13
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, R13
+ ADCQ $0x00, DX
+ ADDQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w7 @ R14
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R14
+ ADCQ $0x00, DX
+ ADDQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w8 @ R15
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R15
+ ADCQ $0x00, DX
+ ADDQ BX, R15
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w9 @ R8
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, R8
+ ADCQ DX, CX
+ ADDQ BX, R8
+
+ // | move to idle register
+ MOVQ 8(SP), DI
+
+ // | w10 @ DI
+ ADCQ CX, DI
+ MOVQ $0x00, CX
+ ADCQ $0x00, CX
+
+ // |
+
+/* i5 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 R12
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 DI | 11 (SP)
+
+
+ // | | u5 = w5 * inp
+ MOVQ R12, AX
+ MULQ ·inp+0(SB)
+ MOVQ AX, R9
+ MOVQ $0x00, BX
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w5 @ R12
+ MOVQ ·modulus+0(SB), AX
+ MULQ R9
+ ADDQ AX, R12
+ ADCQ DX, BX
+
+ // | j1
+
+ // | w6 @ R13
+ MOVQ ·modulus+8(SB), AX
+ MULQ R9
+ ADDQ AX, R13
+ ADCQ $0x00, DX
+ ADDQ BX, R13
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j2
+
+ // | w7 @ R14
+ MOVQ ·modulus+16(SB), AX
+ MULQ R9
+ ADDQ AX, R14
+ ADCQ $0x00, DX
+ ADDQ BX, R14
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j3
+
+ // | w8 @ R15
+ MOVQ ·modulus+24(SB), AX
+ MULQ R9
+ ADDQ AX, R15
+ ADCQ $0x00, DX
+ ADDQ BX, R15
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j4
+
+ // | w9 @ R8
+ MOVQ ·modulus+32(SB), AX
+ MULQ R9
+ ADDQ AX, R8
+ ADCQ $0x00, DX
+ ADDQ BX, R8
+ MOVQ $0x00, BX
+ ADCQ DX, BX
+
+ // | j5
+
+ // | w10 @ DI
+ MOVQ ·modulus+40(SB), AX
+ MULQ R9
+ ADDQ AX, DI
+ ADCQ DX, CX
+ ADDQ BX, DI
+
+ // | w11 @ CX
+ ADCQ (SP), CX
+
+ // |
+ // | W montgomerry reduction ends
+ // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 -
+ // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 DI | 11 CX
+
+
+ // |
+
+
+/* modular reduction */
+
+ MOVQ R13, R10
+ SUBQ ·modulus+0(SB), R10
+ MOVQ R14, R11
+ SBBQ ·modulus+8(SB), R11
+ MOVQ R15, R12
+ SBBQ ·modulus+16(SB), R12
+ MOVQ R8, AX
+ SBBQ ·modulus+24(SB), AX
+ MOVQ DI, BX
+ SBBQ ·modulus+32(SB), BX
+ MOVQ CX, R9
+ SBBQ ·modulus+40(SB), R9
+ // |
+
+/* out */
+
+ MOVQ c+0(FP), SI
+ CMOVQCC R10, R13
+ MOVQ R13, (SI)
+ CMOVQCC R11, R14
+ MOVQ R14, 8(SI)
+ CMOVQCC R12, R15
+ MOVQ R15, 16(SI)
+ CMOVQCC AX, R8
+ MOVQ R8, 24(SI)
+ CMOVQCC BX, DI
+ MOVQ DI, 32(SI)
+ CMOVQCC R9, CX
+ MOVQ CX, 40(SI)
+ RET
+
+ // |
+
+/* end */
+
+
+// multiplication
+// c = a * b % p
+TEXT ·mulADX(SB), NOSPLIT, $16-24
+ // |
+
+/* inputs */
+
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+ XORQ AX, AX
+
+ // |
+
+/* i0 */
+
+ // | a0 @ DX
+ MOVQ (DI), DX
+
+ // | a0 * b0
+ MULXQ (SI), AX, CX
+ MOVQ AX, (SP)
+
+ // | a0 * b1
+ MULXQ 8(SI), AX, R8
+ ADCXQ AX, CX
+
+ // | a0 * b2
+ MULXQ 16(SI), AX, R9
+ ADCXQ AX, R8
+
+ // | a0 * b3
+ MULXQ 24(SI), AX, R10
+ ADCXQ AX, R9
+
+ // | a0 * b4
+ MULXQ 32(SI), AX, R11
+ ADCXQ AX, R10
+
+ // | a0 * b5
+ MULXQ 40(SI), AX, R12
+ ADCXQ AX, R11
+ ADCQ $0x00, R12
+
+ // |
+
+/* i1 */
+
+ // | a1 @ DX
+ MOVQ 8(DI), DX
+ XORQ R13, R13
+
+ // | a1 * b0
+ MULXQ (SI), AX, BX
+ ADOXQ AX, CX
+ ADCXQ BX, R8
+ MOVQ CX, 8(SP)
+
+ // | a1 * b1
+ MULXQ 8(SI), AX, BX
+ ADOXQ AX, R8
+ ADCXQ BX, R9
+
+ // | a1 * b2
+ MULXQ 16(SI), AX, BX
+ ADOXQ AX, R9
+ ADCXQ BX, R10
+
+ // | a1 * b3
+ MULXQ 24(SI), AX, BX
+ ADOXQ AX, R10
+ ADCXQ BX, R11
+
+ // | a1 * b4
+ MULXQ 32(SI), AX, BX
+ ADOXQ AX, R11
+ ADCXQ BX, R12
+
+ // | a1 * b5
+ MULXQ 40(SI), AX, BX
+ ADOXQ AX, R12
+ ADOXQ R13, R13
+ ADCXQ BX, R13
+
+ // |
+
+/* i2 */
+
+ // | a2 @ DX
+ MOVQ 16(DI), DX
+ XORQ R14, R14
+
+ // | a2 * b0
+ MULXQ (SI), AX, BX
+ ADOXQ AX, R8
+ ADCXQ BX, R9
+
+ // | a2 * b1
+ MULXQ 8(SI), AX, BX
+ ADOXQ AX, R9
+ ADCXQ BX, R10
+
+ // | a2 * b2
+ MULXQ 16(SI), AX, BX
+ ADOXQ AX, R10
+ ADCXQ BX, R11
+
+ // | a2 * b3
+ MULXQ 24(SI), AX, BX
+ ADOXQ AX, R11
+ ADCXQ BX, R12
+
+ // | a2 * b4
+ MULXQ 32(SI), AX, BX
+ ADOXQ AX, R12
+ ADCXQ BX, R13
+
+ // | a2 * b5
+ MULXQ 40(SI), AX, BX
+ ADOXQ AX, R13
+ ADOXQ R14, R14
+ ADCXQ BX, R14
+
+ // |
+
+/* i3 */
+
+ // | a3 @ DX
+ MOVQ 24(DI), DX
+ XORQ R15, R15
+
+ // | a3 * b0
+ MULXQ (SI), AX, BX
+ ADOXQ AX, R9
+ ADCXQ BX, R10
+
+ // | a3 * b1
+ MULXQ 8(SI), AX, BX
+ ADOXQ AX, R10
+ ADCXQ BX, R11
+
+ // | a3 * b2
+ MULXQ 16(SI), AX, BX
+ ADOXQ AX, R11
+ ADCXQ BX, R12
+
+ // | a3 * b3
+ MULXQ 24(SI), AX, BX
+ ADOXQ AX, R12
+ ADCXQ BX, R13
+
+ // | a3 * b4
+ MULXQ 32(SI), AX, BX
+ ADOXQ AX, R13
+ ADCXQ BX, R14
+
+ // | a3 * b5
+ MULXQ 40(SI), AX, BX
+ ADOXQ AX, R14
+ ADOXQ R15, R15
+ ADCXQ BX, R15
+
+ // |
+
+/* i4 */
+
+ // | a4 @ DX
+ MOVQ 32(DI), DX
+ XORQ CX, CX
+
+ // | a4 * b0
+ MULXQ (SI), AX, BX
+ ADOXQ AX, R10
+ ADCXQ BX, R11
+
+ // | a4 * b1
+ MULXQ 8(SI), AX, BX
+ ADOXQ AX, R11
+ ADCXQ BX, R12
+
+ // | a4 * b2
+ MULXQ 16(SI), AX, BX
+ ADOXQ AX, R12
+ ADCXQ BX, R13
+
+ // | a4 * b3
+ MULXQ 24(SI), AX, BX
+ ADOXQ AX, R13
+ ADCXQ BX, R14
+
+ // | a4 * b4
+ MULXQ 32(SI), AX, BX
+ ADOXQ AX, R14
+ ADCXQ BX, R15
+
+ // | a4 * b5
+ MULXQ 40(SI), AX, BX
+ ADOXQ AX, R15
+ ADOXQ CX, CX
+ ADCXQ BX, CX
+
+ // |
+
+/* i5 */
+
+ // | a5 @ DX
+ MOVQ 40(DI), DX
+ XORQ DI, DI
+
+ // | a5 * b0
+ MULXQ (SI), AX, BX
+ ADOXQ AX, R11
+ ADCXQ BX, R12
+
+ // | a5 * b1
+ MULXQ 8(SI), AX, BX
+ ADOXQ AX, R12
+ ADCXQ BX, R13
+
+ // | a5 * b2
+ MULXQ 16(SI), AX, BX
+ ADOXQ AX, R13
+ ADCXQ BX, R14
+
+ // | a5 * b3
+ MULXQ 24(SI), AX, BX
+ ADOXQ AX, R14
+ ADCXQ BX, R15
+
+ // | a5 * b4
+ MULXQ 32(SI), AX, BX
+ ADOXQ AX, R15
+ ADCXQ BX, CX
+
+ // | a5 * b5
+ MULXQ 40(SI), AX, BX
+ ADOXQ AX, CX
+ ADOXQ BX, DI
+ ADCQ $0x00, DI
+
+ // |
+
+/* */
+
+ // |
+ // | W
+ // | 0 (SP) | 1 8(SP) | 2 R8 | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 DI
+
+
+ MOVQ (SP), BX
+ MOVQ 8(SP), SI
+ MOVQ DI, (SP)
+
+ // |
+ // | W ready to mont
+ // | 0 BX | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // |
+
+/* montgomery reduction */
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i0 */
+
+ // |
+ // | W
+ // | 0 BX | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u0 = w0 * inp
+ MOVQ BX, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w0 @ BX
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, BX
+ ADCXQ DI, SI
+
+ // | j1
+
+ // | w1 @ SI
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, SI
+ ADCXQ DI, R8
+
+ // | j2
+
+ // | w2 @ R8
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R8
+ ADCXQ DI, R9
+
+ // | j3
+
+ // | w3 @ R9
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R9
+ ADCXQ DI, R10
+
+ // | j4
+
+ // | w4 @ R10
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R10
+ ADCXQ DI, R11
+
+ // | j5
+
+ // | w5 @ R11
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+ ADOXQ BX, R12
+ ADCXQ BX, BX
+ MOVQ $0x00, AX
+ ADOXQ AX, BX
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i1 */
+
+ // |
+ // | W
+ // | 0 - | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u1 = w1 * inp
+ MOVQ SI, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w1 @ SI
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, SI
+ ADCXQ DI, R8
+
+ // | j1
+
+ // | w2 @ R8
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, R8
+ ADCXQ DI, R9
+
+ // | j2
+
+ // | w3 @ R9
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R9
+ ADCXQ DI, R10
+
+ // | j3
+
+ // | w4 @ R10
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R10
+ ADCXQ DI, R11
+
+ // | j4
+
+ // | w5 @ R11
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+
+ // | j5
+
+ // | w6 @ R12
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, R12
+ ADCXQ DI, R13
+ ADOXQ BX, R13
+ ADCXQ SI, SI
+ MOVQ $0x00, AX
+ ADOXQ AX, SI
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i2 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 R8 | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u2 = w2 * inp
+ MOVQ R8, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w2 @ R8
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, R8
+ ADCXQ DI, R9
+
+ // | j1
+
+ // | w3 @ R9
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, R9
+ ADCXQ DI, R10
+
+ // | j2
+
+ // | w4 @ R10
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R10
+ ADCXQ DI, R11
+
+ // | j3
+
+ // | w5 @ R11
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+
+ // | j4
+
+ // | w6 @ R12
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R12
+ ADCXQ DI, R13
+
+ // | j5
+
+ // | w7 @ R13
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, R13
+ ADCXQ DI, R14
+ ADOXQ SI, R14
+ ADCXQ R8, R8
+ MOVQ $0x00, AX
+ ADOXQ AX, R8
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i3 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 R9 | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u3 = w3 * inp
+ MOVQ R9, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w3 @ R9
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, R9
+ ADCXQ DI, R10
+
+ // | j1
+
+ // | w4 @ R10
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, R10
+ ADCXQ DI, R11
+
+ // | j2
+
+ // | w5 @ R11
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+
+ // | j3
+
+ // | w6 @ R12
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R12
+ ADCXQ DI, R13
+
+ // | j4
+
+ // | w7 @ R13
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R13
+ ADCXQ DI, R14
+
+ // | j5
+
+ // | w8 @ R14
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, R14
+ ADCXQ DI, R15
+ ADOXQ R8, R15
+ ADCXQ R9, R9
+ MOVQ $0x00, AX
+ ADOXQ AX, R9
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i4 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 - | 4 R10 | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u4 = w4 * inp
+ MOVQ R10, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w4 @ R10
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, R10
+ ADCXQ DI, R11
+
+ // | j1
+
+ // | w5 @ R11
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+
+ // | j2
+
+ // | w6 @ R12
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R12
+ ADCXQ DI, R13
+
+ // | j3
+
+ // | w7 @ R13
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R13
+ ADCXQ DI, R14
+
+ // | j4
+
+ // | w8 @ R14
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R14
+ ADCXQ DI, R15
+
+ // | j5
+
+ // | w9 @ R15
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, R15
+ ADCXQ DI, CX
+ ADOXQ R9, CX
+ ADCXQ R10, R10
+ MOVQ $0x00, AX
+ ADOXQ AX, R10
+
+ // | clear flags
+ XORQ AX, AX
+
+ // |
+
+/* i5 */
+
+ // |
+ // | W
+ // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 R11
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP)
+
+
+ // | | u5 = w5 * inp
+ MOVQ R11, DX
+ MULXQ ·inp+0(SB), DX, DI
+
+ // |
+
+/* */
+
+ // | j0
+
+ // | w5 @ R11
+ MULXQ ·modulus+0(SB), AX, DI
+ ADOXQ AX, R11
+ ADCXQ DI, R12
+
+ // | j1
+
+ // | w6 @ R12
+ MULXQ ·modulus+8(SB), AX, DI
+ ADOXQ AX, R12
+ ADCXQ DI, R13
+
+ // | j2
+
+ // | w7 @ R13
+ MULXQ ·modulus+16(SB), AX, DI
+ ADOXQ AX, R13
+ ADCXQ DI, R14
+
+ // | j3
+
+ // | w8 @ R14
+ MULXQ ·modulus+24(SB), AX, DI
+ ADOXQ AX, R14
+ ADCXQ DI, R15
+
+ // | j4
+
+ // | w9 @ R15
+ MULXQ ·modulus+32(SB), AX, DI
+ ADOXQ AX, R15
+ ADCXQ DI, CX
+
+ // | j5
+
+ // | w10 @ CX
+ MULXQ ·modulus+40(SB), AX, DI
+ ADOXQ AX, CX
+
+ // | w11 @ (SP)
+ // | move to an idle register
+ MOVQ (SP), BX
+ ADCXQ DI, BX
+ ADOXQ R10, BX
+
+ // |
+ // | W montgomery reduction ends
+ // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 -
+ // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 BX
+
+
+ // |
+
+/* modular reduction */
+
+ MOVQ R12, AX
+ SUBQ ·modulus+0(SB), AX
+ MOVQ R13, DI
+ SBBQ ·modulus+8(SB), DI
+ MOVQ R14, SI
+ SBBQ ·modulus+16(SB), SI
+ MOVQ R15, R8
+ SBBQ ·modulus+24(SB), R8
+ MOVQ CX, R9
+ SBBQ ·modulus+32(SB), R9
+ MOVQ BX, R10
+ SBBQ ·modulus+40(SB), R10
+
+ // |
+
+/* out */
+
+ MOVQ c+0(FP), R11
+ CMOVQCC AX, R12
+ MOVQ R12, (R11)
+ CMOVQCC DI, R13
+ MOVQ R13, 8(R11)
+ CMOVQCC SI, R14
+ MOVQ R14, 16(R11)
+ CMOVQCC R8, R15
+ MOVQ R15, 24(R11)
+ CMOVQCC R9, CX
+ MOVQ CX, 32(R11)
+ CMOVQCC R10, BX
+ MOVQ BX, 40(R11)
+ RET
+
+ // |
+
+/* end */
diff --git a/common/crypto/bls12381/arithmetic_x86_adx.go b/common/crypto/bls12381/arithmetic_x86_adx.go
new file mode 100644
index 0000000..fffa568
--- /dev/null
+++ b/common/crypto/bls12381/arithmetic_x86_adx.go
@@ -0,0 +1,24 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build amd64 && blsadx
+
+package bls12381
+
+// enableADX is true if the ADX/BMI2 instruction set was requested for the BLS
+// implementation. The system may still fall back to plain ASM if the necessary
+// instructions are unavailable on the CPU.
+const enableADX = true
diff --git a/common/crypto/bls12381/arithmetic_x86_noadx.go b/common/crypto/bls12381/arithmetic_x86_noadx.go
new file mode 100644
index 0000000..ae68090
--- /dev/null
+++ b/common/crypto/bls12381/arithmetic_x86_noadx.go
@@ -0,0 +1,24 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build amd64 && blsasm
+
+package bls12381
+
+// enableADX is true if the ADX/BMI2 instruction set was requested for the BLS
+// implementation. The system may still fall back to plain ASM if the necessary
+// instructions are unavailable on the CPU.
+const enableADX = false
diff --git a/common/crypto/bls12381/bls12_381.go b/common/crypto/bls12381/bls12_381.go
new file mode 100644
index 0000000..58d208a
--- /dev/null
+++ b/common/crypto/bls12381/bls12_381.go
@@ -0,0 +1,231 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//nolint:gofmt
+package bls12381
+
+/*
+ Field Constants
+*/
+
+// Base field modulus
+// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
+
+// Size of six words
+// r = 2 ^ 384
+
+// modulus = p
+var modulus = fe{0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a}
+
+var (
+ // -p^(-1) mod 2^64
+ inp uint64 = 0x89f3fffcfffcfffd
+ // This value is used in assembly code
+ _ = inp
+)
+
+// r mod p
+var r1 = &fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}
+
+// r^2 mod p
+var r2 = &fe{
+ 0xf4df1f341c341746, 0x0a76e6a609d104f1, 0x8de5476c4c95b6d5, 0x67eb88a9939d83c0, 0x9a793e85b519952d, 0x11988fe592cae3aa,
+}
+
+// -1 + 0 * u
+var negativeOne2 = &fe2{
+ fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+}
+
+// 2 ^ (-1)
+var twoInv = &fe{0x1804000000015554, 0x855000053ab00001, 0x633cb57c253c276f, 0x6e22d1ec31ebb502, 0xd3916126f2d14ca2, 0x17fbb8571a006596}
+
+// (p - 3) / 4
+var pMinus3Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaaa")
+
+// (p + 1) / 4
+var pPlus1Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaab")
+
+// (p - 1) / 2
+var pMinus1Over2 = bigFromHex("0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555")
+
+// -1
+var nonResidue1 = &fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}
+
+// (1 + 1 * u)
+var nonResidue2 = &fe2{
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+}
+
+/*
+ Curve Constants
+*/
+
+// b coefficient for G1
+var b = &fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e}
+
+// b coefficient for G2
+var b2 = &fe2{
+ fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e},
+ fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e},
+}
+
+// Curve order
+var q = bigFromHex("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001")
+
+// Efficient cofactor of G1
+var cofactorEFFG1 = bigFromHex("0xd201000000010001")
+
+// Efficient cofactor of G2
+var cofactorEFFG2 = bigFromHex("0x0bc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551")
+
+var g1One = PointG1{
+ fe{0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75},
+ fe{0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, 0x51ac582950405194, 0x0e1c8c3fad0059c0, 0x0bbc3efc5008a26a},
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+}
+
+var g2One = PointG2{
+ fe2{
+ fe{0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, 0x6f67b7631863366b, 0x058191924350bcd7},
+ fe{0xa5a9c0759e23f606, 0xaaa0c59dbccd60c3, 0x3bb17e18e2867806, 0x1b1ab6cc8541b367, 0xc2b6ed0ef2158547, 0x11922a097360edf3},
+ },
+ fe2{
+ fe{0x4c730af860494c4a, 0x597cfa1f5e369c5a, 0xe7e6856caa0a635a, 0xbbefb5e96e0d495f, 0x07d3a975f0ef25a2, 0x083fd8e7e80dae5},
+ fe{0xadc0fc92df64b05d, 0x18aa270a2b1461dc, 0x86adac6a3be4eba0, 0x79495c4ec93da33a, 0xe7175850a43ccaed, 0xb2bc2a163de1bf2},
+ },
+ fe2{
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+}
+
+/*
+ Frobenious Coeffs
+*/
+
+var frobeniusCoeffs61 = [6]fe2{
+ {
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741},
+ },
+ {
+ fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ },
+ {
+ fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160},
+ },
+}
+
+var frobeniusCoeffs62 = [6]fe2{
+ {
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+}
+
+var frobeniusCoeffs12 = [12]fe2{
+ {
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb},
+ fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf},
+ },
+ {
+ fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8},
+ fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2},
+ },
+ {
+ fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd},
+ fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd},
+ },
+ {
+ fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf},
+ fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb},
+ },
+ {
+ fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2},
+ fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8},
+ },
+ {
+ fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a},
+ fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000},
+ },
+ {
+ fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd},
+ fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd},
+ },
+}
+
+/*
+ x
+*/
+
+var x = bigFromHex("0xd201000000010000")
diff --git a/common/crypto/bls12381/bls12_381_test.go b/common/crypto/bls12381/bls12_381_test.go
new file mode 100644
index 0000000..6bf5834
--- /dev/null
+++ b/common/crypto/bls12381/bls12_381_test.go
@@ -0,0 +1,13 @@
+package bls12381
+
+import (
+ "crypto/rand"
+ "math/big"
+)
+
+var fuz = 10
+
+func randScalar(max *big.Int) *big.Int {
+ a, _ := rand.Int(rand.Reader, max)
+ return a
+}
diff --git a/common/crypto/bls12381/field_element.go b/common/crypto/bls12381/field_element.go
new file mode 100644
index 0000000..a983824
--- /dev/null
+++ b/common/crypto/bls12381/field_element.go
@@ -0,0 +1,342 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//nolint:stylecheck
+package bls12381
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "math/big"
+)
+
+// fe is base field element representation
+type fe [6]uint64
+
+// fe2 is element representation of 'fp2' which is quadratic extension of base field 'fp'
+// Representation follows c[0] + c[1] * u encoding order.
+type fe2 [2]fe
+
+// fe6 is element representation of 'fp6' field which is cubic extension of 'fp2'
+// Representation follows c[0] + c[1] * v + c[2] * v^2 encoding order.
+type fe6 [3]fe2
+
+// fe12 is element representation of 'fp12' field which is quadratic extension of 'fp6'
+// Representation follows c[0] + c[1] * w encoding order.
+type fe12 [2]fe6
+
+func (fe *fe) setBytes(in []byte) *fe {
+ size := 48
+ l := len(in)
+ if l >= size {
+ l = size
+ }
+ padded := make([]byte, size)
+ copy(padded[size-l:], in)
+ var a int
+ for i := 0; i < 6; i++ {
+ a = size - i*8
+ fe[i] = uint64(padded[a-1]) | uint64(padded[a-2])<<8 |
+ uint64(padded[a-3])<<16 | uint64(padded[a-4])<<24 |
+ uint64(padded[a-5])<<32 | uint64(padded[a-6])<<40 |
+ uint64(padded[a-7])<<48 | uint64(padded[a-8])<<56
+ }
+ return fe
+}
+
+func (fe *fe) setBig(a *big.Int) *fe {
+ return fe.setBytes(a.Bytes())
+}
+
+func (fe *fe) setString(s string) (*fe, error) {
+ if s[:2] == "0x" {
+ s = s[2:]
+ }
+ bytes, err := hex.DecodeString(s)
+ if err != nil {
+ return nil, err
+ }
+ return fe.setBytes(bytes), nil
+}
+
+func (fe *fe) set(fe2 *fe) *fe {
+ fe[0] = fe2[0]
+ fe[1] = fe2[1]
+ fe[2] = fe2[2]
+ fe[3] = fe2[3]
+ fe[4] = fe2[4]
+ fe[5] = fe2[5]
+ return fe
+}
+
+func (fe *fe) bytes() []byte {
+ out := make([]byte, 48)
+ var a int
+ for i := 0; i < 6; i++ {
+ a = 48 - i*8
+ out[a-1] = byte(fe[i])
+ out[a-2] = byte(fe[i] >> 8)
+ out[a-3] = byte(fe[i] >> 16)
+ out[a-4] = byte(fe[i] >> 24)
+ out[a-5] = byte(fe[i] >> 32)
+ out[a-6] = byte(fe[i] >> 40)
+ out[a-7] = byte(fe[i] >> 48)
+ out[a-8] = byte(fe[i] >> 56)
+ }
+ return out
+}
+
+func (fe *fe) big() *big.Int {
+ return new(big.Int).SetBytes(fe.bytes())
+}
+
+func (fe *fe) string() (s string) {
+ for i := 5; i >= 0; i-- {
+ s = fmt.Sprintf("%s%16.16x", s, fe[i])
+ }
+ return "0x" + s
+}
+
+func (fe *fe) zero() *fe {
+ fe[0] = 0
+ fe[1] = 0
+ fe[2] = 0
+ fe[3] = 0
+ fe[4] = 0
+ fe[5] = 0
+ return fe
+}
+
+func (fe *fe) one() *fe {
+ return fe.set(r1)
+}
+
+func (fe *fe) rand(r io.Reader) (*fe, error) {
+ bi, err := rand.Int(r, modulus.big())
+ if err != nil {
+ return nil, err
+ }
+ return fe.setBig(bi), nil
+}
+
+func (fe *fe) isValid() bool {
+ return fe.cmp(&modulus) < 0
+}
+
+func (fe *fe) isOdd() bool {
+ var mask uint64 = 1
+ return fe[0]&mask != 0
+}
+
+func (fe *fe) isEven() bool {
+ var mask uint64 = 1
+ return fe[0]&mask == 0
+}
+
+func (fe *fe) isZero() bool {
+ return (fe[5] | fe[4] | fe[3] | fe[2] | fe[1] | fe[0]) == 0
+}
+
+func (fe *fe) isOne() bool {
+ return fe.equal(r1)
+}
+
+func (fe *fe) cmp(fe2 *fe) int {
+ for i := 5; i >= 0; i-- {
+ if fe[i] > fe2[i] {
+ return 1
+ } else if fe[i] < fe2[i] {
+ return -1
+ }
+ }
+ return 0
+}
+
+func (fe *fe) equal(fe2 *fe) bool {
+ return fe2[0] == fe[0] && fe2[1] == fe[1] && fe2[2] == fe[2] && fe2[3] == fe[3] && fe2[4] == fe[4] && fe2[5] == fe[5]
+}
+
+func (e *fe) sign() bool {
+ r := new(fe)
+ fromMont(r, e)
+ return r[0]&1 == 0
+}
+
+//nolint:unparam
+func (fe *fe) div2(e uint64) {
+ fe[0] = fe[0]>>1 | fe[1]<<63
+ fe[1] = fe[1]>>1 | fe[2]<<63
+ fe[2] = fe[2]>>1 | fe[3]<<63
+ fe[3] = fe[3]>>1 | fe[4]<<63
+ fe[4] = fe[4]>>1 | fe[5]<<63
+ fe[5] = fe[5]>>1 | e<<63
+}
+
+func (fe *fe) mul2() uint64 {
+ e := fe[5] >> 63
+ fe[5] = fe[5]<<1 | fe[4]>>63
+ fe[4] = fe[4]<<1 | fe[3]>>63
+ fe[3] = fe[3]<<1 | fe[2]>>63
+ fe[2] = fe[2]<<1 | fe[1]>>63
+ fe[1] = fe[1]<<1 | fe[0]>>63
+ fe[0] = fe[0] << 1
+ return e
+}
+
+func (e *fe2) zero() *fe2 {
+ e[0].zero()
+ e[1].zero()
+ return e
+}
+
+func (e *fe2) one() *fe2 {
+ e[0].one()
+ e[1].zero()
+ return e
+}
+
+func (e *fe2) set(e2 *fe2) *fe2 {
+ e[0].set(&e2[0])
+ e[1].set(&e2[1])
+ return e
+}
+
+func (e *fe2) rand(r io.Reader) (*fe2, error) {
+ a0, err := new(fe).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ a1, err := new(fe).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ return &fe2{*a0, *a1}, nil
+}
+
+func (e *fe2) isOne() bool {
+ return e[0].isOne() && e[1].isZero()
+}
+
+func (e *fe2) isZero() bool {
+ return e[0].isZero() && e[1].isZero()
+}
+
+func (e *fe2) equal(e2 *fe2) bool {
+ return e[0].equal(&e2[0]) && e[1].equal(&e2[1])
+}
+
+func (e *fe2) sign() bool {
+ r := new(fe)
+ if !e[0].isZero() {
+ fromMont(r, &e[0])
+ return r[0]&1 == 0
+ }
+ fromMont(r, &e[1])
+ return r[0]&1 == 0
+}
+
+func (e *fe6) zero() *fe6 {
+ e[0].zero()
+ e[1].zero()
+ e[2].zero()
+ return e
+}
+
+func (e *fe6) one() *fe6 {
+ e[0].one()
+ e[1].zero()
+ e[2].zero()
+ return e
+}
+
+func (e *fe6) set(e2 *fe6) *fe6 {
+ e[0].set(&e2[0])
+ e[1].set(&e2[1])
+ e[2].set(&e2[2])
+ return e
+}
+
+func (e *fe6) rand(r io.Reader) (*fe6, error) {
+ a0, err := new(fe2).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ a1, err := new(fe2).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ a2, err := new(fe2).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ return &fe6{*a0, *a1, *a2}, nil
+}
+
+func (e *fe6) isOne() bool {
+ return e[0].isOne() && e[1].isZero() && e[2].isZero()
+}
+
+func (e *fe6) isZero() bool {
+ return e[0].isZero() && e[1].isZero() && e[2].isZero()
+}
+
+func (e *fe6) equal(e2 *fe6) bool {
+ return e[0].equal(&e2[0]) && e[1].equal(&e2[1]) && e[2].equal(&e2[2])
+}
+
+func (e *fe12) zero() *fe12 {
+ e[0].zero()
+ e[1].zero()
+ return e
+}
+
+func (e *fe12) one() *fe12 {
+ e[0].one()
+ e[1].zero()
+ return e
+}
+
+func (e *fe12) set(e2 *fe12) *fe12 {
+ e[0].set(&e2[0])
+ e[1].set(&e2[1])
+ return e
+}
+
+func (e *fe12) rand(r io.Reader) (*fe12, error) {
+ a0, err := new(fe6).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ a1, err := new(fe6).rand(r)
+ if err != nil {
+ return nil, err
+ }
+ return &fe12{*a0, *a1}, nil
+}
+
+func (e *fe12) isOne() bool {
+ return e[0].isOne() && e[1].isZero()
+}
+
+func (e *fe12) isZero() bool {
+ return e[0].isZero() && e[1].isZero()
+}
+
+func (e *fe12) equal(e2 *fe12) bool {
+ return e[0].equal(&e2[0]) && e[1].equal(&e2[1])
+}
diff --git a/common/crypto/bls12381/field_element_test.go b/common/crypto/bls12381/field_element_test.go
new file mode 100644
index 0000000..0f6abd2
--- /dev/null
+++ b/common/crypto/bls12381/field_element_test.go
@@ -0,0 +1,251 @@
+package bls12381
+
+import (
+ "bytes"
+ "crypto/rand"
+ "math/big"
+ "testing"
+)
+
+func TestFieldElementValidation(t *testing.T) {
+ zero := new(fe).zero()
+ if !zero.isValid() {
+ t.Fatal("zero must be valid")
+ }
+ one := new(fe).one()
+ if !one.isValid() {
+ t.Fatal("one must be valid")
+ }
+ if modulus.isValid() {
+ t.Fatal("modulus must be invalid")
+ }
+ n := modulus.big()
+ n.Add(n, big.NewInt(1))
+ if new(fe).setBig(n).isValid() {
+ t.Fatal("number greater than modulus must be invalid")
+ }
+}
+
+func TestFieldElementEquality(t *testing.T) {
+ // fe
+ zero := new(fe).zero()
+ if !zero.equal(zero) {
+ t.Fatal("0 == 0")
+ }
+ one := new(fe).one()
+ if !one.equal(one) {
+ t.Fatal("1 == 1")
+ }
+ a, _ := new(fe).rand(rand.Reader)
+ if !a.equal(a) {
+ t.Fatal("a == a")
+ }
+ b := new(fe)
+ add(b, a, one)
+ if a.equal(b) {
+ t.Fatal("a != a + 1")
+ }
+ // fe2
+ zero2 := new(fe2).zero()
+ if !zero2.equal(zero2) {
+ t.Fatal("0 == 0")
+ }
+ one2 := new(fe2).one()
+ if !one2.equal(one2) {
+ t.Fatal("1 == 1")
+ }
+ a2, _ := new(fe2).rand(rand.Reader)
+ if !a2.equal(a2) {
+ t.Fatal("a == a")
+ }
+ b2 := new(fe2)
+ fp2 := newFp2()
+ fp2.add(b2, a2, one2)
+ if a2.equal(b2) {
+ t.Fatal("a != a + 1")
+ }
+ // fe6
+ zero6 := new(fe6).zero()
+ if !zero6.equal(zero6) {
+ t.Fatal("0 == 0")
+ }
+ one6 := new(fe6).one()
+ if !one6.equal(one6) {
+ t.Fatal("1 == 1")
+ }
+ a6, _ := new(fe6).rand(rand.Reader)
+ if !a6.equal(a6) {
+ t.Fatal("a == a")
+ }
+ b6 := new(fe6)
+ fp6 := newFp6(fp2)
+ fp6.add(b6, a6, one6)
+ if a6.equal(b6) {
+ t.Fatal("a != a + 1")
+ }
+ // fe12
+ zero12 := new(fe12).zero()
+ if !zero12.equal(zero12) {
+ t.Fatal("0 == 0")
+ }
+ one12 := new(fe12).one()
+ if !one12.equal(one12) {
+ t.Fatal("1 == 1")
+ }
+ a12, _ := new(fe12).rand(rand.Reader)
+ if !a12.equal(a12) {
+ t.Fatal("a == a")
+ }
+ b12 := new(fe12)
+ fp12 := newFp12(fp6)
+ fp12.add(b12, a12, one12)
+ if a12.equal(b12) {
+ t.Fatal("a != a + 1")
+ }
+
+}
+
+func TestFieldElementHelpers(t *testing.T) {
+ // fe
+ zero := new(fe).zero()
+ if !zero.isZero() {
+ t.Fatal("'zero' is not zero")
+ }
+ one := new(fe).one()
+ if !one.isOne() {
+ t.Fatal("'one' is not one")
+ }
+ odd := new(fe).setBig(big.NewInt(1))
+ if !odd.isOdd() {
+ t.Fatal("1 must be odd")
+ }
+ if odd.isEven() {
+ t.Fatal("1 must not be even")
+ }
+ even := new(fe).setBig(big.NewInt(2))
+ if !even.isEven() {
+ t.Fatal("2 must be even")
+ }
+ if even.isOdd() {
+ t.Fatal("2 must not be odd")
+ }
+ // fe2
+ zero2 := new(fe2).zero()
+ if !zero2.isZero() {
+ t.Fatal("'zero' is not zero, 2")
+ }
+ one2 := new(fe2).one()
+ if !one2.isOne() {
+ t.Fatal("'one' is not one, 2")
+ }
+ // fe6
+ zero6 := new(fe6).zero()
+ if !zero6.isZero() {
+ t.Fatal("'zero' is not zero, 6")
+ }
+ one6 := new(fe6).one()
+ if !one6.isOne() {
+ t.Fatal("'one' is not one, 6")
+ }
+ // fe12
+ zero12 := new(fe12).zero()
+ if !zero12.isZero() {
+ t.Fatal("'zero' is not zero, 12")
+ }
+ one12 := new(fe12).one()
+ if !one12.isOne() {
+ t.Fatal("'one' is not one, 12")
+ }
+}
+
+func TestFieldElementSerialization(t *testing.T) {
+ t.Run("zero", func(t *testing.T) {
+ in := make([]byte, 48)
+ fe := new(fe).setBytes(in)
+ if !fe.isZero() {
+ t.Fatal("bad serialization")
+ }
+ if !bytes.Equal(in, fe.bytes()) {
+ t.Fatal("bad serialization")
+ }
+ })
+ t.Run("bytes", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b := new(fe).setBytes(a.bytes())
+ if !a.equal(b) {
+ t.Fatal("bad serialization")
+ }
+ }
+ })
+ t.Run("big", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b := new(fe).setBig(a.big())
+ if !a.equal(b) {
+ t.Fatal("bad encoding or decoding")
+ }
+ }
+ })
+ t.Run("string", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, err := new(fe).setString(a.string())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad encoding or decoding")
+ }
+ }
+ })
+}
+
+func TestFieldElementByteInputs(t *testing.T) {
+ zero := new(fe).zero()
+ in := make([]byte, 0)
+ a := new(fe).setBytes(in)
+ if !a.equal(zero) {
+ t.Fatal("bad serialization")
+ }
+ in = make([]byte, 48)
+ a = new(fe).setBytes(in)
+ if !a.equal(zero) {
+ t.Fatal("bad serialization")
+ }
+ in = make([]byte, 64)
+ a = new(fe).setBytes(in)
+ if !a.equal(zero) {
+ t.Fatal("bad serialization")
+ }
+ in = make([]byte, 49)
+ in[47] = 1
+ normalOne := &fe{1, 0, 0, 0, 0, 0}
+ a = new(fe).setBytes(in)
+ if !a.equal(normalOne) {
+ t.Fatal("bad serialization")
+ }
+}
+
+func TestFieldElementCopy(t *testing.T) {
+ a, _ := new(fe).rand(rand.Reader)
+ b := new(fe).set(a)
+ if !a.equal(b) {
+ t.Fatal("bad copy, 1")
+ }
+ a2, _ := new(fe2).rand(rand.Reader)
+ b2 := new(fe2).set(a2)
+ if !a2.equal(b2) {
+ t.Fatal("bad copy, 2")
+ }
+ a6, _ := new(fe6).rand(rand.Reader)
+ b6 := new(fe6).set(a6)
+ if !a6.equal(b6) {
+ t.Fatal("bad copy, 6")
+ }
+ a12, _ := new(fe12).rand(rand.Reader)
+ b12 := new(fe12).set(a12)
+ if !a12.equal(b12) {
+ t.Fatal("bad copy, 12")
+ }
+}
diff --git a/common/crypto/bls12381/fp.go b/common/crypto/bls12381/fp.go
new file mode 100644
index 0000000..d850b5c
--- /dev/null
+++ b/common/crypto/bls12381/fp.go
@@ -0,0 +1,167 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+import (
+ "errors"
+ "math/big"
+)
+
+func fromBytes(in []byte) (*fe, error) {
+ fe := &fe{}
+ if len(in) != 48 {
+ return nil, errors.New("input string should be equal 48 bytes")
+ }
+ fe.setBytes(in)
+ if !fe.isValid() {
+ return nil, errors.New("must be less than modulus")
+ }
+ toMont(fe, fe)
+ return fe, nil
+}
+
+func fromBig(in *big.Int) (*fe, error) {
+ fe := new(fe).setBig(in)
+ if !fe.isValid() {
+ return nil, errors.New("invalid input string")
+ }
+ toMont(fe, fe)
+ return fe, nil
+}
+
+func fromString(in string) (*fe, error) {
+ fe, err := new(fe).setString(in)
+ if err != nil {
+ return nil, err
+ }
+ if !fe.isValid() {
+ return nil, errors.New("invalid input string")
+ }
+ toMont(fe, fe)
+ return fe, nil
+}
+
+func toBytes(e *fe) []byte {
+ e2 := new(fe)
+ fromMont(e2, e)
+ return e2.bytes()
+}
+
+func toBig(e *fe) *big.Int {
+ e2 := new(fe)
+ fromMont(e2, e)
+ return e2.big()
+}
+
+func toString(e *fe) (s string) {
+ e2 := new(fe)
+ fromMont(e2, e)
+ return e2.string()
+}
+
+func toMont(c, a *fe) {
+ mul(c, a, r2)
+}
+
+func fromMont(c, a *fe) {
+ mul(c, a, &fe{1})
+}
+
+func exp(c, a *fe, e *big.Int) {
+ z := new(fe).set(r1)
+ for i := e.BitLen(); i >= 0; i-- {
+ mul(z, z, z)
+ if e.Bit(i) == 1 {
+ mul(z, z, a)
+ }
+ }
+ c.set(z)
+}
+
+func inverse(inv, e *fe) {
+ if e.isZero() {
+ inv.zero()
+ return
+ }
+ u := new(fe).set(&modulus)
+ v := new(fe).set(e)
+ s := &fe{1}
+ r := &fe{0}
+ var k int
+ var z uint64
+ var found = false
+ // Phase 1
+ for i := 0; i < 768; i++ {
+ if v.isZero() {
+ found = true
+ break
+ }
+ if u.isEven() {
+ u.div2(0)
+ s.mul2()
+ } else if v.isEven() {
+ v.div2(0)
+ z += r.mul2()
+ } else if u.cmp(v) == 1 {
+ lsubAssign(u, v)
+ u.div2(0)
+ laddAssign(r, s)
+ s.mul2()
+ } else {
+ lsubAssign(v, u)
+ v.div2(0)
+ laddAssign(s, r)
+ z += r.mul2()
+ }
+ k++
+ }
+
+ if !found {
+ inv.zero()
+ return
+ }
+
+ if k < 381 || k > 381+384 {
+ inv.zero()
+ return
+ }
+
+ if r.cmp(&modulus) != -1 || z > 0 {
+ lsubAssign(r, &modulus)
+ }
+ u.set(&modulus)
+ lsubAssign(u, r)
+
+ // Phase 2
+ for i := k; i < 384*2; i++ {
+ double(u, u)
+ }
+ inv.set(u)
+}
+
+func sqrt(c, a *fe) bool {
+ u, v := new(fe).set(a), new(fe)
+ exp(c, a, pPlus1Over4)
+ square(v, c)
+ return u.equal(v)
+}
+
+func isQuadraticNonResidue(elem *fe) bool {
+ result := new(fe)
+ exp(result, elem, pMinus1Over2)
+ return !result.isOne()
+}
diff --git a/common/crypto/bls12381/fp12.go b/common/crypto/bls12381/fp12.go
new file mode 100644
index 0000000..bd7d7ec
--- /dev/null
+++ b/common/crypto/bls12381/fp12.go
@@ -0,0 +1,279 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+import (
+ "errors"
+ "math/big"
+)
+
+type fp12 struct {
+ fp12temp
+ fp6 *fp6
+}
+
+type fp12temp struct {
+ t2 [9]*fe2
+ t6 [5]*fe6
+ t12 *fe12
+}
+
+func newFp12Temp() fp12temp {
+ t2 := [9]*fe2{}
+ t6 := [5]*fe6{}
+ for i := 0; i < len(t2); i++ {
+ t2[i] = &fe2{}
+ }
+ for i := 0; i < len(t6); i++ {
+ t6[i] = &fe6{}
+ }
+ return fp12temp{t2, t6, &fe12{}}
+}
+
+func newFp12(fp6 *fp6) *fp12 {
+ t := newFp12Temp()
+ if fp6 == nil {
+ return &fp12{t, newFp6(nil)}
+ }
+ return &fp12{t, fp6}
+}
+
+func (e *fp12) fp2() *fp2 {
+ return e.fp6.fp2
+}
+
+func (e *fp12) fromBytes(in []byte) (*fe12, error) {
+ if len(in) != 576 {
+ return nil, errors.New("input string should be larger than 96 bytes")
+ }
+ fp6 := e.fp6
+ c1, err := fp6.fromBytes(in[:288])
+ if err != nil {
+ return nil, err
+ }
+ c0, err := fp6.fromBytes(in[288:])
+ if err != nil {
+ return nil, err
+ }
+ return &fe12{*c0, *c1}, nil
+}
+
+func (e *fp12) toBytes(a *fe12) []byte {
+ fp6 := e.fp6
+ out := make([]byte, 576)
+ copy(out[:288], fp6.toBytes(&a[1]))
+ copy(out[288:], fp6.toBytes(&a[0]))
+ return out
+}
+
+func (e *fp12) new() *fe12 {
+ return new(fe12)
+}
+
+func (e *fp12) zero() *fe12 {
+ return new(fe12)
+}
+
+func (e *fp12) one() *fe12 {
+ return new(fe12).one()
+}
+
+func (e *fp12) add(c, a, b *fe12) {
+ fp6 := e.fp6
+ fp6.add(&c[0], &a[0], &b[0])
+ fp6.add(&c[1], &a[1], &b[1])
+
+}
+
+func (e *fp12) double(c, a *fe12) {
+ fp6 := e.fp6
+ fp6.double(&c[0], &a[0])
+ fp6.double(&c[1], &a[1])
+}
+
+func (e *fp12) sub(c, a, b *fe12) {
+ fp6 := e.fp6
+ fp6.sub(&c[0], &a[0], &b[0])
+ fp6.sub(&c[1], &a[1], &b[1])
+
+}
+
+func (e *fp12) neg(c, a *fe12) {
+ fp6 := e.fp6
+ fp6.neg(&c[0], &a[0])
+ fp6.neg(&c[1], &a[1])
+}
+
+func (e *fp12) conjugate(c, a *fe12) {
+ fp6 := e.fp6
+ c[0].set(&a[0])
+ fp6.neg(&c[1], &a[1])
+}
+
+func (e *fp12) square(c, a *fe12) {
+ fp6, t := e.fp6, e.t6
+ fp6.add(t[0], &a[0], &a[1])
+ fp6.mul(t[2], &a[0], &a[1])
+ fp6.mulByNonResidue(t[1], &a[1])
+ fp6.addAssign(t[1], &a[0])
+ fp6.mulByNonResidue(t[3], t[2])
+ fp6.mulAssign(t[0], t[1])
+ fp6.subAssign(t[0], t[2])
+ fp6.sub(&c[0], t[0], t[3])
+ fp6.double(&c[1], t[2])
+}
+
+func (e *fp12) cyclotomicSquare(c, a *fe12) {
+ t, fp2 := e.t2, e.fp2()
+ e.fp4Square(t[3], t[4], &a[0][0], &a[1][1])
+ fp2.sub(t[2], t[3], &a[0][0])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[0][0], t[2], t[3])
+ fp2.add(t[2], t[4], &a[1][1])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[1][1], t[2], t[4])
+ e.fp4Square(t[3], t[4], &a[1][0], &a[0][2])
+ e.fp4Square(t[5], t[6], &a[0][1], &a[1][2])
+ fp2.sub(t[2], t[3], &a[0][1])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[0][1], t[2], t[3])
+ fp2.add(t[2], t[4], &a[1][2])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[1][2], t[2], t[4])
+ fp2.mulByNonResidue(t[3], t[6])
+ fp2.add(t[2], t[3], &a[1][0])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[1][0], t[2], t[3])
+ fp2.sub(t[2], t[5], &a[0][2])
+ fp2.doubleAssign(t[2])
+ fp2.add(&c[0][2], t[2], t[5])
+}
+
+func (e *fp12) mul(c, a, b *fe12) {
+ t, fp6 := e.t6, e.fp6
+ fp6.mul(t[1], &a[0], &b[0])
+ fp6.mul(t[2], &a[1], &b[1])
+ fp6.add(t[0], t[1], t[2])
+ fp6.mulByNonResidue(t[2], t[2])
+ fp6.add(t[3], t[1], t[2])
+ fp6.add(t[1], &a[0], &a[1])
+ fp6.add(t[2], &b[0], &b[1])
+ fp6.mulAssign(t[1], t[2])
+ c[0].set(t[3])
+ fp6.sub(&c[1], t[1], t[0])
+}
+
+func (e *fp12) mulAssign(a, b *fe12) {
+ t, fp6 := e.t6, e.fp6
+ fp6.mul(t[1], &a[0], &b[0])
+ fp6.mul(t[2], &a[1], &b[1])
+ fp6.add(t[0], t[1], t[2])
+ fp6.mulByNonResidue(t[2], t[2])
+ fp6.add(t[3], t[1], t[2])
+ fp6.add(t[1], &a[0], &a[1])
+ fp6.add(t[2], &b[0], &b[1])
+ fp6.mulAssign(t[1], t[2])
+ a[0].set(t[3])
+ fp6.sub(&a[1], t[1], t[0])
+}
+
+func (e *fp12) fp4Square(c0, c1, a0, a1 *fe2) {
+ t, fp2 := e.t2, e.fp2()
+ fp2.square(t[0], a0)
+ fp2.square(t[1], a1)
+ fp2.mulByNonResidue(t[2], t[1])
+ fp2.add(c0, t[2], t[0])
+ fp2.add(t[2], a0, a1)
+ fp2.squareAssign(t[2])
+ fp2.subAssign(t[2], t[0])
+ fp2.sub(c1, t[2], t[1])
+}
+
+func (e *fp12) inverse(c, a *fe12) {
+ fp6, t := e.fp6, e.t6
+ fp6.square(t[0], &a[0])
+ fp6.square(t[1], &a[1])
+ fp6.mulByNonResidue(t[1], t[1])
+ fp6.sub(t[1], t[0], t[1])
+ fp6.inverse(t[0], t[1])
+ fp6.mul(&c[0], &a[0], t[0])
+ fp6.mulAssign(t[0], &a[1])
+ fp6.neg(&c[1], t[0])
+}
+
+func (e *fp12) mulBy014Assign(a *fe12, c0, c1, c4 *fe2) {
+ fp2, fp6, t, t2 := e.fp2(), e.fp6, e.t6, e.t2[0]
+ fp6.mulBy01(t[0], &a[0], c0, c1)
+ fp6.mulBy1(t[1], &a[1], c4)
+ fp2.add(t2, c1, c4)
+ fp6.add(t[2], &a[1], &a[0])
+ fp6.mulBy01Assign(t[2], c0, t2)
+ fp6.subAssign(t[2], t[0])
+ fp6.sub(&a[1], t[2], t[1])
+ fp6.mulByNonResidue(t[1], t[1])
+ fp6.add(&a[0], t[1], t[0])
+}
+
+func (e *fp12) exp(c, a *fe12, s *big.Int) {
+ z := e.one()
+ for i := s.BitLen() - 1; i >= 0; i-- {
+ e.square(z, z)
+ if s.Bit(i) == 1 {
+ e.mul(z, z, a)
+ }
+ }
+ c.set(z)
+}
+
+func (e *fp12) cyclotomicExp(c, a *fe12, s *big.Int) {
+ z := e.one()
+ for i := s.BitLen() - 1; i >= 0; i-- {
+ e.cyclotomicSquare(z, z)
+ if s.Bit(i) == 1 {
+ e.mul(z, z, a)
+ }
+ }
+ c.set(z)
+}
+
+func (e *fp12) frobeniusMap(c, a *fe12, power uint) {
+ fp6 := e.fp6
+ fp6.frobeniusMap(&c[0], &a[0], power)
+ fp6.frobeniusMap(&c[1], &a[1], power)
+ switch power {
+ case 0:
+ return
+ case 6:
+ fp6.neg(&c[1], &c[1])
+ default:
+ fp6.mulByBaseField(&c[1], &c[1], &frobeniusCoeffs12[power])
+ }
+}
+
+func (e *fp12) frobeniusMapAssign(a *fe12, power uint) {
+ fp6 := e.fp6
+ fp6.frobeniusMapAssign(&a[0], power)
+ fp6.frobeniusMapAssign(&a[1], power)
+ switch power {
+ case 0:
+ return
+ case 6:
+ fp6.neg(&a[1], &a[1])
+ default:
+ fp6.mulByBaseField(&a[1], &a[1], &frobeniusCoeffs12[power])
+ }
+}
diff --git a/common/crypto/bls12381/fp2.go b/common/crypto/bls12381/fp2.go
new file mode 100644
index 0000000..785330f
--- /dev/null
+++ b/common/crypto/bls12381/fp2.go
@@ -0,0 +1,252 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+import (
+ "errors"
+ "math/big"
+)
+
+type fp2Temp struct {
+ t [4]*fe
+}
+
+type fp2 struct {
+ fp2Temp
+}
+
+func newFp2Temp() fp2Temp {
+ t := [4]*fe{}
+ for i := 0; i < len(t); i++ {
+ t[i] = &fe{}
+ }
+ return fp2Temp{t}
+}
+
+func newFp2() *fp2 {
+ t := newFp2Temp()
+ return &fp2{t}
+}
+
+func (e *fp2) fromBytes(in []byte) (*fe2, error) {
+ if len(in) != 96 {
+ return nil, errors.New("length of input string should be 96 bytes")
+ }
+ c1, err := fromBytes(in[:48])
+ if err != nil {
+ return nil, err
+ }
+ c0, err := fromBytes(in[48:])
+ if err != nil {
+ return nil, err
+ }
+ return &fe2{*c0, *c1}, nil
+}
+
+func (e *fp2) toBytes(a *fe2) []byte {
+ out := make([]byte, 96)
+ copy(out[:48], toBytes(&a[1]))
+ copy(out[48:], toBytes(&a[0]))
+ return out
+}
+
+func (e *fp2) new() *fe2 {
+ return new(fe2).zero()
+}
+
+func (e *fp2) zero() *fe2 {
+ return new(fe2).zero()
+}
+
+func (e *fp2) one() *fe2 {
+ return new(fe2).one()
+}
+
+func (e *fp2) add(c, a, b *fe2) {
+ add(&c[0], &a[0], &b[0])
+ add(&c[1], &a[1], &b[1])
+}
+
+func (e *fp2) addAssign(a, b *fe2) {
+ addAssign(&a[0], &b[0])
+ addAssign(&a[1], &b[1])
+}
+
+func (e *fp2) ladd(c, a, b *fe2) {
+ ladd(&c[0], &a[0], &b[0])
+ ladd(&c[1], &a[1], &b[1])
+}
+
+func (e *fp2) double(c, a *fe2) {
+ double(&c[0], &a[0])
+ double(&c[1], &a[1])
+}
+
+func (e *fp2) doubleAssign(a *fe2) {
+ doubleAssign(&a[0])
+ doubleAssign(&a[1])
+}
+
+func (e *fp2) ldouble(c, a *fe2) {
+ ldouble(&c[0], &a[0])
+ ldouble(&c[1], &a[1])
+}
+
+func (e *fp2) sub(c, a, b *fe2) {
+ sub(&c[0], &a[0], &b[0])
+ sub(&c[1], &a[1], &b[1])
+}
+
+func (e *fp2) subAssign(c, a *fe2) {
+ subAssign(&c[0], &a[0])
+ subAssign(&c[1], &a[1])
+}
+
+func (e *fp2) neg(c, a *fe2) {
+ neg(&c[0], &a[0])
+ neg(&c[1], &a[1])
+}
+
+func (e *fp2) mul(c, a, b *fe2) {
+ t := e.t
+ mul(t[1], &a[0], &b[0])
+ mul(t[2], &a[1], &b[1])
+ add(t[0], &a[0], &a[1])
+ add(t[3], &b[0], &b[1])
+ sub(&c[0], t[1], t[2])
+ addAssign(t[1], t[2])
+ mul(t[0], t[0], t[3])
+ sub(&c[1], t[0], t[1])
+}
+
+func (e *fp2) mulAssign(a, b *fe2) {
+ t := e.t
+ mul(t[1], &a[0], &b[0])
+ mul(t[2], &a[1], &b[1])
+ add(t[0], &a[0], &a[1])
+ add(t[3], &b[0], &b[1])
+ sub(&a[0], t[1], t[2])
+ addAssign(t[1], t[2])
+ mul(t[0], t[0], t[3])
+ sub(&a[1], t[0], t[1])
+}
+
+func (e *fp2) square(c, a *fe2) {
+ t := e.t
+ ladd(t[0], &a[0], &a[1])
+ sub(t[1], &a[0], &a[1])
+ ldouble(t[2], &a[0])
+ mul(&c[0], t[0], t[1])
+ mul(&c[1], t[2], &a[1])
+}
+
+func (e *fp2) squareAssign(a *fe2) {
+ t := e.t
+ ladd(t[0], &a[0], &a[1])
+ sub(t[1], &a[0], &a[1])
+ ldouble(t[2], &a[0])
+ mul(&a[0], t[0], t[1])
+ mul(&a[1], t[2], &a[1])
+}
+
+func (e *fp2) mulByNonResidue(c, a *fe2) {
+ t := e.t
+ sub(t[0], &a[0], &a[1])
+ add(&c[1], &a[0], &a[1])
+ c[0].set(t[0])
+}
+
+func (e *fp2) mulByB(c, a *fe2) {
+ t := e.t
+ double(t[0], &a[0])
+ double(t[1], &a[1])
+ doubleAssign(t[0])
+ doubleAssign(t[1])
+ sub(&c[0], t[0], t[1])
+ add(&c[1], t[0], t[1])
+}
+
+func (e *fp2) inverse(c, a *fe2) {
+ t := e.t
+ square(t[0], &a[0])
+ square(t[1], &a[1])
+ addAssign(t[0], t[1])
+ inverse(t[0], t[0])
+ mul(&c[0], &a[0], t[0])
+ mul(t[0], t[0], &a[1])
+ neg(&c[1], t[0])
+}
+
+func (e *fp2) mulByFq(c, a *fe2, b *fe) {
+ mul(&c[0], &a[0], b)
+ mul(&c[1], &a[1], b)
+}
+
+func (e *fp2) exp(c, a *fe2, s *big.Int) {
+ z := e.one()
+ for i := s.BitLen() - 1; i >= 0; i-- {
+ e.square(z, z)
+ if s.Bit(i) == 1 {
+ e.mul(z, z, a)
+ }
+ }
+ c.set(z)
+}
+
+func (e *fp2) frobeniusMap(c, a *fe2, power uint) {
+ c[0].set(&a[0])
+ if power%2 == 1 {
+ neg(&c[1], &a[1])
+ return
+ }
+ c[1].set(&a[1])
+}
+
+func (e *fp2) frobeniusMapAssign(a *fe2, power uint) {
+ if power%2 == 1 {
+ neg(&a[1], &a[1])
+ return
+ }
+}
+
+func (e *fp2) sqrt(c, a *fe2) bool {
+ u, x0, a1, alpha := &fe2{}, &fe2{}, &fe2{}, &fe2{}
+ u.set(a)
+ e.exp(a1, a, pMinus3Over4)
+ e.square(alpha, a1)
+ e.mul(alpha, alpha, a)
+ e.mul(x0, a1, a)
+ if alpha.equal(negativeOne2) {
+ neg(&c[0], &x0[1])
+ c[1].set(&x0[0])
+ return true
+ }
+ e.add(alpha, alpha, e.one())
+ e.exp(alpha, alpha, pMinus1Over2)
+ e.mul(c, alpha, x0)
+ e.square(alpha, c)
+ return alpha.equal(u)
+}
+
+func (e *fp2) isQuadraticNonResidue(a *fe2) bool {
+ // https://github.com/leovt/constructible/wiki/Taking-Square-Roots-in-quadratic-extension-Fields
+ c0, c1 := new(fe), new(fe)
+ square(c0, &a[0])
+ square(c1, &a[1])
+ add(c1, c1, c0)
+ return isQuadraticNonResidue(c1)
+}
diff --git a/common/crypto/bls12381/fp6.go b/common/crypto/bls12381/fp6.go
new file mode 100644
index 0000000..e913505
--- /dev/null
+++ b/common/crypto/bls12381/fp6.go
@@ -0,0 +1,351 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+import (
+ "errors"
+ "math/big"
+)
+
+type fp6Temp struct {
+ t [6]*fe2
+}
+
+type fp6 struct {
+ fp2 *fp2
+ fp6Temp
+}
+
+func newFp6Temp() fp6Temp {
+ t := [6]*fe2{}
+ for i := 0; i < len(t); i++ {
+ t[i] = &fe2{}
+ }
+ return fp6Temp{t}
+}
+
+func newFp6(f *fp2) *fp6 {
+ t := newFp6Temp()
+ if f == nil {
+ return &fp6{newFp2(), t}
+ }
+ return &fp6{f, t}
+}
+
+func (e *fp6) fromBytes(b []byte) (*fe6, error) {
+ if len(b) < 288 {
+ return nil, errors.New("input string should be larger than 288 bytes")
+ }
+ fp2 := e.fp2
+ u2, err := fp2.fromBytes(b[:96])
+ if err != nil {
+ return nil, err
+ }
+ u1, err := fp2.fromBytes(b[96:192])
+ if err != nil {
+ return nil, err
+ }
+ u0, err := fp2.fromBytes(b[192:])
+ if err != nil {
+ return nil, err
+ }
+ return &fe6{*u0, *u1, *u2}, nil
+}
+
+func (e *fp6) toBytes(a *fe6) []byte {
+ fp2 := e.fp2
+ out := make([]byte, 288)
+ copy(out[:96], fp2.toBytes(&a[2]))
+ copy(out[96:192], fp2.toBytes(&a[1]))
+ copy(out[192:], fp2.toBytes(&a[0]))
+ return out
+}
+
+func (e *fp6) new() *fe6 {
+ return new(fe6)
+}
+
+func (e *fp6) zero() *fe6 {
+ return new(fe6)
+}
+
+func (e *fp6) one() *fe6 {
+ return new(fe6).one()
+}
+
+func (e *fp6) add(c, a, b *fe6) {
+ fp2 := e.fp2
+ fp2.add(&c[0], &a[0], &b[0])
+ fp2.add(&c[1], &a[1], &b[1])
+ fp2.add(&c[2], &a[2], &b[2])
+}
+
+func (e *fp6) addAssign(a, b *fe6) {
+ fp2 := e.fp2
+ fp2.addAssign(&a[0], &b[0])
+ fp2.addAssign(&a[1], &b[1])
+ fp2.addAssign(&a[2], &b[2])
+}
+
+func (e *fp6) double(c, a *fe6) {
+ fp2 := e.fp2
+ fp2.double(&c[0], &a[0])
+ fp2.double(&c[1], &a[1])
+ fp2.double(&c[2], &a[2])
+}
+
+func (e *fp6) doubleAssign(a *fe6) {
+ fp2 := e.fp2
+ fp2.doubleAssign(&a[0])
+ fp2.doubleAssign(&a[1])
+ fp2.doubleAssign(&a[2])
+}
+
+func (e *fp6) sub(c, a, b *fe6) {
+ fp2 := e.fp2
+ fp2.sub(&c[0], &a[0], &b[0])
+ fp2.sub(&c[1], &a[1], &b[1])
+ fp2.sub(&c[2], &a[2], &b[2])
+}
+
+func (e *fp6) subAssign(a, b *fe6) {
+ fp2 := e.fp2
+ fp2.subAssign(&a[0], &b[0])
+ fp2.subAssign(&a[1], &b[1])
+ fp2.subAssign(&a[2], &b[2])
+}
+
+func (e *fp6) neg(c, a *fe6) {
+ fp2 := e.fp2
+ fp2.neg(&c[0], &a[0])
+ fp2.neg(&c[1], &a[1])
+ fp2.neg(&c[2], &a[2])
+}
+
+func (e *fp6) mul(c, a, b *fe6) {
+ fp2, t := e.fp2, e.t
+ fp2.mul(t[0], &a[0], &b[0])
+ fp2.mul(t[1], &a[1], &b[1])
+ fp2.mul(t[2], &a[2], &b[2])
+ fp2.add(t[3], &a[1], &a[2])
+ fp2.add(t[4], &b[1], &b[2])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[1], t[2])
+ fp2.subAssign(t[3], t[4])
+ fp2.mulByNonResidue(t[3], t[3])
+ fp2.add(t[5], t[0], t[3])
+ fp2.add(t[3], &a[0], &a[1])
+ fp2.add(t[4], &b[0], &b[1])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[0], t[1])
+ fp2.subAssign(t[3], t[4])
+ fp2.mulByNonResidue(t[4], t[2])
+ fp2.add(&c[1], t[3], t[4])
+ fp2.add(t[3], &a[0], &a[2])
+ fp2.add(t[4], &b[0], &b[2])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[0], t[2])
+ fp2.subAssign(t[3], t[4])
+ fp2.add(&c[2], t[1], t[3])
+ c[0].set(t[5])
+}
+
+func (e *fp6) mulAssign(a, b *fe6) {
+ fp2, t := e.fp2, e.t
+ fp2.mul(t[0], &a[0], &b[0])
+ fp2.mul(t[1], &a[1], &b[1])
+ fp2.mul(t[2], &a[2], &b[2])
+ fp2.add(t[3], &a[1], &a[2])
+ fp2.add(t[4], &b[1], &b[2])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[1], t[2])
+ fp2.subAssign(t[3], t[4])
+ fp2.mulByNonResidue(t[3], t[3])
+ fp2.add(t[5], t[0], t[3])
+ fp2.add(t[3], &a[0], &a[1])
+ fp2.add(t[4], &b[0], &b[1])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[0], t[1])
+ fp2.subAssign(t[3], t[4])
+ fp2.mulByNonResidue(t[4], t[2])
+ fp2.add(&a[1], t[3], t[4])
+ fp2.add(t[3], &a[0], &a[2])
+ fp2.add(t[4], &b[0], &b[2])
+ fp2.mulAssign(t[3], t[4])
+ fp2.add(t[4], t[0], t[2])
+ fp2.subAssign(t[3], t[4])
+ fp2.add(&a[2], t[1], t[3])
+ a[0].set(t[5])
+}
+
+func (e *fp6) square(c, a *fe6) {
+ fp2, t := e.fp2, e.t
+ fp2.square(t[0], &a[0])
+ fp2.mul(t[1], &a[0], &a[1])
+ fp2.doubleAssign(t[1])
+ fp2.sub(t[2], &a[0], &a[1])
+ fp2.addAssign(t[2], &a[2])
+ fp2.squareAssign(t[2])
+ fp2.mul(t[3], &a[1], &a[2])
+ fp2.doubleAssign(t[3])
+ fp2.square(t[4], &a[2])
+ fp2.mulByNonResidue(t[5], t[3])
+ fp2.add(&c[0], t[0], t[5])
+ fp2.mulByNonResidue(t[5], t[4])
+ fp2.add(&c[1], t[1], t[5])
+ fp2.addAssign(t[1], t[2])
+ fp2.addAssign(t[1], t[3])
+ fp2.addAssign(t[0], t[4])
+ fp2.sub(&c[2], t[1], t[0])
+}
+
+func (e *fp6) mulBy01Assign(a *fe6, b0, b1 *fe2) {
+ fp2, t := e.fp2, e.t
+ fp2.mul(t[0], &a[0], b0)
+ fp2.mul(t[1], &a[1], b1)
+ fp2.add(t[5], &a[1], &a[2])
+ fp2.mul(t[2], b1, t[5])
+ fp2.subAssign(t[2], t[1])
+ fp2.mulByNonResidue(t[2], t[2])
+ fp2.add(t[5], &a[0], &a[2])
+ fp2.mul(t[3], b0, t[5])
+ fp2.subAssign(t[3], t[0])
+ fp2.add(&a[2], t[3], t[1])
+ fp2.add(t[4], b0, b1)
+ fp2.add(t[5], &a[0], &a[1])
+ fp2.mulAssign(t[4], t[5])
+ fp2.subAssign(t[4], t[0])
+ fp2.sub(&a[1], t[4], t[1])
+ fp2.add(&a[0], t[2], t[0])
+}
+
+func (e *fp6) mulBy01(c, a *fe6, b0, b1 *fe2) {
+ fp2, t := e.fp2, e.t
+ fp2.mul(t[0], &a[0], b0)
+ fp2.mul(t[1], &a[1], b1)
+ fp2.add(t[2], &a[1], &a[2])
+ fp2.mulAssign(t[2], b1)
+ fp2.subAssign(t[2], t[1])
+ fp2.mulByNonResidue(t[2], t[2])
+ fp2.add(t[3], &a[0], &a[2])
+ fp2.mulAssign(t[3], b0)
+ fp2.subAssign(t[3], t[0])
+ fp2.add(&c[2], t[3], t[1])
+ fp2.add(t[4], b0, b1)
+ fp2.add(t[3], &a[0], &a[1])
+ fp2.mulAssign(t[4], t[3])
+ fp2.subAssign(t[4], t[0])
+ fp2.sub(&c[1], t[4], t[1])
+ fp2.add(&c[0], t[2], t[0])
+}
+
+func (e *fp6) mulBy1(c, a *fe6, b1 *fe2) {
+ fp2, t := e.fp2, e.t
+ fp2.mul(t[0], &a[2], b1)
+ fp2.mul(&c[2], &a[1], b1)
+ fp2.mul(&c[1], &a[0], b1)
+ fp2.mulByNonResidue(&c[0], t[0])
+}
+
+func (e *fp6) mulByNonResidue(c, a *fe6) {
+ fp2, t := e.fp2, e.t
+ t[0].set(&a[0])
+ fp2.mulByNonResidue(&c[0], &a[2])
+ c[2].set(&a[1])
+ c[1].set(t[0])
+}
+
+func (e *fp6) mulByBaseField(c, a *fe6, b *fe2) {
+ fp2 := e.fp2
+ fp2.mul(&c[0], &a[0], b)
+ fp2.mul(&c[1], &a[1], b)
+ fp2.mul(&c[2], &a[2], b)
+}
+
+func (e *fp6) exp(c, a *fe6, s *big.Int) {
+ z := e.one()
+ for i := s.BitLen() - 1; i >= 0; i-- {
+ e.square(z, z)
+ if s.Bit(i) == 1 {
+ e.mul(z, z, a)
+ }
+ }
+ c.set(z)
+}
+
+func (e *fp6) inverse(c, a *fe6) {
+ fp2, t := e.fp2, e.t
+ fp2.square(t[0], &a[0])
+ fp2.mul(t[1], &a[1], &a[2])
+ fp2.mulByNonResidue(t[1], t[1])
+ fp2.subAssign(t[0], t[1])
+ fp2.square(t[1], &a[1])
+ fp2.mul(t[2], &a[0], &a[2])
+ fp2.subAssign(t[1], t[2])
+ fp2.square(t[2], &a[2])
+ fp2.mulByNonResidue(t[2], t[2])
+ fp2.mul(t[3], &a[0], &a[1])
+ fp2.subAssign(t[2], t[3])
+ fp2.mul(t[3], &a[2], t[2])
+ fp2.mul(t[4], &a[1], t[1])
+ fp2.addAssign(t[3], t[4])
+ fp2.mulByNonResidue(t[3], t[3])
+ fp2.mul(t[4], &a[0], t[0])
+ fp2.addAssign(t[3], t[4])
+ fp2.inverse(t[3], t[3])
+ fp2.mul(&c[0], t[0], t[3])
+ fp2.mul(&c[1], t[2], t[3])
+ fp2.mul(&c[2], t[1], t[3])
+}
+
+func (e *fp6) frobeniusMap(c, a *fe6, power uint) {
+ fp2 := e.fp2
+ fp2.frobeniusMap(&c[0], &a[0], power)
+ fp2.frobeniusMap(&c[1], &a[1], power)
+ fp2.frobeniusMap(&c[2], &a[2], power)
+ switch power % 6 {
+ case 0:
+ return
+ case 3:
+ neg(&c[0][0], &a[1][1])
+ c[1][1].set(&a[1][0])
+ fp2.neg(&a[2], &a[2])
+ default:
+ fp2.mul(&c[1], &c[1], &frobeniusCoeffs61[power%6])
+ fp2.mul(&c[2], &c[2], &frobeniusCoeffs62[power%6])
+ }
+}
+
+func (e *fp6) frobeniusMapAssign(a *fe6, power uint) {
+ fp2 := e.fp2
+ fp2.frobeniusMapAssign(&a[0], power)
+ fp2.frobeniusMapAssign(&a[1], power)
+ fp2.frobeniusMapAssign(&a[2], power)
+ t := e.t
+ switch power % 6 {
+ case 0:
+ return
+ case 3:
+ neg(&t[0][0], &a[1][1])
+ a[1][1].set(&a[1][0])
+ a[1][0].set(&t[0][0])
+ fp2.neg(&a[2], &a[2])
+ default:
+ fp2.mulAssign(&a[1], &frobeniusCoeffs61[power%6])
+ fp2.mulAssign(&a[2], &frobeniusCoeffs62[power%6])
+ }
+}
diff --git a/common/crypto/bls12381/fp_test.go b/common/crypto/bls12381/fp_test.go
new file mode 100644
index 0000000..ba0a0f5
--- /dev/null
+++ b/common/crypto/bls12381/fp_test.go
@@ -0,0 +1,1412 @@
+package bls12381
+
+import (
+ "bytes"
+ "crypto/rand"
+ "math/big"
+ "testing"
+)
+
+func TestFpSerialization(t *testing.T) {
+ t.Run("zero", func(t *testing.T) {
+ in := make([]byte, 48)
+ fe, err := fromBytes(in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !fe.isZero() {
+ t.Fatal("bad serialization")
+ }
+ if !bytes.Equal(in, toBytes(fe)) {
+ t.Fatal("bad serialization")
+ }
+ })
+ t.Run("bytes", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, err := fromBytes(toBytes(a))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad serialization")
+ }
+ }
+ })
+ t.Run("string", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, err := fromString(toString(a))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad encoding or decoding")
+ }
+ }
+ })
+ t.Run("big", func(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, err := fromBig(toBig(a))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad encoding or decoding")
+ }
+ }
+ })
+}
+
+func TestFpAdditionCrossAgainstBigInt(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ c := new(fe)
+ big_a := toBig(a)
+ big_b := toBig(b)
+ big_c := new(big.Int)
+ add(c, a, b)
+ out_1 := toBytes(c)
+ out_2 := padBytes(big_c.Add(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied A")
+ }
+ double(c, a)
+ out_1 = toBytes(c)
+ out_2 = padBytes(big_c.Add(big_a, big_a).Mod(big_c, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied B")
+ }
+ sub(c, a, b)
+ out_1 = toBytes(c)
+ out_2 = padBytes(big_c.Sub(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied C")
+ }
+ neg(c, a)
+ out_1 = toBytes(c)
+ out_2 = padBytes(big_c.Neg(big_a).Mod(big_c, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied D")
+ }
+ }
+}
+
+func TestFpAdditionCrossAgainstBigIntAssigned(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ big_a, big_b := toBig(a), toBig(b)
+ addAssign(a, b)
+ out_1 := toBytes(a)
+ out_2 := padBytes(big_a.Add(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied A")
+ }
+ a, _ = new(fe).rand(rand.Reader)
+ big_a = toBig(a)
+ doubleAssign(a)
+ out_1 = toBytes(a)
+ out_2 = padBytes(big_a.Add(big_a, big_a).Mod(big_a, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied B")
+ }
+ a, _ = new(fe).rand(rand.Reader)
+ b, _ = new(fe).rand(rand.Reader)
+ big_a, big_b = toBig(a), toBig(b)
+ subAssign(a, b)
+ out_1 = toBytes(a)
+ out_2 = padBytes(big_a.Sub(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied A")
+ }
+ }
+}
+
+func TestFpAdditionProperties(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ zero := new(fe).zero()
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ c_1, c_2 := new(fe), new(fe)
+ add(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a + 0 == a")
+ }
+ sub(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a - 0 == a")
+ }
+ double(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ neg(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("-0 == 0")
+ }
+ sub(c_1, zero, a)
+ neg(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("0-a == -a")
+ }
+ double(c_1, a)
+ add(c_2, a, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("2 * a == a + a")
+ }
+ add(c_1, a, b)
+ add(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ sub(c_1, a, b)
+ sub(c_2, b, a)
+ neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ c_x, _ := new(fe).rand(rand.Reader)
+ add(c_1, a, b)
+ add(c_1, c_1, c_x)
+ add(c_2, a, c_x)
+ add(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a + b) + c == (a + c ) + b")
+ }
+ sub(c_1, a, b)
+ sub(c_1, c_1, c_x)
+ sub(c_2, a, c_x)
+ sub(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a - b) - c == (a - c ) -b")
+ }
+ }
+}
+
+func TestFpAdditionPropertiesAssigned(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ zero := new(fe).zero()
+ a, b := new(fe), new(fe)
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ addAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a + 0 == a")
+ }
+ subAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a - 0 == a")
+ }
+ a.set(zero)
+ doubleAssign(a)
+ if !a.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ a.set(zero)
+ subAssign(a, b)
+ neg(b, b)
+ if !a.equal(b) {
+ t.Fatal("0-a == -a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ doubleAssign(a)
+ addAssign(b, b)
+ if !a.equal(b) {
+ t.Fatal("2 * a == a + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1, c_2 := new(fe).set(a), new(fe).set(b)
+ addAssign(c_1, b)
+ addAssign(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1.set(a)
+ c_2.set(b)
+ subAssign(c_1, b)
+ subAssign(c_2, a)
+ neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c, _ := new(fe).rand(rand.Reader)
+ a0 := new(fe).set(a)
+ addAssign(a, b)
+ addAssign(a, c)
+ addAssign(b, c)
+ addAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("(a + b) + c == (b + c) + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ _, _ = c.rand(rand.Reader)
+ a0.set(a)
+ subAssign(a, b)
+ subAssign(a, c)
+ subAssign(a0, c)
+ subAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a - b) - c == (a - c) -b")
+ }
+ }
+}
+
+func TestFpLazyOperations(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ c, _ := new(fe).rand(rand.Reader)
+ c0 := new(fe)
+ c1 := new(fe)
+ ladd(c0, a, b)
+ add(c1, a, b)
+ mul(c0, c0, c)
+ mul(c1, c1, c)
+ if !c0.equal(c1) {
+ // l+ operator stands for lazy addition
+ t.Fatal("(a + b) * c == (a l+ b) * c")
+ }
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ ldouble(a, a)
+ ladd(b, b, b)
+ if !a.equal(b) {
+ t.Fatal("2 l* a = a l+ a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ _, _ = c.rand(rand.Reader)
+ a0 := new(fe).set(a)
+ lsubAssign(a, b)
+ laddAssign(a, &modulus)
+ mul(a, a, c)
+ subAssign(a0, b)
+ mul(a0, a0, c)
+ if !a.equal(a0) {
+ t.Fatal("((a l- b) + p) * c = (a-b) * c")
+ }
+ }
+}
+
+func TestFpMultiplicationCrossAgainstBigInt(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ c := new(fe)
+ big_a := toBig(a)
+ big_b := toBig(b)
+ big_c := new(big.Int)
+ mul(c, a, b)
+ out_1 := toBytes(c)
+ out_2 := padBytes(big_c.Mul(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48)
+ if !bytes.Equal(out_1, out_2) {
+ t.Fatal("cross test against big.Int is not satisfied")
+ }
+ }
+}
+
+func TestFpMultiplicationProperties(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ zero, one := new(fe).zero(), new(fe).one()
+ c_1, c_2 := new(fe), new(fe)
+ mul(c_1, a, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ mul(c_1, a, one)
+ if !c_1.equal(a) {
+ t.Fatal("a * 1 == a")
+ }
+ mul(c_1, a, b)
+ mul(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a * b == b * a")
+ }
+ c_x, _ := new(fe).rand(rand.Reader)
+ mul(c_1, a, b)
+ mul(c_1, c_1, c_x)
+ mul(c_2, c_x, b)
+ mul(c_2, c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ square(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("0^2 == 0")
+ }
+ square(a, one)
+ if !a.equal(one) {
+ t.Fatal("1^2 == 1")
+ }
+ _, _ = a.rand(rand.Reader)
+ square(c_1, a)
+ mul(c_2, a, a)
+ if !c_1.equal(c_1) {
+ t.Fatal("a^2 == a*a")
+ }
+ }
+}
+
+func TestFpExponentiation(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe).rand(rand.Reader)
+ u := new(fe)
+ exp(u, a, big.NewInt(0))
+ if !u.isOne() {
+ t.Fatal("a^0 == 1")
+ }
+ exp(u, a, big.NewInt(1))
+ if !u.equal(a) {
+ t.Fatal("a^1 == a")
+ }
+ v := new(fe)
+ mul(u, a, a)
+ mul(u, u, u)
+ mul(u, u, u)
+ exp(v, a, big.NewInt(8))
+ if !u.equal(v) {
+ t.Fatal("((a^2)^2)^2 == a^8")
+ }
+ p := modulus.big()
+ exp(u, a, p)
+ if !u.equal(a) {
+ t.Fatal("a^p == a")
+ }
+ exp(u, a, p.Sub(p, big.NewInt(1)))
+ if !u.isOne() {
+ t.Fatal("a^(p-1) == 1")
+ }
+ }
+}
+
+func TestFpInversion(t *testing.T) {
+ for i := 0; i < fuz; i++ {
+ u := new(fe)
+ zero, one := new(fe).zero(), new(fe).one()
+ inverse(u, zero)
+ if !u.equal(zero) {
+ t.Fatal("(0^-1) == 0)")
+ }
+ inverse(u, one)
+ if !u.equal(one) {
+ t.Fatal("(1^-1) == 1)")
+ }
+ a, _ := new(fe).rand(rand.Reader)
+ inverse(u, a)
+ mul(u, u, a)
+ if !u.equal(one) {
+ t.Fatal("(r*a) * r*(a^-1) == r)")
+ }
+ v := new(fe)
+ p := modulus.big()
+ exp(u, a, p.Sub(p, big.NewInt(2)))
+ inverse(v, a)
+ if !v.equal(u) {
+ t.Fatal("a^(p-2) == a^-1")
+ }
+ }
+}
+
+//func TestFpSquareRoot(t *testing.T) {
+// r := new(fe)
+// if sqrt(r, nonResidue1) {
+// t.Fatal("non residue cannot have a sqrt")
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe).rand(rand.Reader)
+// aa, rr, r := &fe{}, &fe{}, &fe{}
+// square(aa, a)
+// if !sqrt(r, aa) {
+// t.Fatal("bad sqrt 1")
+// }
+// square(rr, r)
+// if !rr.equal(aa) {
+// t.Fatal("bad sqrt 2")
+// }
+// }
+//}
+
+//func TestFpNonResidue(t *testing.T) {
+// if !isQuadraticNonResidue(nonResidue1) {
+// t.Fatal("element is quadratic non residue, 1")
+// }
+// if isQuadraticNonResidue(new(fe).one()) {
+// t.Fatal("one is not quadratic non residue")
+// }
+// if !isQuadraticNonResidue(new(fe).zero()) {
+// t.Fatal("should accept zero as quadratic non residue")
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe).rand(rand.Reader)
+// square(a, a)
+// if isQuadraticNonResidue(new(fe).one()) {
+// t.Fatal("element is not quadratic non residue")
+// }
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe).rand(rand.Reader)
+// if !sqrt(new(fe), a) {
+// if !isQuadraticNonResidue(a) {
+// t.Fatal("element is quadratic non residue, 2", i)
+// }
+// } else {
+// i--
+// }
+// }
+//
+//}
+
+//func TestFp2Serialization(t *testing.T) {
+// field := newFp2()
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe2).rand(rand.Reader)
+// b, err := field.fromBytes(field.toBytes(a))
+// if err != nil {
+// t.Fatal(err)
+// }
+// if !a.equal(b) {
+// t.Fatal("bad serialization")
+// }
+// }
+//}
+
+func TestFp2AdditionProperties(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ zero := field.zero()
+ a, _ := new(fe2).rand(rand.Reader)
+ b, _ := new(fe2).rand(rand.Reader)
+ c_1 := field.new()
+ c_2 := field.new()
+ field.add(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a + 0 == a")
+ }
+ field.sub(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a - 0 == a")
+ }
+ field.double(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ field.neg(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("-0 == 0")
+ }
+ field.sub(c_1, zero, a)
+ field.neg(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("0-a == -a")
+ }
+ field.double(c_1, a)
+ field.add(c_2, a, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("2 * a == a + a")
+ }
+ field.add(c_1, a, b)
+ field.add(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_2, b, a)
+ field.neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ c_x, _ := new(fe2).rand(rand.Reader)
+ field.add(c_1, a, b)
+ field.add(c_1, c_1, c_x)
+ field.add(c_2, a, c_x)
+ field.add(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a + b) + c == (a + c ) + b")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_1, c_1, c_x)
+ field.sub(c_2, a, c_x)
+ field.sub(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a - b) - c == (a - c ) -b")
+ }
+ }
+}
+
+func TestFp2AdditionPropertiesAssigned(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ zero := new(fe2).zero()
+ a, b := new(fe2), new(fe2)
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ field.addAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a + 0 == a")
+ }
+ field.subAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a - 0 == a")
+ }
+ a.set(zero)
+ field.doubleAssign(a)
+ if !a.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ a.set(zero)
+ field.subAssign(a, b)
+ field.neg(b, b)
+ if !a.equal(b) {
+ t.Fatal("0-a == -a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ field.doubleAssign(a)
+ field.addAssign(b, b)
+ if !a.equal(b) {
+ t.Fatal("2 * a == a + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1, c_2 := new(fe2).set(a), new(fe2).set(b)
+ field.addAssign(c_1, b)
+ field.addAssign(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1.set(a)
+ c_2.set(b)
+ field.subAssign(c_1, b)
+ field.subAssign(c_2, a)
+ field.neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c, _ := new(fe2).rand(rand.Reader)
+ a0 := new(fe2).set(a)
+ field.addAssign(a, b)
+ field.addAssign(a, c)
+ field.addAssign(b, c)
+ field.addAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("(a + b) + c == (b + c) + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ _, _ = c.rand(rand.Reader)
+ a0.set(a)
+ field.subAssign(a, b)
+ field.subAssign(a, c)
+ field.subAssign(a0, c)
+ field.subAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a - b) - c == (a - c) -b")
+ }
+ }
+}
+
+func TestFp2LazyOperations(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe2).rand(rand.Reader)
+ b, _ := new(fe2).rand(rand.Reader)
+ c, _ := new(fe2).rand(rand.Reader)
+ c0 := new(fe2)
+ c1 := new(fe2)
+ field.ladd(c0, a, b)
+ field.add(c1, a, b)
+ field.mulAssign(c0, c)
+ field.mulAssign(c1, c)
+ if !c0.equal(c1) {
+ // l+ operator stands for lazy addition
+ t.Fatal("(a + b) * c == (a l+ b) * c")
+ }
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ field.ldouble(a, a)
+ field.ladd(b, b, b)
+ if !a.equal(b) {
+ t.Fatal("2 l* a = a l+ a")
+ }
+ }
+}
+
+func TestFp2MultiplicationProperties(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe2).rand(rand.Reader)
+ b, _ := new(fe2).rand(rand.Reader)
+ zero := field.zero()
+ one := field.one()
+ c_1, c_2 := field.new(), field.new()
+ field.mul(c_1, a, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ field.mul(c_1, a, one)
+ if !c_1.equal(a) {
+ t.Fatal("a * 1 == a")
+ }
+ field.mul(c_1, a, b)
+ field.mul(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a * b == b * a")
+ }
+ c_x, _ := new(fe2).rand(rand.Reader)
+ field.mul(c_1, a, b)
+ field.mul(c_1, c_1, c_x)
+ field.mul(c_2, c_x, b)
+ field.mul(c_2, c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ field.square(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("0^2 == 0")
+ }
+ field.square(a, one)
+ if !a.equal(one) {
+ t.Fatal("1^2 == 1")
+ }
+ _, _ = a.rand(rand.Reader)
+ field.square(c_1, a)
+ field.mul(c_2, a, a)
+ if !c_2.equal(c_1) {
+ t.Fatal("a^2 == a*a")
+ }
+ }
+}
+
+func TestFp2MultiplicationPropertiesAssigned(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe2).rand(rand.Reader)
+ zero, one := new(fe2).zero(), new(fe2).one()
+ field.mulAssign(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ _, _ = a.rand(rand.Reader)
+ a0 := new(fe2).set(a)
+ field.mulAssign(a, one)
+ if !a.equal(a0) {
+ t.Fatal("a * 1 == a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b, _ := new(fe2).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("a * b == b * a")
+ }
+ c, _ := new(fe2).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(a, c)
+ field.mulAssign(a0, c)
+ field.mulAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ a0.set(a)
+ field.squareAssign(a)
+ field.mulAssign(a0, a0)
+ if !a.equal(a0) {
+ t.Fatal("a^2 == a*a")
+ }
+ }
+}
+
+func TestFp2Exponentiation(t *testing.T) {
+ field := newFp2()
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe2).rand(rand.Reader)
+ u := field.new()
+ field.exp(u, a, big.NewInt(0))
+ if !u.equal(field.one()) {
+ t.Fatal("a^0 == 1")
+ }
+ field.exp(u, a, big.NewInt(1))
+ if !u.equal(a) {
+ t.Fatal("a^1 == a")
+ }
+ v := field.new()
+ field.mul(u, a, a)
+ field.mul(u, u, u)
+ field.mul(u, u, u)
+ field.exp(v, a, big.NewInt(8))
+ if !u.equal(v) {
+ t.Fatal("((a^2)^2)^2 == a^8")
+ }
+ }
+}
+
+func TestFp2Inversion(t *testing.T) {
+ field := newFp2()
+ u := field.new()
+ zero := field.zero()
+ one := field.one()
+ field.inverse(u, zero)
+ if !u.equal(zero) {
+ t.Fatal("(0 ^ -1) == 0)")
+ }
+ field.inverse(u, one)
+ if !u.equal(one) {
+ t.Fatal("(1 ^ -1) == 1)")
+ }
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe2).rand(rand.Reader)
+ field.inverse(u, a)
+ field.mul(u, u, a)
+ if !u.equal(one) {
+ t.Fatal("(r * a) * r * (a ^ -1) == r)")
+ }
+ }
+}
+
+//func TestFp2SquareRoot(t *testing.T) {
+// field := newFp2()
+// for z := 0; z < 1000; z++ {
+// zi := new(fe)
+// sub(zi, &modulus, &fe{uint64(z * z)})
+// // r = (-z*z, 0)
+// r := &fe2{*zi, fe{0}}
+// toMont(&r[0], &r[0])
+// toMont(&r[1], &r[1])
+// c := field.new()
+// // sqrt((-z*z, 0)) = (0, z)
+// if !field.sqrt(c, r) {
+// t.Fatal("z*z does have a square root")
+// }
+// e := &fe2{fe{uint64(0)}, fe{uint64(z)}}
+// toMont(&e[0], &e[0])
+// toMont(&e[1], &e[1])
+// field.square(e, e)
+// field.square(c, c)
+// if !e.equal(c) {
+// t.Fatal("square root failed")
+// }
+// }
+// if field.sqrt(field.new(), nonResidue2) {
+// t.Fatal("non residue cannot have a sqrt")
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe2).rand(rand.Reader)
+// aa, rr, r := field.new(), field.new(), field.new()
+// field.square(aa, a)
+// if !field.sqrt(r, aa) {
+// t.Fatal("bad sqrt 1")
+// }
+// field.square(rr, r)
+// if !rr.equal(aa) {
+// t.Fatal("bad sqrt 2")
+// }
+// }
+//}
+
+//func TestFp2NonResidue(t *testing.T) {
+// field := newFp2()
+// if !field.isQuadraticNonResidue(nonResidue2) {
+// t.Fatal("element is quadratic non residue, 1")
+// }
+// if field.isQuadraticNonResidue(new(fe2).one()) {
+// t.Fatal("one is not quadratic non residue")
+// }
+// if !field.isQuadraticNonResidue(new(fe2).zero()) {
+// t.Fatal("should accept zero as quadratic non residue")
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe2).rand(rand.Reader)
+// field.squareAssign(a)
+// if field.isQuadraticNonResidue(new(fe2).one()) {
+// t.Fatal("element is not quadratic non residue")
+// }
+// }
+// for i := 0; i < fuz; i++ {
+// a, _ := new(fe2).rand(rand.Reader)
+// if !field.sqrt(new(fe2), a) {
+// if !field.isQuadraticNonResidue(a) {
+// t.Fatal("element is quadratic non residue, 2", i)
+// }
+// } else {
+// i--
+// }
+// }
+//}
+
+func TestFp6Serialization(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe6).rand(rand.Reader)
+ b, err := field.fromBytes(field.toBytes(a))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad serialization")
+ }
+ }
+}
+
+func TestFp6AdditionProperties(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ zero := field.zero()
+ a, _ := new(fe6).rand(rand.Reader)
+ b, _ := new(fe6).rand(rand.Reader)
+ c_1 := field.new()
+ c_2 := field.new()
+ field.add(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a + 0 == a")
+ }
+ field.sub(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a - 0 == a")
+ }
+ field.double(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ field.neg(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("-0 == 0")
+ }
+ field.sub(c_1, zero, a)
+ field.neg(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("0-a == -a")
+ }
+ field.double(c_1, a)
+ field.add(c_2, a, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("2 * a == a + a")
+ }
+ field.add(c_1, a, b)
+ field.add(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_2, b, a)
+ field.neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ c_x, _ := new(fe6).rand(rand.Reader)
+ field.add(c_1, a, b)
+ field.add(c_1, c_1, c_x)
+ field.add(c_2, a, c_x)
+ field.add(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a + b) + c == (a + c ) + b")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_1, c_1, c_x)
+ field.sub(c_2, a, c_x)
+ field.sub(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a - b) - c == (a - c ) -b")
+ }
+ }
+}
+
+func TestFp6AdditionPropertiesAssigned(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ zero := new(fe6).zero()
+ a, b := new(fe6), new(fe6)
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ field.addAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a + 0 == a")
+ }
+ field.subAssign(a, zero)
+ if !a.equal(b) {
+ t.Fatal("a - 0 == a")
+ }
+ a.set(zero)
+ field.doubleAssign(a)
+ if !a.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ a.set(zero)
+ field.subAssign(a, b)
+ field.neg(b, b)
+ if !a.equal(b) {
+ t.Fatal("0-a == -a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b.set(a)
+ field.doubleAssign(a)
+ field.addAssign(b, b)
+ if !a.equal(b) {
+ t.Fatal("2 * a == a + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1, c_2 := new(fe6).set(a), new(fe6).set(b)
+ field.addAssign(c_1, b)
+ field.addAssign(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c_1.set(a)
+ c_2.set(b)
+ field.subAssign(c_1, b)
+ field.subAssign(c_2, a)
+ field.neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ c, _ := new(fe6).rand(rand.Reader)
+ a0 := new(fe6).set(a)
+ field.addAssign(a, b)
+ field.addAssign(a, c)
+ field.addAssign(b, c)
+ field.addAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("(a + b) + c == (b + c) + a")
+ }
+ _, _ = a.rand(rand.Reader)
+ _, _ = b.rand(rand.Reader)
+ _, _ = c.rand(rand.Reader)
+ a0.set(a)
+ field.subAssign(a, b)
+ field.subAssign(a, c)
+ field.subAssign(a0, c)
+ field.subAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a - b) - c == (a - c) -b")
+ }
+ }
+}
+
+func TestFp6SparseMultiplication(t *testing.T) {
+ fp6 := newFp6(nil)
+ var a, b, u *fe6
+ for j := 0; j < fuz; j++ {
+ a, _ = new(fe6).rand(rand.Reader)
+ b, _ = new(fe6).rand(rand.Reader)
+ u, _ = new(fe6).rand(rand.Reader)
+ b[2].zero()
+ fp6.mul(u, a, b)
+ fp6.mulBy01(a, a, &b[0], &b[1])
+ if !a.equal(u) {
+ t.Fatal("bad mul by 01")
+ }
+ }
+ for j := 0; j < fuz; j++ {
+ a, _ = new(fe6).rand(rand.Reader)
+ b, _ = new(fe6).rand(rand.Reader)
+ u, _ = new(fe6).rand(rand.Reader)
+ b[2].zero()
+ b[0].zero()
+ fp6.mul(u, a, b)
+ fp6.mulBy1(a, a, &b[1])
+ if !a.equal(u) {
+ t.Fatal("bad mul by 1")
+ }
+ }
+}
+
+func TestFp6MultiplicationProperties(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe6).rand(rand.Reader)
+ b, _ := new(fe6).rand(rand.Reader)
+ zero := field.zero()
+ one := field.one()
+ c_1, c_2 := field.new(), field.new()
+ field.mul(c_1, a, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ field.mul(c_1, a, one)
+ if !c_1.equal(a) {
+ t.Fatal("a * 1 == a")
+ }
+ field.mul(c_1, a, b)
+ field.mul(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a * b == b * a")
+ }
+ c_x, _ := new(fe6).rand(rand.Reader)
+ field.mul(c_1, a, b)
+ field.mul(c_1, c_1, c_x)
+ field.mul(c_2, c_x, b)
+ field.mul(c_2, c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ field.square(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("0^2 == 0")
+ }
+ field.square(a, one)
+ if !a.equal(one) {
+ t.Fatal("1^2 == 1")
+ }
+ _, _ = a.rand(rand.Reader)
+ field.square(c_1, a)
+ field.mul(c_2, a, a)
+ if !c_2.equal(c_1) {
+ t.Fatal("a^2 == a*a")
+ }
+ }
+}
+
+func TestFp6MultiplicationPropertiesAssigned(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe6).rand(rand.Reader)
+ zero, one := new(fe6).zero(), new(fe6).one()
+ field.mulAssign(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ _, _ = a.rand(rand.Reader)
+ a0 := new(fe6).set(a)
+ field.mulAssign(a, one)
+ if !a.equal(a0) {
+ t.Fatal("a * 1 == a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b, _ := new(fe6).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("a * b == b * a")
+ }
+ c, _ := new(fe6).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(a, c)
+ field.mulAssign(a0, c)
+ field.mulAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ }
+}
+
+func TestFp6Exponentiation(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe6).rand(rand.Reader)
+ u := field.new()
+ field.exp(u, a, big.NewInt(0))
+ if !u.equal(field.one()) {
+ t.Fatal("a^0 == 1")
+ }
+ field.exp(u, a, big.NewInt(1))
+ if !u.equal(a) {
+ t.Fatal("a^1 == a")
+ }
+ v := field.new()
+ field.mul(u, a, a)
+ field.mul(u, u, u)
+ field.mul(u, u, u)
+ field.exp(v, a, big.NewInt(8))
+ if !u.equal(v) {
+ t.Fatal("((a^2)^2)^2 == a^8")
+ }
+ }
+}
+
+func TestFp6Inversion(t *testing.T) {
+ field := newFp6(nil)
+ for i := 0; i < fuz; i++ {
+ u := field.new()
+ zero := field.zero()
+ one := field.one()
+ field.inverse(u, zero)
+ if !u.equal(zero) {
+ t.Fatal("(0^-1) == 0)")
+ }
+ field.inverse(u, one)
+ if !u.equal(one) {
+ t.Fatal("(1^-1) == 1)")
+ }
+ a, _ := new(fe6).rand(rand.Reader)
+ field.inverse(u, a)
+ field.mul(u, u, a)
+ if !u.equal(one) {
+ t.Fatal("(r*a) * r*(a^-1) == r)")
+ }
+ }
+}
+
+func TestFp12Serialization(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe12).rand(rand.Reader)
+ b, err := field.fromBytes(field.toBytes(a))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !a.equal(b) {
+ t.Fatal("bad serialization")
+ }
+ }
+}
+
+func TestFp12AdditionProperties(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ zero := field.zero()
+ a, _ := new(fe12).rand(rand.Reader)
+ b, _ := new(fe12).rand(rand.Reader)
+ c_1 := field.new()
+ c_2 := field.new()
+ field.add(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a + 0 == a")
+ }
+ field.sub(c_1, a, zero)
+ if !c_1.equal(a) {
+ t.Fatal("a - 0 == a")
+ }
+ field.double(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("2 * 0 == 0")
+ }
+ field.neg(c_1, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("-0 == 0")
+ }
+ field.sub(c_1, zero, a)
+ field.neg(c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("0-a == -a")
+ }
+ field.double(c_1, a)
+ field.add(c_2, a, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("2 * a == a + a")
+ }
+ field.add(c_1, a, b)
+ field.add(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a + b = b + a")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_2, b, a)
+ field.neg(c_2, c_2)
+ if !c_1.equal(c_2) {
+ t.Fatal("a - b = - ( b - a )")
+ }
+ c_x, _ := new(fe12).rand(rand.Reader)
+ field.add(c_1, a, b)
+ field.add(c_1, c_1, c_x)
+ field.add(c_2, a, c_x)
+ field.add(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a + b) + c == (a + c ) + b")
+ }
+ field.sub(c_1, a, b)
+ field.sub(c_1, c_1, c_x)
+ field.sub(c_2, a, c_x)
+ field.sub(c_2, c_2, b)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a - b) - c == (a - c ) -b")
+ }
+ }
+}
+
+func TestFp12MultiplicationProperties(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe12).rand(rand.Reader)
+ b, _ := new(fe12).rand(rand.Reader)
+ zero := field.zero()
+ one := field.one()
+ c_1, c_2 := field.new(), field.new()
+ field.mul(c_1, a, zero)
+ if !c_1.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ field.mul(c_1, a, one)
+ if !c_1.equal(a) {
+ t.Fatal("a * 1 == a")
+ }
+ field.mul(c_1, a, b)
+ field.mul(c_2, b, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("a * b == b * a")
+ }
+ c_x, _ := new(fe12).rand(rand.Reader)
+ field.mul(c_1, a, b)
+ field.mul(c_1, c_1, c_x)
+ field.mul(c_2, c_x, b)
+ field.mul(c_2, c_2, a)
+ if !c_1.equal(c_2) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ field.square(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("0^2 == 0")
+ }
+ field.square(a, one)
+ if !a.equal(one) {
+ t.Fatal("1^2 == 1")
+ }
+ _, _ = a.rand(rand.Reader)
+ field.square(c_1, a)
+ field.mul(c_2, a, a)
+ if !c_2.equal(c_1) {
+ t.Fatal("a^2 == a*a")
+ }
+ }
+}
+
+func TestFp12MultiplicationPropertiesAssigned(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe12).rand(rand.Reader)
+ zero, one := new(fe12).zero(), new(fe12).one()
+ field.mulAssign(a, zero)
+ if !a.equal(zero) {
+ t.Fatal("a * 0 == 0")
+ }
+ _, _ = a.rand(rand.Reader)
+ a0 := new(fe12).set(a)
+ field.mulAssign(a, one)
+ if !a.equal(a0) {
+ t.Fatal("a * 1 == a")
+ }
+ _, _ = a.rand(rand.Reader)
+ b, _ := new(fe12).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(b, a0)
+ if !a.equal(b) {
+ t.Fatal("a * b == b * a")
+ }
+ c, _ := new(fe12).rand(rand.Reader)
+ a0.set(a)
+ field.mulAssign(a, b)
+ field.mulAssign(a, c)
+ field.mulAssign(a0, c)
+ field.mulAssign(a0, b)
+ if !a.equal(a0) {
+ t.Fatal("(a * b) * c == (a * c) * b")
+ }
+ }
+}
+
+func TestFp12SparseMultiplication(t *testing.T) {
+ fp12 := newFp12(nil)
+ var a, b, u *fe12
+ for j := 0; j < fuz; j++ {
+ a, _ = new(fe12).rand(rand.Reader)
+ b, _ = new(fe12).rand(rand.Reader)
+ u, _ = new(fe12).rand(rand.Reader)
+ b[0][2].zero()
+ b[1][0].zero()
+ b[1][2].zero()
+ fp12.mul(u, a, b)
+ fp12.mulBy014Assign(a, &b[0][0], &b[0][1], &b[1][1])
+ if !a.equal(u) {
+ t.Fatal("bad mul by 01")
+ }
+ }
+}
+
+func TestFp12Exponentiation(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ a, _ := new(fe12).rand(rand.Reader)
+ u := field.new()
+ field.exp(u, a, big.NewInt(0))
+ if !u.equal(field.one()) {
+ t.Fatal("a^0 == 1")
+ }
+ field.exp(u, a, big.NewInt(1))
+ if !u.equal(a) {
+ t.Fatal("a^1 == a")
+ }
+ v := field.new()
+ field.mul(u, a, a)
+ field.mul(u, u, u)
+ field.mul(u, u, u)
+ field.exp(v, a, big.NewInt(8))
+ if !u.equal(v) {
+ t.Fatal("((a^2)^2)^2 == a^8")
+ }
+ }
+}
+
+func TestFp12Inversion(t *testing.T) {
+ field := newFp12(nil)
+ for i := 0; i < fuz; i++ {
+ u := field.new()
+ zero := field.zero()
+ one := field.one()
+ field.inverse(u, zero)
+ if !u.equal(zero) {
+ t.Fatal("(0^-1) == 0)")
+ }
+ field.inverse(u, one)
+ if !u.equal(one) {
+ t.Fatal("(1^-1) == 1)")
+ }
+ a, _ := new(fe12).rand(rand.Reader)
+ field.inverse(u, a)
+ field.mul(u, u, a)
+ if !u.equal(one) {
+ t.Fatal("(r*a) * r*(a^-1) == r)")
+ }
+ }
+}
+
+func BenchmarkMultiplication(t *testing.B) {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ c, _ := new(fe).rand(rand.Reader)
+ t.ResetTimer()
+ for i := 0; i < t.N; i++ {
+ mul(c, a, b)
+ }
+}
+
+func BenchmarkInverse(t *testing.B) {
+ a, _ := new(fe).rand(rand.Reader)
+ b, _ := new(fe).rand(rand.Reader)
+ t.ResetTimer()
+ for i := 0; i < t.N; i++ {
+ inverse(a, b)
+ }
+}
+
+func padBytes(in []byte, size int) []byte {
+ out := make([]byte, size)
+ if len(in) > size {
+ panic("bad input for padding")
+ }
+ copy(out[size-len(in):], in)
+ return out
+}
diff --git a/common/crypto/bls12381/g1.go b/common/crypto/bls12381/g1.go
new file mode 100644
index 0000000..dd867c2
--- /dev/null
+++ b/common/crypto/bls12381/g1.go
@@ -0,0 +1,435 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//nolint:golint
+package bls12381
+
+import (
+ "errors"
+ "math"
+ "math/big"
+)
+
+// PointG1 is type for point in G1.
+// PointG1 is both used for Affine and Jacobian point representation.
+// If z is equal to one the point is considered as in affine form.
+type PointG1 [3]fe
+
+func (p *PointG1) Set(p2 *PointG1) *PointG1 {
+ p[0].set(&p2[0])
+ p[1].set(&p2[1])
+ p[2].set(&p2[2])
+ return p
+}
+
+// Zero returns G1 point in point at infinity representation
+func (p *PointG1) Zero() *PointG1 {
+ p[0].zero()
+ p[1].one()
+ p[2].zero()
+ return p
+}
+
+type tempG1 struct {
+ t [9]*fe
+}
+
+// G1 is struct for G1 group.
+type G1 struct {
+ tempG1
+}
+
+// NewG1 constructs a new G1 instance.
+func NewG1() *G1 {
+ t := newTempG1()
+ return &G1{t}
+}
+
+func newTempG1() tempG1 {
+ t := [9]*fe{}
+ for i := 0; i < 9; i++ {
+ t[i] = &fe{}
+ }
+ return tempG1{t}
+}
+
+// Q returns group order in big.Int.
+func (g *G1) Q() *big.Int {
+ return new(big.Int).Set(q)
+}
+
+func (g *G1) fromBytesUnchecked(in []byte) (*PointG1, error) {
+ p0, err := fromBytes(in[:48])
+ if err != nil {
+ return nil, err
+ }
+ p1, err := fromBytes(in[48:])
+ if err != nil {
+ return nil, err
+ }
+ p2 := new(fe).one()
+ return &PointG1{*p0, *p1, *p2}, nil
+}
+
+// FromBytes constructs a new point given uncompressed byte input.
+// FromBytes does not take zcash flags into account.
+// Byte input expected to be larger than 96 bytes.
+// First 96 bytes should be concatenation of x and y values.
+// Point (0, 0) is considered as infinity.
+func (g *G1) FromBytes(in []byte) (*PointG1, error) {
+ if len(in) != 96 {
+ return nil, errors.New("input string should be equal or larger than 96")
+ }
+ p0, err := fromBytes(in[:48])
+ if err != nil {
+ return nil, err
+ }
+ p1, err := fromBytes(in[48:])
+ if err != nil {
+ return nil, err
+ }
+ // check if given input points to infinity
+ if p0.isZero() && p1.isZero() {
+ return g.Zero(), nil
+ }
+ p2 := new(fe).one()
+ p := &PointG1{*p0, *p1, *p2}
+ if !g.IsOnCurve(p) {
+ return nil, errors.New("point is not on curve")
+ }
+ return p, nil
+}
+
+// DecodePoint given encoded (x, y) coordinates in 128 bytes returns a valid G1 Point.
+func (g *G1) DecodePoint(in []byte) (*PointG1, error) {
+ if len(in) != 128 {
+ return nil, errors.New("invalid g1 point length")
+ }
+ pointBytes := make([]byte, 96)
+ // decode x
+ xBytes, err := decodeFieldElement(in[:64])
+ if err != nil {
+ return nil, err
+ }
+ // decode y
+ yBytes, err := decodeFieldElement(in[64:])
+ if err != nil {
+ return nil, err
+ }
+ copy(pointBytes[:48], xBytes)
+ copy(pointBytes[48:], yBytes)
+ return g.FromBytes(pointBytes)
+}
+
+// ToBytes serializes a point into bytes in uncompressed form.
+// ToBytes does not take zcash flags into account.
+// ToBytes returns (0, 0) if point is infinity.
+func (g *G1) ToBytes(p *PointG1) []byte {
+ out := make([]byte, 96)
+ if g.IsZero(p) {
+ return out
+ }
+ g.Affine(p)
+ copy(out[:48], toBytes(&p[0]))
+ copy(out[48:], toBytes(&p[1]))
+ return out
+}
+
+// EncodePoint encodes a point into 128 bytes.
+func (g *G1) EncodePoint(p *PointG1) []byte {
+ outRaw := g.ToBytes(p)
+ out := make([]byte, 128)
+ // encode x
+ copy(out[16:], outRaw[:48])
+ // encode y
+ copy(out[64+16:], outRaw[48:])
+ return out
+}
+
+// New creates a new G1 Point which is equal to zero in other words point at infinity.
+func (g *G1) New() *PointG1 {
+ return g.Zero()
+}
+
+// Zero returns a new G1 Point which is equal to point at infinity.
+func (g *G1) Zero() *PointG1 {
+ return new(PointG1).Zero()
+}
+
+// One returns a new G1 Point which is equal to generator point.
+func (g *G1) One() *PointG1 {
+ p := &PointG1{}
+ return p.Set(&g1One)
+}
+
+// IsZero returns true if given point is equal to zero.
+func (g *G1) IsZero(p *PointG1) bool {
+ return p[2].isZero()
+}
+
+// Equal checks if given two G1 point is equal in their affine form.
+func (g *G1) Equal(p1, p2 *PointG1) bool {
+ if g.IsZero(p1) {
+ return g.IsZero(p2)
+ }
+ if g.IsZero(p2) {
+ return g.IsZero(p1)
+ }
+ t := g.t
+ square(t[0], &p1[2])
+ square(t[1], &p2[2])
+ mul(t[2], t[0], &p2[0])
+ mul(t[3], t[1], &p1[0])
+ mul(t[0], t[0], &p1[2])
+ mul(t[1], t[1], &p2[2])
+ mul(t[1], t[1], &p1[1])
+ mul(t[0], t[0], &p2[1])
+ return t[0].equal(t[1]) && t[2].equal(t[3])
+}
+
+// InCorrectSubgroup checks whether given point is in correct subgroup.
+func (g *G1) InCorrectSubgroup(p *PointG1) bool {
+ tmp := &PointG1{}
+ g.MulScalar(tmp, p, q)
+ return g.IsZero(tmp)
+}
+
+// IsOnCurve checks a G1 point is on curve.
+func (g *G1) IsOnCurve(p *PointG1) bool {
+ if g.IsZero(p) {
+ return true
+ }
+ t := g.t
+ square(t[0], &p[1])
+ square(t[1], &p[0])
+ mul(t[1], t[1], &p[0])
+ square(t[2], &p[2])
+ square(t[3], t[2])
+ mul(t[2], t[2], t[3])
+ mul(t[2], b, t[2])
+ add(t[1], t[1], t[2])
+ return t[0].equal(t[1])
+}
+
+// IsAffine checks a G1 point whether it is in affine form.
+func (g *G1) IsAffine(p *PointG1) bool {
+ return p[2].isOne()
+}
+
+// Add adds two G1 points p1, p2 and assigns the result to point at first argument.
+func (g *G1) Affine(p *PointG1) *PointG1 {
+ if g.IsZero(p) {
+ return p
+ }
+ if !g.IsAffine(p) {
+ t := g.t
+ inverse(t[0], &p[2])
+ square(t[1], t[0])
+ mul(&p[0], &p[0], t[1])
+ mul(t[0], t[0], t[1])
+ mul(&p[1], &p[1], t[0])
+ p[2].one()
+ }
+ return p
+}
+
+// Add adds two G1 points p1, p2 and assigns the result to point at first argument.
+func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 {
+ // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ if g.IsZero(p1) {
+ return r.Set(p2)
+ }
+ if g.IsZero(p2) {
+ return r.Set(p1)
+ }
+ t := g.t
+ square(t[7], &p1[2])
+ mul(t[1], &p2[0], t[7])
+ mul(t[2], &p1[2], t[7])
+ mul(t[0], &p2[1], t[2])
+ square(t[8], &p2[2])
+ mul(t[3], &p1[0], t[8])
+ mul(t[4], &p2[2], t[8])
+ mul(t[2], &p1[1], t[4])
+ if t[1].equal(t[3]) {
+ if t[0].equal(t[2]) {
+ return g.Double(r, p1)
+ }
+ return r.Zero()
+ }
+ sub(t[1], t[1], t[3])
+ double(t[4], t[1])
+ square(t[4], t[4])
+ mul(t[5], t[1], t[4])
+ sub(t[0], t[0], t[2])
+ double(t[0], t[0])
+ square(t[6], t[0])
+ sub(t[6], t[6], t[5])
+ mul(t[3], t[3], t[4])
+ double(t[4], t[3])
+ sub(&r[0], t[6], t[4])
+ sub(t[4], t[3], &r[0])
+ mul(t[6], t[2], t[5])
+ double(t[6], t[6])
+ mul(t[0], t[0], t[4])
+ sub(&r[1], t[0], t[6])
+ add(t[0], &p1[2], &p2[2])
+ square(t[0], t[0])
+ sub(t[0], t[0], t[7])
+ sub(t[0], t[0], t[8])
+ mul(&r[2], t[0], t[1])
+ return r
+}
+
+// Double doubles a G1 point p and assigns the result to the point at first argument.
+func (g *G1) Double(r, p *PointG1) *PointG1 {
+ // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ if g.IsZero(p) {
+ return r.Set(p)
+ }
+ t := g.t
+ square(t[0], &p[0])
+ square(t[1], &p[1])
+ square(t[2], t[1])
+ add(t[1], &p[0], t[1])
+ square(t[1], t[1])
+ sub(t[1], t[1], t[0])
+ sub(t[1], t[1], t[2])
+ double(t[1], t[1])
+ double(t[3], t[0])
+ add(t[0], t[3], t[0])
+ square(t[4], t[0])
+ double(t[3], t[1])
+ sub(&r[0], t[4], t[3])
+ sub(t[1], t[1], &r[0])
+ double(t[2], t[2])
+ double(t[2], t[2])
+ double(t[2], t[2])
+ mul(t[0], t[0], t[1])
+ sub(t[1], t[0], t[2])
+ mul(t[0], &p[1], &p[2])
+ r[1].set(t[1])
+ double(&r[2], t[0])
+ return r
+}
+
+// Neg negates a G1 point p and assigns the result to the point at first argument.
+func (g *G1) Neg(r, p *PointG1) *PointG1 {
+ r[0].set(&p[0])
+ r[2].set(&p[2])
+ neg(&r[1], &p[1])
+ return r
+}
+
+// Sub subtracts two G1 points p1, p2 and assigns the result to point at first argument.
+func (g *G1) Sub(c, a, b *PointG1) *PointG1 {
+ d := &PointG1{}
+ g.Neg(d, b)
+ g.Add(c, a, d)
+ return c
+}
+
+// MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument.
+func (g *G1) MulScalar(c, p *PointG1, e *big.Int) *PointG1 {
+ q, n := &PointG1{}, &PointG1{}
+ n.Set(p)
+ l := e.BitLen()
+ for i := 0; i < l; i++ {
+ if e.Bit(i) == 1 {
+ g.Add(q, q, n)
+ }
+ g.Double(n, n)
+ }
+ return c.Set(q)
+}
+
+// ClearCofactor maps given a G1 point to correct subgroup
+func (g *G1) ClearCofactor(p *PointG1) {
+ g.MulScalar(p, p, cofactorEFFG1)
+}
+
+// MultiExp calculates multi exponentiation. Given pairs of G1 point and scalar values
+// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n
+// Length of points and scalars are expected to be equal, otherwise an error is returned.
+// Result is assigned to point at first argument.
+func (g *G1) MultiExp(r *PointG1, points []*PointG1, powers []*big.Int) (*PointG1, error) {
+ if len(points) != len(powers) {
+ return nil, errors.New("point and scalar vectors should be in same length")
+ }
+ var c uint32 = 3
+ if len(powers) >= 32 {
+ c = uint32(math.Ceil(math.Log10(float64(len(powers)))))
+ }
+ bucketSize, numBits := (1<= 0; i-- {
+ g.Add(sum, sum, bucket[i])
+ g.Add(acc, acc, sum)
+ }
+ windows[j] = g.New()
+ windows[j].Set(acc)
+ j++
+ cur += c
+ }
+ acc.Zero()
+ for i := len(windows) - 1; i >= 0; i-- {
+ for j := uint32(0); j < c; j++ {
+ g.Double(acc, acc)
+ }
+ g.Add(acc, acc, windows[i])
+ }
+ return r.Set(acc), nil
+}
+
+// MapToCurve given a byte slice returns a valid G1 point.
+// This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method.
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06
+// Input byte slice should be a valid field element, otherwise an error is returned.
+func (g *G1) MapToCurve(in []byte) (*PointG1, error) {
+ u, err := fromBytes(in)
+ if err != nil {
+ return nil, err
+ }
+ x, y := swuMapG1(u)
+ isogenyMapG1(x, y)
+ one := new(fe).one()
+ p := &PointG1{*x, *y, *one}
+ g.ClearCofactor(p)
+ return g.Affine(p), nil
+}
diff --git a/common/crypto/bls12381/g2.go b/common/crypto/bls12381/g2.go
new file mode 100644
index 0000000..0b40b24
--- /dev/null
+++ b/common/crypto/bls12381/g2.go
@@ -0,0 +1,457 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//noling:golint
+package bls12381
+
+import (
+ "errors"
+ "math"
+ "math/big"
+)
+
+// PointG2 is type for point in G2.
+// PointG2 is both used for Affine and Jacobian point representation.
+// If z is equal to one the point is considered as in affine form.
+type PointG2 [3]fe2
+
+// Set copies valeus of one point to another.
+func (p *PointG2) Set(p2 *PointG2) *PointG2 {
+ p[0].set(&p2[0])
+ p[1].set(&p2[1])
+ p[2].set(&p2[2])
+ return p
+}
+
+// Zero returns G2 point in point at infinity representation
+func (p *PointG2) Zero() *PointG2 {
+ p[0].zero()
+ p[1].one()
+ p[2].zero()
+ return p
+
+}
+
+type tempG2 struct {
+ t [9]*fe2
+}
+
+// G2 is struct for G2 group.
+type G2 struct {
+ f *fp2
+ tempG2
+}
+
+// NewG2 constructs a new G2 instance.
+func NewG2() *G2 {
+ return newG2(nil)
+}
+
+func newG2(f *fp2) *G2 {
+ if f == nil {
+ f = newFp2()
+ }
+ t := newTempG2()
+ return &G2{f, t}
+}
+
+func newTempG2() tempG2 {
+ t := [9]*fe2{}
+ for i := 0; i < 9; i++ {
+ t[i] = &fe2{}
+ }
+ return tempG2{t}
+}
+
+// Q returns group order in big.Int.
+func (g *G2) Q() *big.Int {
+ return new(big.Int).Set(q)
+}
+
+func (g *G2) fromBytesUnchecked(in []byte) (*PointG2, error) {
+ p0, err := g.f.fromBytes(in[:96])
+ if err != nil {
+ return nil, err
+ }
+ p1, err := g.f.fromBytes(in[96:])
+ if err != nil {
+ return nil, err
+ }
+ p2 := new(fe2).one()
+ return &PointG2{*p0, *p1, *p2}, nil
+}
+
+// FromBytes constructs a new point given uncompressed byte input.
+// FromBytes does not take zcash flags into account.
+// Byte input expected to be larger than 96 bytes.
+// First 192 bytes should be concatenation of x and y values
+// Point (0, 0) is considered as infinity.
+func (g *G2) FromBytes(in []byte) (*PointG2, error) {
+ if len(in) != 192 {
+ return nil, errors.New("input string should be equal or larger than 192")
+ }
+ p0, err := g.f.fromBytes(in[:96])
+ if err != nil {
+ return nil, err
+ }
+ p1, err := g.f.fromBytes(in[96:])
+ if err != nil {
+ return nil, err
+ }
+ // check if given input points to infinity
+ if p0.isZero() && p1.isZero() {
+ return g.Zero(), nil
+ }
+ p2 := new(fe2).one()
+ p := &PointG2{*p0, *p1, *p2}
+ if !g.IsOnCurve(p) {
+ return nil, errors.New("point is not on curve")
+ }
+ return p, nil
+}
+
+// DecodePoint given encoded (x, y) coordinates in 256 bytes returns a valid G1 Point.
+func (g *G2) DecodePoint(in []byte) (*PointG2, error) {
+ if len(in) != 256 {
+ return nil, errors.New("invalid g2 point length")
+ }
+ pointBytes := make([]byte, 192)
+ x0Bytes, err := decodeFieldElement(in[:64])
+ if err != nil {
+ return nil, err
+ }
+ x1Bytes, err := decodeFieldElement(in[64:128])
+ if err != nil {
+ return nil, err
+ }
+ y0Bytes, err := decodeFieldElement(in[128:192])
+ if err != nil {
+ return nil, err
+ }
+ y1Bytes, err := decodeFieldElement(in[192:])
+ if err != nil {
+ return nil, err
+ }
+ copy(pointBytes[:48], x1Bytes)
+ copy(pointBytes[48:96], x0Bytes)
+ copy(pointBytes[96:144], y1Bytes)
+ copy(pointBytes[144:192], y0Bytes)
+ return g.FromBytes(pointBytes)
+}
+
+// ToBytes serializes a point into bytes in uncompressed form,
+// does not take zcash flags into account,
+// returns (0, 0) if point is infinity.
+func (g *G2) ToBytes(p *PointG2) []byte {
+ out := make([]byte, 192)
+ if g.IsZero(p) {
+ return out
+ }
+ g.Affine(p)
+ copy(out[:96], g.f.toBytes(&p[0]))
+ copy(out[96:], g.f.toBytes(&p[1]))
+ return out
+}
+
+// EncodePoint encodes a point into 256 bytes.
+func (g *G2) EncodePoint(p *PointG2) []byte {
+ // outRaw is 96 bytes
+ outRaw := g.ToBytes(p)
+ out := make([]byte, 256)
+ // encode x
+ copy(out[16:16+48], outRaw[48:96])
+ copy(out[80:80+48], outRaw[:48])
+ // encode y
+ copy(out[144:144+48], outRaw[144:])
+ copy(out[208:208+48], outRaw[96:144])
+ return out
+}
+
+// New creates a new G2 Point which is equal to zero in other words point at infinity.
+func (g *G2) New() *PointG2 {
+ return new(PointG2).Zero()
+}
+
+// Zero returns a new G2 Point which is equal to point at infinity.
+func (g *G2) Zero() *PointG2 {
+ return new(PointG2).Zero()
+}
+
+// One returns a new G2 Point which is equal to generator point.
+func (g *G2) One() *PointG2 {
+ p := &PointG2{}
+ return p.Set(&g2One)
+}
+
+// IsZero returns true if given point is equal to zero.
+func (g *G2) IsZero(p *PointG2) bool {
+ return p[2].isZero()
+}
+
+// Equal checks if given two G2 point is equal in their affine form.
+func (g *G2) Equal(p1, p2 *PointG2) bool {
+ if g.IsZero(p1) {
+ return g.IsZero(p2)
+ }
+ if g.IsZero(p2) {
+ return g.IsZero(p1)
+ }
+ t := g.t
+ g.f.square(t[0], &p1[2])
+ g.f.square(t[1], &p2[2])
+ g.f.mul(t[2], t[0], &p2[0])
+ g.f.mul(t[3], t[1], &p1[0])
+ g.f.mul(t[0], t[0], &p1[2])
+ g.f.mul(t[1], t[1], &p2[2])
+ g.f.mul(t[1], t[1], &p1[1])
+ g.f.mul(t[0], t[0], &p2[1])
+ return t[0].equal(t[1]) && t[2].equal(t[3])
+}
+
+// InCorrectSubgroup checks whether given point is in correct subgroup.
+func (g *G2) InCorrectSubgroup(p *PointG2) bool {
+ tmp := &PointG2{}
+ g.MulScalar(tmp, p, q)
+ return g.IsZero(tmp)
+}
+
+// IsOnCurve checks a G2 point is on curve.
+func (g *G2) IsOnCurve(p *PointG2) bool {
+ if g.IsZero(p) {
+ return true
+ }
+ t := g.t
+ g.f.square(t[0], &p[1])
+ g.f.square(t[1], &p[0])
+ g.f.mul(t[1], t[1], &p[0])
+ g.f.square(t[2], &p[2])
+ g.f.square(t[3], t[2])
+ g.f.mul(t[2], t[2], t[3])
+ g.f.mul(t[2], b2, t[2])
+ g.f.add(t[1], t[1], t[2])
+ return t[0].equal(t[1])
+}
+
+// IsAffine checks a G2 point whether it is in affine form.
+func (g *G2) IsAffine(p *PointG2) bool {
+ return p[2].isOne()
+}
+
+// Affine calculates affine form of given G2 point.
+func (g *G2) Affine(p *PointG2) *PointG2 {
+ if g.IsZero(p) {
+ return p
+ }
+ if !g.IsAffine(p) {
+ t := g.t
+ g.f.inverse(t[0], &p[2])
+ g.f.square(t[1], t[0])
+ g.f.mul(&p[0], &p[0], t[1])
+ g.f.mul(t[0], t[0], t[1])
+ g.f.mul(&p[1], &p[1], t[0])
+ p[2].one()
+ }
+ return p
+}
+
+// Add adds two G2 points p1, p2 and assigns the result to point at first argument.
+func (g *G2) Add(r, p1, p2 *PointG2) *PointG2 {
+ // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#addition-add-2007-bl
+ if g.IsZero(p1) {
+ return r.Set(p2)
+ }
+ if g.IsZero(p2) {
+ return r.Set(p1)
+ }
+ t := g.t
+ g.f.square(t[7], &p1[2])
+ g.f.mul(t[1], &p2[0], t[7])
+ g.f.mul(t[2], &p1[2], t[7])
+ g.f.mul(t[0], &p2[1], t[2])
+ g.f.square(t[8], &p2[2])
+ g.f.mul(t[3], &p1[0], t[8])
+ g.f.mul(t[4], &p2[2], t[8])
+ g.f.mul(t[2], &p1[1], t[4])
+ if t[1].equal(t[3]) {
+ if t[0].equal(t[2]) {
+ return g.Double(r, p1)
+ }
+ return r.Zero()
+ }
+ g.f.sub(t[1], t[1], t[3])
+ g.f.double(t[4], t[1])
+ g.f.square(t[4], t[4])
+ g.f.mul(t[5], t[1], t[4])
+ g.f.sub(t[0], t[0], t[2])
+ g.f.double(t[0], t[0])
+ g.f.square(t[6], t[0])
+ g.f.sub(t[6], t[6], t[5])
+ g.f.mul(t[3], t[3], t[4])
+ g.f.double(t[4], t[3])
+ g.f.sub(&r[0], t[6], t[4])
+ g.f.sub(t[4], t[3], &r[0])
+ g.f.mul(t[6], t[2], t[5])
+ g.f.double(t[6], t[6])
+ g.f.mul(t[0], t[0], t[4])
+ g.f.sub(&r[1], t[0], t[6])
+ g.f.add(t[0], &p1[2], &p2[2])
+ g.f.square(t[0], t[0])
+ g.f.sub(t[0], t[0], t[7])
+ g.f.sub(t[0], t[0], t[8])
+ g.f.mul(&r[2], t[0], t[1])
+ return r
+}
+
+// Double doubles a G2 point p and assigns the result to the point at first argument.
+func (g *G2) Double(r, p *PointG2) *PointG2 {
+ // http://www.hyperelliptic.org/EFD/gp/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+ if g.IsZero(p) {
+ return r.Set(p)
+ }
+ t := g.t
+ g.f.square(t[0], &p[0])
+ g.f.square(t[1], &p[1])
+ g.f.square(t[2], t[1])
+ g.f.add(t[1], &p[0], t[1])
+ g.f.square(t[1], t[1])
+ g.f.sub(t[1], t[1], t[0])
+ g.f.sub(t[1], t[1], t[2])
+ g.f.double(t[1], t[1])
+ g.f.double(t[3], t[0])
+ g.f.add(t[0], t[3], t[0])
+ g.f.square(t[4], t[0])
+ g.f.double(t[3], t[1])
+ g.f.sub(&r[0], t[4], t[3])
+ g.f.sub(t[1], t[1], &r[0])
+ g.f.double(t[2], t[2])
+ g.f.double(t[2], t[2])
+ g.f.double(t[2], t[2])
+ g.f.mul(t[0], t[0], t[1])
+ g.f.sub(t[1], t[0], t[2])
+ g.f.mul(t[0], &p[1], &p[2])
+ r[1].set(t[1])
+ g.f.double(&r[2], t[0])
+ return r
+}
+
+// Neg negates a G2 point p and assigns the result to the point at first argument.
+func (g *G2) Neg(r, p *PointG2) *PointG2 {
+ r[0].set(&p[0])
+ g.f.neg(&r[1], &p[1])
+ r[2].set(&p[2])
+ return r
+}
+
+// Sub subtracts two G2 points p1, p2 and assigns the result to point at first argument.
+func (g *G2) Sub(c, a, b *PointG2) *PointG2 {
+ d := &PointG2{}
+ g.Neg(d, b)
+ g.Add(c, a, d)
+ return c
+}
+
+// MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument.
+func (g *G2) MulScalar(c, p *PointG2, e *big.Int) *PointG2 {
+ q, n := &PointG2{}, &PointG2{}
+ n.Set(p)
+ l := e.BitLen()
+ for i := 0; i < l; i++ {
+ if e.Bit(i) == 1 {
+ g.Add(q, q, n)
+ }
+ g.Double(n, n)
+ }
+ return c.Set(q)
+}
+
+// ClearCofactor maps given a G2 point to correct subgroup
+func (g *G2) ClearCofactor(p *PointG2) {
+ g.MulScalar(p, p, cofactorEFFG2)
+}
+
+// MultiExp calculates multi exponentiation. Given pairs of G2 point and scalar values
+// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n
+// Length of points and scalars are expected to be equal, otherwise an error is returned.
+// Result is assigned to point at first argument.
+func (g *G2) MultiExp(r *PointG2, points []*PointG2, powers []*big.Int) (*PointG2, error) {
+ if len(points) != len(powers) {
+ return nil, errors.New("point and scalar vectors should be in same length")
+ }
+ var c uint32 = 3
+ if len(powers) >= 32 {
+ c = uint32(math.Ceil(math.Log10(float64(len(powers)))))
+ }
+ bucketSize, numBits := (1<= 0; i-- {
+ g.Add(sum, sum, bucket[i])
+ g.Add(acc, acc, sum)
+ }
+ windows[j] = g.New()
+ windows[j].Set(acc)
+ j++
+ cur += c
+ }
+ acc.Zero()
+ for i := len(windows) - 1; i >= 0; i-- {
+ for j := uint32(0); j < c; j++ {
+ g.Double(acc, acc)
+ }
+ g.Add(acc, acc, windows[i])
+ }
+ return r.Set(acc), nil
+}
+
+// MapToCurve given a byte slice returns a valid G2 point.
+// This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method.
+// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-6.6.2
+// Input byte slice should be a valid field element, otherwise an error is returned.
+func (g *G2) MapToCurve(in []byte) (*PointG2, error) {
+ fp2 := g.f
+ u, err := fp2.fromBytes(in)
+ if err != nil {
+ return nil, err
+ }
+ x, y := swuMapG2(fp2, u)
+ isogenyMapG2(fp2, x, y)
+ z := new(fe2).one()
+ q := &PointG2{*x, *y, *z}
+ g.ClearCofactor(q)
+ return g.Affine(q), nil
+}
diff --git a/common/crypto/bls12381/gt.go b/common/crypto/bls12381/gt.go
new file mode 100644
index 0000000..1cc22ce
--- /dev/null
+++ b/common/crypto/bls12381/gt.go
@@ -0,0 +1,122 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//nolint:stylecheck
+package bls12381
+
+import (
+ "errors"
+ "math/big"
+)
+
+// E is type for target group element
+type E = fe12
+
+// GT is type for target multiplicative group GT.
+type GT struct {
+ fp12 *fp12
+}
+
+func (e *E) Set(e2 *E) *E {
+ return e.set(e2)
+}
+
+// One sets a new target group element to one
+func (e *E) One() *E {
+ e = new(fe12).one()
+ return e
+}
+
+// IsOne returns true if given element equals to one
+func (e *E) IsOne() bool {
+ return e.isOne()
+}
+
+// Equal returns true if given two element is equal, otherwise returns false
+func (g *E) Equal(g2 *E) bool {
+ return g.equal(g2)
+}
+
+// NewGT constructs new target group instance.
+func NewGT() *GT {
+ fp12 := newFp12(nil)
+ return >{fp12}
+}
+
+// Q returns group order in big.Int.
+func (g *GT) Q() *big.Int {
+ return new(big.Int).Set(q)
+}
+
+// FromBytes expects 576 byte input and returns target group element
+// FromBytes returns error if given element is not on correct subgroup.
+func (g *GT) FromBytes(in []byte) (*E, error) {
+ e, err := g.fp12.fromBytes(in)
+ if err != nil {
+ return nil, err
+ }
+ if !g.IsValid(e) {
+ return e, errors.New("invalid element")
+ }
+ return e, nil
+}
+
+// ToBytes serializes target group element.
+func (g *GT) ToBytes(e *E) []byte {
+ return g.fp12.toBytes(e)
+}
+
+// IsValid checks whether given target group element is in correct subgroup.
+func (g *GT) IsValid(e *E) bool {
+ r := g.New()
+ g.fp12.exp(r, e, q)
+ return r.isOne()
+}
+
+// New initializes a new target group element which is equal to one
+func (g *GT) New() *E {
+ return new(E).One()
+}
+
+// Add adds two field element `a` and `b` and assigns the result to the element in first argument.
+func (g *GT) Add(c, a, b *E) {
+ g.fp12.add(c, a, b)
+}
+
+// Sub subtracts two field element `a` and `b`, and assigns the result to the element in first argument.
+func (g *GT) Sub(c, a, b *E) {
+ g.fp12.sub(c, a, b)
+}
+
+// Mul multiplies two field element `a` and `b` and assigns the result to the element in first argument.
+func (g *GT) Mul(c, a, b *E) {
+ g.fp12.mul(c, a, b)
+}
+
+// Square squares an element `a` and assigns the result to the element in first argument.
+func (g *GT) Square(c, a *E) {
+ g.fp12.cyclotomicSquare(c, a)
+}
+
+// Exp exponents an element `a` by a scalar `s` and assigns the result to the element in first argument.
+func (g *GT) Exp(c, a *E, s *big.Int) {
+ g.fp12.cyclotomicExp(c, a, s)
+}
+
+// Inverse inverses an element `a` and assigns the result to the element in first argument.
+func (g *GT) Inverse(c, a *E) {
+ g.fp12.inverse(c, a)
+}
diff --git a/common/crypto/bls12381/isogeny.go b/common/crypto/bls12381/isogeny.go
new file mode 100644
index 0000000..f3893cb
--- /dev/null
+++ b/common/crypto/bls12381/isogeny.go
@@ -0,0 +1,228 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//nolint:gofmt
+package bls12381
+
+// isogenyMapG1 applies 11-isogeny map for BLS12-381 G1 defined at draft-irtf-cfrg-hash-to-curve-06.
+func isogenyMapG1(x, y *fe) {
+ // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-C.2
+ params := isogenyConstansG1
+ degree := 15
+ xNum, xDen, yNum, yDen := new(fe), new(fe), new(fe), new(fe)
+ xNum.set(params[0][degree])
+ xDen.set(params[1][degree])
+ yNum.set(params[2][degree])
+ yDen.set(params[3][degree])
+ for i := degree - 1; i >= 0; i-- {
+ mul(xNum, xNum, x)
+ mul(xDen, xDen, x)
+ mul(yNum, yNum, x)
+ mul(yDen, yDen, x)
+ add(xNum, xNum, params[0][i])
+ add(xDen, xDen, params[1][i])
+ add(yNum, yNum, params[2][i])
+ add(yDen, yDen, params[3][i])
+ }
+ inverse(xDen, xDen)
+ inverse(yDen, yDen)
+ mul(xNum, xNum, xDen)
+ mul(yNum, yNum, yDen)
+ mul(yNum, yNum, y)
+ x.set(xNum)
+ y.set(yNum)
+}
+
+// isogenyMapG2 applies 11-isogeny map for BLS12-381 G1 defined at draft-irtf-cfrg-hash-to-curve-06.
+func isogenyMapG2(e *fp2, x, y *fe2) {
+ if e == nil {
+ e = newFp2()
+ }
+ // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-C.2
+ params := isogenyConstantsG2
+ degree := 3
+ xNum := new(fe2).set(params[0][degree])
+ xDen := new(fe2).set(params[1][degree])
+ yNum := new(fe2).set(params[2][degree])
+ yDen := new(fe2).set(params[3][degree])
+ for i := degree - 1; i >= 0; i-- {
+ e.mul(xNum, xNum, x)
+ e.mul(xDen, xDen, x)
+ e.mul(yNum, yNum, x)
+ e.mul(yDen, yDen, x)
+ e.add(xNum, xNum, params[0][i])
+ e.add(xDen, xDen, params[1][i])
+ e.add(yNum, yNum, params[2][i])
+ e.add(yDen, yDen, params[3][i])
+ }
+ e.inverse(xDen, xDen)
+ e.inverse(yDen, yDen)
+ e.mul(xNum, xNum, xDen)
+ e.mul(yNum, yNum, yDen)
+ e.mul(yNum, yNum, y)
+ x.set(xNum)
+ y.set(yNum)
+}
+
+var isogenyConstansG1 = [4][16]*fe{
+ {
+ {0x4d18b6f3af00131c, 0x19fa219793fee28c, 0x3f2885f1467f19ae, 0x23dcea34f2ffb304, 0xd15b58d2ffc00054, 0x0913be200a20bef4},
+ {0x898985385cdbbd8b, 0x3c79e43cc7d966aa, 0x1597e193f4cd233a, 0x8637ef1e4d6623ad, 0x11b22deed20d827b, 0x07097bc5998784ad},
+ {0xa542583a480b664b, 0xfc7169c026e568c6, 0x5ba2ef314ed8b5a6, 0x5b5491c05102f0e7, 0xdf6e99707d2a0079, 0x0784151ed7605524},
+ {0x494e212870f72741, 0xab9be52fbda43021, 0x26f5577994e34c3d, 0x049dfee82aefbd60, 0x65dadd7828505289, 0x0e93d431ea011aeb},
+ {0x90ee774bd6a74d45, 0x7ada1c8a41bfb185, 0x0f1a8953b325f464, 0x104c24211be4805c, 0x169139d319ea7a8f, 0x09f20ead8e532bf6},
+ {0x6ddd93e2f43626b7, 0xa5482c9aa1ccd7bd, 0x143245631883f4bd, 0x2e0a94ccf77ec0db, 0xb0282d480e56489f, 0x18f4bfcbb4368929},
+ {0x23c5f0c953402dfd, 0x7a43ff6958ce4fe9, 0x2c390d3d2da5df63, 0xd0df5c98e1f9d70f, 0xffd89869a572b297, 0x1277ffc72f25e8fe},
+ {0x79f4f0490f06a8a6, 0x85f894a88030fd81, 0x12da3054b18b6410, 0xe2a57f6505880d65, 0xbba074f260e400f1, 0x08b76279f621d028},
+ {0xe67245ba78d5b00b, 0x8456ba9a1f186475, 0x7888bff6e6b33bb4, 0xe21585b9a30f86cb, 0x05a69cdcef55feee, 0x09e699dd9adfa5ac},
+ {0x0de5c357bff57107, 0x0a0db4ae6b1a10b2, 0xe256bb67b3b3cd8d, 0x8ad456574e9db24f, 0x0443915f50fd4179, 0x098c4bf7de8b6375},
+ {0xe6b0617e7dd929c7, 0xfe6e37d442537375, 0x1dafdeda137a489e, 0xe4efd1ad3f767ceb, 0x4a51d8667f0fe1cf, 0x054fdf4bbf1d821c},
+ {0x72db2a50658d767b, 0x8abf91faa257b3d5, 0xe969d6833764ab47, 0x464170142a1009eb, 0xb14f01aadb30be2f, 0x18ae6a856f40715d},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ },
+ {
+ {0xb962a077fdb0f945, 0xa6a9740fefda13a0, 0xc14d568c3ed6c544, 0xb43fc37b908b133e, 0x9c0b3ac929599016, 0x0165aa6c93ad115f},
+ {0x23279a3ba506c1d9, 0x92cfca0a9465176a, 0x3b294ab13755f0ff, 0x116dda1c5070ae93, 0xed4530924cec2045, 0x083383d6ed81f1ce},
+ {0x9885c2a6449fecfc, 0x4a2b54ccd37733f0, 0x17da9ffd8738c142, 0xa0fba72732b3fafd, 0xff364f36e54b6812, 0x0f29c13c660523e2},
+ {0xe349cc118278f041, 0xd487228f2f3204fb, 0xc9d325849ade5150, 0x43a92bd69c15c2df, 0x1c2c7844bc417be4, 0x12025184f407440c},
+ {0x587f65ae6acb057b, 0x1444ef325140201f, 0xfbf995e71270da49, 0xccda066072436a42, 0x7408904f0f186bb2, 0x13b93c63edf6c015},
+ {0xfb918622cd141920, 0x4a4c64423ecaddb4, 0x0beb232927f7fb26, 0x30f94df6f83a3dc2, 0xaeedd424d780f388, 0x06cc402dd594bbeb},
+ {0xd41f761151b23f8f, 0x32a92465435719b3, 0x64f436e888c62cb9, 0xdf70a9a1f757c6e4, 0x6933a38d5b594c81, 0x0c6f7f7237b46606},
+ {0x693c08747876c8f7, 0x22c9850bf9cf80f0, 0x8e9071dab950c124, 0x89bc62d61c7baf23, 0xbc6be2d8dad57c23, 0x17916987aa14a122},
+ {0x1be3ff439c1316fd, 0x9965243a7571dfa7, 0xc7f7f62962f5cd81, 0x32c6aa9af394361c, 0xbbc2ee18e1c227f4, 0x0c102cbac531bb34},
+ {0x997614c97bacbf07, 0x61f86372b99192c0, 0x5b8c95fc14353fc3, 0xca2b066c2a87492f, 0x16178f5bbf698711, 0x12a6dcd7f0f4e0e8},
+ {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0},
+ },
+ {
+ {0x2b567ff3e2837267, 0x1d4d9e57b958a767, 0xce028fea04bd7373, 0xcc31a30a0b6cd3df, 0x7d7b18a682692693, 0x0d300744d42a0310},
+ {0x99c2555fa542493f, 0xfe7f53cc4874f878, 0x5df0608b8f97608a, 0x14e03832052b49c8, 0x706326a6957dd5a4, 0x0a8dadd9c2414555},
+ {0x13d942922a5cf63a, 0x357e33e36e261e7d, 0xcf05a27c8456088d, 0x0000bd1de7ba50f0, 0x83d0c7532f8c1fde, 0x13f70bf38bbf2905},
+ {0x5c57fd95bfafbdbb, 0x28a359a65e541707, 0x3983ceb4f6360b6d, 0xafe19ff6f97e6d53, 0xb3468f4550192bf7, 0x0bb6cde49d8ba257},
+ {0x590b62c7ff8a513f, 0x314b4ce372cacefd, 0x6bef32ce94b8a800, 0x6ddf84a095713d5f, 0x64eace4cb0982191, 0x0386213c651b888d},
+ {0xa5310a31111bbcdd, 0xa14ac0f5da148982, 0xf9ad9cc95423d2e9, 0xaa6ec095283ee4a7, 0xcf5b1f022e1c9107, 0x01fddf5aed881793},
+ {0x65a572b0d7a7d950, 0xe25c2d8183473a19, 0xc2fcebe7cb877dbd, 0x05b2d36c769a89b0, 0xba12961be86e9efb, 0x07eb1b29c1dfde1f},
+ {0x93e09572f7c4cd24, 0x364e929076795091, 0x8569467e68af51b5, 0xa47da89439f5340f, 0xf4fa918082e44d64, 0x0ad52ba3e6695a79},
+ {0x911429844e0d5f54, 0xd03f51a3516bb233, 0x3d587e5640536e66, 0xfa86d2a3a9a73482, 0xa90ed5adf1ed5537, 0x149c9c326a5e7393},
+ {0x462bbeb03c12921a, 0xdc9af5fa0a274a17, 0x9a558ebde836ebed, 0x649ef8f11a4fae46, 0x8100e1652b3cdc62, 0x1862bd62c291dacb},
+ {0x05c9b8ca89f12c26, 0x0194160fa9b9ac4f, 0x6a643d5a6879fa2c, 0x14665bdd8846e19d, 0xbb1d0d53af3ff6bf, 0x12c7e1c3b28962e5},
+ {0xb55ebf900b8a3e17, 0xfedc77ec1a9201c4, 0x1f07db10ea1a4df4, 0x0dfbd15dc41a594d, 0x389547f2334a5391, 0x02419f98165871a4},
+ {0xb416af000745fc20, 0x8e563e9d1ea6d0f5, 0x7c763e17763a0652, 0x01458ef0159ebbef, 0x8346fe421f96bb13, 0x0d2d7b829ce324d2},
+ {0x93096bb538d64615, 0x6f2a2619951d823a, 0x8f66b3ea59514fa4, 0xf563e63704f7092f, 0x724b136c4cf2d9fa, 0x046959cfcfd0bf49},
+ {0xea748d4b6e405346, 0x91e9079c2c02d58f, 0x41064965946d9b59, 0xa06731f1d2bbe1ee, 0x07f897e267a33f1b, 0x1017290919210e5f},
+ {0x872aa6c17d985097, 0xeecc53161264562a, 0x07afe37afff55002, 0x54759078e5be6838, 0xc4b92d15db8acca8, 0x106d87d1b51d13b9},
+ },
+ {
+ {0xeb6c359d47e52b1c, 0x18ef5f8a10634d60, 0xddfa71a0889d5b7e, 0x723e71dcc5fc1323, 0x52f45700b70d5c69, 0x0a8b981ee47691f1},
+ {0x616a3c4f5535b9fb, 0x6f5f037395dbd911, 0xf25f4cc5e35c65da, 0x3e50dffea3c62658, 0x6a33dca523560776, 0x0fadeff77b6bfe3e},
+ {0x2be9b66df470059c, 0x24a2c159a3d36742, 0x115dbe7ad10c2a37, 0xb6634a652ee5884d, 0x04fe8bb2b8d81af4, 0x01c2a7a256fe9c41},
+ {0xf27bf8ef3b75a386, 0x898b367476c9073f, 0x24482e6b8c2f4e5f, 0xc8e0bbd6fe110806, 0x59b0c17f7631448a, 0x11037cd58b3dbfbd},
+ {0x31c7912ea267eec6, 0x1dbf6f1c5fcdb700, 0xd30d4fe3ba86fdb1, 0x3cae528fbee9a2a4, 0xb1cce69b6aa9ad9a, 0x044393bb632d94fb},
+ {0xc66ef6efeeb5c7e8, 0x9824c289dd72bb55, 0x71b1a4d2f119981d, 0x104fc1aafb0919cc, 0x0e49df01d942a628, 0x096c3a09773272d4},
+ {0x9abc11eb5fadeff4, 0x32dca50a885728f0, 0xfb1fa3721569734c, 0xc4b76271ea6506b3, 0xd466a75599ce728e, 0x0c81d4645f4cb6ed},
+ {0x4199f10e5b8be45b, 0xda64e495b1e87930, 0xcb353efe9b33e4ff, 0x9e9efb24aa6424c6, 0xf08d33680a237465, 0x0d3378023e4c7406},
+ {0x7eb4ae92ec74d3a5, 0xc341b4aa9fac3497, 0x5be603899e907687, 0x03bfd9cca75cbdeb, 0x564c2935a96bfa93, 0x0ef3c33371e2fdb5},
+ {0x7ee91fd449f6ac2e, 0xe5d5bd5cb9357a30, 0x773a8ca5196b1380, 0xd0fda172174ed023, 0x6cb95e0fa776aead, 0x0d22d5a40cec7cff},
+ {0xf727e09285fd8519, 0xdc9d55a83017897b, 0x7549d8bd057894ae, 0x178419613d90d8f8, 0xfce95ebdeb5b490a, 0x0467ffaef23fc49e},
+ {0xc1769e6a7c385f1b, 0x79bc930deac01c03, 0x5461c75a23ede3b5, 0x6e20829e5c230c45, 0x828e0f1e772a53cd, 0x116aefa749127bff},
+ {0x101c10bf2744c10a, 0xbbf18d053a6a3154, 0xa0ecf39ef026f602, 0xfc009d4996dc5153, 0xb9000209d5bd08d3, 0x189e5fe4470cd73c},
+ {0x7ebd546ca1575ed2, 0xe47d5a981d081b55, 0x57b2b625b6d4ca21, 0xb0a1ba04228520cc, 0x98738983c2107ff3, 0x13dddbc4799d81d6},
+ {0x09319f2e39834935, 0x039e952cbdb05c21, 0x55ba77a9a2f76493, 0xfd04e3dfc6086467, 0xfb95832e7d78742e, 0x0ef9c24eccaf5e0e},
+ {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ },
+}
+
+var isogenyConstantsG2 = [4][4]*fe2{
+ {
+ {
+ fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41},
+ fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41},
+ },
+ {
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0x5fe55555554c71d0, 0x873fffdd236aaaa3, 0x6a6b4619b26ef918, 0x21c2888408874945, 0x2836cda7028cabc5, 0x0ac73310a7fd5abd},
+ },
+ {
+ fe{0x0a0c5555555971c3, 0xdb0c00101f9eaaae, 0xb1fb2f941d797997, 0xd3960742ef416e1c, 0xb70040e2c20556f4, 0x149d7861e581393b},
+ fe{0xaff2aaaaaaa638e8, 0x439fffee91b55551, 0xb535a30cd9377c8c, 0x90e144420443a4a2, 0x941b66d3814655e2, 0x0563998853fead5e},
+ },
+ {
+ fe{0x40aac71c71c725ed, 0x190955557a84e38e, 0xd817050a8f41abc3, 0xd86485d4c87f6fb1, 0x696eb479f885d059, 0x198e1a74328002d2},
+ fe{0, 0, 0, 0, 0, 0},
+ },
+ },
+ {
+ {
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0x1f3affffff13ab97, 0xf25bfc611da3ff3e, 0xca3757cb3819b208, 0x3e6427366f8cec18, 0x03977bc86095b089, 0x04f69db13f39a952},
+ },
+ {
+ fe{0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1},
+ fe{0x7588ffffffd8557d, 0x41f3ff646e0bffdf, 0xf7b1e8d2ac426aca, 0xb3741acd32dbb6f8, 0xe9daf5b9482d581f, 0x167f53e0ba7431b8},
+ },
+ {
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0, 0, 0, 0, 0, 0},
+ },
+ {
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0, 0, 0, 0, 0, 0},
+ },
+ },
+ {
+ {
+ fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3},
+ fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3},
+ },
+ {
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0xbf0a71c71c91b406, 0x4d6d55d28b7638fd, 0x9d82f98e5f205aee, 0xa27aa27b1d1a18d5, 0x02c3b2b2d2938e86, 0x0c7d13420b09807f},
+ },
+ {
+ fe{0xd7f9555555531c74, 0x21cffff748daaaa8, 0x5a9ad1866c9bbe46, 0x4870a2210221d251, 0x4a0db369c0a32af1, 0x02b1ccc429ff56af},
+ fe{0xe205aaaaaaac8e37, 0xfcdc000768795556, 0x0c96011a8a1537dd, 0x1c06a963f163406e, 0x010df44c82a881e6, 0x174f45260f808feb},
+ },
+ {
+ fe{0xa470bda12f67f35c, 0xc0fe38e23327b425, 0xc9d3d0f2c6f0678d, 0x1c55c9935b5a982e, 0x27f6c0e2f0746764, 0x117c5e6e28aa9054},
+ fe{0, 0, 0, 0, 0, 0},
+ },
+ },
+ {
+ {
+ fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151},
+ fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151},
+ },
+ {
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0x5db0fffffd3b02c5, 0xd713f52358ebfdba, 0x5ea60761a84d161a, 0xbb2c75a34ea6c44a, 0x0ac6735921c1119b, 0x0ee3d913bdacfbf6},
+ },
+ {
+ fe{0x66b10000003affc5, 0xcb1400e764ec0030, 0xa73e5eb56fa5d106, 0x8984c913a0fe09a9, 0x11e10afb78ad7f13, 0x05429d0e3e918f52},
+ fe{0x534dffffffc4aae6, 0x5397ff174c67ffcf, 0xbff273eb870b251d, 0xdaf2827152870915, 0x393a9cbaca9e2dc3, 0x14be74dbfaee5748},
+ },
+ {
+ fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493},
+ fe{0, 0, 0, 0, 0, 0},
+ },
+ },
+}
diff --git a/common/crypto/bls12381/pairing.go b/common/crypto/bls12381/pairing.go
new file mode 100644
index 0000000..15d3fb0
--- /dev/null
+++ b/common/crypto/bls12381/pairing.go
@@ -0,0 +1,282 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+type pair struct {
+ g1 *PointG1
+ g2 *PointG2
+}
+
+func newPair(g1 *PointG1, g2 *PointG2) pair {
+ return pair{g1, g2}
+}
+
+// Engine is BLS12-381 elliptic curve pairing engine
+type Engine struct {
+ G1 *G1
+ G2 *G2
+ fp12 *fp12
+ fp2 *fp2
+ pairingEngineTemp
+ pairs []pair
+}
+
+// NewPairingEngine creates new pairing engine instance.
+func NewPairingEngine() *Engine {
+ fp2 := newFp2()
+ fp6 := newFp6(fp2)
+ fp12 := newFp12(fp6)
+ g1 := NewG1()
+ g2 := newG2(fp2)
+ return &Engine{
+ fp2: fp2,
+ fp12: fp12,
+ G1: g1,
+ G2: g2,
+ pairingEngineTemp: newEngineTemp(),
+ }
+}
+
+type pairingEngineTemp struct {
+ t2 [10]*fe2
+ t12 [9]fe12
+}
+
+func newEngineTemp() pairingEngineTemp {
+ t2 := [10]*fe2{}
+ for i := 0; i < 10; i++ {
+ t2[i] = &fe2{}
+ }
+ t12 := [9]fe12{}
+ return pairingEngineTemp{t2, t12}
+}
+
+// AddPair adds a g1, g2 point pair to pairing engine
+func (e *Engine) AddPair(g1 *PointG1, g2 *PointG2) *Engine {
+ p := newPair(g1, g2)
+ if !e.isZero(p) {
+ e.affine(p)
+ e.pairs = append(e.pairs, p)
+ }
+ return e
+}
+
+// AddPairInv adds a G1, G2 point pair to pairing engine. G1 point is negated.
+func (e *Engine) AddPairInv(g1 *PointG1, g2 *PointG2) *Engine {
+ e.G1.Neg(g1, g1)
+ e.AddPair(g1, g2)
+ return e
+}
+
+// Reset deletes added pairs.
+func (e *Engine) Reset() *Engine {
+ e.pairs = []pair{}
+ return e
+}
+
+func (e *Engine) isZero(p pair) bool {
+ return e.G1.IsZero(p.g1) || e.G2.IsZero(p.g2)
+}
+
+func (e *Engine) affine(p pair) {
+ e.G1.Affine(p.g1)
+ e.G2.Affine(p.g2)
+}
+
+func (e *Engine) doublingStep(coeff *[3]fe2, r *PointG2) {
+ // Adaptation of Formula 3 in https://eprint.iacr.org/2010/526.pdf
+ fp2 := e.fp2
+ t := e.t2
+ fp2.mul(t[0], &r[0], &r[1])
+ fp2.mulByFq(t[0], t[0], twoInv)
+ fp2.square(t[1], &r[1])
+ fp2.square(t[2], &r[2])
+ fp2.double(t[7], t[2])
+ fp2.add(t[7], t[7], t[2])
+ fp2.mulByB(t[3], t[7])
+ fp2.double(t[4], t[3])
+ fp2.add(t[4], t[4], t[3])
+ fp2.add(t[5], t[1], t[4])
+ fp2.mulByFq(t[5], t[5], twoInv)
+ fp2.add(t[6], &r[1], &r[2])
+ fp2.square(t[6], t[6])
+ fp2.add(t[7], t[2], t[1])
+ fp2.sub(t[6], t[6], t[7])
+ fp2.sub(&coeff[0], t[3], t[1])
+ fp2.square(t[7], &r[0])
+ fp2.sub(t[4], t[1], t[4])
+ fp2.mul(&r[0], t[4], t[0])
+ fp2.square(t[2], t[3])
+ fp2.double(t[3], t[2])
+ fp2.add(t[3], t[3], t[2])
+ fp2.square(t[5], t[5])
+ fp2.sub(&r[1], t[5], t[3])
+ fp2.mul(&r[2], t[1], t[6])
+ fp2.double(t[0], t[7])
+ fp2.add(&coeff[1], t[0], t[7])
+ fp2.neg(&coeff[2], t[6])
+}
+
+func (e *Engine) additionStep(coeff *[3]fe2, r, q *PointG2) {
+ // Algorithm 12 in https://eprint.iacr.org/2010/526.pdf
+ fp2 := e.fp2
+ t := e.t2
+ fp2.mul(t[0], &q[1], &r[2])
+ fp2.neg(t[0], t[0])
+ fp2.add(t[0], t[0], &r[1])
+ fp2.mul(t[1], &q[0], &r[2])
+ fp2.neg(t[1], t[1])
+ fp2.add(t[1], t[1], &r[0])
+ fp2.square(t[2], t[0])
+ fp2.square(t[3], t[1])
+ fp2.mul(t[4], t[1], t[3])
+ fp2.mul(t[2], &r[2], t[2])
+ fp2.mul(t[3], &r[0], t[3])
+ fp2.double(t[5], t[3])
+ fp2.sub(t[5], t[4], t[5])
+ fp2.add(t[5], t[5], t[2])
+ fp2.mul(&r[0], t[1], t[5])
+ fp2.sub(t[2], t[3], t[5])
+ fp2.mul(t[2], t[2], t[0])
+ fp2.mul(t[3], &r[1], t[4])
+ fp2.sub(&r[1], t[2], t[3])
+ fp2.mul(&r[2], &r[2], t[4])
+ fp2.mul(t[2], t[1], &q[1])
+ fp2.mul(t[3], t[0], &q[0])
+ fp2.sub(&coeff[0], t[3], t[2])
+ fp2.neg(&coeff[1], t[0])
+ coeff[2].set(t[1])
+}
+
+func (e *Engine) preCompute(ellCoeffs *[68][3]fe2, twistPoint *PointG2) {
+ // Algorithm 5 in https://eprint.iacr.org/2019/077.pdf
+ if e.G2.IsZero(twistPoint) {
+ return
+ }
+ r := new(PointG2).Set(twistPoint)
+ j := 0
+ for i := x.BitLen() - 2; i >= 0; i-- {
+ e.doublingStep(&ellCoeffs[j], r)
+ if x.Bit(i) != 0 {
+ j++
+ ellCoeffs[j] = fe6{}
+ e.additionStep(&ellCoeffs[j], r, twistPoint)
+ }
+ j++
+ }
+}
+
+func (e *Engine) millerLoop(f *fe12) {
+ pairs := e.pairs
+ ellCoeffs := make([][68][3]fe2, len(pairs))
+ for i := 0; i < len(pairs); i++ {
+ e.preCompute(&ellCoeffs[i], pairs[i].g2)
+ }
+ fp12, fp2 := e.fp12, e.fp2
+ t := e.t2
+ f.one()
+ j := 0
+ for i := 62; /* x.BitLen() - 2 */ i >= 0; i-- {
+ if i != 62 {
+ fp12.square(f, f)
+ }
+ for i := 0; i <= len(pairs)-1; i++ { //nolint:govet
+ fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1])
+ fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0])
+ fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0])
+ }
+ if x.Bit(i) != 0 {
+ j++
+ for i := 0; i <= len(pairs)-1; i++ { //nolint:govet
+ fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1])
+ fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0])
+ fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0])
+ }
+ }
+ j++
+ }
+ fp12.conjugate(f, f)
+}
+
+func (e *Engine) exp(c, a *fe12) {
+ fp12 := e.fp12
+ fp12.cyclotomicExp(c, a, x)
+ fp12.conjugate(c, c)
+}
+
+func (e *Engine) finalExp(f *fe12) {
+ fp12 := e.fp12
+ t := e.t12
+ // easy part
+ fp12.frobeniusMap(&t[0], f, 6)
+ fp12.inverse(&t[1], f)
+ fp12.mul(&t[2], &t[0], &t[1])
+ t[1].set(&t[2])
+ fp12.frobeniusMapAssign(&t[2], 2)
+ fp12.mulAssign(&t[2], &t[1])
+ fp12.cyclotomicSquare(&t[1], &t[2])
+ fp12.conjugate(&t[1], &t[1])
+ // hard part
+ e.exp(&t[3], &t[2])
+ fp12.cyclotomicSquare(&t[4], &t[3])
+ fp12.mul(&t[5], &t[1], &t[3])
+ e.exp(&t[1], &t[5])
+ e.exp(&t[0], &t[1])
+ e.exp(&t[6], &t[0])
+ fp12.mulAssign(&t[6], &t[4])
+ e.exp(&t[4], &t[6])
+ fp12.conjugate(&t[5], &t[5])
+ fp12.mulAssign(&t[4], &t[5])
+ fp12.mulAssign(&t[4], &t[2])
+ fp12.conjugate(&t[5], &t[2])
+ fp12.mulAssign(&t[1], &t[2])
+ fp12.frobeniusMapAssign(&t[1], 3)
+ fp12.mulAssign(&t[6], &t[5])
+ fp12.frobeniusMapAssign(&t[6], 1)
+ fp12.mulAssign(&t[3], &t[0])
+ fp12.frobeniusMapAssign(&t[3], 2)
+ fp12.mulAssign(&t[3], &t[1])
+ fp12.mulAssign(&t[3], &t[6])
+ fp12.mul(f, &t[3], &t[4])
+}
+
+func (e *Engine) calculate() *fe12 {
+ f := e.fp12.one()
+ if len(e.pairs) == 0 {
+ return f
+ }
+ e.millerLoop(f)
+ e.finalExp(f)
+ return f
+}
+
+// Check computes pairing and checks if result is equal to one
+func (e *Engine) Check() bool {
+ return e.calculate().isOne()
+}
+
+// Result computes pairing and returns target group element as result.
+func (e *Engine) Result() *E {
+ r := e.calculate()
+ e.Reset()
+ return r
+}
+
+// GT returns target group instance.
+func (e *Engine) GT() *GT {
+ return NewGT()
+}
diff --git a/common/crypto/bls12381/swu.go b/common/crypto/bls12381/swu.go
new file mode 100644
index 0000000..0f81aa6
--- /dev/null
+++ b/common/crypto/bls12381/swu.go
@@ -0,0 +1,158 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+// swuMapG1 is implementation of Simplified Shallue-van de Woestijne-Ulas Method
+// follows the implmentation at draft-irtf-cfrg-hash-to-curve-06.
+func swuMapG1(u *fe) (*fe, *fe) {
+ var params = swuParamsForG1
+ var tv [4]*fe
+ for i := 0; i < 4; i++ {
+ tv[i] = new(fe)
+ }
+ square(tv[0], u)
+ mul(tv[0], tv[0], params.z)
+ square(tv[1], tv[0])
+ x1 := new(fe)
+ add(x1, tv[0], tv[1])
+ inverse(x1, x1)
+ e1 := x1.isZero()
+ one := new(fe).one()
+ add(x1, x1, one)
+ if e1 {
+ x1.set(params.zInv)
+ }
+ mul(x1, x1, params.minusBOverA)
+ gx1 := new(fe)
+ square(gx1, x1)
+ add(gx1, gx1, params.a)
+ mul(gx1, gx1, x1)
+ add(gx1, gx1, params.b)
+ x2 := new(fe)
+ mul(x2, tv[0], x1)
+ mul(tv[1], tv[0], tv[1])
+ gx2 := new(fe)
+ mul(gx2, gx1, tv[1])
+ e2 := !isQuadraticNonResidue(gx1)
+ x, y2 := new(fe), new(fe)
+ if e2 {
+ x.set(x1)
+ y2.set(gx1)
+ } else {
+ x.set(x2)
+ y2.set(gx2)
+ }
+ y := new(fe)
+ sqrt(y, y2)
+ if y.sign() != u.sign() {
+ neg(y, y)
+ }
+ return x, y
+}
+
+// swuMapG2 is implementation of Simplified Shallue-van de Woestijne-Ulas Method
+// defined at draft-irtf-cfrg-hash-to-curve-06.
+func swuMapG2(e *fp2, u *fe2) (*fe2, *fe2) {
+ if e == nil {
+ e = newFp2()
+ }
+ params := swuParamsForG2
+ var tv [4]*fe2
+ for i := 0; i < 4; i++ {
+ tv[i] = e.new()
+ }
+ e.square(tv[0], u)
+ e.mul(tv[0], tv[0], params.z)
+ e.square(tv[1], tv[0])
+ x1 := e.new()
+ e.add(x1, tv[0], tv[1])
+ e.inverse(x1, x1)
+ e1 := x1.isZero()
+ e.add(x1, x1, e.one())
+ if e1 {
+ x1.set(params.zInv)
+ }
+ e.mul(x1, x1, params.minusBOverA)
+ gx1 := e.new()
+ e.square(gx1, x1)
+ e.add(gx1, gx1, params.a)
+ e.mul(gx1, gx1, x1)
+ e.add(gx1, gx1, params.b)
+ x2 := e.new()
+ e.mul(x2, tv[0], x1)
+ e.mul(tv[1], tv[0], tv[1])
+ gx2 := e.new()
+ e.mul(gx2, gx1, tv[1])
+ e2 := !e.isQuadraticNonResidue(gx1)
+ x, y2 := e.new(), e.new()
+ if e2 {
+ x.set(x1)
+ y2.set(gx1)
+ } else {
+ x.set(x2)
+ y2.set(gx2)
+ }
+ y := e.new()
+ e.sqrt(y, y2)
+ if y.sign() != u.sign() {
+ e.neg(y, y)
+ }
+ return x, y
+}
+
+var swuParamsForG1 = struct {
+ z *fe
+ zInv *fe
+ a *fe
+ b *fe
+ minusBOverA *fe
+}{
+ a: &fe{0x2f65aa0e9af5aa51, 0x86464c2d1e8416c3, 0xb85ce591b7bd31e2, 0x27e11c91b5f24e7c, 0x28376eda6bfc1835, 0x155455c3e5071d85},
+ b: &fe{0xfb996971fe22a1e0, 0x9aa93eb35b742d6f, 0x8c476013de99c5c4, 0x873e27c3a221e571, 0xca72b5e45a52d888, 0x06824061418a386b},
+ z: &fe{0x886c00000023ffdc, 0x0f70008d3090001d, 0x77672417ed5828c3, 0x9dac23e943dc1740, 0x50553f1b9c131521, 0x078c712fbe0ab6e8},
+ zInv: &fe{0x0e8a2e8ba2e83e10, 0x5b28ba2ca4d745d1, 0x678cd5473847377a, 0x4c506dd8a8076116, 0x9bcb227d79284139, 0x0e8d3154b0ba099a},
+ minusBOverA: &fe{0x052583c93555a7fe, 0x3b40d72430f93c82, 0x1b75faa0105ec983, 0x2527e7dc63851767, 0x99fffd1f34fc181d, 0x097cab54770ca0d3},
+}
+
+var swuParamsForG2 = struct {
+ z *fe2
+ zInv *fe2
+ a *fe2
+ b *fe2
+ minusBOverA *fe2
+}{
+ a: &fe2{
+ fe{0, 0, 0, 0, 0, 0},
+ fe{0xe53a000003135242, 0x01080c0fdef80285, 0xe7889edbe340f6bd, 0x0b51375126310601, 0x02d6985717c744ab, 0x1220b4e979ea5467},
+ },
+ b: &fe2{
+ fe{0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1},
+ fe{0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1},
+ },
+ z: &fe2{
+ fe{0x87ebfffffff9555c, 0x656fffe5da8ffffa, 0x0fd0749345d33ad2, 0xd951e663066576f4, 0xde291a3d41e980d3, 0x0815664c7dfe040d},
+ fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206},
+ },
+ zInv: &fe2{
+ fe{0xacd0000000011110, 0x9dd9999dc88ccccd, 0xb5ca2ac9b76352bf, 0xf1b574bcf4bc90ce, 0x42dab41f28a77081, 0x132fc6ac14cd1e12},
+ fe{0xe396ffffffff2223, 0x4fbf332fcd0d9998, 0x0c4bbd3c1aff4cc4, 0x6b9c91267926ca58, 0x29ae4da6aef7f496, 0x10692e942f195791},
+ },
+ minusBOverA: &fe2{
+ fe{0x903c555555474fb3, 0x5f98cc95ce451105, 0x9f8e582eefe0fade, 0xc68946b6aebbd062, 0x467a4ad10ee6de53, 0x0e7146f483e23a05},
+ fe{0x29c2aaaaaab85af8, 0xbf133368e30eeefa, 0xc7a27a7206cffb45, 0x9dee04ce44c9425c, 0x04a15ce53464ce83, 0x0b8fcaf5b59dac95},
+ },
+}
diff --git a/common/crypto/bls12381/utils.go b/common/crypto/bls12381/utils.go
new file mode 100644
index 0000000..c5b292a
--- /dev/null
+++ b/common/crypto/bls12381/utils.go
@@ -0,0 +1,48 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bls12381
+
+import (
+ "errors"
+ "github.com/holiman/uint256"
+ "math/big"
+)
+
+func bigFromHex(hex string) *big.Int {
+ i256, err := uint256.FromHex(hex)
+ if nil != err {
+ return uint256.NewInt(0).ToBig()
+ }
+ return i256.ToBig()
+}
+
+// decodeFieldElement expects 64 byte input with zero top 16 bytes,
+// returns lower 48 bytes.
+func decodeFieldElement(in []byte) ([]byte, error) {
+ if len(in) != 64 {
+ return nil, errors.New("invalid field element length")
+ }
+ // check top bytes
+ for i := 0; i < 16; i++ {
+ if in[i] != byte(0x00) {
+ return nil, errors.New("invalid field element top bytes")
+ }
+ }
+ out := make([]byte, 48)
+ copy(out, in[16:])
+ return out, nil
+}
diff --git a/common/crypto/bn256/LICENSE b/common/crypto/bn256/LICENSE
new file mode 100644
index 0000000..634e0cb
--- /dev/null
+++ b/common/crypto/bn256/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2012 The Go Authors. All rights reserved.
+Copyright (c) 2018 Péter Szilágyi. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of Google Inc. 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
+OWNER 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/common/crypto/bn256/bn256_fast.go b/common/crypto/bn256/bn256_fast.go
new file mode 100644
index 0000000..45e98d7
--- /dev/null
+++ b/common/crypto/bn256/bn256_fast.go
@@ -0,0 +1,25 @@
+// Copyright 2018 Péter Szilágyi. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+//go:build amd64 || arm64
+
+// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
+package bn256
+
+import (
+ bn256cf "github.com/astranetworld/ast/common/crypto/bn256/cloudflare"
+)
+
+// G1 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G1 = bn256cf.G1
+
+// G2 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G2 = bn256cf.G2
+
+// PairingCheck calculates the Optimal Ate pairing for a set of points.
+func PairingCheck(a []*G1, b []*G2) bool {
+ return bn256cf.PairingCheck(a, b)
+}
diff --git a/common/crypto/bn256/bn256_slow.go b/common/crypto/bn256/bn256_slow.go
new file mode 100644
index 0000000..0da7f6c
--- /dev/null
+++ b/common/crypto/bn256/bn256_slow.go
@@ -0,0 +1,23 @@
+// Copyright 2018 Péter Szilágyi. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+//go:build !amd64 && !arm64
+
+// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
+package bn256
+
+import bn256 "github.com/astranetworld/ast/common/crypto/bn256/google"
+
+// G1 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G1 = bn256.G1
+
+// G2 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G2 = bn256.G2
+
+// PairingCheck calculates the Optimal Ate pairing for a set of points.
+func PairingCheck(a []*G1, b []*G2) bool {
+ return bn256.PairingCheck(a, b)
+}
diff --git a/common/crypto/bn256/cloudflare/LICENSE b/common/crypto/bn256/cloudflare/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of Google Inc. 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
+OWNER 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/common/crypto/bn256/cloudflare/bn256.go b/common/crypto/bn256/cloudflare/bn256.go
new file mode 100644
index 0000000..4f607af
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/bn256.go
@@ -0,0 +1,495 @@
+// Package bn256 implements a particular bilinear group at the 128-bit security
+// level.
+//
+// Bilinear groups are the basis of many of the new cryptographic protocols that
+// have been proposed over the past decade. They consist of a triplet of groups
+// (G₁, G₂ and GT) such that there exists a function e(g₁ˣ,g₂ʸ)=gTˣʸ (where gₓ
+// is a generator of the respective group). That function is called a pairing
+// function.
+//
+// This package specifically implements the Optimal Ate pairing over a 256-bit
+// Barreto-Naehrig curve as described in
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not
+// compatible with the implementation described in that paper, as different
+// parameters are chosen.
+//
+// (This package previously claimed to operate at a 128-bit security level.
+// However, recent improvements in attacks mean that is no longer true. See
+// https://moderncrypto.org/mail-archive/curves/2016/000740.html.)
+package bn256
+
+import (
+ "crypto/rand"
+ "errors"
+ "io"
+ "math/big"
+)
+
+func randomK(r io.Reader) (k *big.Int, err error) {
+ for {
+ k, err = rand.Int(r, Order)
+ if err != nil || k.Sign() > 0 {
+ return
+ }
+ }
+}
+
+// G1 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G1 struct {
+ p *curvePoint
+}
+
+// RandomG1 returns x and g₁ˣ where x is a random, non-zero number read from r.
+func RandomG1(r io.Reader) (*big.Int, *G1, error) {
+ k, err := randomK(r)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return k, new(G1).ScalarBaseMult(k), nil
+}
+
+func (g *G1) String() string {
+ return "bn256.G1" + g.p.String()
+}
+
+// ScalarBaseMult sets e to g*k where g is the generator of the group and then
+// returns e.
+func (e *G1) ScalarBaseMult(k *big.Int) *G1 {
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+ e.p.Mul(curveGen, k)
+ return e
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+ e.p.Mul(a.p, k)
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+func (e *G1) Add(a, b *G1) *G1 {
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+ e.p.Add(a.p, b.p)
+ return e
+}
+
+// Neg sets e to -a and then returns e.
+func (e *G1) Neg(a *G1) *G1 {
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+ e.p.Neg(a.p)
+ return e
+}
+
+// Set sets e to a and then returns e.
+func (e *G1) Set(a *G1) *G1 {
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+ e.p.Set(a.p)
+ return e
+}
+
+// Marshal converts e to a byte slice.
+func (e *G1) Marshal() []byte {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if e.p == nil {
+ e.p = &curvePoint{}
+ }
+
+ e.p.MakeAffine()
+ ret := make([]byte, numBytes*2)
+ if e.p.IsInfinity() {
+ return ret
+ }
+ temp := &gfP{}
+
+ montDecode(temp, &e.p.x)
+ temp.Marshal(ret)
+ montDecode(temp, &e.p.y)
+ temp.Marshal(ret[numBytes:])
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *G1) Unmarshal(m []byte) ([]byte, error) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+ if len(m) < 2*numBytes {
+ return nil, errors.New("bn256: not enough data")
+ }
+ // Unmarshal the points and check their caps
+ if e.p == nil {
+ e.p = &curvePoint{}
+ } else {
+ e.p.x, e.p.y = gfP{0}, gfP{0}
+ }
+ var err error
+ if err = e.p.x.Unmarshal(m); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.Unmarshal(m[numBytes:]); err != nil {
+ return nil, err
+ }
+ // Encode into Montgomery form and ensure it's on the curve
+ montEncode(&e.p.x, &e.p.x)
+ montEncode(&e.p.y, &e.p.y)
+
+ zero := gfP{0}
+ if e.p.x == zero && e.p.y == zero {
+ // This is the point at infinity.
+ e.p.y = *newGFp(1)
+ e.p.z = gfP{0}
+ e.p.t = gfP{0}
+ } else {
+ e.p.z = *newGFp(1)
+ e.p.t = *newGFp(1)
+
+ if !e.p.IsOnCurve() {
+ return nil, errors.New("bn256: malformed point")
+ }
+ }
+ return m[2*numBytes:], nil
+}
+
+// G2 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G2 struct {
+ p *twistPoint
+}
+
+// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
+func RandomG2(r io.Reader) (*big.Int, *G2, error) {
+ k, err := randomK(r)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return k, new(G2).ScalarBaseMult(k), nil
+}
+
+func (e *G2) String() string {
+ return "bn256.G2" + e.p.String()
+}
+
+// ScalarBaseMult sets e to g*k where g is the generator of the group and then
+// returns out.
+func (e *G2) ScalarBaseMult(k *big.Int) *G2 {
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ e.p.Mul(twistGen, k)
+ return e
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *G2) ScalarMult(a *G2, k *big.Int) *G2 {
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ e.p.Mul(a.p, k)
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+func (e *G2) Add(a, b *G2) *G2 {
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ e.p.Add(a.p, b.p)
+ return e
+}
+
+// Neg sets e to -a and then returns e.
+func (e *G2) Neg(a *G2) *G2 {
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ e.p.Neg(a.p)
+ return e
+}
+
+// Set sets e to a and then returns e.
+func (e *G2) Set(a *G2) *G2 {
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ e.p.Set(a.p)
+ return e
+}
+
+// Marshal converts e into a byte slice.
+func (e *G2) Marshal() []byte {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+
+ e.p.MakeAffine()
+ ret := make([]byte, numBytes*4)
+ if e.p.IsInfinity() {
+ return ret
+ }
+ temp := &gfP{}
+
+ montDecode(temp, &e.p.x.x)
+ temp.Marshal(ret)
+ montDecode(temp, &e.p.x.y)
+ temp.Marshal(ret[numBytes:])
+ montDecode(temp, &e.p.y.x)
+ temp.Marshal(ret[2*numBytes:])
+ montDecode(temp, &e.p.y.y)
+ temp.Marshal(ret[3*numBytes:])
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *G2) Unmarshal(m []byte) ([]byte, error) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+ if len(m) < 4*numBytes {
+ return nil, errors.New("bn256: not enough data")
+ }
+ // Unmarshal the points and check their caps
+ if e.p == nil {
+ e.p = &twistPoint{}
+ }
+ var err error
+ if err = e.p.x.x.Unmarshal(m); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.y.Unmarshal(m[numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.x.Unmarshal(m[2*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.y.Unmarshal(m[3*numBytes:]); err != nil {
+ return nil, err
+ }
+ // Encode into Montgomery form and ensure it's on the curve
+ montEncode(&e.p.x.x, &e.p.x.x)
+ montEncode(&e.p.x.y, &e.p.x.y)
+ montEncode(&e.p.y.x, &e.p.y.x)
+ montEncode(&e.p.y.y, &e.p.y.y)
+
+ if e.p.x.IsZero() && e.p.y.IsZero() {
+ // This is the point at infinity.
+ e.p.y.SetOne()
+ e.p.z.SetZero()
+ e.p.t.SetZero()
+ } else {
+ e.p.z.SetOne()
+ e.p.t.SetOne()
+
+ if !e.p.IsOnCurve() {
+ return nil, errors.New("bn256: malformed point")
+ }
+ }
+ return m[4*numBytes:], nil
+}
+
+// GT is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type GT struct {
+ p *gfP12
+}
+
+// Pair calculates an Optimal Ate pairing.
+func Pair(g1 *G1, g2 *G2) *GT {
+ return >{optimalAte(g2.p, g1.p)}
+}
+
+// PairingCheck calculates the Optimal Ate pairing for a set of points.
+func PairingCheck(a []*G1, b []*G2) bool {
+ acc := new(gfP12)
+ acc.SetOne()
+
+ for i := 0; i < len(a); i++ {
+ if a[i].p.IsInfinity() || b[i].p.IsInfinity() {
+ continue
+ }
+ acc.Mul(acc, miller(b[i].p, a[i].p))
+ }
+ return finalExponentiation(acc).IsOne()
+}
+
+// Miller applies Miller's algorithm, which is a bilinear function from the
+// source groups to F_p^12. Miller(g1, g2).Finalize() is equivalent to Pair(g1,
+// g2).
+func Miller(g1 *G1, g2 *G2) *GT {
+ return >{miller(g2.p, g1.p)}
+}
+
+func (g *GT) String() string {
+ return "bn256.GT" + g.p.String()
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *GT) ScalarMult(a *GT, k *big.Int) *GT {
+ if e.p == nil {
+ e.p = &gfP12{}
+ }
+ e.p.Exp(a.p, k)
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+func (e *GT) Add(a, b *GT) *GT {
+ if e.p == nil {
+ e.p = &gfP12{}
+ }
+ e.p.Mul(a.p, b.p)
+ return e
+}
+
+// Neg sets e to -a and then returns e.
+func (e *GT) Neg(a *GT) *GT {
+ if e.p == nil {
+ e.p = &gfP12{}
+ }
+ e.p.Conjugate(a.p)
+ return e
+}
+
+// Set sets e to a and then returns e.
+func (e *GT) Set(a *GT) *GT {
+ if e.p == nil {
+ e.p = &gfP12{}
+ }
+ e.p.Set(a.p)
+ return e
+}
+
+// Finalize is a linear function from F_p^12 to GT.
+func (e *GT) Finalize() *GT {
+ ret := finalExponentiation(e.p)
+ e.p.Set(ret)
+ return e
+}
+
+// Marshal converts e into a byte slice.
+func (e *GT) Marshal() []byte {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if e.p == nil {
+ e.p = &gfP12{}
+ e.p.SetOne()
+ }
+
+ ret := make([]byte, numBytes*12)
+ temp := &gfP{}
+
+ montDecode(temp, &e.p.x.x.x)
+ temp.Marshal(ret)
+ montDecode(temp, &e.p.x.x.y)
+ temp.Marshal(ret[numBytes:])
+ montDecode(temp, &e.p.x.y.x)
+ temp.Marshal(ret[2*numBytes:])
+ montDecode(temp, &e.p.x.y.y)
+ temp.Marshal(ret[3*numBytes:])
+ montDecode(temp, &e.p.x.z.x)
+ temp.Marshal(ret[4*numBytes:])
+ montDecode(temp, &e.p.x.z.y)
+ temp.Marshal(ret[5*numBytes:])
+ montDecode(temp, &e.p.y.x.x)
+ temp.Marshal(ret[6*numBytes:])
+ montDecode(temp, &e.p.y.x.y)
+ temp.Marshal(ret[7*numBytes:])
+ montDecode(temp, &e.p.y.y.x)
+ temp.Marshal(ret[8*numBytes:])
+ montDecode(temp, &e.p.y.y.y)
+ temp.Marshal(ret[9*numBytes:])
+ montDecode(temp, &e.p.y.z.x)
+ temp.Marshal(ret[10*numBytes:])
+ montDecode(temp, &e.p.y.z.y)
+ temp.Marshal(ret[11*numBytes:])
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *GT) Unmarshal(m []byte) ([]byte, error) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if len(m) < 12*numBytes {
+ return nil, errors.New("bn256: not enough data")
+ }
+
+ if e.p == nil {
+ e.p = &gfP12{}
+ }
+
+ var err error
+ if err = e.p.x.x.x.Unmarshal(m); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.x.y.Unmarshal(m[numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.y.x.Unmarshal(m[2*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.y.y.Unmarshal(m[3*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.z.x.Unmarshal(m[4*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.x.z.y.Unmarshal(m[5*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.x.x.Unmarshal(m[6*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.x.y.Unmarshal(m[7*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.y.x.Unmarshal(m[8*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.y.y.Unmarshal(m[9*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.z.x.Unmarshal(m[10*numBytes:]); err != nil {
+ return nil, err
+ }
+ if err = e.p.y.z.y.Unmarshal(m[11*numBytes:]); err != nil {
+ return nil, err
+ }
+ montEncode(&e.p.x.x.x, &e.p.x.x.x)
+ montEncode(&e.p.x.x.y, &e.p.x.x.y)
+ montEncode(&e.p.x.y.x, &e.p.x.y.x)
+ montEncode(&e.p.x.y.y, &e.p.x.y.y)
+ montEncode(&e.p.x.z.x, &e.p.x.z.x)
+ montEncode(&e.p.x.z.y, &e.p.x.z.y)
+ montEncode(&e.p.y.x.x, &e.p.y.x.x)
+ montEncode(&e.p.y.x.y, &e.p.y.x.y)
+ montEncode(&e.p.y.y.x, &e.p.y.y.x)
+ montEncode(&e.p.y.y.y, &e.p.y.y.y)
+ montEncode(&e.p.y.z.x, &e.p.y.z.x)
+ montEncode(&e.p.y.z.y, &e.p.y.z.y)
+
+ return m[12*numBytes:], nil
+}
diff --git a/common/crypto/bn256/cloudflare/bn256_test.go b/common/crypto/bn256/cloudflare/bn256_test.go
new file mode 100644
index 0000000..8af2d38
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/bn256_test.go
@@ -0,0 +1,132 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+import (
+ "bytes"
+ "crypto/rand"
+ "testing"
+)
+
+func TestG1Marshal(t *testing.T) {
+ _, Ga, err := RandomG1(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ma := Ga.Marshal()
+
+ Gb := new(G1)
+ _, err = Gb.Unmarshal(ma)
+ if err != nil {
+ t.Fatal(err)
+ }
+ mb := Gb.Marshal()
+
+ if !bytes.Equal(ma, mb) {
+ t.Fatal("bytes are different")
+ }
+}
+
+func TestG2Marshal(t *testing.T) {
+ _, Ga, err := RandomG2(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ma := Ga.Marshal()
+
+ Gb := new(G2)
+ _, err = Gb.Unmarshal(ma)
+ if err != nil {
+ t.Fatal(err)
+ }
+ mb := Gb.Marshal()
+
+ if !bytes.Equal(ma, mb) {
+ t.Fatal("bytes are different")
+ }
+}
+
+func TestBilinearity(t *testing.T) {
+ for i := 0; i < 2; i++ {
+ a, p1, _ := RandomG1(rand.Reader)
+ b, p2, _ := RandomG2(rand.Reader)
+ e1 := Pair(p1, p2)
+
+ e2 := Pair(&G1{curveGen}, &G2{twistGen})
+ e2.ScalarMult(e2, a)
+ e2.ScalarMult(e2, b)
+
+ if *e1.p != *e2.p {
+ t.Fatalf("bad pairing result: %s", e1)
+ }
+ }
+}
+
+func TestTripartiteDiffieHellman(t *testing.T) {
+ a, _ := rand.Int(rand.Reader, Order)
+ b, _ := rand.Int(rand.Reader, Order)
+ c, _ := rand.Int(rand.Reader, Order)
+
+ pa, pb, pc := new(G1), new(G1), new(G1)
+ qa, qb, qc := new(G2), new(G2), new(G2)
+
+ pa.Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
+ qa.Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
+ pb.Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
+ qb.Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
+ pc.Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
+ qc.Unmarshal(new(G2).ScalarBaseMult(c).Marshal())
+
+ k1 := Pair(pb, qc)
+ k1.ScalarMult(k1, a)
+ k1Bytes := k1.Marshal()
+
+ k2 := Pair(pc, qa)
+ k2.ScalarMult(k2, b)
+ k2Bytes := k2.Marshal()
+
+ k3 := Pair(pa, qb)
+ k3.ScalarMult(k3, c)
+ k3Bytes := k3.Marshal()
+
+ if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) {
+ t.Errorf("keys didn't agree")
+ }
+}
+
+func BenchmarkG1(b *testing.B) {
+ x, _ := rand.Int(rand.Reader, Order)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ new(G1).ScalarBaseMult(x)
+ }
+}
+
+func BenchmarkG2(b *testing.B) {
+ x, _ := rand.Int(rand.Reader, Order)
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ new(G2).ScalarBaseMult(x)
+ }
+}
+func BenchmarkPairing(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Pair(&G1{curveGen}, &G2{twistGen})
+ }
+}
diff --git a/common/crypto/bn256/cloudflare/constants.go b/common/crypto/bn256/cloudflare/constants.go
new file mode 100644
index 0000000..f7d2c7c
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/constants.go
@@ -0,0 +1,62 @@
+// Copyright 2012 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 bn256
+
+import (
+ "math/big"
+)
+
+func bigFromBase10(s string) *big.Int {
+ n, _ := new(big.Int).SetString(s, 10)
+ return n
+}
+
+// u is the BN parameter.
+var u = bigFromBase10("4965661367192848881")
+
+// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
+// Needs to be highly 2-adic for efficient SNARK key and proof generation.
+// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081.
+// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters.
+var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617")
+
+// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
+var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583")
+
+// p2 is p, represented as little-endian 64-bit words.
+var p2 = [4]uint64{0x3c208c16d87cfd47, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029}
+
+// np is the negative inverse of p, mod 2^256.
+var np = [4]uint64{0x87d20782e4866389, 0x9ede7d651eca6ac9, 0xd8afcbd01833da80, 0xf57a22b791888c6b}
+
+// rN1 is R^-1 where R = 2^256 mod p.
+var rN1 = &gfP{0xed84884a014afa37, 0xeb2022850278edf8, 0xcf63e9cfb74492d9, 0x2e67157159e5c639}
+
+// r2 is R^2 where R = 2^256 mod p.
+var r2 = &gfP{0xf32cfc5b538afa89, 0xb5e71911d44501fb, 0x47ab1eff0a417ff6, 0x06d89f71cab8351f}
+
+// r3 is R^3 where R = 2^256 mod p.
+var r3 = &gfP{0xb1cd6dafda1530df, 0x62f210e6a7283db6, 0xef7f0b0c0ada0afb, 0x20fd6e902d592544}
+
+// xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9.
+var xiToPMinus1Over6 = &gfP2{gfP{0xa222ae234c492d72, 0xd00f02a4565de15b, 0xdc2ff3a253dfc926, 0x10a75716b3899551}, gfP{0xaf9ba69633144907, 0xca6b1d7387afb78a, 0x11bded5ef08a2087, 0x02f34d751a1f3a7c}}
+
+// xiToPMinus1Over3 is ξ^((p-1)/3) where ξ = i+9.
+var xiToPMinus1Over3 = &gfP2{gfP{0x6e849f1ea0aa4757, 0xaa1c7b6d89f89141, 0xb6e713cdfae0ca3a, 0x26694fbb4e82ebc3}, gfP{0xb5773b104563ab30, 0x347f91c8a9aa6454, 0x7a007127242e0991, 0x1956bcd8118214ec}}
+
+// xiToPMinus1Over2 is ξ^((p-1)/2) where ξ = i+9.
+var xiToPMinus1Over2 = &gfP2{gfP{0xa1d77ce45ffe77c7, 0x07affd117826d1db, 0x6d16bd27bb7edc6b, 0x2c87200285defecc}, gfP{0xe4bbdd0c2936b629, 0xbb30f162e133bacb, 0x31a9d1b6f9645366, 0x253570bea500f8dd}}
+
+// xiToPSquaredMinus1Over3 is ξ^((p²-1)/3) where ξ = i+9.
+var xiToPSquaredMinus1Over3 = &gfP{0x3350c88e13e80b9c, 0x7dce557cdb5e56b9, 0x6001b4b8b615564a, 0x2682e617020217e0}
+
+// xiTo2PSquaredMinus2Over3 is ξ^((2p²-2)/3) where ξ = i+9 (a cubic root of unity, mod p).
+var xiTo2PSquaredMinus2Over3 = &gfP{0x71930c11d782e155, 0xa6bb947cffbe3323, 0xaa303344d4741444, 0x2c3b3f0d26594943}
+
+// xiToPSquaredMinus1Over6 is ξ^((1p²-1)/6) where ξ = i+9 (a cubic root of -1, mod p).
+var xiToPSquaredMinus1Over6 = &gfP{0xca8d800500fa1bf2, 0xf0c5d61468b39769, 0x0e201271ad0d4418, 0x04290f65bad856e6}
+
+// xiTo2PMinus2Over3 is ξ^((2p-2)/3) where ξ = i+9.
+var xiTo2PMinus2Over3 = &gfP2{gfP{0x5dddfd154bd8c949, 0x62cb29a5a4445b60, 0x37bc870a0c7dd2b9, 0x24830a9d3171f0fd}, gfP{0x7361d77f843abe92, 0xa5bb2bd3273411fb, 0x9c941f314b3e2399, 0x15df9cddbb9fd3ec}}
diff --git a/common/crypto/bn256/cloudflare/curve.go b/common/crypto/bn256/cloudflare/curve.go
new file mode 100644
index 0000000..253b5a0
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/curve.go
@@ -0,0 +1,254 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+import (
+ "math/big"
+)
+
+// curvePoint implements the elliptic curve y²=x³+3. Points are kept in Jacobian
+// form and t=z² when valid. G₁ is the set of points of this curve on GF(p).
+type curvePoint struct {
+ x, y, z, t gfP
+}
+
+var curveB = newGFp(3)
+
+// curveGen is the generator of G₁.
+var curveGen = &curvePoint{
+ x: *newGFp(1),
+ y: *newGFp(2),
+ z: *newGFp(1),
+ t: *newGFp(1),
+}
+
+func (c *curvePoint) String() string {
+ c.MakeAffine()
+ x, y := &gfP{}, &gfP{}
+ montDecode(x, &c.x)
+ montDecode(y, &c.y)
+ return "(" + x.String() + ", " + y.String() + ")"
+}
+
+func (c *curvePoint) Set(a *curvePoint) {
+ c.x.Set(&a.x)
+ c.y.Set(&a.y)
+ c.z.Set(&a.z)
+ c.t.Set(&a.t)
+}
+
+// IsOnCurve returns true iff c is on the curve.
+func (c *curvePoint) IsOnCurve() bool {
+ c.MakeAffine()
+ if c.IsInfinity() {
+ return true
+ }
+
+ y2, x3 := &gfP{}, &gfP{}
+ gfpMul(y2, &c.y, &c.y)
+ gfpMul(x3, &c.x, &c.x)
+ gfpMul(x3, x3, &c.x)
+ gfpAdd(x3, x3, curveB)
+
+ return *y2 == *x3
+}
+
+func (c *curvePoint) SetInfinity() {
+ c.x = gfP{0}
+ c.y = *newGFp(1)
+ c.z = gfP{0}
+ c.t = gfP{0}
+}
+
+func (c *curvePoint) IsInfinity() bool {
+ return c.z == gfP{0}
+}
+
+func (c *curvePoint) Add(a, b *curvePoint) {
+ if a.IsInfinity() {
+ c.Set(b)
+ return
+ }
+ if b.IsInfinity() {
+ c.Set(a)
+ return
+ }
+
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
+
+ // Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
+ // by [u1:s1:z1·z2] and [u2:s2:z1·z2]
+ // where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
+ z12, z22 := &gfP{}, &gfP{}
+ gfpMul(z12, &a.z, &a.z)
+ gfpMul(z22, &b.z, &b.z)
+
+ u1, u2 := &gfP{}, &gfP{}
+ gfpMul(u1, &a.x, z22)
+ gfpMul(u2, &b.x, z12)
+
+ t, s1 := &gfP{}, &gfP{}
+ gfpMul(t, &b.z, z22)
+ gfpMul(s1, &a.y, t)
+
+ s2 := &gfP{}
+ gfpMul(t, &a.z, z12)
+ gfpMul(s2, &b.y, t)
+
+ // Compute x = (2h)²(s²-u1-u2)
+ // where s = (s2-s1)/(u2-u1) is the slope of the line through
+ // (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
+ // This is also:
+ // 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
+ // = r² - j - 2v
+ // with the notations below.
+ h := &gfP{}
+ gfpSub(h, u2, u1)
+ xEqual := *h == gfP{0}
+
+ gfpAdd(t, h, h)
+ // i = 4h²
+ i := &gfP{}
+ gfpMul(i, t, t)
+ // j = 4h³
+ j := &gfP{}
+ gfpMul(j, h, i)
+
+ gfpSub(t, s2, s1)
+ yEqual := *t == gfP{0}
+ if xEqual && yEqual {
+ c.Double(a)
+ return
+ }
+ r := &gfP{}
+ gfpAdd(r, t, t)
+
+ v := &gfP{}
+ gfpMul(v, u1, i)
+
+ // t4 = 4(s2-s1)²
+ t4, t6 := &gfP{}, &gfP{}
+ gfpMul(t4, r, r)
+ gfpAdd(t, v, v)
+ gfpSub(t6, t4, j)
+
+ gfpSub(&c.x, t6, t)
+
+ // Set y = -(2h)³(s1 + s*(x/4h²-u1))
+ // This is also
+ // y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
+ gfpSub(t, v, &c.x) // t7
+ gfpMul(t4, s1, j) // t8
+ gfpAdd(t6, t4, t4) // t9
+ gfpMul(t4, r, t) // t10
+ gfpSub(&c.y, t4, t6)
+
+ // Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
+ gfpAdd(t, &a.z, &b.z) // t11
+ gfpMul(t4, t, t) // t12
+ gfpSub(t, t4, z12) // t13
+ gfpSub(t4, t, z22) // t14
+ gfpMul(&c.z, t4, h)
+}
+
+func (c *curvePoint) Double(a *curvePoint) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
+ A, B, C := &gfP{}, &gfP{}, &gfP{}
+ gfpMul(A, &a.x, &a.x)
+ gfpMul(B, &a.y, &a.y)
+ gfpMul(C, B, B)
+
+ t, t2 := &gfP{}, &gfP{}
+ gfpAdd(t, &a.x, B)
+ gfpMul(t2, t, t)
+ gfpSub(t, t2, A)
+ gfpSub(t2, t, C)
+
+ d, e, f := &gfP{}, &gfP{}, &gfP{}
+ gfpAdd(d, t2, t2)
+ gfpAdd(t, A, A)
+ gfpAdd(e, t, A)
+ gfpMul(f, e, e)
+
+ gfpAdd(t, d, d)
+ gfpSub(&c.x, f, t)
+
+ gfpAdd(t, C, C)
+ gfpAdd(t2, t, t)
+ gfpAdd(t, t2, t2)
+ gfpSub(&c.y, d, &c.x)
+ gfpMul(t2, e, &c.y)
+ gfpSub(&c.y, t2, t)
+
+ gfpMul(t, &a.y, &a.z)
+ gfpAdd(&c.z, t, t)
+}
+
+func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) {
+ precomp := [1 << 2]*curvePoint{nil, {}, {}, {}}
+ precomp[1].Set(a)
+ precomp[2].Set(a)
+ gfpMul(&precomp[2].x, &precomp[2].x, xiTo2PSquaredMinus2Over3)
+ precomp[3].Add(precomp[1], precomp[2])
+
+ multiScalar := curveLattice.Multi(scalar)
+
+ sum := &curvePoint{}
+ sum.SetInfinity()
+ t := &curvePoint{}
+
+ for i := len(multiScalar) - 1; i >= 0; i-- {
+ t.Double(sum)
+ if multiScalar[i] == 0 {
+ sum.Set(t)
+ } else {
+ sum.Add(t, precomp[multiScalar[i]])
+ }
+ }
+ c.Set(sum)
+}
+
+func (c *curvePoint) MakeAffine() {
+ if c.z == *newGFp(1) {
+ return
+ } else if c.z == *newGFp(0) {
+ c.x = gfP{0}
+ c.y = *newGFp(1)
+ c.t = gfP{0}
+ return
+ }
+
+ zInv := &gfP{}
+ zInv.Invert(&c.z)
+
+ t, zInv2 := &gfP{}, &gfP{}
+ gfpMul(t, &c.y, zInv)
+ gfpMul(zInv2, zInv, zInv)
+
+ gfpMul(&c.x, &c.x, zInv2)
+ gfpMul(&c.y, t, zInv2)
+
+ c.z = *newGFp(1)
+ c.t = *newGFp(1)
+}
+
+func (c *curvePoint) Neg(a *curvePoint) {
+ c.x.Set(&a.x)
+ gfpNeg(&c.y, &a.y)
+ c.z.Set(&a.z)
+ c.t = gfP{0}
+}
diff --git a/common/crypto/bn256/cloudflare/example_test.go b/common/crypto/bn256/cloudflare/example_test.go
new file mode 100644
index 0000000..6c28599
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/example_test.go
@@ -0,0 +1,51 @@
+// Copyright 2012 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 bn256
+
+import (
+ "crypto/rand"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestExamplePair(t *testing.T) {
+ // This implements the tripartite Diffie-Hellman algorithm from "A One
+ // Round Protocol for Tripartite Diffie-Hellman", A. Joux.
+ // http://www.springerlink.com/content/cddc57yyva0hburb/fulltext.pdf
+
+ // Each of three parties, a, b and c, generate a private value.
+ a, _ := rand.Int(rand.Reader, Order)
+ b, _ := rand.Int(rand.Reader, Order)
+ c, _ := rand.Int(rand.Reader, Order)
+
+ // Then each party calculates g₁ and g₂ times their private value.
+ pa := new(G1).ScalarBaseMult(a)
+ qa := new(G2).ScalarBaseMult(a)
+
+ pb := new(G1).ScalarBaseMult(b)
+ qb := new(G2).ScalarBaseMult(b)
+
+ pc := new(G1).ScalarBaseMult(c)
+ qc := new(G2).ScalarBaseMult(c)
+
+ // Now each party exchanges its public values with the other two and
+ // all parties can calculate the shared key.
+ k1 := Pair(pb, qc)
+ k1.ScalarMult(k1, a)
+
+ k2 := Pair(pc, qa)
+ k2.ScalarMult(k2, b)
+
+ k3 := Pair(pa, qb)
+ k3.ScalarMult(k3, c)
+
+ // k1, k2 and k3 will all be equal.
+
+ require.Equal(t, k1, k2)
+ require.Equal(t, k1, k3)
+
+ require.Equal(t, len(np), 4) //Avoid gometalinter varcheck err on np
+}
diff --git a/common/crypto/bn256/cloudflare/gfp.go b/common/crypto/bn256/cloudflare/gfp.go
new file mode 100644
index 0000000..842b190
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp.go
@@ -0,0 +1,97 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+import (
+ "errors"
+ "fmt"
+)
+
+type gfP [4]uint64
+
+func newGFp(x int64) (out *gfP) {
+ if x >= 0 {
+ out = &gfP{uint64(x)}
+ } else {
+ out = &gfP{uint64(-x)}
+ gfpNeg(out, out)
+ }
+
+ montEncode(out, out)
+ return out
+}
+
+func (e *gfP) String() string {
+ return fmt.Sprintf("%16.16x%16.16x%16.16x%16.16x", e[3], e[2], e[1], e[0])
+}
+
+func (e *gfP) Set(f *gfP) {
+ e[0] = f[0]
+ e[1] = f[1]
+ e[2] = f[2]
+ e[3] = f[3]
+}
+
+func (e *gfP) Invert(f *gfP) {
+ bits := [4]uint64{0x3c208c16d87cfd45, 0x97816a916871ca8d, 0xb85045b68181585d, 0x30644e72e131a029}
+
+ sum, power := &gfP{}, &gfP{}
+ sum.Set(rN1)
+ power.Set(f)
+
+ for word := 0; word < 4; word++ {
+ for bit := uint(0); bit < 64; bit++ {
+ if (bits[word]>>bit)&1 == 1 {
+ gfpMul(sum, sum, power)
+ }
+ gfpMul(power, power, power)
+ }
+ }
+
+ gfpMul(sum, sum, r3)
+ e.Set(sum)
+}
+
+func (e *gfP) Marshal(out []byte) {
+ for w := uint(0); w < 4; w++ {
+ for b := uint(0); b < 8; b++ {
+ out[8*w+b] = byte(e[3-w] >> (56 - 8*b))
+ }
+ }
+}
+
+func (e *gfP) Unmarshal(in []byte) error {
+ // Unmarshal the bytes into little endian form
+ for w := uint(0); w < 4; w++ {
+ for b := uint(0); b < 8; b++ {
+ e[3-w] += uint64(in[8*w+b]) << (56 - 8*b)
+ }
+ }
+ // Ensure the point respects the curve modulus
+ for i := 3; i >= 0; i-- {
+ if e[i] < p2[i] {
+ return nil
+ }
+ if e[i] > p2[i] {
+ return errors.New("bn256: coordinate exceeds modulus")
+ }
+ }
+ return errors.New("bn256: coordinate equals modulus")
+}
+
+func montEncode(c, a *gfP) { gfpMul(c, a, r2) }
+func montDecode(c, a *gfP) { gfpMul(c, a, &gfP{1}) }
diff --git a/common/crypto/bn256/cloudflare/gfp12.go b/common/crypto/bn256/cloudflare/gfp12.go
new file mode 100644
index 0000000..9e8a5cd
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp12.go
@@ -0,0 +1,176 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+import (
+ "math/big"
+)
+
+// gfP12 implements the field of size p¹² as a quadratic extension of gfP6
+// where ω²=τ.
+type gfP12 struct {
+ x, y gfP6 // value is xω + y
+}
+
+func (e *gfP12) String() string {
+ return "(" + e.x.String() + "," + e.y.String() + ")"
+}
+
+func (e *gfP12) Set(a *gfP12) *gfP12 {
+ e.x.Set(&a.x)
+ e.y.Set(&a.y)
+ return e
+}
+
+func (e *gfP12) SetZero() *gfP12 {
+ e.x.SetZero()
+ e.y.SetZero()
+ return e
+}
+
+func (e *gfP12) SetOne() *gfP12 {
+ e.x.SetZero()
+ e.y.SetOne()
+ return e
+}
+
+func (e *gfP12) IsZero() bool {
+ return e.x.IsZero() && e.y.IsZero()
+}
+
+func (e *gfP12) IsOne() bool {
+ return e.x.IsZero() && e.y.IsOne()
+}
+
+func (e *gfP12) Conjugate(a *gfP12) *gfP12 {
+ e.x.Neg(&a.x)
+ e.y.Set(&a.y)
+ return e
+}
+
+func (e *gfP12) Neg(a *gfP12) *gfP12 {
+ e.x.Neg(&a.x)
+ e.y.Neg(&a.y)
+ return e
+}
+
+// Frobenius computes (xω+y)^p = x^p ω·ξ^((p-1)/6) + y^p
+func (e *gfP12) Frobenius(a *gfP12) *gfP12 {
+ e.x.Frobenius(&a.x)
+ e.y.Frobenius(&a.y)
+ e.x.MulScalar(&e.x, xiToPMinus1Over6)
+ return e
+}
+
+// FrobeniusP2 computes (xω+y)^p² = x^p² ω·ξ^((p²-1)/6) + y^p²
+func (e *gfP12) FrobeniusP2(a *gfP12) *gfP12 {
+ e.x.FrobeniusP2(&a.x)
+ e.x.MulGFP(&e.x, xiToPSquaredMinus1Over6)
+ e.y.FrobeniusP2(&a.y)
+ return e
+}
+
+func (e *gfP12) FrobeniusP4(a *gfP12) *gfP12 {
+ e.x.FrobeniusP4(&a.x)
+ e.x.MulGFP(&e.x, xiToPSquaredMinus1Over3)
+ e.y.FrobeniusP4(&a.y)
+ return e
+}
+
+func (e *gfP12) Add(a, b *gfP12) *gfP12 {
+ e.x.Add(&a.x, &b.x)
+ e.y.Add(&a.y, &b.y)
+ return e
+}
+
+func (e *gfP12) Sub(a, b *gfP12) *gfP12 {
+ e.x.Sub(&a.x, &b.x)
+ e.y.Sub(&a.y, &b.y)
+ return e
+}
+
+func (e *gfP12) Mul(a, b *gfP12) *gfP12 {
+ tx := (&gfP6{}).Mul(&a.x, &b.y)
+ t := (&gfP6{}).Mul(&b.x, &a.y)
+ tx.Add(tx, t)
+
+ ty := (&gfP6{}).Mul(&a.y, &b.y)
+ t.Mul(&a.x, &b.x).MulTau(t)
+
+ e.x.Set(tx)
+ e.y.Add(ty, t)
+ return e
+}
+
+func (e *gfP12) MulScalar(a *gfP12, b *gfP6) *gfP12 {
+ e.x.Mul(&e.x, b)
+ e.y.Mul(&e.y, b)
+ return e
+}
+
+func (c *gfP12) Exp(a *gfP12, power *big.Int) *gfP12 {
+ sum := (&gfP12{}).SetOne()
+ t := &gfP12{}
+
+ for i := power.BitLen() - 1; i >= 0; i-- {
+ t.Square(sum)
+ if power.Bit(i) != 0 {
+ sum.Mul(t, a)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+ return c
+}
+
+func (e *gfP12) Square(a *gfP12) *gfP12 {
+ // Complex squaring algorithm
+ v0 := (&gfP6{}).Mul(&a.x, &a.y)
+
+ t := (&gfP6{}).MulTau(&a.x)
+ t.Add(&a.y, t)
+ ty := (&gfP6{}).Add(&a.x, &a.y)
+ ty.Mul(ty, t).Sub(ty, v0)
+ t.MulTau(v0)
+ ty.Sub(ty, t)
+
+ e.x.Add(v0, v0)
+ e.y.Set(ty)
+ return e
+}
+
+func (e *gfP12) Invert(a *gfP12) *gfP12 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+ t1, t2 := &gfP6{}, &gfP6{}
+
+ t1.Square(&a.x)
+ t2.Square(&a.y)
+ t1.MulTau(t1).Sub(t2, t1)
+ t2.Invert(t1)
+
+ e.x.Neg(&a.x)
+ e.y.Set(&a.y)
+ e.MulScalar(e, t2)
+ return e
+}
diff --git a/common/crypto/bn256/cloudflare/gfp2.go b/common/crypto/bn256/cloudflare/gfp2.go
new file mode 100644
index 0000000..3294979
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp2.go
@@ -0,0 +1,172 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+// gfP2 implements a field of size p² as a quadratic extension of the base field
+// where i²=-1.
+type gfP2 struct {
+ x, y gfP // value is xi+y.
+}
+
+func gfP2Decode(in *gfP2) *gfP2 {
+ out := &gfP2{}
+ montDecode(&out.x, &in.x)
+ montDecode(&out.y, &in.y)
+ return out
+}
+
+func (e *gfP2) String() string {
+ return "(" + e.x.String() + ", " + e.y.String() + ")"
+}
+
+func (e *gfP2) Set(a *gfP2) *gfP2 {
+ e.x.Set(&a.x)
+ e.y.Set(&a.y)
+ return e
+}
+
+func (e *gfP2) SetZero() *gfP2 {
+ e.x = gfP{0}
+ e.y = gfP{0}
+ return e
+}
+
+func (e *gfP2) SetOne() *gfP2 {
+ e.x = gfP{0}
+ e.y = *newGFp(1)
+ return e
+}
+
+func (e *gfP2) IsZero() bool {
+ zero := gfP{0}
+ return e.x == zero && e.y == zero
+}
+
+func (e *gfP2) IsOne() bool {
+ zero, one := gfP{0}, *newGFp(1)
+ return e.x == zero && e.y == one
+}
+
+func (e *gfP2) Conjugate(a *gfP2) *gfP2 {
+ e.y.Set(&a.y)
+ gfpNeg(&e.x, &a.x)
+ return e
+}
+
+func (e *gfP2) Neg(a *gfP2) *gfP2 {
+ gfpNeg(&e.x, &a.x)
+ gfpNeg(&e.y, &a.y)
+ return e
+}
+
+func (e *gfP2) Add(a, b *gfP2) *gfP2 {
+ gfpAdd(&e.x, &a.x, &b.x)
+ gfpAdd(&e.y, &a.y, &b.y)
+ return e
+}
+
+func (e *gfP2) Sub(a, b *gfP2) *gfP2 {
+ gfpSub(&e.x, &a.x, &b.x)
+ gfpSub(&e.y, &a.y, &b.y)
+ return e
+}
+
+// See "Multiplication and Squaring in Pairing-Friendly Fields",
+// http://eprint.iacr.org/2006/471.pdf
+func (e *gfP2) Mul(a, b *gfP2) *gfP2 {
+ tx, t := &gfP{}, &gfP{}
+ gfpMul(tx, &a.x, &b.y)
+ gfpMul(t, &b.x, &a.y)
+ gfpAdd(tx, tx, t)
+
+ ty := &gfP{}
+ gfpMul(ty, &a.y, &b.y)
+ gfpMul(t, &a.x, &b.x)
+ gfpSub(ty, ty, t)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+ return e
+}
+
+func (e *gfP2) MulScalar(a *gfP2, b *gfP) *gfP2 {
+ gfpMul(&e.x, &a.x, b)
+ gfpMul(&e.y, &a.y, b)
+ return e
+}
+
+// MulXi sets e=ξa where ξ=i+9 and then returns e.
+func (e *gfP2) MulXi(a *gfP2) *gfP2 {
+ // (xi+y)(i+9) = (9x+y)i+(9y-x)
+ tx := &gfP{}
+ gfpAdd(tx, &a.x, &a.x)
+ gfpAdd(tx, tx, tx)
+ gfpAdd(tx, tx, tx)
+ gfpAdd(tx, tx, &a.x)
+
+ gfpAdd(tx, tx, &a.y)
+
+ ty := &gfP{}
+ gfpAdd(ty, &a.y, &a.y)
+ gfpAdd(ty, ty, ty)
+ gfpAdd(ty, ty, ty)
+ gfpAdd(ty, ty, &a.y)
+
+ gfpSub(ty, ty, &a.x)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+ return e
+}
+
+func (e *gfP2) Square(a *gfP2) *gfP2 {
+ // Complex squaring algorithm:
+ // (xi+y)² = (x+y)(y-x) + 2*i*x*y
+ tx, ty := &gfP{}, &gfP{}
+ gfpSub(tx, &a.y, &a.x)
+ gfpAdd(ty, &a.x, &a.y)
+ gfpMul(ty, tx, ty)
+
+ gfpMul(tx, &a.x, &a.y)
+ gfpAdd(tx, tx, tx)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+ return e
+}
+
+func (e *gfP2) Invert(a *gfP2) *gfP2 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+ t1, t2 := &gfP{}, &gfP{}
+ gfpMul(t1, &a.x, &a.x)
+ gfpMul(t2, &a.y, &a.y)
+ gfpAdd(t1, t1, t2)
+
+ inv := &gfP{}
+ inv.Invert(t1)
+
+ gfpNeg(t1, &a.x)
+
+ gfpMul(&e.x, t1, inv)
+ gfpMul(&e.y, &a.y, inv)
+ return e
+}
diff --git a/common/crypto/bn256/cloudflare/gfp6.go b/common/crypto/bn256/cloudflare/gfp6.go
new file mode 100644
index 0000000..8cbbcd3
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp6.go
@@ -0,0 +1,229 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+// gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ
+// and ξ=i+9.
+type gfP6 struct {
+ x, y, z gfP2 // value is xτ² + yτ + z
+}
+
+func (e *gfP6) String() string {
+ return "(" + e.x.String() + ", " + e.y.String() + ", " + e.z.String() + ")"
+}
+
+func (e *gfP6) Set(a *gfP6) *gfP6 {
+ e.x.Set(&a.x)
+ e.y.Set(&a.y)
+ e.z.Set(&a.z)
+ return e
+}
+
+func (e *gfP6) SetZero() *gfP6 {
+ e.x.SetZero()
+ e.y.SetZero()
+ e.z.SetZero()
+ return e
+}
+
+func (e *gfP6) SetOne() *gfP6 {
+ e.x.SetZero()
+ e.y.SetZero()
+ e.z.SetOne()
+ return e
+}
+
+func (e *gfP6) IsZero() bool {
+ return e.x.IsZero() && e.y.IsZero() && e.z.IsZero()
+}
+
+func (e *gfP6) IsOne() bool {
+ return e.x.IsZero() && e.y.IsZero() && e.z.IsOne()
+}
+
+func (e *gfP6) Neg(a *gfP6) *gfP6 {
+ e.x.Neg(&a.x)
+ e.y.Neg(&a.y)
+ e.z.Neg(&a.z)
+ return e
+}
+
+func (e *gfP6) Frobenius(a *gfP6) *gfP6 {
+ e.x.Conjugate(&a.x)
+ e.y.Conjugate(&a.y)
+ e.z.Conjugate(&a.z)
+
+ e.x.Mul(&e.x, xiTo2PMinus2Over3)
+ e.y.Mul(&e.y, xiToPMinus1Over3)
+ return e
+}
+
+// FrobeniusP2 computes (xτ²+yτ+z)^(p²) = xτ^(2p²) + yτ^(p²) + z
+func (e *gfP6) FrobeniusP2(a *gfP6) *gfP6 {
+ // τ^(2p²) = τ²τ^(2p²-2) = τ²ξ^((2p²-2)/3)
+ e.x.MulScalar(&a.x, xiTo2PSquaredMinus2Over3)
+ // τ^(p²) = ττ^(p²-1) = τξ^((p²-1)/3)
+ e.y.MulScalar(&a.y, xiToPSquaredMinus1Over3)
+ e.z.Set(&a.z)
+ return e
+}
+
+func (e *gfP6) FrobeniusP4(a *gfP6) *gfP6 {
+ e.x.MulScalar(&a.x, xiToPSquaredMinus1Over3)
+ e.y.MulScalar(&a.y, xiTo2PSquaredMinus2Over3)
+ e.z.Set(&a.z)
+ return e
+}
+
+func (e *gfP6) Add(a, b *gfP6) *gfP6 {
+ e.x.Add(&a.x, &b.x)
+ e.y.Add(&a.y, &b.y)
+ e.z.Add(&a.z, &b.z)
+ return e
+}
+
+func (e *gfP6) Sub(a, b *gfP6) *gfP6 {
+ e.x.Sub(&a.x, &b.x)
+ e.y.Sub(&a.y, &b.y)
+ e.z.Sub(&a.z, &b.z)
+ return e
+}
+
+func (e *gfP6) Mul(a, b *gfP6) *gfP6 {
+ // "Multiplication and Squaring on Pairing-Friendly Fields"
+ // Section 4, Karatsuba method.
+ // http://eprint.iacr.org/2006/471.pdf
+ v0 := (&gfP2{}).Mul(&a.z, &b.z)
+ v1 := (&gfP2{}).Mul(&a.y, &b.y)
+ v2 := (&gfP2{}).Mul(&a.x, &b.x)
+
+ t0 := (&gfP2{}).Add(&a.x, &a.y)
+ t1 := (&gfP2{}).Add(&b.x, &b.y)
+ tz := (&gfP2{}).Mul(t0, t1)
+ tz.Sub(tz, v1).Sub(tz, v2).MulXi(tz).Add(tz, v0)
+
+ t0.Add(&a.y, &a.z)
+ t1.Add(&b.y, &b.z)
+ ty := (&gfP2{}).Mul(t0, t1)
+ t0.MulXi(v2)
+ ty.Sub(ty, v0).Sub(ty, v1).Add(ty, t0)
+
+ t0.Add(&a.x, &a.z)
+ t1.Add(&b.x, &b.z)
+ tx := (&gfP2{}).Mul(t0, t1)
+ tx.Sub(tx, v0).Add(tx, v1).Sub(tx, v2)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+ e.z.Set(tz)
+ return e
+}
+
+func (e *gfP6) MulScalar(a *gfP6, b *gfP2) *gfP6 {
+ e.x.Mul(&a.x, b)
+ e.y.Mul(&a.y, b)
+ e.z.Mul(&a.z, b)
+ return e
+}
+
+func (e *gfP6) MulGFP(a *gfP6, b *gfP) *gfP6 {
+ e.x.MulScalar(&a.x, b)
+ e.y.MulScalar(&a.y, b)
+ e.z.MulScalar(&a.z, b)
+ return e
+}
+
+// MulTau computes τ·(aτ²+bτ+c) = bτ²+cτ+aξ
+func (e *gfP6) MulTau(a *gfP6) *gfP6 {
+ tz := (&gfP2{}).MulXi(&a.x)
+ ty := (&gfP2{}).Set(&a.y)
+
+ e.y.Set(&a.z)
+ e.x.Set(ty)
+ e.z.Set(tz)
+ return e
+}
+
+func (e *gfP6) Square(a *gfP6) *gfP6 {
+ v0 := (&gfP2{}).Square(&a.z)
+ v1 := (&gfP2{}).Square(&a.y)
+ v2 := (&gfP2{}).Square(&a.x)
+
+ c0 := (&gfP2{}).Add(&a.x, &a.y)
+ c0.Square(c0).Sub(c0, v1).Sub(c0, v2).MulXi(c0).Add(c0, v0)
+
+ c1 := (&gfP2{}).Add(&a.y, &a.z)
+ c1.Square(c1).Sub(c1, v0).Sub(c1, v1)
+ xiV2 := (&gfP2{}).MulXi(v2)
+ c1.Add(c1, xiV2)
+
+ c2 := (&gfP2{}).Add(&a.x, &a.z)
+ c2.Square(c2).Sub(c2, v0).Add(c2, v1).Sub(c2, v2)
+
+ e.x.Set(c2)
+ e.y.Set(c1)
+ e.z.Set(c0)
+ return e
+}
+
+func (e *gfP6) Invert(a *gfP6) *gfP6 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+
+ // Here we can give a short explanation of how it works: let j be a cubic root of
+ // unity in GF(p²) so that 1+j+j²=0.
+ // Then (xτ² + yτ + z)(xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
+ // = (xτ² + yτ + z)(Cτ²+Bτ+A)
+ // = (x³ξ²+y³ξ+z³-3ξxyz) = F is an element of the base field (the norm).
+ //
+ // On the other hand (xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
+ // = τ²(y²-ξxz) + τ(ξx²-yz) + (z²-ξxy)
+ //
+ // So that's why A = (z²-ξxy), B = (ξx²-yz), C = (y²-ξxz)
+ t1 := (&gfP2{}).Mul(&a.x, &a.y)
+ t1.MulXi(t1)
+
+ A := (&gfP2{}).Square(&a.z)
+ A.Sub(A, t1)
+
+ B := (&gfP2{}).Square(&a.x)
+ B.MulXi(B)
+ t1.Mul(&a.y, &a.z)
+ B.Sub(B, t1)
+
+ C := (&gfP2{}).Square(&a.y)
+ t1.Mul(&a.x, &a.z)
+ C.Sub(C, t1)
+
+ F := (&gfP2{}).Mul(C, &a.y)
+ F.MulXi(F)
+ t1.Mul(A, &a.z)
+ F.Add(F, t1)
+ t1.Mul(B, &a.x).MulXi(t1)
+ F.Add(F, t1)
+
+ F.Invert(F)
+
+ e.x.Mul(C, F)
+ e.y.Mul(B, F)
+ e.z.Mul(A, F)
+ return e
+}
diff --git a/common/crypto/bn256/cloudflare/gfp_amd64.s b/common/crypto/bn256/cloudflare/gfp_amd64.s
new file mode 100644
index 0000000..bdb4ffb
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp_amd64.s
@@ -0,0 +1,129 @@
+// +build amd64,!generic
+
+#define storeBlock(a0,a1,a2,a3, r) \
+ MOVQ a0, 0+r \
+ MOVQ a1, 8+r \
+ MOVQ a2, 16+r \
+ MOVQ a3, 24+r
+
+#define loadBlock(r, a0,a1,a2,a3) \
+ MOVQ 0+r, a0 \
+ MOVQ 8+r, a1 \
+ MOVQ 16+r, a2 \
+ MOVQ 24+r, a3
+
+#define gfpCarry(a0,a1,a2,a3,a4, b0,b1,b2,b3,b4) \
+ \ // b = a-p
+ MOVQ a0, b0 \
+ MOVQ a1, b1 \
+ MOVQ a2, b2 \
+ MOVQ a3, b3 \
+ MOVQ a4, b4 \
+ \
+ SUBQ ·p2+0(SB), b0 \
+ SBBQ ·p2+8(SB), b1 \
+ SBBQ ·p2+16(SB), b2 \
+ SBBQ ·p2+24(SB), b3 \
+ SBBQ $0, b4 \
+ \
+ \ // if b is negative then return a
+ \ // else return b
+ CMOVQCC b0, a0 \
+ CMOVQCC b1, a1 \
+ CMOVQCC b2, a2 \
+ CMOVQCC b3, a3
+
+#include "mul_amd64.h"
+#include "mul_bmi2_amd64.h"
+
+TEXT ·gfpNeg(SB),0,$0-16
+ MOVQ ·p2+0(SB), R8
+ MOVQ ·p2+8(SB), R9
+ MOVQ ·p2+16(SB), R10
+ MOVQ ·p2+24(SB), R11
+
+ MOVQ a+8(FP), DI
+ SUBQ 0(DI), R8
+ SBBQ 8(DI), R9
+ SBBQ 16(DI), R10
+ SBBQ 24(DI), R11
+
+ MOVQ $0, AX
+ gfpCarry(R8,R9,R10,R11,AX, R12,R13,R14,R15,BX)
+
+ MOVQ c+0(FP), DI
+ storeBlock(R8,R9,R10,R11, 0(DI))
+ RET
+
+TEXT ·gfpAdd(SB),0,$0-24
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+
+ loadBlock(0(DI), R8,R9,R10,R11)
+ MOVQ $0, R12
+
+ ADDQ 0(SI), R8
+ ADCQ 8(SI), R9
+ ADCQ 16(SI), R10
+ ADCQ 24(SI), R11
+ ADCQ $0, R12
+
+ gfpCarry(R8,R9,R10,R11,R12, R13,R14,R15,AX,BX)
+
+ MOVQ c+0(FP), DI
+ storeBlock(R8,R9,R10,R11, 0(DI))
+ RET
+
+TEXT ·gfpSub(SB),0,$0-24
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+
+ loadBlock(0(DI), R8,R9,R10,R11)
+
+ MOVQ ·p2+0(SB), R12
+ MOVQ ·p2+8(SB), R13
+ MOVQ ·p2+16(SB), R14
+ MOVQ ·p2+24(SB), R15
+ MOVQ $0, AX
+
+ SUBQ 0(SI), R8
+ SBBQ 8(SI), R9
+ SBBQ 16(SI), R10
+ SBBQ 24(SI), R11
+
+ CMOVQCC AX, R12
+ CMOVQCC AX, R13
+ CMOVQCC AX, R14
+ CMOVQCC AX, R15
+
+ ADDQ R12, R8
+ ADCQ R13, R9
+ ADCQ R14, R10
+ ADCQ R15, R11
+
+ MOVQ c+0(FP), DI
+ storeBlock(R8,R9,R10,R11, 0(DI))
+ RET
+
+TEXT ·gfpMul(SB),0,$160-24
+ MOVQ a+8(FP), DI
+ MOVQ b+16(FP), SI
+
+ // Jump to a slightly different implementation if MULX isn't supported.
+ CMPB ·hasBMI2(SB), $0
+ JE nobmi2Mul
+
+ mulBMI2(0(DI),8(DI),16(DI),24(DI), 0(SI))
+ storeBlock( R8, R9,R10,R11, 0(SP))
+ storeBlock(R12,R13,R14,R15, 32(SP))
+ gfpReduceBMI2()
+ JMP end
+
+nobmi2Mul:
+ mul(0(DI),8(DI),16(DI),24(DI), 0(SI), 0(SP))
+ gfpReduce(0(SP))
+
+end:
+ MOVQ c+0(FP), DI
+ storeBlock(R12,R13,R14,R15, 0(DI))
+ RET
diff --git a/common/crypto/bn256/cloudflare/gfp_arm64.s b/common/crypto/bn256/cloudflare/gfp_arm64.s
new file mode 100644
index 0000000..c65e801
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp_arm64.s
@@ -0,0 +1,113 @@
+// +build arm64,!generic
+
+#define storeBlock(a0,a1,a2,a3, r) \
+ MOVD a0, 0+r \
+ MOVD a1, 8+r \
+ MOVD a2, 16+r \
+ MOVD a3, 24+r
+
+#define loadBlock(r, a0,a1,a2,a3) \
+ MOVD 0+r, a0 \
+ MOVD 8+r, a1 \
+ MOVD 16+r, a2 \
+ MOVD 24+r, a3
+
+#define loadModulus(p0,p1,p2,p3) \
+ MOVD ·p2+0(SB), p0 \
+ MOVD ·p2+8(SB), p1 \
+ MOVD ·p2+16(SB), p2 \
+ MOVD ·p2+24(SB), p3
+
+#include "mul_arm64.h"
+
+TEXT ·gfpNeg(SB),0,$0-16
+ MOVD a+8(FP), R0
+ loadBlock(0(R0), R1,R2,R3,R4)
+ loadModulus(R5,R6,R7,R8)
+
+ SUBS R1, R5, R1
+ SBCS R2, R6, R2
+ SBCS R3, R7, R3
+ SBCS R4, R8, R4
+
+ SUBS R5, R1, R5
+ SBCS R6, R2, R6
+ SBCS R7, R3, R7
+ SBCS R8, R4, R8
+
+ CSEL CS, R5, R1, R1
+ CSEL CS, R6, R2, R2
+ CSEL CS, R7, R3, R3
+ CSEL CS, R8, R4, R4
+
+ MOVD c+0(FP), R0
+ storeBlock(R1,R2,R3,R4, 0(R0))
+ RET
+
+TEXT ·gfpAdd(SB),0,$0-24
+ MOVD a+8(FP), R0
+ loadBlock(0(R0), R1,R2,R3,R4)
+ MOVD b+16(FP), R0
+ loadBlock(0(R0), R5,R6,R7,R8)
+ loadModulus(R9,R10,R11,R12)
+ MOVD ZR, R0
+
+ ADDS R5, R1
+ ADCS R6, R2
+ ADCS R7, R3
+ ADCS R8, R4
+ ADCS ZR, R0
+
+ SUBS R9, R1, R5
+ SBCS R10, R2, R6
+ SBCS R11, R3, R7
+ SBCS R12, R4, R8
+ SBCS ZR, R0, R0
+
+ CSEL CS, R5, R1, R1
+ CSEL CS, R6, R2, R2
+ CSEL CS, R7, R3, R3
+ CSEL CS, R8, R4, R4
+
+ MOVD c+0(FP), R0
+ storeBlock(R1,R2,R3,R4, 0(R0))
+ RET
+
+TEXT ·gfpSub(SB),0,$0-24
+ MOVD a+8(FP), R0
+ loadBlock(0(R0), R1,R2,R3,R4)
+ MOVD b+16(FP), R0
+ loadBlock(0(R0), R5,R6,R7,R8)
+ loadModulus(R9,R10,R11,R12)
+
+ SUBS R5, R1
+ SBCS R6, R2
+ SBCS R7, R3
+ SBCS R8, R4
+
+ CSEL CS, ZR, R9, R9
+ CSEL CS, ZR, R10, R10
+ CSEL CS, ZR, R11, R11
+ CSEL CS, ZR, R12, R12
+
+ ADDS R9, R1
+ ADCS R10, R2
+ ADCS R11, R3
+ ADCS R12, R4
+
+ MOVD c+0(FP), R0
+ storeBlock(R1,R2,R3,R4, 0(R0))
+ RET
+
+TEXT ·gfpMul(SB),0,$0-24
+ MOVD a+8(FP), R0
+ loadBlock(0(R0), R1,R2,R3,R4)
+ MOVD b+16(FP), R0
+ loadBlock(0(R0), R5,R6,R7,R8)
+
+ mul(R9,R10,R11,R12,R13,R14,R15,R16)
+ gfpReduce()
+
+ MOVD c+0(FP), R0
+ storeBlock(R1,R2,R3,R4, 0(R0))
+ RET
diff --git a/common/crypto/bn256/cloudflare/gfp_decl.go b/common/crypto/bn256/cloudflare/gfp_decl.go
new file mode 100644
index 0000000..072e32b
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp_decl.go
@@ -0,0 +1,24 @@
+//go:build (amd64 && !generic) || (arm64 && !generic)
+
+package bn256
+
+// This file contains forward declarations for the architecture-specific
+// assembly implementations of these functions, provided that they exist.
+
+import (
+ "golang.org/x/sys/cpu"
+)
+
+var hasBMI2 = cpu.X86.HasBMI2
+
+// go:noescape
+func gfpNeg(c, a *gfP)
+
+//go:noescape
+func gfpAdd(c, a, b *gfP)
+
+//go:noescape
+func gfpSub(c, a, b *gfP)
+
+//go:noescape
+func gfpMul(c, a, b *gfP)
diff --git a/common/crypto/bn256/cloudflare/gfp_generic.go b/common/crypto/bn256/cloudflare/gfp_generic.go
new file mode 100644
index 0000000..944208c
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp_generic.go
@@ -0,0 +1,173 @@
+//go:build (!amd64 && !arm64) || generic
+
+package bn256
+
+func gfpCarry(a *gfP, head uint64) {
+ b := &gfP{}
+
+ var carry uint64
+ for i, pi := range p2 {
+ ai := a[i]
+ bi := ai - pi - carry
+ b[i] = bi
+ carry = (pi&^ai | (pi|^ai)&bi) >> 63
+ }
+ carry = carry &^ head
+
+ // If b is negative, then return a.
+ // Else return b.
+ carry = -carry
+ ncarry := ^carry
+ for i := 0; i < 4; i++ {
+ a[i] = (a[i] & carry) | (b[i] & ncarry)
+ }
+}
+
+func gfpNeg(c, a *gfP) {
+ var carry uint64
+ for i, pi := range p2 {
+ ai := a[i]
+ ci := pi - ai - carry
+ c[i] = ci
+ carry = (ai&^pi | (ai|^pi)&ci) >> 63
+ }
+ gfpCarry(c, 0)
+}
+
+func gfpAdd(c, a, b *gfP) {
+ var carry uint64
+ for i, ai := range a {
+ bi := b[i]
+ ci := ai + bi + carry
+ c[i] = ci
+ carry = (ai&bi | (ai|bi)&^ci) >> 63
+ }
+ gfpCarry(c, carry)
+}
+
+func gfpSub(c, a, b *gfP) {
+ t := &gfP{}
+
+ var carry uint64
+ for i, pi := range p2 {
+ bi := b[i]
+ ti := pi - bi - carry
+ t[i] = ti
+ carry = (bi&^pi | (bi|^pi)&ti) >> 63
+ }
+
+ carry = 0
+ for i, ai := range a {
+ ti := t[i]
+ ci := ai + ti + carry
+ c[i] = ci
+ carry = (ai&ti | (ai|ti)&^ci) >> 63
+ }
+ gfpCarry(c, carry)
+}
+
+func mul(a, b [4]uint64) [8]uint64 {
+ const (
+ mask16 uint64 = 0x0000ffff
+ mask32 uint64 = 0xffffffff
+ )
+
+ var buff [32]uint64
+ for i, ai := range a {
+ a0, a1, a2, a3 := ai&mask16, (ai>>16)&mask16, (ai>>32)&mask16, ai>>48
+
+ for j, bj := range b {
+ b0, b2 := bj&mask32, bj>>32
+
+ off := 4 * (i + j)
+ buff[off+0] += a0 * b0
+ buff[off+1] += a1 * b0
+ buff[off+2] += a2*b0 + a0*b2
+ buff[off+3] += a3*b0 + a1*b2
+ buff[off+4] += a2 * b2
+ buff[off+5] += a3 * b2
+ }
+ }
+
+ for i := uint(1); i < 4; i++ {
+ shift := 16 * i
+
+ var head, carry uint64
+ for j := uint(0); j < 8; j++ {
+ block := 4 * j
+
+ xi := buff[block]
+ yi := (buff[block+i] << shift) + head
+ zi := xi + yi + carry
+ buff[block] = zi
+ carry = (xi&yi | (xi|yi)&^zi) >> 63
+
+ head = buff[block+i] >> (64 - shift)
+ }
+ }
+
+ return [8]uint64{buff[0], buff[4], buff[8], buff[12], buff[16], buff[20], buff[24], buff[28]}
+}
+
+func halfMul(a, b [4]uint64) [4]uint64 {
+ const (
+ mask16 uint64 = 0x0000ffff
+ mask32 uint64 = 0xffffffff
+ )
+
+ var buff [18]uint64
+ for i, ai := range a {
+ a0, a1, a2, a3 := ai&mask16, (ai>>16)&mask16, (ai>>32)&mask16, ai>>48
+
+ for j, bj := range b {
+ if i+j > 3 {
+ break
+ }
+ b0, b2 := bj&mask32, bj>>32
+
+ off := 4 * (i + j)
+ buff[off+0] += a0 * b0
+ buff[off+1] += a1 * b0
+ buff[off+2] += a2*b0 + a0*b2
+ buff[off+3] += a3*b0 + a1*b2
+ buff[off+4] += a2 * b2
+ buff[off+5] += a3 * b2
+ }
+ }
+
+ for i := uint(1); i < 4; i++ {
+ shift := 16 * i
+
+ var head, carry uint64
+ for j := uint(0); j < 4; j++ {
+ block := 4 * j
+
+ xi := buff[block]
+ yi := (buff[block+i] << shift) + head
+ zi := xi + yi + carry
+ buff[block] = zi
+ carry = (xi&yi | (xi|yi)&^zi) >> 63
+
+ head = buff[block+i] >> (64 - shift)
+ }
+ }
+
+ return [4]uint64{buff[0], buff[4], buff[8], buff[12]}
+}
+
+func gfpMul(c, a, b *gfP) {
+ T := mul(*a, *b)
+ m := halfMul([4]uint64{T[0], T[1], T[2], T[3]}, np)
+ t := mul([4]uint64{m[0], m[1], m[2], m[3]}, p2)
+
+ var carry uint64
+ for i, Ti := range T {
+ ti := t[i]
+ zi := Ti + ti + carry
+ T[i] = zi
+ carry = (Ti&ti | (Ti|ti)&^zi) >> 63
+ }
+
+ *c = gfP{T[4], T[5], T[6], T[7]}
+ gfpCarry(c, carry)
+}
diff --git a/common/crypto/bn256/cloudflare/gfp_test.go b/common/crypto/bn256/cloudflare/gfp_test.go
new file mode 100644
index 0000000..16ab2a8
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/gfp_test.go
@@ -0,0 +1,60 @@
+package bn256
+
+import (
+ "testing"
+)
+
+// Tests that negation works the same way on both assembly-optimized and pure Go
+// implementation.
+func TestGFpNeg(t *testing.T) {
+ n := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
+ w := &gfP{0xfedcba9876543211, 0x0123456789abcdef, 0x2152411021524110, 0x0114251201142512}
+ h := &gfP{}
+
+ gfpNeg(h, n)
+ if *h != *w {
+ t.Errorf("negation mismatch: have %#x, want %#x", *h, *w)
+ }
+}
+
+// Tests that addition works the same way on both assembly-optimized and pure Go
+// implementation.
+func TestGFpAdd(t *testing.T) {
+ a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
+ b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
+ w := &gfP{0xc3df73e9278302b8, 0x687e956e978e3572, 0x254954275c18417f, 0xad354b6afc67f9b4}
+ h := &gfP{}
+
+ gfpAdd(h, a, b)
+ if *h != *w {
+ t.Errorf("addition mismatch: have %#x, want %#x", *h, *w)
+ }
+}
+
+// Tests that subtraction works the same way on both assembly-optimized and pure Go
+// implementation.
+func TestGFpSub(t *testing.T) {
+ a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
+ b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
+ w := &gfP{0x02468acf13579bdf, 0xfdb97530eca86420, 0xdfc1e401dfc1e402, 0x203e1bfe203e1bfd}
+ h := &gfP{}
+
+ gfpSub(h, a, b)
+ if *h != *w {
+ t.Errorf("subtraction mismatch: have %#x, want %#x", *h, *w)
+ }
+}
+
+// Tests that multiplication works the same way on both assembly-optimized and pure Go
+// implementation.
+func TestGFpMul(t *testing.T) {
+ a := &gfP{0x0123456789abcdef, 0xfedcba9876543210, 0xdeadbeefdeadbeef, 0xfeebdaedfeebdaed}
+ b := &gfP{0xfedcba9876543210, 0x0123456789abcdef, 0xfeebdaedfeebdaed, 0xdeadbeefdeadbeef}
+ w := &gfP{0xcbcbd377f7ad22d3, 0x3b89ba5d849379bf, 0x87b61627bd38b6d2, 0xc44052a2a0e654b2}
+ h := &gfP{}
+
+ gfpMul(h, a, b)
+ if *h != *w {
+ t.Errorf("multiplication mismatch: have %#x, want %#x", *h, *w)
+ }
+}
diff --git a/common/crypto/bn256/cloudflare/lattice.go b/common/crypto/bn256/cloudflare/lattice.go
new file mode 100644
index 0000000..f9ace4d
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/lattice.go
@@ -0,0 +1,115 @@
+package bn256
+
+import (
+ "math/big"
+)
+
+var half = new(big.Int).Rsh(Order, 1)
+
+var curveLattice = &lattice{
+ vectors: [][]*big.Int{
+ {bigFromBase10("147946756881789319000765030803803410728"), bigFromBase10("147946756881789319010696353538189108491")},
+ {bigFromBase10("147946756881789319020627676272574806254"), bigFromBase10("-147946756881789318990833708069417712965")},
+ },
+ inverse: []*big.Int{
+ bigFromBase10("147946756881789318990833708069417712965"),
+ bigFromBase10("147946756881789319010696353538189108491"),
+ },
+ det: bigFromBase10("43776485743678550444492811490514550177096728800832068687396408373151616991234"),
+}
+
+var targetLattice = &lattice{
+ vectors: [][]*big.Int{
+ {bigFromBase10("9931322734385697761"), bigFromBase10("9931322734385697761"), bigFromBase10("9931322734385697763"), bigFromBase10("9931322734385697764")},
+ {bigFromBase10("4965661367192848881"), bigFromBase10("4965661367192848881"), bigFromBase10("4965661367192848882"), bigFromBase10("-9931322734385697762")},
+ {bigFromBase10("-9931322734385697762"), bigFromBase10("-4965661367192848881"), bigFromBase10("4965661367192848881"), bigFromBase10("-4965661367192848882")},
+ {bigFromBase10("9931322734385697763"), bigFromBase10("-4965661367192848881"), bigFromBase10("-4965661367192848881"), bigFromBase10("-4965661367192848881")},
+ },
+ inverse: []*big.Int{
+ bigFromBase10("734653495049373973658254490726798021314063399421879442165"),
+ bigFromBase10("147946756881789319000765030803803410728"),
+ bigFromBase10("-147946756881789319005730692170996259609"),
+ bigFromBase10("1469306990098747947464455738335385361643788813749140841702"),
+ },
+ det: new(big.Int).Set(Order),
+}
+
+type lattice struct {
+ vectors [][]*big.Int
+ inverse []*big.Int
+ det *big.Int
+}
+
+// decompose takes a scalar mod Order as input and finds a short, positive decomposition of it wrt to the lattice basis.
+func (l *lattice) decompose(k *big.Int) []*big.Int {
+ n := len(l.inverse)
+
+ // Calculate closest vector in lattice to with Babai's rounding.
+ c := make([]*big.Int, n)
+ for i := 0; i < n; i++ {
+ c[i] = new(big.Int).Mul(k, l.inverse[i])
+ round(c[i], l.det)
+ }
+
+ // Transform vectors according to c and subtract .
+ out := make([]*big.Int, n)
+ temp := new(big.Int)
+
+ for i := 0; i < n; i++ {
+ out[i] = new(big.Int)
+
+ for j := 0; j < n; j++ {
+ temp.Mul(c[j], l.vectors[j][i])
+ out[i].Add(out[i], temp)
+ }
+
+ out[i].Neg(out[i])
+ out[i].Add(out[i], l.vectors[0][i]).Add(out[i], l.vectors[0][i])
+ }
+ out[0].Add(out[0], k)
+
+ return out
+}
+
+func (l *lattice) Precompute(add func(i, j uint)) {
+ n := uint(len(l.vectors))
+ total := uint(1) << n
+
+ for i := uint(0); i < n; i++ {
+ for j := uint(0); j < total; j++ {
+ if (j>>i)&1 == 1 {
+ add(i, j)
+ }
+ }
+ }
+}
+
+func (l *lattice) Multi(scalar *big.Int) []uint8 {
+ decomp := l.decompose(scalar)
+
+ maxLen := 0
+ for _, x := range decomp {
+ if x.BitLen() > maxLen {
+ maxLen = x.BitLen()
+ }
+ }
+
+ out := make([]uint8, maxLen)
+ for j, x := range decomp {
+ for i := 0; i < maxLen; i++ {
+ out[i] += uint8(x.Bit(i)) << uint(j)
+ }
+ }
+
+ return out
+}
+
+// round sets num to num/denom rounded to the nearest integer.
+func round(num, denom *big.Int) {
+ r := new(big.Int)
+ num.DivMod(num, denom, r)
+
+ if r.Cmp(half) == 1 {
+ num.Add(num, big.NewInt(1))
+ }
+}
diff --git a/common/crypto/bn256/cloudflare/lattice_test.go b/common/crypto/bn256/cloudflare/lattice_test.go
new file mode 100644
index 0000000..4d52ad9
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/lattice_test.go
@@ -0,0 +1,29 @@
+package bn256
+
+import (
+ "crypto/rand"
+
+ "testing"
+)
+
+func TestLatticeReduceCurve(t *testing.T) {
+ k, _ := rand.Int(rand.Reader, Order)
+ ks := curveLattice.decompose(k)
+
+ if ks[0].BitLen() > 130 || ks[1].BitLen() > 130 {
+ t.Fatal("reduction too large")
+ } else if ks[0].Sign() < 0 || ks[1].Sign() < 0 {
+ t.Fatal("reduction must be positive")
+ }
+}
+
+func TestLatticeReduceTarget(t *testing.T) {
+ k, _ := rand.Int(rand.Reader, Order)
+ ks := targetLattice.decompose(k)
+
+ if ks[0].BitLen() > 66 || ks[1].BitLen() > 66 || ks[2].BitLen() > 66 || ks[3].BitLen() > 66 {
+ t.Fatal("reduction too large")
+ } else if ks[0].Sign() < 0 || ks[1].Sign() < 0 || ks[2].Sign() < 0 || ks[3].Sign() < 0 {
+ t.Fatal("reduction must be positive")
+ }
+}
diff --git a/common/crypto/bn256/cloudflare/main_test.go b/common/crypto/bn256/cloudflare/main_test.go
new file mode 100644
index 0000000..f0610ed
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/main_test.go
@@ -0,0 +1,72 @@
+package bn256
+
+import (
+ "testing"
+
+ "crypto/rand"
+)
+
+func TestRandomG2Marshal(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ n, g2, err := RandomG2(rand.Reader)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ t.Logf("%v: %x\n", n, g2.Marshal())
+ }
+}
+
+func TestPairings(t *testing.T) {
+ _ = hasBMI2
+ a1 := new(G1).ScalarBaseMult(bigFromBase10("1"))
+ a2 := new(G1).ScalarBaseMult(bigFromBase10("2"))
+ a37 := new(G1).ScalarBaseMult(bigFromBase10("37"))
+ an1 := new(G1).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
+
+ b0 := new(G2).ScalarBaseMult(bigFromBase10("0"))
+ b1 := new(G2).ScalarBaseMult(bigFromBase10("1"))
+ b2 := new(G2).ScalarBaseMult(bigFromBase10("2"))
+ b27 := new(G2).ScalarBaseMult(bigFromBase10("27"))
+ b999 := new(G2).ScalarBaseMult(bigFromBase10("999"))
+ bn1 := new(G2).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
+
+ p1 := Pair(a1, b1)
+ pn1 := Pair(a1, bn1)
+ np1 := Pair(an1, b1)
+ if pn1.String() != np1.String() {
+ t.Error("Pairing mismatch: e(a, -b) != e(-a, b)")
+ }
+ if !PairingCheck([]*G1{a1, an1}, []*G2{b1, b1}) {
+ t.Error("MultiAte check gave false negative!")
+ }
+ p0 := new(GT).Add(p1, pn1)
+ p0_2 := Pair(a1, b0)
+ if p0.String() != p0_2.String() {
+ t.Error("Pairing mismatch: e(a, b) * e(a, -b) != 1")
+ }
+ p0_3 := new(GT).ScalarMult(p1, bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617"))
+ if p0.String() != p0_3.String() {
+ t.Error("Pairing mismatch: e(a, b) has wrong order")
+ }
+ p2 := Pair(a2, b1)
+ p2_2 := Pair(a1, b2)
+ p2_3 := new(GT).ScalarMult(p1, bigFromBase10("2"))
+ if p2.String() != p2_2.String() {
+ t.Error("Pairing mismatch: e(a, b * 2) != e(a * 2, b)")
+ }
+ if p2.String() != p2_3.String() {
+ t.Error("Pairing mismatch: e(a, b * 2) != e(a, b) ** 2")
+ }
+ if p2.String() == p1.String() {
+ t.Error("Pairing is degenerate!")
+ }
+ if PairingCheck([]*G1{a1, a1}, []*G2{b1, b1}) {
+ t.Error("MultiAte check gave false positive!")
+ }
+ p999 := Pair(a37, b27)
+ p999_2 := Pair(a1, b999)
+ if p999.String() != p999_2.String() {
+ t.Error("Pairing mismatch: e(a * 37, b * 27) != e(a, b * 999)")
+ }
+}
diff --git a/common/crypto/bn256/cloudflare/mul_amd64.h b/common/crypto/bn256/cloudflare/mul_amd64.h
new file mode 100644
index 0000000..bab5da8
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/mul_amd64.h
@@ -0,0 +1,181 @@
+#define mul(a0,a1,a2,a3, rb, stack) \
+ MOVQ a0, AX \
+ MULQ 0+rb \
+ MOVQ AX, R8 \
+ MOVQ DX, R9 \
+ MOVQ a0, AX \
+ MULQ 8+rb \
+ ADDQ AX, R9 \
+ ADCQ $0, DX \
+ MOVQ DX, R10 \
+ MOVQ a0, AX \
+ MULQ 16+rb \
+ ADDQ AX, R10 \
+ ADCQ $0, DX \
+ MOVQ DX, R11 \
+ MOVQ a0, AX \
+ MULQ 24+rb \
+ ADDQ AX, R11 \
+ ADCQ $0, DX \
+ MOVQ DX, R12 \
+ \
+ storeBlock(R8,R9,R10,R11, 0+stack) \
+ MOVQ R12, 32+stack \
+ \
+ MOVQ a1, AX \
+ MULQ 0+rb \
+ MOVQ AX, R8 \
+ MOVQ DX, R9 \
+ MOVQ a1, AX \
+ MULQ 8+rb \
+ ADDQ AX, R9 \
+ ADCQ $0, DX \
+ MOVQ DX, R10 \
+ MOVQ a1, AX \
+ MULQ 16+rb \
+ ADDQ AX, R10 \
+ ADCQ $0, DX \
+ MOVQ DX, R11 \
+ MOVQ a1, AX \
+ MULQ 24+rb \
+ ADDQ AX, R11 \
+ ADCQ $0, DX \
+ MOVQ DX, R12 \
+ \
+ ADDQ 8+stack, R8 \
+ ADCQ 16+stack, R9 \
+ ADCQ 24+stack, R10 \
+ ADCQ 32+stack, R11 \
+ ADCQ $0, R12 \
+ storeBlock(R8,R9,R10,R11, 8+stack) \
+ MOVQ R12, 40+stack \
+ \
+ MOVQ a2, AX \
+ MULQ 0+rb \
+ MOVQ AX, R8 \
+ MOVQ DX, R9 \
+ MOVQ a2, AX \
+ MULQ 8+rb \
+ ADDQ AX, R9 \
+ ADCQ $0, DX \
+ MOVQ DX, R10 \
+ MOVQ a2, AX \
+ MULQ 16+rb \
+ ADDQ AX, R10 \
+ ADCQ $0, DX \
+ MOVQ DX, R11 \
+ MOVQ a2, AX \
+ MULQ 24+rb \
+ ADDQ AX, R11 \
+ ADCQ $0, DX \
+ MOVQ DX, R12 \
+ \
+ ADDQ 16+stack, R8 \
+ ADCQ 24+stack, R9 \
+ ADCQ 32+stack, R10 \
+ ADCQ 40+stack, R11 \
+ ADCQ $0, R12 \
+ storeBlock(R8,R9,R10,R11, 16+stack) \
+ MOVQ R12, 48+stack \
+ \
+ MOVQ a3, AX \
+ MULQ 0+rb \
+ MOVQ AX, R8 \
+ MOVQ DX, R9 \
+ MOVQ a3, AX \
+ MULQ 8+rb \
+ ADDQ AX, R9 \
+ ADCQ $0, DX \
+ MOVQ DX, R10 \
+ MOVQ a3, AX \
+ MULQ 16+rb \
+ ADDQ AX, R10 \
+ ADCQ $0, DX \
+ MOVQ DX, R11 \
+ MOVQ a3, AX \
+ MULQ 24+rb \
+ ADDQ AX, R11 \
+ ADCQ $0, DX \
+ MOVQ DX, R12 \
+ \
+ ADDQ 24+stack, R8 \
+ ADCQ 32+stack, R9 \
+ ADCQ 40+stack, R10 \
+ ADCQ 48+stack, R11 \
+ ADCQ $0, R12 \
+ storeBlock(R8,R9,R10,R11, 24+stack) \
+ MOVQ R12, 56+stack
+
+#define gfpReduce(stack) \
+ \ // m = (T * N') mod R, store m in R8:R9:R10:R11
+ MOVQ ·np+0(SB), AX \
+ MULQ 0+stack \
+ MOVQ AX, R8 \
+ MOVQ DX, R9 \
+ MOVQ ·np+0(SB), AX \
+ MULQ 8+stack \
+ ADDQ AX, R9 \
+ ADCQ $0, DX \
+ MOVQ DX, R10 \
+ MOVQ ·np+0(SB), AX \
+ MULQ 16+stack \
+ ADDQ AX, R10 \
+ ADCQ $0, DX \
+ MOVQ DX, R11 \
+ MOVQ ·np+0(SB), AX \
+ MULQ 24+stack \
+ ADDQ AX, R11 \
+ \
+ MOVQ ·np+8(SB), AX \
+ MULQ 0+stack \
+ MOVQ AX, R12 \
+ MOVQ DX, R13 \
+ MOVQ ·np+8(SB), AX \
+ MULQ 8+stack \
+ ADDQ AX, R13 \
+ ADCQ $0, DX \
+ MOVQ DX, R14 \
+ MOVQ ·np+8(SB), AX \
+ MULQ 16+stack \
+ ADDQ AX, R14 \
+ \
+ ADDQ R12, R9 \
+ ADCQ R13, R10 \
+ ADCQ R14, R11 \
+ \
+ MOVQ ·np+16(SB), AX \
+ MULQ 0+stack \
+ MOVQ AX, R12 \
+ MOVQ DX, R13 \
+ MOVQ ·np+16(SB), AX \
+ MULQ 8+stack \
+ ADDQ AX, R13 \
+ \
+ ADDQ R12, R10 \
+ ADCQ R13, R11 \
+ \
+ MOVQ ·np+24(SB), AX \
+ MULQ 0+stack \
+ ADDQ AX, R11 \
+ \
+ storeBlock(R8,R9,R10,R11, 64+stack) \
+ \
+ \ // m * N
+ mul(·p2+0(SB),·p2+8(SB),·p2+16(SB),·p2+24(SB), 64+stack, 96+stack) \
+ \
+ \ // Add the 512-bit intermediate to m*N
+ loadBlock(96+stack, R8,R9,R10,R11) \
+ loadBlock(128+stack, R12,R13,R14,R15) \
+ \
+ MOVQ $0, AX \
+ ADDQ 0+stack, R8 \
+ ADCQ 8+stack, R9 \
+ ADCQ 16+stack, R10 \
+ ADCQ 24+stack, R11 \
+ ADCQ 32+stack, R12 \
+ ADCQ 40+stack, R13 \
+ ADCQ 48+stack, R14 \
+ ADCQ 56+stack, R15 \
+ ADCQ $0, AX \
+ \
+ gfpCarry(R12,R13,R14,R15,AX, R8,R9,R10,R11,BX)
diff --git a/common/crypto/bn256/cloudflare/mul_arm64.h b/common/crypto/bn256/cloudflare/mul_arm64.h
new file mode 100644
index 0000000..d405eb8
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/mul_arm64.h
@@ -0,0 +1,133 @@
+#define mul(c0,c1,c2,c3,c4,c5,c6,c7) \
+ MUL R1, R5, c0 \
+ UMULH R1, R5, c1 \
+ MUL R1, R6, R0 \
+ ADDS R0, c1 \
+ UMULH R1, R6, c2 \
+ MUL R1, R7, R0 \
+ ADCS R0, c2 \
+ UMULH R1, R7, c3 \
+ MUL R1, R8, R0 \
+ ADCS R0, c3 \
+ UMULH R1, R8, c4 \
+ ADCS ZR, c4 \
+ \
+ MUL R2, R5, R1 \
+ UMULH R2, R5, R26 \
+ MUL R2, R6, R0 \
+ ADDS R0, R26 \
+ UMULH R2, R6, R27 \
+ MUL R2, R7, R0 \
+ ADCS R0, R27 \
+ UMULH R2, R7, R29 \
+ MUL R2, R8, R0 \
+ ADCS R0, R29 \
+ UMULH R2, R8, c5 \
+ ADCS ZR, c5 \
+ ADDS R1, c1 \
+ ADCS R26, c2 \
+ ADCS R27, c3 \
+ ADCS R29, c4 \
+ ADCS ZR, c5 \
+ \
+ MUL R3, R5, R1 \
+ UMULH R3, R5, R26 \
+ MUL R3, R6, R0 \
+ ADDS R0, R26 \
+ UMULH R3, R6, R27 \
+ MUL R3, R7, R0 \
+ ADCS R0, R27 \
+ UMULH R3, R7, R29 \
+ MUL R3, R8, R0 \
+ ADCS R0, R29 \
+ UMULH R3, R8, c6 \
+ ADCS ZR, c6 \
+ ADDS R1, c2 \
+ ADCS R26, c3 \
+ ADCS R27, c4 \
+ ADCS R29, c5 \
+ ADCS ZR, c6 \
+ \
+ MUL R4, R5, R1 \
+ UMULH R4, R5, R26 \
+ MUL R4, R6, R0 \
+ ADDS R0, R26 \
+ UMULH R4, R6, R27 \
+ MUL R4, R7, R0 \
+ ADCS R0, R27 \
+ UMULH R4, R7, R29 \
+ MUL R4, R8, R0 \
+ ADCS R0, R29 \
+ UMULH R4, R8, c7 \
+ ADCS ZR, c7 \
+ ADDS R1, c3 \
+ ADCS R26, c4 \
+ ADCS R27, c5 \
+ ADCS R29, c6 \
+ ADCS ZR, c7
+
+#define gfpReduce() \
+ \ // m = (T * N') mod R, store m in R1:R2:R3:R4
+ MOVD ·np+0(SB), R17 \
+ MOVD ·np+8(SB), R25 \
+ MOVD ·np+16(SB), R19 \
+ MOVD ·np+24(SB), R20 \
+ \
+ MUL R9, R17, R1 \
+ UMULH R9, R17, R2 \
+ MUL R9, R25, R0 \
+ ADDS R0, R2 \
+ UMULH R9, R25, R3 \
+ MUL R9, R19, R0 \
+ ADCS R0, R3 \
+ UMULH R9, R19, R4 \
+ MUL R9, R20, R0 \
+ ADCS R0, R4 \
+ \
+ MUL R10, R17, R21 \
+ UMULH R10, R17, R22 \
+ MUL R10, R25, R0 \
+ ADDS R0, R22 \
+ UMULH R10, R25, R23 \
+ MUL R10, R19, R0 \
+ ADCS R0, R23 \
+ ADDS R21, R2 \
+ ADCS R22, R3 \
+ ADCS R23, R4 \
+ \
+ MUL R11, R17, R21 \
+ UMULH R11, R17, R22 \
+ MUL R11, R25, R0 \
+ ADDS R0, R22 \
+ ADDS R21, R3 \
+ ADCS R22, R4 \
+ \
+ MUL R12, R17, R21 \
+ ADDS R21, R4 \
+ \
+ \ // m * N
+ loadModulus(R5,R6,R7,R8) \
+ mul(R17,R25,R19,R20,R21,R22,R23,R24) \
+ \
+ \ // Add the 512-bit intermediate to m*N
+ MOVD ZR, R0 \
+ ADDS R9, R17 \
+ ADCS R10, R25 \
+ ADCS R11, R19 \
+ ADCS R12, R20 \
+ ADCS R13, R21 \
+ ADCS R14, R22 \
+ ADCS R15, R23 \
+ ADCS R16, R24 \
+ ADCS ZR, R0 \
+ \
+ \ // Our output is R21:R22:R23:R24. Reduce mod p if necessary.
+ SUBS R5, R21, R10 \
+ SBCS R6, R22, R11 \
+ SBCS R7, R23, R12 \
+ SBCS R8, R24, R13 \
+ \
+ CSEL CS, R10, R21, R1 \
+ CSEL CS, R11, R22, R2 \
+ CSEL CS, R12, R23, R3 \
+ CSEL CS, R13, R24, R4
diff --git a/common/crypto/bn256/cloudflare/mul_bmi2_amd64.h b/common/crypto/bn256/cloudflare/mul_bmi2_amd64.h
new file mode 100644
index 0000000..71ad049
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/mul_bmi2_amd64.h
@@ -0,0 +1,112 @@
+#define mulBMI2(a0,a1,a2,a3, rb) \
+ MOVQ a0, DX \
+ MOVQ $0, R13 \
+ MULXQ 0+rb, R8, R9 \
+ MULXQ 8+rb, AX, R10 \
+ ADDQ AX, R9 \
+ MULXQ 16+rb, AX, R11 \
+ ADCQ AX, R10 \
+ MULXQ 24+rb, AX, R12 \
+ ADCQ AX, R11 \
+ ADCQ $0, R12 \
+ ADCQ $0, R13 \
+ \
+ MOVQ a1, DX \
+ MOVQ $0, R14 \
+ MULXQ 0+rb, AX, BX \
+ ADDQ AX, R9 \
+ ADCQ BX, R10 \
+ MULXQ 16+rb, AX, BX \
+ ADCQ AX, R11 \
+ ADCQ BX, R12 \
+ ADCQ $0, R13 \
+ MULXQ 8+rb, AX, BX \
+ ADDQ AX, R10 \
+ ADCQ BX, R11 \
+ MULXQ 24+rb, AX, BX \
+ ADCQ AX, R12 \
+ ADCQ BX, R13 \
+ ADCQ $0, R14 \
+ \
+ MOVQ a2, DX \
+ MOVQ $0, R15 \
+ MULXQ 0+rb, AX, BX \
+ ADDQ AX, R10 \
+ ADCQ BX, R11 \
+ MULXQ 16+rb, AX, BX \
+ ADCQ AX, R12 \
+ ADCQ BX, R13 \
+ ADCQ $0, R14 \
+ MULXQ 8+rb, AX, BX \
+ ADDQ AX, R11 \
+ ADCQ BX, R12 \
+ MULXQ 24+rb, AX, BX \
+ ADCQ AX, R13 \
+ ADCQ BX, R14 \
+ ADCQ $0, R15 \
+ \
+ MOVQ a3, DX \
+ MULXQ 0+rb, AX, BX \
+ ADDQ AX, R11 \
+ ADCQ BX, R12 \
+ MULXQ 16+rb, AX, BX \
+ ADCQ AX, R13 \
+ ADCQ BX, R14 \
+ ADCQ $0, R15 \
+ MULXQ 8+rb, AX, BX \
+ ADDQ AX, R12 \
+ ADCQ BX, R13 \
+ MULXQ 24+rb, AX, BX \
+ ADCQ AX, R14 \
+ ADCQ BX, R15
+
+#define gfpReduceBMI2() \
+ \ // m = (T * N') mod R, store m in R8:R9:R10:R11
+ MOVQ ·np+0(SB), DX \
+ MULXQ 0(SP), R8, R9 \
+ MULXQ 8(SP), AX, R10 \
+ ADDQ AX, R9 \
+ MULXQ 16(SP), AX, R11 \
+ ADCQ AX, R10 \
+ MULXQ 24(SP), AX, BX \
+ ADCQ AX, R11 \
+ \
+ MOVQ ·np+8(SB), DX \
+ MULXQ 0(SP), AX, BX \
+ ADDQ AX, R9 \
+ ADCQ BX, R10 \
+ MULXQ 16(SP), AX, BX \
+ ADCQ AX, R11 \
+ MULXQ 8(SP), AX, BX \
+ ADDQ AX, R10 \
+ ADCQ BX, R11 \
+ \
+ MOVQ ·np+16(SB), DX \
+ MULXQ 0(SP), AX, BX \
+ ADDQ AX, R10 \
+ ADCQ BX, R11 \
+ MULXQ 8(SP), AX, BX \
+ ADDQ AX, R11 \
+ \
+ MOVQ ·np+24(SB), DX \
+ MULXQ 0(SP), AX, BX \
+ ADDQ AX, R11 \
+ \
+ storeBlock(R8,R9,R10,R11, 64(SP)) \
+ \
+ \ // m * N
+ mulBMI2(·p2+0(SB),·p2+8(SB),·p2+16(SB),·p2+24(SB), 64(SP)) \
+ \
+ \ // Add the 512-bit intermediate to m*N
+ MOVQ $0, AX \
+ ADDQ 0(SP), R8 \
+ ADCQ 8(SP), R9 \
+ ADCQ 16(SP), R10 \
+ ADCQ 24(SP), R11 \
+ ADCQ 32(SP), R12 \
+ ADCQ 40(SP), R13 \
+ ADCQ 48(SP), R14 \
+ ADCQ 56(SP), R15 \
+ ADCQ $0, AX \
+ \
+ gfpCarry(R12,R13,R14,R15,AX, R8,R9,R10,R11,BX)
diff --git a/common/crypto/bn256/cloudflare/optate.go b/common/crypto/bn256/cloudflare/optate.go
new file mode 100644
index 0000000..25418e8
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/optate.go
@@ -0,0 +1,286 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2) (a, b, c *gfP2, rOut *twistPoint) {
+ // See the mixed addition algorithm from "Faster Computation of the
+ // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
+ B := (&gfP2{}).Mul(&p.x, &r.t)
+
+ D := (&gfP2{}).Add(&p.y, &r.z)
+ D.Square(D).Sub(D, r2).Sub(D, &r.t).Mul(D, &r.t)
+
+ H := (&gfP2{}).Sub(B, &r.x)
+ I := (&gfP2{}).Square(H)
+
+ E := (&gfP2{}).Add(I, I)
+ E.Add(E, E)
+
+ J := (&gfP2{}).Mul(H, E)
+
+ L1 := (&gfP2{}).Sub(D, &r.y)
+ L1.Sub(L1, &r.y)
+
+ V := (&gfP2{}).Mul(&r.x, E)
+
+ rOut = &twistPoint{}
+ rOut.x.Square(L1).Sub(&rOut.x, J).Sub(&rOut.x, V).Sub(&rOut.x, V)
+
+ rOut.z.Add(&r.z, H).Square(&rOut.z).Sub(&rOut.z, &r.t).Sub(&rOut.z, I)
+
+ t := (&gfP2{}).Sub(V, &rOut.x)
+ t.Mul(t, L1)
+ t2 := (&gfP2{}).Mul(&r.y, J)
+ t2.Add(t2, t2)
+ rOut.y.Sub(t, t2)
+
+ rOut.t.Square(&rOut.z)
+
+ t.Add(&p.y, &rOut.z).Square(t).Sub(t, r2).Sub(t, &rOut.t)
+
+ t2.Mul(L1, &p.x)
+ t2.Add(t2, t2)
+ a = (&gfP2{}).Sub(t2, t)
+
+ c = (&gfP2{}).MulScalar(&rOut.z, &q.y)
+ c.Add(c, c)
+
+ b = (&gfP2{}).Neg(L1)
+ b.MulScalar(b, &q.x).Add(b, b)
+
+ return
+}
+
+func lineFunctionDouble(r *twistPoint, q *curvePoint) (a, b, c *gfP2, rOut *twistPoint) {
+ // See the doubling algorithm for a=0 from "Faster Computation of the
+ // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
+ A := (&gfP2{}).Square(&r.x)
+ B := (&gfP2{}).Square(&r.y)
+ C := (&gfP2{}).Square(B)
+
+ D := (&gfP2{}).Add(&r.x, B)
+ D.Square(D).Sub(D, A).Sub(D, C).Add(D, D)
+
+ E := (&gfP2{}).Add(A, A)
+ E.Add(E, A)
+
+ G := (&gfP2{}).Square(E)
+
+ rOut = &twistPoint{}
+ rOut.x.Sub(G, D).Sub(&rOut.x, D)
+
+ rOut.z.Add(&r.y, &r.z).Square(&rOut.z).Sub(&rOut.z, B).Sub(&rOut.z, &r.t)
+
+ rOut.y.Sub(D, &rOut.x).Mul(&rOut.y, E)
+ t := (&gfP2{}).Add(C, C)
+ t.Add(t, t).Add(t, t)
+ rOut.y.Sub(&rOut.y, t)
+
+ rOut.t.Square(&rOut.z)
+
+ t.Mul(E, &r.t).Add(t, t)
+ b = (&gfP2{}).Neg(t)
+ b.MulScalar(b, &q.x)
+
+ a = (&gfP2{}).Add(&r.x, E)
+ a.Square(a).Sub(a, A).Sub(a, G)
+ t.Add(B, B).Add(t, t)
+ a.Sub(a, t)
+
+ c = (&gfP2{}).Mul(&rOut.z, &r.t)
+ c.Add(c, c).MulScalar(c, &q.y)
+
+ return
+}
+
+func mulLine(ret *gfP12, a, b, c *gfP2) {
+ a2 := &gfP6{}
+ a2.y.Set(a)
+ a2.z.Set(b)
+ a2.Mul(a2, &ret.x)
+ t3 := (&gfP6{}).MulScalar(&ret.y, c)
+
+ t := (&gfP2{}).Add(b, c)
+ t2 := &gfP6{}
+ t2.y.Set(a)
+ t2.z.Set(t)
+ ret.x.Add(&ret.x, &ret.y)
+
+ ret.y.Set(t3)
+
+ ret.x.Mul(&ret.x, t2).Sub(&ret.x, a2).Sub(&ret.x, &ret.y)
+ a2.MulTau(a2)
+ ret.y.Add(&ret.y, a2)
+}
+
+// sixuPlus2NAF is 6u+2 in non-adjacent form.
+var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
+ 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
+ 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}
+
+// miller implements the Miller loop for calculating the Optimal Ate pairing.
+// See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf
+func miller(q *twistPoint, p *curvePoint) *gfP12 {
+ ret := (&gfP12{}).SetOne()
+
+ aAffine := &twistPoint{}
+ aAffine.Set(q)
+ aAffine.MakeAffine()
+
+ bAffine := &curvePoint{}
+ bAffine.Set(p)
+ bAffine.MakeAffine()
+
+ minusA := &twistPoint{}
+ minusA.Neg(aAffine)
+
+ r := &twistPoint{}
+ r.Set(aAffine)
+
+ r2 := (&gfP2{}).Square(&aAffine.y)
+
+ for i := len(sixuPlus2NAF) - 1; i > 0; i-- {
+ a, b, c, newR := lineFunctionDouble(r, bAffine)
+ if i != len(sixuPlus2NAF)-1 {
+ ret.Square(ret)
+ }
+
+ mulLine(ret, a, b, c)
+ r = newR
+
+ switch sixuPlus2NAF[i-1] {
+ case 1:
+ a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2)
+ case -1:
+ a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2)
+ default:
+ continue
+ }
+
+ mulLine(ret, a, b, c)
+ r = newR
+ }
+
+ // In order to calculate Q1 we have to convert q from the sextic twist
+ // to the full GF(p^12) group, apply the Frobenius there, and convert
+ // back.
+ //
+ // The twist isomorphism is (x', y') -> (xω², yω³). If we consider just
+ // x for a moment, then after applying the Frobenius, we have x̄ω^(2p)
+ // where x̄ is the conjugate of x. If we are going to apply the inverse
+ // isomorphism we need a value with a single coefficient of ω² so we
+ // rewrite this as x̄ω^(2p-2)ω². ξ⁶ = ω and, due to the construction of
+ // p, 2p-2 is a multiple of six. Therefore we can rewrite as
+ // x̄ξ^((p-1)/3)ω² and applying the inverse isomorphism eliminates the
+ // ω².
+ //
+ // A similar argument can be made for the y value.
+
+ q1 := &twistPoint{}
+ q1.x.Conjugate(&aAffine.x).Mul(&q1.x, xiToPMinus1Over3)
+ q1.y.Conjugate(&aAffine.y).Mul(&q1.y, xiToPMinus1Over2)
+ q1.z.SetOne()
+ q1.t.SetOne()
+
+ // For Q2 we are applying the p² Frobenius. The two conjugations cancel
+ // out and we are left only with the factors from the isomorphism. In
+ // the case of x, we end up with a pure number which is why
+ // xiToPSquaredMinus1Over3 is ∈ GF(p). With y we get a factor of -1. We
+ // ignore this to end up with -Q2.
+
+ minusQ2 := &twistPoint{}
+ minusQ2.x.MulScalar(&aAffine.x, xiToPSquaredMinus1Over3)
+ minusQ2.y.Set(&aAffine.y)
+ minusQ2.z.SetOne()
+ minusQ2.t.SetOne()
+
+ r2.Square(&q1.y)
+ a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2)
+ mulLine(ret, a, b, c)
+ r = newR
+
+ r2.Square(&minusQ2.y)
+ a, b, c, _ = lineFunctionAdd(r, minusQ2, bAffine, r2)
+ mulLine(ret, a, b, c)
+
+ return ret
+}
+
+// finalExponentiation computes the (p¹²-1)/Order-th power of an element of
+// GF(p¹²) to obtain an element of GT (steps 13-15 of algorithm 1 from
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf)
+func finalExponentiation(in *gfP12) *gfP12 {
+ t1 := &gfP12{}
+
+ // This is the p^6-Frobenius
+ t1.x.Neg(&in.x)
+ t1.y.Set(&in.y)
+
+ inv := &gfP12{}
+ inv.Invert(in)
+ t1.Mul(t1, inv)
+
+ t2 := (&gfP12{}).FrobeniusP2(t1)
+ t1.Mul(t1, t2)
+
+ fp := (&gfP12{}).Frobenius(t1)
+ fp2 := (&gfP12{}).FrobeniusP2(t1)
+ fp3 := (&gfP12{}).Frobenius(fp2)
+
+ fu := (&gfP12{}).Exp(t1, u)
+ fu2 := (&gfP12{}).Exp(fu, u)
+ fu3 := (&gfP12{}).Exp(fu2, u)
+
+ y3 := (&gfP12{}).Frobenius(fu)
+ fu2p := (&gfP12{}).Frobenius(fu2)
+ fu3p := (&gfP12{}).Frobenius(fu3)
+ y2 := (&gfP12{}).FrobeniusP2(fu2)
+
+ y0 := &gfP12{}
+ y0.Mul(fp, fp2).Mul(y0, fp3)
+
+ y1 := (&gfP12{}).Conjugate(t1)
+ y5 := (&gfP12{}).Conjugate(fu2)
+ y3.Conjugate(y3)
+ y4 := (&gfP12{}).Mul(fu, fu2p)
+ y4.Conjugate(y4)
+
+ y6 := (&gfP12{}).Mul(fu3, fu3p)
+ y6.Conjugate(y6)
+
+ t0 := (&gfP12{}).Square(y6)
+ t0.Mul(t0, y4).Mul(t0, y5)
+ t1.Mul(y3, y5).Mul(t1, t0)
+ t0.Mul(t0, y2)
+ t1.Square(t1).Mul(t1, t0).Square(t1)
+ t0.Mul(t1, y1)
+ t1.Mul(t1, y0)
+ t0.Square(t0).Mul(t0, t1)
+
+ return t0
+}
+
+func optimalAte(a *twistPoint, b *curvePoint) *gfP12 {
+ e := miller(a, b)
+ ret := finalExponentiation(e)
+
+ if a.IsInfinity() || b.IsInfinity() {
+ ret.SetOne()
+ }
+ return ret
+}
diff --git a/common/crypto/bn256/cloudflare/twist.go b/common/crypto/bn256/cloudflare/twist.go
new file mode 100644
index 0000000..a3798d5
--- /dev/null
+++ b/common/crypto/bn256/cloudflare/twist.go
@@ -0,0 +1,220 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package bn256
+
+import (
+ "math/big"
+)
+
+// twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are
+// kept in Jacobian form and t=z² when valid. The group G₂ is the set of
+// n-torsion points of this curve over GF(p²) (where n = Order)
+type twistPoint struct {
+ x, y, z, t gfP2
+}
+
+var twistB = &gfP2{
+ gfP{0x38e7ecccd1dcff67, 0x65f0b37d93ce0d3e, 0xd749d0dd22ac00aa, 0x0141b9ce4a688d4d},
+ gfP{0x3bf938e377b802a8, 0x020b1b273633535d, 0x26b7edf049755260, 0x2514c6324384a86d},
+}
+
+// twistGen is the generator of group G₂.
+var twistGen = &twistPoint{
+ gfP2{
+ gfP{0xafb4737da84c6140, 0x6043dd5a5802d8c4, 0x09e950fc52a02f86, 0x14fef0833aea7b6b},
+ gfP{0x8e83b5d102bc2026, 0xdceb1935497b0172, 0xfbb8264797811adf, 0x19573841af96503b},
+ },
+ gfP2{
+ gfP{0x64095b56c71856ee, 0xdc57f922327d3cbb, 0x55f935be33351076, 0x0da4a0e693fd6482},
+ gfP{0x619dfa9d886be9f6, 0xfe7fd297f59e9b78, 0xff9e1a62231b7dfe, 0x28fd7eebae9e4206},
+ },
+ gfP2{*newGFp(0), *newGFp(1)},
+ gfP2{*newGFp(0), *newGFp(1)},
+}
+
+func (c *twistPoint) String() string {
+ c.MakeAffine()
+ x, y := gfP2Decode(&c.x), gfP2Decode(&c.y)
+ return "(" + x.String() + ", " + y.String() + ")"
+}
+
+func (c *twistPoint) Set(a *twistPoint) {
+ c.x.Set(&a.x)
+ c.y.Set(&a.y)
+ c.z.Set(&a.z)
+ c.t.Set(&a.t)
+}
+
+// IsOnCurve returns true iff c is on the curve.
+func (c *twistPoint) IsOnCurve() bool {
+ c.MakeAffine()
+ if c.IsInfinity() {
+ return true
+ }
+
+ y2, x3 := &gfP2{}, &gfP2{}
+ y2.Square(&c.y)
+ x3.Square(&c.x).Mul(x3, &c.x).Add(x3, twistB)
+
+ if *y2 != *x3 {
+ return false
+ }
+ cneg := &twistPoint{}
+ cneg.Mul(c, Order)
+ return cneg.z.IsZero()
+}
+
+func (c *twistPoint) SetInfinity() {
+ c.x.SetZero()
+ c.y.SetOne()
+ c.z.SetZero()
+ c.t.SetZero()
+}
+
+func (c *twistPoint) IsInfinity() bool {
+ return c.z.IsZero()
+}
+
+func (c *twistPoint) Add(a, b *twistPoint) {
+ // For additional comments, see the same function in curve.go.
+
+ if a.IsInfinity() {
+ c.Set(b)
+ return
+ }
+ if b.IsInfinity() {
+ c.Set(a)
+ return
+ }
+
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
+ z12 := (&gfP2{}).Square(&a.z)
+ z22 := (&gfP2{}).Square(&b.z)
+ u1 := (&gfP2{}).Mul(&a.x, z22)
+ u2 := (&gfP2{}).Mul(&b.x, z12)
+
+ t := (&gfP2{}).Mul(&b.z, z22)
+ s1 := (&gfP2{}).Mul(&a.y, t)
+
+ t.Mul(&a.z, z12)
+ s2 := (&gfP2{}).Mul(&b.y, t)
+
+ h := (&gfP2{}).Sub(u2, u1)
+ xEqual := h.IsZero()
+
+ t.Add(h, h)
+ i := (&gfP2{}).Square(t)
+ j := (&gfP2{}).Mul(h, i)
+
+ t.Sub(s2, s1)
+ yEqual := t.IsZero()
+ if xEqual && yEqual {
+ c.Double(a)
+ return
+ }
+ r := (&gfP2{}).Add(t, t)
+
+ v := (&gfP2{}).Mul(u1, i)
+
+ t4 := (&gfP2{}).Square(r)
+ t.Add(v, v)
+ t6 := (&gfP2{}).Sub(t4, j)
+ c.x.Sub(t6, t)
+
+ t.Sub(v, &c.x) // t7
+ t4.Mul(s1, j) // t8
+ t6.Add(t4, t4) // t9
+ t4.Mul(r, t) // t10
+ c.y.Sub(t4, t6)
+
+ t.Add(&a.z, &b.z) // t11
+ t4.Square(t) // t12
+ t.Sub(t4, z12) // t13
+ t4.Sub(t, z22) // t14
+ c.z.Mul(t4, h)
+}
+
+func (c *twistPoint) Double(a *twistPoint) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
+ A := (&gfP2{}).Square(&a.x)
+ B := (&gfP2{}).Square(&a.y)
+ C := (&gfP2{}).Square(B)
+
+ t := (&gfP2{}).Add(&a.x, B)
+ t2 := (&gfP2{}).Square(t)
+ t.Sub(t2, A)
+ t2.Sub(t, C)
+ d := (&gfP2{}).Add(t2, t2)
+ t.Add(A, A)
+ e := (&gfP2{}).Add(t, A)
+ f := (&gfP2{}).Square(e)
+
+ t.Add(d, d)
+ c.x.Sub(f, t)
+
+ t.Add(C, C)
+ t2.Add(t, t)
+ t.Add(t2, t2)
+ c.y.Sub(d, &c.x)
+ t2.Mul(e, &c.y)
+ c.y.Sub(t2, t)
+
+ t.Mul(&a.y, &a.z)
+ c.z.Add(t, t)
+}
+
+func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int) {
+ sum, t := &twistPoint{}, &twistPoint{}
+
+ for i := scalar.BitLen(); i >= 0; i-- {
+ t.Double(sum)
+ if scalar.Bit(i) != 0 {
+ sum.Add(t, a)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+}
+
+func (c *twistPoint) MakeAffine() {
+ if c.z.IsOne() {
+ return
+ } else if c.z.IsZero() {
+ c.x.SetZero()
+ c.y.SetOne()
+ c.t.SetZero()
+ return
+ }
+
+ zInv := (&gfP2{}).Invert(&c.z)
+ t := (&gfP2{}).Mul(&c.y, zInv)
+ zInv2 := (&gfP2{}).Square(zInv)
+ c.y.Mul(t, zInv2)
+ t.Mul(&c.x, zInv2)
+ c.x.Set(t)
+ c.z.SetOne()
+ c.t.SetOne()
+}
+
+func (c *twistPoint) Neg(a *twistPoint) {
+ c.x.Set(&a.x)
+ c.y.Neg(&a.y)
+ c.z.Set(&a.z)
+ c.t.SetZero()
+}
diff --git a/common/crypto/bn256/google/bn256.go b/common/crypto/bn256/google/bn256.go
new file mode 100644
index 0000000..0a9d5cd
--- /dev/null
+++ b/common/crypto/bn256/google/bn256.go
@@ -0,0 +1,460 @@
+// Copyright 2012 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 bn256 implements a particular bilinear group.
+//
+// Bilinear groups are the basis of many of the new cryptographic protocols
+// that have been proposed over the past decade. They consist of a triplet of
+// groups (G₁, G₂ and GT) such that there exists a function e(g₁ˣ,g₂ʸ)=gTˣʸ
+// (where gₓ is a generator of the respective group). That function is called
+// a pairing function.
+//
+// This package specifically implements the Optimal Ate pairing over a 256-bit
+// Barreto-Naehrig curve as described in
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not
+// compatible with the implementation described in that paper, as different
+// parameters are chosen.
+//
+// (This package previously claimed to operate at a 128-bit security level.
+// However, recent improvements in attacks mean that is no longer true. See
+// https://moderncrypto.org/mail-archive/curves/2016/000740.html.)
+package bn256
+
+import (
+ "crypto/rand"
+ "errors"
+ "io"
+ "math/big"
+)
+
+// BUG(agl): this implementation is not constant time.
+// TODO(agl): keep GF(p²) elements in Mongomery form.
+
+// G1 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G1 struct {
+ p *curvePoint
+}
+
+// RandomG1 returns x and g₁ˣ where x is a random, non-zero number read from r.
+func RandomG1(r io.Reader) (*big.Int, *G1, error) {
+ var k *big.Int
+ var err error
+
+ for {
+ k, err = rand.Int(r, Order)
+ if err != nil {
+ return nil, nil, err
+ }
+ if k.Sign() > 0 {
+ break
+ }
+ }
+
+ return k, new(G1).ScalarBaseMult(k), nil
+}
+
+func (e *G1) String() string {
+ return "bn256.G1" + e.p.String()
+}
+
+// CurvePoints returns p's curve points in big integer
+func (e *G1) CurvePoints() (*big.Int, *big.Int, *big.Int, *big.Int) {
+ return e.p.x, e.p.y, e.p.z, e.p.t
+}
+
+// ScalarBaseMult sets e to g*k where g is the generator of the group and
+// then returns e.
+func (e *G1) ScalarBaseMult(k *big.Int) *G1 {
+ if e.p == nil {
+ e.p = newCurvePoint(nil)
+ }
+ e.p.Mul(curveGen, k, new(bnPool))
+ return e
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *G1) ScalarMult(a *G1, k *big.Int) *G1 {
+ if e.p == nil {
+ e.p = newCurvePoint(nil)
+ }
+ e.p.Mul(a.p, k, new(bnPool))
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+// BUG(agl): this function is not complete: a==b fails.
+func (e *G1) Add(a, b *G1) *G1 {
+ if e.p == nil {
+ e.p = newCurvePoint(nil)
+ }
+ e.p.Add(a.p, b.p, new(bnPool))
+ return e
+}
+
+// Neg sets e to -a and then returns e.
+func (e *G1) Neg(a *G1) *G1 {
+ if e.p == nil {
+ e.p = newCurvePoint(nil)
+ }
+ e.p.Negative(a.p)
+ return e
+}
+
+// Marshal converts n to a byte slice.
+func (e *G1) Marshal() []byte {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if e.p.IsInfinity() {
+ return make([]byte, numBytes*2)
+ }
+
+ e.p.MakeAffine(nil)
+
+ xBytes := new(big.Int).Mod(e.p.x, P).Bytes()
+ yBytes := new(big.Int).Mod(e.p.y, P).Bytes()
+
+ ret := make([]byte, numBytes*2)
+ copy(ret[1*numBytes-len(xBytes):], xBytes)
+ copy(ret[2*numBytes-len(yBytes):], yBytes)
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *G1) Unmarshal(m []byte) ([]byte, error) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+ if len(m) != 2*numBytes {
+ return nil, errors.New("bn256: not enough data")
+ }
+ // Unmarshal the points and check their caps
+ if e.p == nil {
+ e.p = newCurvePoint(nil)
+ }
+ e.p.x.SetBytes(m[0*numBytes : 1*numBytes])
+ if e.p.x.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ e.p.y.SetBytes(m[1*numBytes : 2*numBytes])
+ if e.p.y.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ // Ensure the point is on the curve
+ if e.p.x.Sign() == 0 && e.p.y.Sign() == 0 {
+ // This is the point at infinity.
+ e.p.y.SetInt64(1)
+ e.p.z.SetInt64(0)
+ e.p.t.SetInt64(0)
+ } else {
+ e.p.z.SetInt64(1)
+ e.p.t.SetInt64(1)
+
+ if !e.p.IsOnCurve() {
+ return nil, errors.New("bn256: malformed point")
+ }
+ }
+ return m[2*numBytes:], nil
+}
+
+// G2 is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type G2 struct {
+ p *twistPoint
+}
+
+// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r.
+func RandomG2(r io.Reader) (*big.Int, *G2, error) {
+ var k *big.Int
+ var err error
+
+ for {
+ k, err = rand.Int(r, Order)
+ if err != nil {
+ return nil, nil, err
+ }
+ if k.Sign() > 0 {
+ break
+ }
+ }
+
+ return k, new(G2).ScalarBaseMult(k), nil
+}
+
+func (e *G2) String() string {
+ return "bn256.G2" + e.p.String()
+}
+
+// CurvePoints returns the curve points of p which includes the real
+// and imaginary parts of the curve point.
+func (e *G2) CurvePoints() (*gfP2, *gfP2, *gfP2, *gfP2) {
+ return e.p.x, e.p.y, e.p.z, e.p.t
+}
+
+// ScalarBaseMult sets e to g*k where g is the generator of the group and
+// then returns out.
+func (e *G2) ScalarBaseMult(k *big.Int) *G2 {
+ if e.p == nil {
+ e.p = newTwistPoint(nil)
+ }
+ e.p.Mul(twistGen, k, new(bnPool))
+ return e
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *G2) ScalarMult(a *G2, k *big.Int) *G2 {
+ if e.p == nil {
+ e.p = newTwistPoint(nil)
+ }
+ e.p.Mul(a.p, k, new(bnPool))
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+// BUG(agl): this function is not complete: a==b fails.
+func (e *G2) Add(a, b *G2) *G2 {
+ if e.p == nil {
+ e.p = newTwistPoint(nil)
+ }
+ e.p.Add(a.p, b.p, new(bnPool))
+ return e
+}
+
+// Marshal converts n into a byte slice.
+func (n *G2) Marshal() []byte {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if n.p.IsInfinity() {
+ return make([]byte, numBytes*4)
+ }
+
+ n.p.MakeAffine(nil)
+
+ xxBytes := new(big.Int).Mod(n.p.x.x, P).Bytes()
+ xyBytes := new(big.Int).Mod(n.p.x.y, P).Bytes()
+ yxBytes := new(big.Int).Mod(n.p.y.x, P).Bytes()
+ yyBytes := new(big.Int).Mod(n.p.y.y, P).Bytes()
+
+ ret := make([]byte, numBytes*4)
+ copy(ret[1*numBytes-len(xxBytes):], xxBytes)
+ copy(ret[2*numBytes-len(xyBytes):], xyBytes)
+ copy(ret[3*numBytes-len(yxBytes):], yxBytes)
+ copy(ret[4*numBytes-len(yyBytes):], yyBytes)
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *G2) Unmarshal(m []byte) ([]byte, error) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+ if len(m) != 4*numBytes {
+ return nil, errors.New("bn256: not enough data")
+ }
+ // Unmarshal the points and check their caps
+ if e.p == nil {
+ e.p = newTwistPoint(nil)
+ }
+ e.p.x.x.SetBytes(m[0*numBytes : 1*numBytes])
+ if e.p.x.x.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ e.p.x.y.SetBytes(m[1*numBytes : 2*numBytes])
+ if e.p.x.y.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ e.p.y.x.SetBytes(m[2*numBytes : 3*numBytes])
+ if e.p.y.x.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ e.p.y.y.SetBytes(m[3*numBytes : 4*numBytes])
+ if e.p.y.y.Cmp(P) >= 0 {
+ return nil, errors.New("bn256: coordinate exceeds modulus")
+ }
+ // Ensure the point is on the curve
+ if e.p.x.x.Sign() == 0 &&
+ e.p.x.y.Sign() == 0 &&
+ e.p.y.x.Sign() == 0 &&
+ e.p.y.y.Sign() == 0 {
+ // This is the point at infinity.
+ e.p.y.SetOne()
+ e.p.z.SetZero()
+ e.p.t.SetZero()
+ } else {
+ e.p.z.SetOne()
+ e.p.t.SetOne()
+
+ if !e.p.IsOnCurve() {
+ return nil, errors.New("bn256: malformed point")
+ }
+ }
+ return m[4*numBytes:], nil
+}
+
+// GT is an abstract cyclic group. The zero value is suitable for use as the
+// output of an operation, but cannot be used as an input.
+type GT struct {
+ p *gfP12
+}
+
+func (g *GT) String() string {
+ return "bn256.GT" + g.p.String()
+}
+
+// ScalarMult sets e to a*k and then returns e.
+func (e *GT) ScalarMult(a *GT, k *big.Int) *GT {
+ if e.p == nil {
+ e.p = newGFp12(nil)
+ }
+ e.p.Exp(a.p, k, new(bnPool))
+ return e
+}
+
+// Add sets e to a+b and then returns e.
+func (e *GT) Add(a, b *GT) *GT {
+ if e.p == nil {
+ e.p = newGFp12(nil)
+ }
+ e.p.Mul(a.p, b.p, new(bnPool))
+ return e
+}
+
+// Neg sets e to -a and then returns e.
+func (e *GT) Neg(a *GT) *GT {
+ if e.p == nil {
+ e.p = newGFp12(nil)
+ }
+ e.p.Invert(a.p, new(bnPool))
+ return e
+}
+
+// Marshal converts n into a byte slice.
+func (n *GT) Marshal() []byte {
+ n.p.Minimal()
+
+ xxxBytes := n.p.x.x.x.Bytes()
+ xxyBytes := n.p.x.x.y.Bytes()
+ xyxBytes := n.p.x.y.x.Bytes()
+ xyyBytes := n.p.x.y.y.Bytes()
+ xzxBytes := n.p.x.z.x.Bytes()
+ xzyBytes := n.p.x.z.y.Bytes()
+ yxxBytes := n.p.y.x.x.Bytes()
+ yxyBytes := n.p.y.x.y.Bytes()
+ yyxBytes := n.p.y.y.x.Bytes()
+ yyyBytes := n.p.y.y.y.Bytes()
+ yzxBytes := n.p.y.z.x.Bytes()
+ yzyBytes := n.p.y.z.y.Bytes()
+
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ ret := make([]byte, numBytes*12)
+ copy(ret[1*numBytes-len(xxxBytes):], xxxBytes)
+ copy(ret[2*numBytes-len(xxyBytes):], xxyBytes)
+ copy(ret[3*numBytes-len(xyxBytes):], xyxBytes)
+ copy(ret[4*numBytes-len(xyyBytes):], xyyBytes)
+ copy(ret[5*numBytes-len(xzxBytes):], xzxBytes)
+ copy(ret[6*numBytes-len(xzyBytes):], xzyBytes)
+ copy(ret[7*numBytes-len(yxxBytes):], yxxBytes)
+ copy(ret[8*numBytes-len(yxyBytes):], yxyBytes)
+ copy(ret[9*numBytes-len(yyxBytes):], yyxBytes)
+ copy(ret[10*numBytes-len(yyyBytes):], yyyBytes)
+ copy(ret[11*numBytes-len(yzxBytes):], yzxBytes)
+ copy(ret[12*numBytes-len(yzyBytes):], yzyBytes)
+
+ return ret
+}
+
+// Unmarshal sets e to the result of converting the output of Marshal back into
+// a group element and then returns e.
+func (e *GT) Unmarshal(m []byte) (*GT, bool) {
+ // Each value is a 256-bit number.
+ const numBytes = 256 / 8
+
+ if len(m) != 12*numBytes {
+ return nil, false
+ }
+
+ if e.p == nil {
+ e.p = newGFp12(nil)
+ }
+
+ e.p.x.x.x.SetBytes(m[0*numBytes : 1*numBytes])
+ e.p.x.x.y.SetBytes(m[1*numBytes : 2*numBytes])
+ e.p.x.y.x.SetBytes(m[2*numBytes : 3*numBytes])
+ e.p.x.y.y.SetBytes(m[3*numBytes : 4*numBytes])
+ e.p.x.z.x.SetBytes(m[4*numBytes : 5*numBytes])
+ e.p.x.z.y.SetBytes(m[5*numBytes : 6*numBytes])
+ e.p.y.x.x.SetBytes(m[6*numBytes : 7*numBytes])
+ e.p.y.x.y.SetBytes(m[7*numBytes : 8*numBytes])
+ e.p.y.y.x.SetBytes(m[8*numBytes : 9*numBytes])
+ e.p.y.y.y.SetBytes(m[9*numBytes : 10*numBytes])
+ e.p.y.z.x.SetBytes(m[10*numBytes : 11*numBytes])
+ e.p.y.z.y.SetBytes(m[11*numBytes : 12*numBytes])
+
+ return e, true
+}
+
+// Pair calculates an Optimal Ate pairing.
+func Pair(g1 *G1, g2 *G2) *GT {
+ return >{optimalAte(g2.p, g1.p, new(bnPool))}
+}
+
+// PairingCheck calculates the Optimal Ate pairing for a set of points.
+func PairingCheck(a []*G1, b []*G2) bool {
+ pool := new(bnPool)
+
+ acc := newGFp12(pool)
+ acc.SetOne()
+
+ for i := 0; i < len(a); i++ {
+ if a[i].p.IsInfinity() || b[i].p.IsInfinity() {
+ continue
+ }
+ acc.Mul(acc, miller(b[i].p, a[i].p, pool), pool)
+ }
+ ret := finalExponentiation(acc, pool)
+ acc.Put(pool)
+
+ return ret.IsOne()
+}
+
+// bnPool implements a tiny cache of *big.Int objects that's used to reduce the
+// number of allocations made during processing.
+type bnPool struct {
+ bns []*big.Int
+ count int
+}
+
+func (pool *bnPool) Get() *big.Int {
+ if pool == nil {
+ return new(big.Int)
+ }
+
+ pool.count++
+ l := len(pool.bns)
+ if l == 0 {
+ return new(big.Int)
+ }
+
+ bn := pool.bns[l-1]
+ pool.bns = pool.bns[:l-1]
+ return bn
+}
+
+func (pool *bnPool) Put(bn *big.Int) {
+ if pool == nil {
+ return
+ }
+ pool.bns = append(pool.bns, bn)
+ pool.count--
+}
+
+func (pool *bnPool) Count() int {
+ return pool.count
+}
diff --git a/common/crypto/bn256/google/bn256_test.go b/common/crypto/bn256/google/bn256_test.go
new file mode 100644
index 0000000..a4497ad
--- /dev/null
+++ b/common/crypto/bn256/google/bn256_test.go
@@ -0,0 +1,311 @@
+// Copyright 2012 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 bn256
+
+import (
+ "bytes"
+ "crypto/rand"
+ "math/big"
+ "testing"
+)
+
+func TestGFp2Invert(t *testing.T) {
+ pool := new(bnPool)
+
+ a := newGFp2(pool)
+ a.x.SetString("23423492374", 10)
+ a.y.SetString("12934872398472394827398470", 10)
+
+ inv := newGFp2(pool)
+ inv.Invert(a, pool)
+
+ b := newGFp2(pool).Mul(inv, a, pool)
+ if b.x.Int64() != 0 || b.y.Int64() != 1 {
+ t.Fatalf("bad result for a^-1*a: %s %s", b.x, b.y)
+ }
+
+ a.Put(pool)
+ b.Put(pool)
+ inv.Put(pool)
+
+ if c := pool.Count(); c > 0 {
+ t.Errorf("Pool count non-zero: %d\n", c)
+ }
+}
+
+func isZero(n *big.Int) bool {
+ return new(big.Int).Mod(n, P).Int64() == 0
+}
+
+func isOne(n *big.Int) bool {
+ return new(big.Int).Mod(n, P).Int64() == 1
+}
+
+func TestGFp6Invert(t *testing.T) {
+ pool := new(bnPool)
+
+ a := newGFp6(pool)
+ a.x.x.SetString("239487238491", 10)
+ a.x.y.SetString("2356249827341", 10)
+ a.y.x.SetString("082659782", 10)
+ a.y.y.SetString("182703523765", 10)
+ a.z.x.SetString("978236549263", 10)
+ a.z.y.SetString("64893242", 10)
+
+ inv := newGFp6(pool)
+ inv.Invert(a, pool)
+
+ b := newGFp6(pool).Mul(inv, a, pool)
+ if !isZero(b.x.x) ||
+ !isZero(b.x.y) ||
+ !isZero(b.y.x) ||
+ !isZero(b.y.y) ||
+ !isZero(b.z.x) ||
+ !isOne(b.z.y) {
+ t.Fatalf("bad result for a^-1*a: %s", b)
+ }
+
+ a.Put(pool)
+ b.Put(pool)
+ inv.Put(pool)
+
+ if c := pool.Count(); c > 0 {
+ t.Errorf("Pool count non-zero: %d\n", c)
+ }
+}
+
+func TestGFp12Invert(t *testing.T) {
+ pool := new(bnPool)
+
+ a := newGFp12(pool)
+ a.x.x.x.SetString("239846234862342323958623", 10)
+ a.x.x.y.SetString("2359862352529835623", 10)
+ a.x.y.x.SetString("928836523", 10)
+ a.x.y.y.SetString("9856234", 10)
+ a.x.z.x.SetString("235635286", 10)
+ a.x.z.y.SetString("5628392833", 10)
+ a.y.x.x.SetString("252936598265329856238956532167968", 10)
+ a.y.x.y.SetString("23596239865236954178968", 10)
+ a.y.y.x.SetString("95421692834", 10)
+ a.y.y.y.SetString("236548", 10)
+ a.y.z.x.SetString("924523", 10)
+ a.y.z.y.SetString("12954623", 10)
+
+ inv := newGFp12(pool)
+ inv.Invert(a, pool)
+
+ b := newGFp12(pool).Mul(inv, a, pool)
+ if !isZero(b.x.x.x) ||
+ !isZero(b.x.x.y) ||
+ !isZero(b.x.y.x) ||
+ !isZero(b.x.y.y) ||
+ !isZero(b.x.z.x) ||
+ !isZero(b.x.z.y) ||
+ !isZero(b.y.x.x) ||
+ !isZero(b.y.x.y) ||
+ !isZero(b.y.y.x) ||
+ !isZero(b.y.y.y) ||
+ !isZero(b.y.z.x) ||
+ !isOne(b.y.z.y) {
+ t.Fatalf("bad result for a^-1*a: %s", b)
+ }
+
+ a.Put(pool)
+ b.Put(pool)
+ inv.Put(pool)
+
+ if c := pool.Count(); c > 0 {
+ t.Errorf("Pool count non-zero: %d\n", c)
+ }
+}
+
+func TestCurveImpl(t *testing.T) {
+ pool := new(bnPool)
+
+ g := &curvePoint{
+ pool.Get().SetInt64(1),
+ pool.Get().SetInt64(-2),
+ pool.Get().SetInt64(1),
+ pool.Get().SetInt64(0),
+ }
+
+ x := pool.Get().SetInt64(32498273234)
+ X := newCurvePoint(pool).Mul(g, x, pool)
+
+ y := pool.Get().SetInt64(98732423523)
+ Y := newCurvePoint(pool).Mul(g, y, pool)
+
+ s1 := newCurvePoint(pool).Mul(X, y, pool).MakeAffine(pool)
+ s2 := newCurvePoint(pool).Mul(Y, x, pool).MakeAffine(pool)
+
+ if s1.x.Cmp(s2.x) != 0 ||
+ s2.x.Cmp(s1.x) != 0 {
+ t.Errorf("DH points don't match: (%s, %s) (%s, %s)", s1.x, s1.y, s2.x, s2.y)
+ }
+
+ pool.Put(x)
+ X.Put(pool)
+ pool.Put(y)
+ Y.Put(pool)
+ s1.Put(pool)
+ s2.Put(pool)
+ g.Put(pool)
+
+ if c := pool.Count(); c > 0 {
+ t.Errorf("Pool count non-zero: %d\n", c)
+ }
+}
+
+func TestOrderG1(t *testing.T) {
+ g := new(G1).ScalarBaseMult(Order)
+ if !g.p.IsInfinity() {
+ t.Error("G1 has incorrect order")
+ }
+
+ one := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1))
+ g.Add(g, one)
+ g.p.MakeAffine(nil)
+ if g.p.x.Cmp(one.p.x) != 0 || g.p.y.Cmp(one.p.y) != 0 {
+ t.Errorf("1+0 != 1 in G1")
+ }
+}
+
+func TestOrderG2(t *testing.T) {
+ g := new(G2).ScalarBaseMult(Order)
+ if !g.p.IsInfinity() {
+ t.Error("G2 has incorrect order")
+ }
+
+ one := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1))
+ g.Add(g, one)
+ g.p.MakeAffine(nil)
+ if g.p.x.x.Cmp(one.p.x.x) != 0 ||
+ g.p.x.y.Cmp(one.p.x.y) != 0 ||
+ g.p.y.x.Cmp(one.p.y.x) != 0 ||
+ g.p.y.y.Cmp(one.p.y.y) != 0 {
+ t.Errorf("1+0 != 1 in G2")
+ }
+}
+
+func TestOrderGT(t *testing.T) {
+ gt := Pair(&G1{curveGen}, &G2{twistGen})
+ g := new(GT).ScalarMult(gt, Order)
+ if !g.p.IsOne() {
+ t.Error("GT has incorrect order")
+ }
+}
+
+func TestBilinearity(t *testing.T) {
+ for i := 0; i < 2; i++ {
+ a, p1, _ := RandomG1(rand.Reader)
+ b, p2, _ := RandomG2(rand.Reader)
+ e1 := Pair(p1, p2)
+
+ e2 := Pair(&G1{curveGen}, &G2{twistGen})
+ e2.ScalarMult(e2, a)
+ e2.ScalarMult(e2, b)
+
+ minusE2 := new(GT).Neg(e2)
+ e1.Add(e1, minusE2)
+
+ if !e1.p.IsOne() {
+ t.Fatalf("bad pairing result: %s", e1)
+ }
+ }
+}
+
+func TestG1Marshal(t *testing.T) {
+ g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(1))
+ form := g.Marshal()
+ _, err := new(G1).Unmarshal(form)
+ if err != nil {
+ t.Fatalf("failed to unmarshal")
+ }
+
+ g.ScalarBaseMult(Order)
+ form = g.Marshal()
+
+ g2 := new(G1)
+ if _, err = g2.Unmarshal(form); err != nil {
+ t.Fatalf("failed to unmarshal ∞")
+ }
+ if !g2.p.IsInfinity() {
+ t.Fatalf("∞ unmarshaled incorrectly")
+ }
+}
+
+func TestG2Marshal(t *testing.T) {
+ g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(1))
+ form := g.Marshal()
+ _, err := new(G2).Unmarshal(form)
+ if err != nil {
+ t.Fatalf("failed to unmarshal")
+ }
+
+ g.ScalarBaseMult(Order)
+ form = g.Marshal()
+ g2 := new(G2)
+ if _, err = g2.Unmarshal(form); err != nil {
+ t.Fatalf("failed to unmarshal ∞")
+ }
+ if !g2.p.IsInfinity() {
+ t.Fatalf("∞ unmarshaled incorrectly")
+ }
+}
+
+func TestG1Identity(t *testing.T) {
+ g := new(G1).ScalarBaseMult(new(big.Int).SetInt64(0))
+ if !g.p.IsInfinity() {
+ t.Error("failure")
+ }
+}
+
+func TestG2Identity(t *testing.T) {
+ g := new(G2).ScalarBaseMult(new(big.Int).SetInt64(0))
+ if !g.p.IsInfinity() {
+ t.Error("failure")
+ }
+}
+
+func TestTripartiteDiffieHellman(t *testing.T) {
+ a, _ := rand.Int(rand.Reader, Order)
+ b, _ := rand.Int(rand.Reader, Order)
+ c, _ := rand.Int(rand.Reader, Order)
+
+ pa := new(G1)
+ pa.Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
+ qa := new(G2)
+ qa.Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
+ pb := new(G1)
+ pb.Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
+ qb := new(G2)
+ qb.Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
+ pc := new(G1)
+ pc.Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
+ qc := new(G2)
+ qc.Unmarshal(new(G2).ScalarBaseMult(c).Marshal())
+
+ k1 := Pair(pb, qc)
+ k1.ScalarMult(k1, a)
+ k1Bytes := k1.Marshal()
+
+ k2 := Pair(pc, qa)
+ k2.ScalarMult(k2, b)
+ k2Bytes := k2.Marshal()
+
+ k3 := Pair(pa, qb)
+ k3.ScalarMult(k3, c)
+ k3Bytes := k3.Marshal()
+
+ if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) {
+ t.Errorf("keys didn't agree")
+ }
+}
+
+func BenchmarkPairing(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Pair(&G1{curveGen}, &G2{twistGen})
+ }
+}
diff --git a/common/crypto/bn256/google/constants.go b/common/crypto/bn256/google/constants.go
new file mode 100644
index 0000000..2990bd9
--- /dev/null
+++ b/common/crypto/bn256/google/constants.go
@@ -0,0 +1,47 @@
+// Copyright 2012 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 bn256
+
+import (
+ "math/big"
+)
+
+func bigFromBase10(s string) *big.Int {
+ n, _ := new(big.Int).SetString(s, 10)
+ return n
+}
+
+// u is the BN parameter that determines the prime.
+var u = bigFromBase10("4965661367192848881")
+
+// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
+var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583")
+
+// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
+// Needs to be highly 2-adic for efficient SNARK key and proof generation.
+// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081.
+// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters.
+var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617")
+
+// xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9.
+var xiToPMinus1Over6 = &gfP2{bigFromBase10("16469823323077808223889137241176536799009286646108169935659301613961712198316"), bigFromBase10("8376118865763821496583973867626364092589906065868298776909617916018768340080")}
+
+// xiToPMinus1Over3 is ξ^((p-1)/3) where ξ = i+9.
+var xiToPMinus1Over3 = &gfP2{bigFromBase10("10307601595873709700152284273816112264069230130616436755625194854815875713954"), bigFromBase10("21575463638280843010398324269430826099269044274347216827212613867836435027261")}
+
+// xiToPMinus1Over2 is ξ^((p-1)/2) where ξ = i+9.
+var xiToPMinus1Over2 = &gfP2{bigFromBase10("3505843767911556378687030309984248845540243509899259641013678093033130930403"), bigFromBase10("2821565182194536844548159561693502659359617185244120367078079554186484126554")}
+
+// xiToPSquaredMinus1Over3 is ξ^((p²-1)/3) where ξ = i+9.
+var xiToPSquaredMinus1Over3 = bigFromBase10("21888242871839275220042445260109153167277707414472061641714758635765020556616")
+
+// xiTo2PSquaredMinus2Over3 is ξ^((2p²-2)/3) where ξ = i+9 (a cubic root of unity, mod p).
+var xiTo2PSquaredMinus2Over3 = bigFromBase10("2203960485148121921418603742825762020974279258880205651966")
+
+// xiToPSquaredMinus1Over6 is ξ^((1p²-1)/6) where ξ = i+9 (a cubic root of -1, mod p).
+var xiToPSquaredMinus1Over6 = bigFromBase10("21888242871839275220042445260109153167277707414472061641714758635765020556617")
+
+// xiTo2PMinus2Over3 is ξ^((2p-2)/3) where ξ = i+9.
+var xiTo2PMinus2Over3 = &gfP2{bigFromBase10("19937756971775647987995932169929341994314640652964949448313374472400716661030"), bigFromBase10("2581911344467009335267311115468803099551665605076196740867805258568234346338")}
diff --git a/common/crypto/bn256/google/curve.go b/common/crypto/bn256/google/curve.go
new file mode 100644
index 0000000..819cb81
--- /dev/null
+++ b/common/crypto/bn256/google/curve.go
@@ -0,0 +1,286 @@
+// Copyright 2012 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 bn256
+
+import (
+ "math/big"
+)
+
+// curvePoint implements the elliptic curve y²=x³+3. Points are kept in
+// Jacobian form and t=z² when valid. G₁ is the set of points of this curve on
+// GF(p).
+type curvePoint struct {
+ x, y, z, t *big.Int
+}
+
+var curveB = new(big.Int).SetInt64(3)
+
+// curveGen is the generator of G₁.
+var curveGen = &curvePoint{
+ new(big.Int).SetInt64(1),
+ new(big.Int).SetInt64(2),
+ new(big.Int).SetInt64(1),
+ new(big.Int).SetInt64(1),
+}
+
+func newCurvePoint(pool *bnPool) *curvePoint {
+ return &curvePoint{
+ pool.Get(),
+ pool.Get(),
+ pool.Get(),
+ pool.Get(),
+ }
+}
+
+func (c *curvePoint) String() string {
+ c.MakeAffine(new(bnPool))
+ return "(" + c.x.String() + ", " + c.y.String() + ")"
+}
+
+func (c *curvePoint) Put(pool *bnPool) {
+ pool.Put(c.x)
+ pool.Put(c.y)
+ pool.Put(c.z)
+ pool.Put(c.t)
+}
+
+func (c *curvePoint) Set(a *curvePoint) {
+ c.x.Set(a.x)
+ c.y.Set(a.y)
+ c.z.Set(a.z)
+ c.t.Set(a.t)
+}
+
+// IsOnCurve returns true iff c is on the curve where c must be in affine form.
+func (c *curvePoint) IsOnCurve() bool {
+ yy := new(big.Int).Mul(c.y, c.y)
+ xxx := new(big.Int).Mul(c.x, c.x)
+ xxx.Mul(xxx, c.x)
+ yy.Sub(yy, xxx)
+ yy.Sub(yy, curveB)
+ if yy.Sign() < 0 || yy.Cmp(P) >= 0 {
+ yy.Mod(yy, P)
+ }
+ return yy.Sign() == 0
+}
+
+func (c *curvePoint) SetInfinity() {
+ c.z.SetInt64(0)
+}
+
+func (c *curvePoint) IsInfinity() bool {
+ return c.z.Sign() == 0
+}
+
+func (c *curvePoint) Add(a, b *curvePoint, pool *bnPool) {
+ if a.IsInfinity() {
+ c.Set(b)
+ return
+ }
+ if b.IsInfinity() {
+ c.Set(a)
+ return
+ }
+
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
+
+ // Normalize the points by replacing a = [x1:y1:z1] and b = [x2:y2:z2]
+ // by [u1:s1:z1·z2] and [u2:s2:z1·z2]
+ // where u1 = x1·z2², s1 = y1·z2³ and u1 = x2·z1², s2 = y2·z1³
+ z1z1 := pool.Get().Mul(a.z, a.z)
+ z1z1.Mod(z1z1, P)
+ z2z2 := pool.Get().Mul(b.z, b.z)
+ z2z2.Mod(z2z2, P)
+ u1 := pool.Get().Mul(a.x, z2z2)
+ u1.Mod(u1, P)
+ u2 := pool.Get().Mul(b.x, z1z1)
+ u2.Mod(u2, P)
+
+ t := pool.Get().Mul(b.z, z2z2)
+ t.Mod(t, P)
+ s1 := pool.Get().Mul(a.y, t)
+ s1.Mod(s1, P)
+
+ t.Mul(a.z, z1z1)
+ t.Mod(t, P)
+ s2 := pool.Get().Mul(b.y, t)
+ s2.Mod(s2, P)
+
+ // Compute x = (2h)²(s²-u1-u2)
+ // where s = (s2-s1)/(u2-u1) is the slope of the line through
+ // (u1,s1) and (u2,s2). The extra factor 2h = 2(u2-u1) comes from the value of z below.
+ // This is also:
+ // 4(s2-s1)² - 4h²(u1+u2) = 4(s2-s1)² - 4h³ - 4h²(2u1)
+ // = r² - j - 2v
+ // with the notations below.
+ h := pool.Get().Sub(u2, u1)
+ xEqual := h.Sign() == 0
+
+ t.Add(h, h)
+ // i = 4h²
+ i := pool.Get().Mul(t, t)
+ i.Mod(i, P)
+ // j = 4h³
+ j := pool.Get().Mul(h, i)
+ j.Mod(j, P)
+
+ t.Sub(s2, s1)
+ yEqual := t.Sign() == 0
+ if xEqual && yEqual {
+ c.Double(a, pool)
+ return
+ }
+ r := pool.Get().Add(t, t)
+
+ v := pool.Get().Mul(u1, i)
+ v.Mod(v, P)
+
+ // t4 = 4(s2-s1)²
+ t4 := pool.Get().Mul(r, r)
+ t4.Mod(t4, P)
+ t.Add(v, v)
+ t6 := pool.Get().Sub(t4, j)
+ c.x.Sub(t6, t)
+
+ // Set y = -(2h)³(s1 + s*(x/4h²-u1))
+ // This is also
+ // y = - 2·s1·j - (s2-s1)(2x - 2i·u1) = r(v-x) - 2·s1·j
+ t.Sub(v, c.x) // t7
+ t4.Mul(s1, j) // t8
+ t4.Mod(t4, P)
+ t6.Add(t4, t4) // t9
+ t4.Mul(r, t) // t10
+ t4.Mod(t4, P)
+ c.y.Sub(t4, t6)
+
+ // Set z = 2(u2-u1)·z1·z2 = 2h·z1·z2
+ t.Add(a.z, b.z) // t11
+ t4.Mul(t, t) // t12
+ t4.Mod(t4, P)
+ t.Sub(t4, z1z1) // t13
+ t4.Sub(t, z2z2) // t14
+ c.z.Mul(t4, h)
+ c.z.Mod(c.z, P)
+
+ pool.Put(z1z1)
+ pool.Put(z2z2)
+ pool.Put(u1)
+ pool.Put(u2)
+ pool.Put(t)
+ pool.Put(s1)
+ pool.Put(s2)
+ pool.Put(h)
+ pool.Put(i)
+ pool.Put(j)
+ pool.Put(r)
+ pool.Put(v)
+ pool.Put(t4)
+ pool.Put(t6)
+}
+
+func (c *curvePoint) Double(a *curvePoint, pool *bnPool) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
+ A := pool.Get().Mul(a.x, a.x)
+ A.Mod(A, P)
+ B := pool.Get().Mul(a.y, a.y)
+ B.Mod(B, P)
+ C_ := pool.Get().Mul(B, B)
+ C_.Mod(C_, P)
+
+ t := pool.Get().Add(a.x, B)
+ t2 := pool.Get().Mul(t, t)
+ t2.Mod(t2, P)
+ t.Sub(t2, A)
+ t2.Sub(t, C_)
+ d := pool.Get().Add(t2, t2)
+ t.Add(A, A)
+ e := pool.Get().Add(t, A)
+ f := pool.Get().Mul(e, e)
+ f.Mod(f, P)
+
+ t.Add(d, d)
+ c.x.Sub(f, t)
+
+ t.Add(C_, C_)
+ t2.Add(t, t)
+ t.Add(t2, t2)
+ c.y.Sub(d, c.x)
+ t2.Mul(e, c.y)
+ t2.Mod(t2, P)
+ c.y.Sub(t2, t)
+
+ t.Mul(a.y, a.z)
+ t.Mod(t, P)
+ c.z.Add(t, t)
+
+ pool.Put(A)
+ pool.Put(B)
+ pool.Put(C_)
+ pool.Put(t)
+ pool.Put(t2)
+ pool.Put(d)
+ pool.Put(e)
+ pool.Put(f)
+}
+
+func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int, pool *bnPool) *curvePoint {
+ sum := newCurvePoint(pool)
+ sum.SetInfinity()
+ t := newCurvePoint(pool)
+
+ for i := scalar.BitLen(); i >= 0; i-- {
+ t.Double(sum, pool)
+ if scalar.Bit(i) != 0 {
+ sum.Add(t, a, pool)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+ sum.Put(pool)
+ t.Put(pool)
+ return c
+}
+
+// MakeAffine converts c to affine form and returns c. If c is ∞, then it sets
+// c to 0 : 1 : 0.
+func (c *curvePoint) MakeAffine(pool *bnPool) *curvePoint {
+ if words := c.z.Bits(); len(words) == 1 && words[0] == 1 {
+ return c
+ }
+ if c.IsInfinity() {
+ c.x.SetInt64(0)
+ c.y.SetInt64(1)
+ c.z.SetInt64(0)
+ c.t.SetInt64(0)
+ return c
+ }
+ zInv := pool.Get().ModInverse(c.z, P)
+ t := pool.Get().Mul(c.y, zInv)
+ t.Mod(t, P)
+ zInv2 := pool.Get().Mul(zInv, zInv)
+ zInv2.Mod(zInv2, P)
+ c.y.Mul(t, zInv2)
+ c.y.Mod(c.y, P)
+ t.Mul(c.x, zInv2)
+ t.Mod(t, P)
+ c.x.Set(t)
+ c.z.SetInt64(1)
+ c.t.SetInt64(1)
+
+ pool.Put(zInv)
+ pool.Put(t)
+ pool.Put(zInv2)
+
+ return c
+}
+
+func (c *curvePoint) Negative(a *curvePoint) {
+ c.x.Set(a.x)
+ c.y.Neg(a.y)
+ c.z.Set(a.z)
+ c.t.SetInt64(0)
+}
diff --git a/common/crypto/bn256/google/example_test.go b/common/crypto/bn256/google/example_test.go
new file mode 100644
index 0000000..b2d1980
--- /dev/null
+++ b/common/crypto/bn256/google/example_test.go
@@ -0,0 +1,43 @@
+// Copyright 2012 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 bn256
+
+import (
+ "crypto/rand"
+)
+
+func ExamplePair() {
+ // This implements the tripartite Diffie-Hellman algorithm from "A One
+ // Round Protocol for Tripartite Diffie-Hellman", A. Joux.
+ // http://www.springerlink.com/content/cddc57yyva0hburb/fulltext.pdf
+
+ // Each of three parties, a, b and c, generate a private value.
+ a, _ := rand.Int(rand.Reader, Order)
+ b, _ := rand.Int(rand.Reader, Order)
+ c, _ := rand.Int(rand.Reader, Order)
+
+ // Then each party calculates g₁ and g₂ times their private value.
+ pa := new(G1).ScalarBaseMult(a)
+ qa := new(G2).ScalarBaseMult(a)
+
+ pb := new(G1).ScalarBaseMult(b)
+ qb := new(G2).ScalarBaseMult(b)
+
+ pc := new(G1).ScalarBaseMult(c)
+ qc := new(G2).ScalarBaseMult(c)
+
+ // Now each party exchanges its public values with the other two and
+ // all parties can calculate the shared key.
+ k1 := Pair(pb, qc)
+ k1.ScalarMult(k1, a)
+
+ k2 := Pair(pc, qa)
+ k2.ScalarMult(k2, b)
+
+ k3 := Pair(pa, qb)
+ k3.ScalarMult(k3, c)
+
+ // k1, k2 and k3 will all be equal.
+}
diff --git a/common/crypto/bn256/google/gfp12.go b/common/crypto/bn256/google/gfp12.go
new file mode 100644
index 0000000..f084edd
--- /dev/null
+++ b/common/crypto/bn256/google/gfp12.go
@@ -0,0 +1,200 @@
+// Copyright 2012 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 bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+import (
+ "math/big"
+)
+
+// gfP12 implements the field of size p¹² as a quadratic extension of gfP6
+// where ω²=τ.
+type gfP12 struct {
+ x, y *gfP6 // value is xω + y
+}
+
+func newGFp12(pool *bnPool) *gfP12 {
+ return &gfP12{newGFp6(pool), newGFp6(pool)}
+}
+
+func (e *gfP12) String() string {
+ return "(" + e.x.String() + "," + e.y.String() + ")"
+}
+
+func (e *gfP12) Put(pool *bnPool) {
+ e.x.Put(pool)
+ e.y.Put(pool)
+}
+
+func (e *gfP12) Set(a *gfP12) *gfP12 {
+ e.x.Set(a.x)
+ e.y.Set(a.y)
+ return e
+}
+
+func (e *gfP12) SetZero() *gfP12 {
+ e.x.SetZero()
+ e.y.SetZero()
+ return e
+}
+
+func (e *gfP12) SetOne() *gfP12 {
+ e.x.SetZero()
+ e.y.SetOne()
+ return e
+}
+
+func (e *gfP12) Minimal() {
+ e.x.Minimal()
+ e.y.Minimal()
+}
+
+func (e *gfP12) IsZero() bool {
+ e.Minimal()
+ return e.x.IsZero() && e.y.IsZero()
+}
+
+func (e *gfP12) IsOne() bool {
+ e.Minimal()
+ return e.x.IsZero() && e.y.IsOne()
+}
+
+func (e *gfP12) Conjugate(a *gfP12) *gfP12 {
+ e.x.Negative(a.x)
+ e.y.Set(a.y)
+ return a
+}
+
+func (e *gfP12) Negative(a *gfP12) *gfP12 {
+ e.x.Negative(a.x)
+ e.y.Negative(a.y)
+ return e
+}
+
+// Frobenius computes (xω+y)^p = x^p ω·ξ^((p-1)/6) + y^p
+func (e *gfP12) Frobenius(a *gfP12, pool *bnPool) *gfP12 {
+ e.x.Frobenius(a.x, pool)
+ e.y.Frobenius(a.y, pool)
+ e.x.MulScalar(e.x, xiToPMinus1Over6, pool)
+ return e
+}
+
+// FrobeniusP2 computes (xω+y)^p² = x^p² ω·ξ^((p²-1)/6) + y^p²
+func (e *gfP12) FrobeniusP2(a *gfP12, pool *bnPool) *gfP12 {
+ e.x.FrobeniusP2(a.x)
+ e.x.MulGFP(e.x, xiToPSquaredMinus1Over6)
+ e.y.FrobeniusP2(a.y)
+ return e
+}
+
+func (e *gfP12) Add(a, b *gfP12) *gfP12 {
+ e.x.Add(a.x, b.x)
+ e.y.Add(a.y, b.y)
+ return e
+}
+
+func (e *gfP12) Sub(a, b *gfP12) *gfP12 {
+ e.x.Sub(a.x, b.x)
+ e.y.Sub(a.y, b.y)
+ return e
+}
+
+func (e *gfP12) Mul(a, b *gfP12, pool *bnPool) *gfP12 {
+ tx := newGFp6(pool)
+ tx.Mul(a.x, b.y, pool)
+ t := newGFp6(pool)
+ t.Mul(b.x, a.y, pool)
+ tx.Add(tx, t)
+
+ ty := newGFp6(pool)
+ ty.Mul(a.y, b.y, pool)
+ t.Mul(a.x, b.x, pool)
+ t.MulTau(t, pool)
+ e.y.Add(ty, t)
+ e.x.Set(tx)
+
+ tx.Put(pool)
+ ty.Put(pool)
+ t.Put(pool)
+ return e
+}
+
+func (e *gfP12) MulScalar(a *gfP12, b *gfP6, pool *bnPool) *gfP12 {
+ e.x.Mul(e.x, b, pool)
+ e.y.Mul(e.y, b, pool)
+ return e
+}
+
+func (c *gfP12) Exp(a *gfP12, power *big.Int, pool *bnPool) *gfP12 {
+ sum := newGFp12(pool)
+ sum.SetOne()
+ t := newGFp12(pool)
+
+ for i := power.BitLen() - 1; i >= 0; i-- {
+ t.Square(sum, pool)
+ if power.Bit(i) != 0 {
+ sum.Mul(t, a, pool)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+
+ sum.Put(pool)
+ t.Put(pool)
+
+ return c
+}
+
+func (e *gfP12) Square(a *gfP12, pool *bnPool) *gfP12 {
+ // Complex squaring algorithm
+ v0 := newGFp6(pool)
+ v0.Mul(a.x, a.y, pool)
+
+ t := newGFp6(pool)
+ t.MulTau(a.x, pool)
+ t.Add(a.y, t)
+ ty := newGFp6(pool)
+ ty.Add(a.x, a.y)
+ ty.Mul(ty, t, pool)
+ ty.Sub(ty, v0)
+ t.MulTau(v0, pool)
+ ty.Sub(ty, t)
+
+ e.y.Set(ty)
+ e.x.Double(v0)
+
+ v0.Put(pool)
+ t.Put(pool)
+ ty.Put(pool)
+
+ return e
+}
+
+func (e *gfP12) Invert(a *gfP12, pool *bnPool) *gfP12 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+ t1 := newGFp6(pool)
+ t2 := newGFp6(pool)
+
+ t1.Square(a.x, pool)
+ t2.Square(a.y, pool)
+ t1.MulTau(t1, pool)
+ t1.Sub(t2, t1)
+ t2.Invert(t1, pool)
+
+ e.x.Negative(a.x)
+ e.y.Set(a.y)
+ e.MulScalar(e, t2, pool)
+
+ t1.Put(pool)
+ t2.Put(pool)
+
+ return e
+}
diff --git a/common/crypto/bn256/google/gfp2.go b/common/crypto/bn256/google/gfp2.go
new file mode 100644
index 0000000..3981f6c
--- /dev/null
+++ b/common/crypto/bn256/google/gfp2.go
@@ -0,0 +1,227 @@
+// Copyright 2012 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 bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+import (
+ "math/big"
+)
+
+// gfP2 implements a field of size p² as a quadratic extension of the base
+// field where i²=-1.
+type gfP2 struct {
+ x, y *big.Int // value is xi+y.
+}
+
+func newGFp2(pool *bnPool) *gfP2 {
+ return &gfP2{pool.Get(), pool.Get()}
+}
+
+func (e *gfP2) String() string {
+ x := new(big.Int).Mod(e.x, P)
+ y := new(big.Int).Mod(e.y, P)
+ return "(" + x.String() + "," + y.String() + ")"
+}
+
+func (e *gfP2) Put(pool *bnPool) {
+ pool.Put(e.x)
+ pool.Put(e.y)
+}
+
+func (e *gfP2) Set(a *gfP2) *gfP2 {
+ e.x.Set(a.x)
+ e.y.Set(a.y)
+ return e
+}
+
+func (e *gfP2) SetZero() *gfP2 {
+ e.x.SetInt64(0)
+ e.y.SetInt64(0)
+ return e
+}
+
+func (e *gfP2) SetOne() *gfP2 {
+ e.x.SetInt64(0)
+ e.y.SetInt64(1)
+ return e
+}
+
+func (e *gfP2) Minimal() {
+ if e.x.Sign() < 0 || e.x.Cmp(P) >= 0 {
+ e.x.Mod(e.x, P)
+ }
+ if e.y.Sign() < 0 || e.y.Cmp(P) >= 0 {
+ e.y.Mod(e.y, P)
+ }
+}
+
+func (e *gfP2) IsZero() bool {
+ return e.x.Sign() == 0 && e.y.Sign() == 0
+}
+
+func (e *gfP2) IsOne() bool {
+ if e.x.Sign() != 0 {
+ return false
+ }
+ words := e.y.Bits()
+ return len(words) == 1 && words[0] == 1
+}
+
+func (e *gfP2) Conjugate(a *gfP2) *gfP2 {
+ e.y.Set(a.y)
+ e.x.Neg(a.x)
+ return e
+}
+
+func (e *gfP2) Negative(a *gfP2) *gfP2 {
+ e.x.Neg(a.x)
+ e.y.Neg(a.y)
+ return e
+}
+
+func (e *gfP2) Add(a, b *gfP2) *gfP2 {
+ e.x.Add(a.x, b.x)
+ e.y.Add(a.y, b.y)
+ return e
+}
+
+func (e *gfP2) Sub(a, b *gfP2) *gfP2 {
+ e.x.Sub(a.x, b.x)
+ e.y.Sub(a.y, b.y)
+ return e
+}
+
+func (e *gfP2) Double(a *gfP2) *gfP2 {
+ e.x.Lsh(a.x, 1)
+ e.y.Lsh(a.y, 1)
+ return e
+}
+
+func (c *gfP2) Exp(a *gfP2, power *big.Int, pool *bnPool) *gfP2 {
+ sum := newGFp2(pool)
+ sum.SetOne()
+ t := newGFp2(pool)
+
+ for i := power.BitLen() - 1; i >= 0; i-- {
+ t.Square(sum, pool)
+ if power.Bit(i) != 0 {
+ sum.Mul(t, a, pool)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+
+ sum.Put(pool)
+ t.Put(pool)
+
+ return c
+}
+
+// See "Multiplication and Squaring in Pairing-Friendly Fields",
+// http://eprint.iacr.org/2006/471.pdf
+func (e *gfP2) Mul(a, b *gfP2, pool *bnPool) *gfP2 {
+ tx := pool.Get().Mul(a.x, b.y)
+ t := pool.Get().Mul(b.x, a.y)
+ tx.Add(tx, t)
+ tx.Mod(tx, P)
+
+ ty := pool.Get().Mul(a.y, b.y)
+ t.Mul(a.x, b.x)
+ ty.Sub(ty, t)
+ e.y.Mod(ty, P)
+ e.x.Set(tx)
+
+ pool.Put(tx)
+ pool.Put(ty)
+ pool.Put(t)
+
+ return e
+}
+
+func (e *gfP2) MulScalar(a *gfP2, b *big.Int) *gfP2 {
+ e.x.Mul(a.x, b)
+ e.y.Mul(a.y, b)
+ return e
+}
+
+// MulXi sets e=ξa where ξ=i+9 and then returns e.
+func (e *gfP2) MulXi(a *gfP2, pool *bnPool) *gfP2 {
+ // (xi+y)(i+3) = (9x+y)i+(9y-x)
+ tx := pool.Get().Lsh(a.x, 3)
+ tx.Add(tx, a.x)
+ tx.Add(tx, a.y)
+
+ ty := pool.Get().Lsh(a.y, 3)
+ ty.Add(ty, a.y)
+ ty.Sub(ty, a.x)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+
+ pool.Put(tx)
+ pool.Put(ty)
+
+ return e
+}
+
+func (e *gfP2) Square(a *gfP2, pool *bnPool) *gfP2 {
+ // Complex squaring algorithm:
+ // (xi+b)² = (x+y)(y-x) + 2*i*x*y
+ t1 := pool.Get().Sub(a.y, a.x)
+ t2 := pool.Get().Add(a.x, a.y)
+ ty := pool.Get().Mul(t1, t2)
+ ty.Mod(ty, P)
+
+ t1.Mul(a.x, a.y)
+ t1.Lsh(t1, 1)
+
+ e.x.Mod(t1, P)
+ e.y.Set(ty)
+
+ pool.Put(t1)
+ pool.Put(t2)
+ pool.Put(ty)
+
+ return e
+}
+
+func (e *gfP2) Invert(a *gfP2, pool *bnPool) *gfP2 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+ t := pool.Get()
+ t.Mul(a.y, a.y)
+ t2 := pool.Get()
+ t2.Mul(a.x, a.x)
+ t.Add(t, t2)
+
+ inv := pool.Get()
+ inv.ModInverse(t, P)
+
+ e.x.Neg(a.x)
+ e.x.Mul(e.x, inv)
+ e.x.Mod(e.x, P)
+
+ e.y.Mul(a.y, inv)
+ e.y.Mod(e.y, P)
+
+ pool.Put(t)
+ pool.Put(t2)
+ pool.Put(inv)
+
+ return e
+}
+
+func (e *gfP2) Real() *big.Int {
+ return e.x
+}
+
+func (e *gfP2) Imag() *big.Int {
+ return e.y
+}
diff --git a/common/crypto/bn256/google/gfp6.go b/common/crypto/bn256/google/gfp6.go
new file mode 100644
index 0000000..2188566
--- /dev/null
+++ b/common/crypto/bn256/google/gfp6.go
@@ -0,0 +1,296 @@
+// Copyright 2012 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 bn256
+
+// For details of the algorithms used, see "Multiplication and Squaring on
+// Pairing-Friendly Fields, Devegili et al.
+// http://eprint.iacr.org/2006/471.pdf.
+
+import (
+ "math/big"
+)
+
+// gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ
+// and ξ=i+9.
+type gfP6 struct {
+ x, y, z *gfP2 // value is xτ² + yτ + z
+}
+
+func newGFp6(pool *bnPool) *gfP6 {
+ return &gfP6{newGFp2(pool), newGFp2(pool), newGFp2(pool)}
+}
+
+func (e *gfP6) String() string {
+ return "(" + e.x.String() + "," + e.y.String() + "," + e.z.String() + ")"
+}
+
+func (e *gfP6) Put(pool *bnPool) {
+ e.x.Put(pool)
+ e.y.Put(pool)
+ e.z.Put(pool)
+}
+
+func (e *gfP6) Set(a *gfP6) *gfP6 {
+ e.x.Set(a.x)
+ e.y.Set(a.y)
+ e.z.Set(a.z)
+ return e
+}
+
+func (e *gfP6) SetZero() *gfP6 {
+ e.x.SetZero()
+ e.y.SetZero()
+ e.z.SetZero()
+ return e
+}
+
+func (e *gfP6) SetOne() *gfP6 {
+ e.x.SetZero()
+ e.y.SetZero()
+ e.z.SetOne()
+ return e
+}
+
+func (e *gfP6) Minimal() {
+ e.x.Minimal()
+ e.y.Minimal()
+ e.z.Minimal()
+}
+
+func (e *gfP6) IsZero() bool {
+ return e.x.IsZero() && e.y.IsZero() && e.z.IsZero()
+}
+
+func (e *gfP6) IsOne() bool {
+ return e.x.IsZero() && e.y.IsZero() && e.z.IsOne()
+}
+
+func (e *gfP6) Negative(a *gfP6) *gfP6 {
+ e.x.Negative(a.x)
+ e.y.Negative(a.y)
+ e.z.Negative(a.z)
+ return e
+}
+
+func (e *gfP6) Frobenius(a *gfP6, pool *bnPool) *gfP6 {
+ e.x.Conjugate(a.x)
+ e.y.Conjugate(a.y)
+ e.z.Conjugate(a.z)
+
+ e.x.Mul(e.x, xiTo2PMinus2Over3, pool)
+ e.y.Mul(e.y, xiToPMinus1Over3, pool)
+ return e
+}
+
+// FrobeniusP2 computes (xτ²+yτ+z)^(p²) = xτ^(2p²) + yτ^(p²) + z
+func (e *gfP6) FrobeniusP2(a *gfP6) *gfP6 {
+ // τ^(2p²) = τ²τ^(2p²-2) = τ²ξ^((2p²-2)/3)
+ e.x.MulScalar(a.x, xiTo2PSquaredMinus2Over3)
+ // τ^(p²) = ττ^(p²-1) = τξ^((p²-1)/3)
+ e.y.MulScalar(a.y, xiToPSquaredMinus1Over3)
+ e.z.Set(a.z)
+ return e
+}
+
+func (e *gfP6) Add(a, b *gfP6) *gfP6 {
+ e.x.Add(a.x, b.x)
+ e.y.Add(a.y, b.y)
+ e.z.Add(a.z, b.z)
+ return e
+}
+
+func (e *gfP6) Sub(a, b *gfP6) *gfP6 {
+ e.x.Sub(a.x, b.x)
+ e.y.Sub(a.y, b.y)
+ e.z.Sub(a.z, b.z)
+ return e
+}
+
+func (e *gfP6) Double(a *gfP6) *gfP6 {
+ e.x.Double(a.x)
+ e.y.Double(a.y)
+ e.z.Double(a.z)
+ return e
+}
+
+func (e *gfP6) Mul(a, b *gfP6, pool *bnPool) *gfP6 {
+ // "Multiplication and Squaring on Pairing-Friendly Fields"
+ // Section 4, Karatsuba method.
+ // http://eprint.iacr.org/2006/471.pdf
+
+ v0 := newGFp2(pool)
+ v0.Mul(a.z, b.z, pool)
+ v1 := newGFp2(pool)
+ v1.Mul(a.y, b.y, pool)
+ v2 := newGFp2(pool)
+ v2.Mul(a.x, b.x, pool)
+
+ t0 := newGFp2(pool)
+ t0.Add(a.x, a.y)
+ t1 := newGFp2(pool)
+ t1.Add(b.x, b.y)
+ tz := newGFp2(pool)
+ tz.Mul(t0, t1, pool)
+
+ tz.Sub(tz, v1)
+ tz.Sub(tz, v2)
+ tz.MulXi(tz, pool)
+ tz.Add(tz, v0)
+
+ t0.Add(a.y, a.z)
+ t1.Add(b.y, b.z)
+ ty := newGFp2(pool)
+ ty.Mul(t0, t1, pool)
+ ty.Sub(ty, v0)
+ ty.Sub(ty, v1)
+ t0.MulXi(v2, pool)
+ ty.Add(ty, t0)
+
+ t0.Add(a.x, a.z)
+ t1.Add(b.x, b.z)
+ tx := newGFp2(pool)
+ tx.Mul(t0, t1, pool)
+ tx.Sub(tx, v0)
+ tx.Add(tx, v1)
+ tx.Sub(tx, v2)
+
+ e.x.Set(tx)
+ e.y.Set(ty)
+ e.z.Set(tz)
+
+ t0.Put(pool)
+ t1.Put(pool)
+ tx.Put(pool)
+ ty.Put(pool)
+ tz.Put(pool)
+ v0.Put(pool)
+ v1.Put(pool)
+ v2.Put(pool)
+ return e
+}
+
+func (e *gfP6) MulScalar(a *gfP6, b *gfP2, pool *bnPool) *gfP6 {
+ e.x.Mul(a.x, b, pool)
+ e.y.Mul(a.y, b, pool)
+ e.z.Mul(a.z, b, pool)
+ return e
+}
+
+func (e *gfP6) MulGFP(a *gfP6, b *big.Int) *gfP6 {
+ e.x.MulScalar(a.x, b)
+ e.y.MulScalar(a.y, b)
+ e.z.MulScalar(a.z, b)
+ return e
+}
+
+// MulTau computes τ·(aτ²+bτ+c) = bτ²+cτ+aξ
+func (e *gfP6) MulTau(a *gfP6, pool *bnPool) {
+ tz := newGFp2(pool)
+ tz.MulXi(a.x, pool)
+ ty := newGFp2(pool)
+ ty.Set(a.y)
+ e.y.Set(a.z)
+ e.x.Set(ty)
+ e.z.Set(tz)
+ tz.Put(pool)
+ ty.Put(pool)
+}
+
+func (e *gfP6) Square(a *gfP6, pool *bnPool) *gfP6 {
+ v0 := newGFp2(pool).Square(a.z, pool)
+ v1 := newGFp2(pool).Square(a.y, pool)
+ v2 := newGFp2(pool).Square(a.x, pool)
+
+ c0 := newGFp2(pool).Add(a.x, a.y)
+ c0.Square(c0, pool)
+ c0.Sub(c0, v1)
+ c0.Sub(c0, v2)
+ c0.MulXi(c0, pool)
+ c0.Add(c0, v0)
+
+ c1 := newGFp2(pool).Add(a.y, a.z)
+ c1.Square(c1, pool)
+ c1.Sub(c1, v0)
+ c1.Sub(c1, v1)
+ xiV2 := newGFp2(pool).MulXi(v2, pool)
+ c1.Add(c1, xiV2)
+
+ c2 := newGFp2(pool).Add(a.x, a.z)
+ c2.Square(c2, pool)
+ c2.Sub(c2, v0)
+ c2.Add(c2, v1)
+ c2.Sub(c2, v2)
+
+ e.x.Set(c2)
+ e.y.Set(c1)
+ e.z.Set(c0)
+
+ v0.Put(pool)
+ v1.Put(pool)
+ v2.Put(pool)
+ c0.Put(pool)
+ c1.Put(pool)
+ c2.Put(pool)
+ xiV2.Put(pool)
+
+ return e
+}
+
+func (e *gfP6) Invert(a *gfP6, pool *bnPool) *gfP6 {
+ // See "Implementing cryptographic pairings", M. Scott, section 3.2.
+ // ftp://136.206.11.249/pub/crypto/pairings.pdf
+
+ // Here we can give a short explanation of how it works: let j be a cubic root of
+ // unity in GF(p²) so that 1+j+j²=0.
+ // Then (xτ² + yτ + z)(xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
+ // = (xτ² + yτ + z)(Cτ²+Bτ+A)
+ // = (x³ξ²+y³ξ+z³-3ξxyz) = F is an element of the base field (the norm).
+ //
+ // On the other hand (xj²τ² + yjτ + z)(xjτ² + yj²τ + z)
+ // = τ²(y²-ξxz) + τ(ξx²-yz) + (z²-ξxy)
+ //
+ // So that's why A = (z²-ξxy), B = (ξx²-yz), C = (y²-ξxz)
+ t1 := newGFp2(pool)
+
+ A := newGFp2(pool)
+ A.Square(a.z, pool)
+ t1.Mul(a.x, a.y, pool)
+ t1.MulXi(t1, pool)
+ A.Sub(A, t1)
+
+ B := newGFp2(pool)
+ B.Square(a.x, pool)
+ B.MulXi(B, pool)
+ t1.Mul(a.y, a.z, pool)
+ B.Sub(B, t1)
+
+ C_ := newGFp2(pool)
+ C_.Square(a.y, pool)
+ t1.Mul(a.x, a.z, pool)
+ C_.Sub(C_, t1)
+
+ F := newGFp2(pool)
+ F.Mul(C_, a.y, pool)
+ F.MulXi(F, pool)
+ t1.Mul(A, a.z, pool)
+ F.Add(F, t1)
+ t1.Mul(B, a.x, pool)
+ t1.MulXi(t1, pool)
+ F.Add(F, t1)
+
+ F.Invert(F, pool)
+
+ e.x.Mul(C_, F, pool)
+ e.y.Mul(B, F, pool)
+ e.z.Mul(A, F, pool)
+
+ t1.Put(pool)
+ A.Put(pool)
+ B.Put(pool)
+ C_.Put(pool)
+ F.Put(pool)
+
+ return e
+}
diff --git a/common/crypto/bn256/google/main_test.go b/common/crypto/bn256/google/main_test.go
new file mode 100644
index 0000000..c0c8545
--- /dev/null
+++ b/common/crypto/bn256/google/main_test.go
@@ -0,0 +1,71 @@
+package bn256
+
+import (
+ "testing"
+
+ "crypto/rand"
+)
+
+func TestRandomG2Marshal(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ n, g2, err := RandomG2(rand.Reader)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ t.Logf("%v: %x\n", n, g2.Marshal())
+ }
+}
+
+func TestPairings(t *testing.T) {
+ a1 := new(G1).ScalarBaseMult(bigFromBase10("1"))
+ a2 := new(G1).ScalarBaseMult(bigFromBase10("2"))
+ a37 := new(G1).ScalarBaseMult(bigFromBase10("37"))
+ an1 := new(G1).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
+
+ b0 := new(G2).ScalarBaseMult(bigFromBase10("0"))
+ b1 := new(G2).ScalarBaseMult(bigFromBase10("1"))
+ b2 := new(G2).ScalarBaseMult(bigFromBase10("2"))
+ b27 := new(G2).ScalarBaseMult(bigFromBase10("27"))
+ b999 := new(G2).ScalarBaseMult(bigFromBase10("999"))
+ bn1 := new(G2).ScalarBaseMult(bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495616"))
+
+ p1 := Pair(a1, b1)
+ pn1 := Pair(a1, bn1)
+ np1 := Pair(an1, b1)
+ if pn1.String() != np1.String() {
+ t.Error("Pairing mismatch: e(a, -b) != e(-a, b)")
+ }
+ if !PairingCheck([]*G1{a1, an1}, []*G2{b1, b1}) {
+ t.Error("MultiAte check gave false negative!")
+ }
+ p0 := new(GT).Add(p1, pn1)
+ p0_2 := Pair(a1, b0)
+ if p0.String() != p0_2.String() {
+ t.Error("Pairing mismatch: e(a, b) * e(a, -b) != 1")
+ }
+ p0_3 := new(GT).ScalarMult(p1, bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617"))
+ if p0.String() != p0_3.String() {
+ t.Error("Pairing mismatch: e(a, b) has wrong order")
+ }
+ p2 := Pair(a2, b1)
+ p2_2 := Pair(a1, b2)
+ p2_3 := new(GT).ScalarMult(p1, bigFromBase10("2"))
+ if p2.String() != p2_2.String() {
+ t.Error("Pairing mismatch: e(a, b * 2) != e(a * 2, b)")
+ }
+ if p2.String() != p2_3.String() {
+ t.Error("Pairing mismatch: e(a, b * 2) != e(a, b) ** 2")
+ }
+ if p2.String() == p1.String() {
+ t.Error("Pairing is degenerate!")
+ }
+ if PairingCheck([]*G1{a1, a1}, []*G2{b1, b1}) {
+ t.Error("MultiAte check gave false positive!")
+ }
+ p999 := Pair(a37, b27)
+ p999_2 := Pair(a1, b999)
+ if p999.String() != p999_2.String() {
+ t.Error("Pairing mismatch: e(a * 37, b * 27) != e(a, b * 999)")
+ }
+}
diff --git a/common/crypto/bn256/google/optate.go b/common/crypto/bn256/google/optate.go
new file mode 100644
index 0000000..9d69570
--- /dev/null
+++ b/common/crypto/bn256/google/optate.go
@@ -0,0 +1,397 @@
+// Copyright 2012 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 bn256
+
+func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
+ // See the mixed addition algorithm from "Faster Computation of the
+ // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
+
+ B := newGFp2(pool).Mul(p.x, r.t, pool)
+
+ D := newGFp2(pool).Add(p.y, r.z)
+ D.Square(D, pool)
+ D.Sub(D, r2)
+ D.Sub(D, r.t)
+ D.Mul(D, r.t, pool)
+
+ H := newGFp2(pool).Sub(B, r.x)
+ I := newGFp2(pool).Square(H, pool)
+
+ E := newGFp2(pool).Add(I, I)
+ E.Add(E, E)
+
+ J := newGFp2(pool).Mul(H, E, pool)
+
+ L1 := newGFp2(pool).Sub(D, r.y)
+ L1.Sub(L1, r.y)
+
+ V := newGFp2(pool).Mul(r.x, E, pool)
+
+ rOut = newTwistPoint(pool)
+ rOut.x.Square(L1, pool)
+ rOut.x.Sub(rOut.x, J)
+ rOut.x.Sub(rOut.x, V)
+ rOut.x.Sub(rOut.x, V)
+
+ rOut.z.Add(r.z, H)
+ rOut.z.Square(rOut.z, pool)
+ rOut.z.Sub(rOut.z, r.t)
+ rOut.z.Sub(rOut.z, I)
+
+ t := newGFp2(pool).Sub(V, rOut.x)
+ t.Mul(t, L1, pool)
+ t2 := newGFp2(pool).Mul(r.y, J, pool)
+ t2.Add(t2, t2)
+ rOut.y.Sub(t, t2)
+
+ rOut.t.Square(rOut.z, pool)
+
+ t.Add(p.y, rOut.z)
+ t.Square(t, pool)
+ t.Sub(t, r2)
+ t.Sub(t, rOut.t)
+
+ t2.Mul(L1, p.x, pool)
+ t2.Add(t2, t2)
+ a = newGFp2(pool)
+ a.Sub(t2, t)
+
+ c = newGFp2(pool)
+ c.MulScalar(rOut.z, q.y)
+ c.Add(c, c)
+
+ b = newGFp2(pool)
+ b.SetZero()
+ b.Sub(b, L1)
+ b.MulScalar(b, q.x)
+ b.Add(b, b)
+
+ B.Put(pool)
+ D.Put(pool)
+ H.Put(pool)
+ I.Put(pool)
+ E.Put(pool)
+ J.Put(pool)
+ L1.Put(pool)
+ V.Put(pool)
+ t.Put(pool)
+ t2.Put(pool)
+
+ return
+}
+
+func lineFunctionDouble(r *twistPoint, q *curvePoint, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
+ // See the doubling algorithm for a=0 from "Faster Computation of the
+ // Tate Pairing", http://arxiv.org/pdf/0904.0854v3.pdf
+
+ A := newGFp2(pool).Square(r.x, pool)
+ B := newGFp2(pool).Square(r.y, pool)
+ C_ := newGFp2(pool).Square(B, pool)
+
+ D := newGFp2(pool).Add(r.x, B)
+ D.Square(D, pool)
+ D.Sub(D, A)
+ D.Sub(D, C_)
+ D.Add(D, D)
+
+ E := newGFp2(pool).Add(A, A)
+ E.Add(E, A)
+
+ G := newGFp2(pool).Square(E, pool)
+
+ rOut = newTwistPoint(pool)
+ rOut.x.Sub(G, D)
+ rOut.x.Sub(rOut.x, D)
+
+ rOut.z.Add(r.y, r.z)
+ rOut.z.Square(rOut.z, pool)
+ rOut.z.Sub(rOut.z, B)
+ rOut.z.Sub(rOut.z, r.t)
+
+ rOut.y.Sub(D, rOut.x)
+ rOut.y.Mul(rOut.y, E, pool)
+ t := newGFp2(pool).Add(C_, C_)
+ t.Add(t, t)
+ t.Add(t, t)
+ rOut.y.Sub(rOut.y, t)
+
+ rOut.t.Square(rOut.z, pool)
+
+ t.Mul(E, r.t, pool)
+ t.Add(t, t)
+ b = newGFp2(pool)
+ b.SetZero()
+ b.Sub(b, t)
+ b.MulScalar(b, q.x)
+
+ a = newGFp2(pool)
+ a.Add(r.x, E)
+ a.Square(a, pool)
+ a.Sub(a, A)
+ a.Sub(a, G)
+ t.Add(B, B)
+ t.Add(t, t)
+ a.Sub(a, t)
+
+ c = newGFp2(pool)
+ c.Mul(rOut.z, r.t, pool)
+ c.Add(c, c)
+ c.MulScalar(c, q.y)
+
+ A.Put(pool)
+ B.Put(pool)
+ C_.Put(pool)
+ D.Put(pool)
+ E.Put(pool)
+ G.Put(pool)
+ t.Put(pool)
+
+ return
+}
+
+func mulLine(ret *gfP12, a, b, c *gfP2, pool *bnPool) {
+ a2 := newGFp6(pool)
+ a2.x.SetZero()
+ a2.y.Set(a)
+ a2.z.Set(b)
+ a2.Mul(a2, ret.x, pool)
+ t3 := newGFp6(pool).MulScalar(ret.y, c, pool)
+
+ t := newGFp2(pool)
+ t.Add(b, c)
+ t2 := newGFp6(pool)
+ t2.x.SetZero()
+ t2.y.Set(a)
+ t2.z.Set(t)
+ ret.x.Add(ret.x, ret.y)
+
+ ret.y.Set(t3)
+
+ ret.x.Mul(ret.x, t2, pool)
+ ret.x.Sub(ret.x, a2)
+ ret.x.Sub(ret.x, ret.y)
+ a2.MulTau(a2, pool)
+ ret.y.Add(ret.y, a2)
+
+ a2.Put(pool)
+ t3.Put(pool)
+ t2.Put(pool)
+ t.Put(pool)
+}
+
+// sixuPlus2NAF is 6u+2 in non-adjacent form.
+var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
+ 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
+ 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
+ 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}
+
+// miller implements the Miller loop for calculating the Optimal Ate pairing.
+// See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf
+func miller(q *twistPoint, p *curvePoint, pool *bnPool) *gfP12 {
+ ret := newGFp12(pool)
+ ret.SetOne()
+
+ aAffine := newTwistPoint(pool)
+ aAffine.Set(q)
+ aAffine.MakeAffine(pool)
+
+ bAffine := newCurvePoint(pool)
+ bAffine.Set(p)
+ bAffine.MakeAffine(pool)
+
+ minusA := newTwistPoint(pool)
+ minusA.Negative(aAffine, pool)
+
+ r := newTwistPoint(pool)
+ r.Set(aAffine)
+
+ r2 := newGFp2(pool)
+ r2.Square(aAffine.y, pool)
+
+ for i := len(sixuPlus2NAF) - 1; i > 0; i-- {
+ a, b, c, newR := lineFunctionDouble(r, bAffine, pool)
+ if i != len(sixuPlus2NAF)-1 {
+ ret.Square(ret, pool)
+ }
+
+ mulLine(ret, a, b, c, pool)
+ a.Put(pool)
+ b.Put(pool)
+ c.Put(pool)
+ r.Put(pool)
+ r = newR
+
+ switch sixuPlus2NAF[i-1] {
+ case 1:
+ a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2, pool)
+ case -1:
+ a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2, pool)
+ default:
+ continue
+ }
+
+ mulLine(ret, a, b, c, pool)
+ a.Put(pool)
+ b.Put(pool)
+ c.Put(pool)
+ r.Put(pool)
+ r = newR
+ }
+
+ // In order to calculate Q1 we have to convert q from the sextic twist
+ // to the full GF(p^12) group, apply the Frobenius there, and convert
+ // back.
+ //
+ // The twist isomorphism is (x', y') -> (xω², yω³). If we consider just
+ // x for a moment, then after applying the Frobenius, we have x̄ω^(2p)
+ // where x̄ is the conjugate of x. If we are going to apply the inverse
+ // isomorphism we need a value with a single coefficient of ω² so we
+ // rewrite this as x̄ω^(2p-2)ω². ξ⁶ = ω and, due to the construction of
+ // p, 2p-2 is a multiple of six. Therefore we can rewrite as
+ // x̄ξ^((p-1)/3)ω² and applying the inverse isomorphism eliminates the
+ // ω².
+ //
+ // A similar argument can be made for the y value.
+
+ q1 := newTwistPoint(pool)
+ q1.x.Conjugate(aAffine.x)
+ q1.x.Mul(q1.x, xiToPMinus1Over3, pool)
+ q1.y.Conjugate(aAffine.y)
+ q1.y.Mul(q1.y, xiToPMinus1Over2, pool)
+ q1.z.SetOne()
+ q1.t.SetOne()
+
+ // For Q2 we are applying the p² Frobenius. The two conjugations cancel
+ // out and we are left only with the factors from the isomorphism. In
+ // the case of x, we end up with a pure number which is why
+ // xiToPSquaredMinus1Over3 is ∈ GF(p). With y we get a factor of -1. We
+ // ignore this to end up with -Q2.
+
+ minusQ2 := newTwistPoint(pool)
+ minusQ2.x.MulScalar(aAffine.x, xiToPSquaredMinus1Over3)
+ minusQ2.y.Set(aAffine.y)
+ minusQ2.z.SetOne()
+ minusQ2.t.SetOne()
+
+ r2.Square(q1.y, pool)
+ a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2, pool)
+ mulLine(ret, a, b, c, pool)
+ a.Put(pool)
+ b.Put(pool)
+ c.Put(pool)
+ r.Put(pool)
+ r = newR
+
+ r2.Square(minusQ2.y, pool)
+ a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2, pool)
+ mulLine(ret, a, b, c, pool)
+ a.Put(pool)
+ b.Put(pool)
+ c.Put(pool)
+ r.Put(pool)
+ r = newR
+
+ aAffine.Put(pool)
+ bAffine.Put(pool)
+ minusA.Put(pool)
+ r.Put(pool)
+ r2.Put(pool)
+
+ return ret
+}
+
+// finalExponentiation computes the (p¹²-1)/Order-th power of an element of
+// GF(p¹²) to obtain an element of GT (steps 13-15 of algorithm 1 from
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf)
+func finalExponentiation(in *gfP12, pool *bnPool) *gfP12 {
+ t1 := newGFp12(pool)
+
+ // This is the p^6-Frobenius
+ t1.x.Negative(in.x)
+ t1.y.Set(in.y)
+
+ inv := newGFp12(pool)
+ inv.Invert(in, pool)
+ t1.Mul(t1, inv, pool)
+
+ t2 := newGFp12(pool).FrobeniusP2(t1, pool)
+ t1.Mul(t1, t2, pool)
+
+ fp := newGFp12(pool).Frobenius(t1, pool)
+ fp2 := newGFp12(pool).FrobeniusP2(t1, pool)
+ fp3 := newGFp12(pool).Frobenius(fp2, pool)
+
+ fu, fu2, fu3 := newGFp12(pool), newGFp12(pool), newGFp12(pool)
+ fu.Exp(t1, u, pool)
+ fu2.Exp(fu, u, pool)
+ fu3.Exp(fu2, u, pool)
+
+ y3 := newGFp12(pool).Frobenius(fu, pool)
+ fu2p := newGFp12(pool).Frobenius(fu2, pool)
+ fu3p := newGFp12(pool).Frobenius(fu3, pool)
+ y2 := newGFp12(pool).FrobeniusP2(fu2, pool)
+
+ y0 := newGFp12(pool)
+ y0.Mul(fp, fp2, pool)
+ y0.Mul(y0, fp3, pool)
+
+ y1, y4, y5 := newGFp12(pool), newGFp12(pool), newGFp12(pool)
+ y1.Conjugate(t1)
+ y5.Conjugate(fu2)
+ y3.Conjugate(y3)
+ y4.Mul(fu, fu2p, pool)
+ y4.Conjugate(y4)
+
+ y6 := newGFp12(pool)
+ y6.Mul(fu3, fu3p, pool)
+ y6.Conjugate(y6)
+
+ t0 := newGFp12(pool)
+ t0.Square(y6, pool)
+ t0.Mul(t0, y4, pool)
+ t0.Mul(t0, y5, pool)
+ t1.Mul(y3, y5, pool)
+ t1.Mul(t1, t0, pool)
+ t0.Mul(t0, y2, pool)
+ t1.Square(t1, pool)
+ t1.Mul(t1, t0, pool)
+ t1.Square(t1, pool)
+ t0.Mul(t1, y1, pool)
+ t1.Mul(t1, y0, pool)
+ t0.Square(t0, pool)
+ t0.Mul(t0, t1, pool)
+
+ inv.Put(pool)
+ t1.Put(pool)
+ t2.Put(pool)
+ fp.Put(pool)
+ fp2.Put(pool)
+ fp3.Put(pool)
+ fu.Put(pool)
+ fu2.Put(pool)
+ fu3.Put(pool)
+ fu2p.Put(pool)
+ fu3p.Put(pool)
+ y0.Put(pool)
+ y1.Put(pool)
+ y2.Put(pool)
+ y3.Put(pool)
+ y4.Put(pool)
+ y5.Put(pool)
+ y6.Put(pool)
+
+ return t0
+}
+
+func optimalAte(a *twistPoint, b *curvePoint, pool *bnPool) *gfP12 {
+ e := miller(a, b, pool)
+ ret := finalExponentiation(e, pool)
+ e.Put(pool)
+
+ if a.IsInfinity() || b.IsInfinity() {
+ ret.SetOne()
+ }
+ return ret
+}
diff --git a/common/crypto/bn256/google/twist.go b/common/crypto/bn256/google/twist.go
new file mode 100644
index 0000000..43364ff
--- /dev/null
+++ b/common/crypto/bn256/google/twist.go
@@ -0,0 +1,263 @@
+// Copyright 2012 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 bn256
+
+import (
+ "math/big"
+)
+
+// twistPoint implements the elliptic curve y²=x³+3/ξ over GF(p²). Points are
+// kept in Jacobian form and t=z² when valid. The group G₂ is the set of
+// n-torsion points of this curve over GF(p²) (where n = Order)
+type twistPoint struct {
+ x, y, z, t *gfP2
+}
+
+var twistB = &gfP2{
+ bigFromBase10("266929791119991161246907387137283842545076965332900288569378510910307636690"),
+ bigFromBase10("19485874751759354771024239261021720505790618469301721065564631296452457478373"),
+}
+
+// twistGen is the generator of group G₂.
+var twistGen = &twistPoint{
+ &gfP2{
+ bigFromBase10("11559732032986387107991004021392285783925812861821192530917403151452391805634"),
+ bigFromBase10("10857046999023057135944570762232829481370756359578518086990519993285655852781"),
+ },
+ &gfP2{
+ bigFromBase10("4082367875863433681332203403145435568316851327593401208105741076214120093531"),
+ bigFromBase10("8495653923123431417604973247489272438418190587263600148770280649306958101930"),
+ },
+ &gfP2{
+ bigFromBase10("0"),
+ bigFromBase10("1"),
+ },
+ &gfP2{
+ bigFromBase10("0"),
+ bigFromBase10("1"),
+ },
+}
+
+func newTwistPoint(pool *bnPool) *twistPoint {
+ return &twistPoint{
+ newGFp2(pool),
+ newGFp2(pool),
+ newGFp2(pool),
+ newGFp2(pool),
+ }
+}
+
+func (c *twistPoint) String() string {
+ return "(" + c.x.String() + ", " + c.y.String() + ", " + c.z.String() + ")"
+}
+
+func (c *twistPoint) Put(pool *bnPool) {
+ c.x.Put(pool)
+ c.y.Put(pool)
+ c.z.Put(pool)
+ c.t.Put(pool)
+}
+
+func (c *twistPoint) Set(a *twistPoint) {
+ c.x.Set(a.x)
+ c.y.Set(a.y)
+ c.z.Set(a.z)
+ c.t.Set(a.t)
+}
+
+// IsOnCurve returns true iff c is on the curve where c must be in affine form.
+func (c *twistPoint) IsOnCurve() bool {
+ pool := new(bnPool)
+ yy := newGFp2(pool).Square(c.y, pool)
+ xxx := newGFp2(pool).Square(c.x, pool)
+ xxx.Mul(xxx, c.x, pool)
+ yy.Sub(yy, xxx)
+ yy.Sub(yy, twistB)
+ yy.Minimal()
+
+ if yy.x.Sign() != 0 || yy.y.Sign() != 0 {
+ return false
+ }
+ cneg := newTwistPoint(pool)
+ cneg.Mul(c, Order, pool)
+ return cneg.z.IsZero()
+}
+
+func (c *twistPoint) SetInfinity() {
+ c.z.SetZero()
+}
+
+func (c *twistPoint) IsInfinity() bool {
+ return c.z.IsZero()
+}
+
+func (c *twistPoint) Add(a, b *twistPoint, pool *bnPool) {
+ // For additional comments, see the same function in curve.go.
+
+ if a.IsInfinity() {
+ c.Set(b)
+ return
+ }
+ if b.IsInfinity() {
+ c.Set(a)
+ return
+ }
+
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/addition/add-2007-bl.op3
+ z1z1 := newGFp2(pool).Square(a.z, pool)
+ z2z2 := newGFp2(pool).Square(b.z, pool)
+ u1 := newGFp2(pool).Mul(a.x, z2z2, pool)
+ u2 := newGFp2(pool).Mul(b.x, z1z1, pool)
+
+ t := newGFp2(pool).Mul(b.z, z2z2, pool)
+ s1 := newGFp2(pool).Mul(a.y, t, pool)
+
+ t.Mul(a.z, z1z1, pool)
+ s2 := newGFp2(pool).Mul(b.y, t, pool)
+
+ h := newGFp2(pool).Sub(u2, u1)
+ xEqual := h.IsZero()
+
+ t.Add(h, h)
+ i := newGFp2(pool).Square(t, pool)
+ j := newGFp2(pool).Mul(h, i, pool)
+
+ t.Sub(s2, s1)
+ yEqual := t.IsZero()
+ if xEqual && yEqual {
+ c.Double(a, pool)
+ return
+ }
+ r := newGFp2(pool).Add(t, t)
+
+ v := newGFp2(pool).Mul(u1, i, pool)
+
+ t4 := newGFp2(pool).Square(r, pool)
+ t.Add(v, v)
+ t6 := newGFp2(pool).Sub(t4, j)
+ c.x.Sub(t6, t)
+
+ t.Sub(v, c.x) // t7
+ t4.Mul(s1, j, pool) // t8
+ t6.Add(t4, t4) // t9
+ t4.Mul(r, t, pool) // t10
+ c.y.Sub(t4, t6)
+
+ t.Add(a.z, b.z) // t11
+ t4.Square(t, pool) // t12
+ t.Sub(t4, z1z1) // t13
+ t4.Sub(t, z2z2) // t14
+ c.z.Mul(t4, h, pool)
+
+ z1z1.Put(pool)
+ z2z2.Put(pool)
+ u1.Put(pool)
+ u2.Put(pool)
+ t.Put(pool)
+ s1.Put(pool)
+ s2.Put(pool)
+ h.Put(pool)
+ i.Put(pool)
+ j.Put(pool)
+ r.Put(pool)
+ v.Put(pool)
+ t4.Put(pool)
+ t6.Put(pool)
+}
+
+func (c *twistPoint) Double(a *twistPoint, pool *bnPool) {
+ // See http://hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian-0/doubling/dbl-2009-l.op3
+ A := newGFp2(pool).Square(a.x, pool)
+ B := newGFp2(pool).Square(a.y, pool)
+ C_ := newGFp2(pool).Square(B, pool)
+
+ t := newGFp2(pool).Add(a.x, B)
+ t2 := newGFp2(pool).Square(t, pool)
+ t.Sub(t2, A)
+ t2.Sub(t, C_)
+ d := newGFp2(pool).Add(t2, t2)
+ t.Add(A, A)
+ e := newGFp2(pool).Add(t, A)
+ f := newGFp2(pool).Square(e, pool)
+
+ t.Add(d, d)
+ c.x.Sub(f, t)
+
+ t.Add(C_, C_)
+ t2.Add(t, t)
+ t.Add(t2, t2)
+ c.y.Sub(d, c.x)
+ t2.Mul(e, c.y, pool)
+ c.y.Sub(t2, t)
+
+ t.Mul(a.y, a.z, pool)
+ c.z.Add(t, t)
+
+ A.Put(pool)
+ B.Put(pool)
+ C_.Put(pool)
+ t.Put(pool)
+ t2.Put(pool)
+ d.Put(pool)
+ e.Put(pool)
+ f.Put(pool)
+}
+
+func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int, pool *bnPool) *twistPoint {
+ sum := newTwistPoint(pool)
+ sum.SetInfinity()
+ t := newTwistPoint(pool)
+
+ for i := scalar.BitLen(); i >= 0; i-- {
+ t.Double(sum, pool)
+ if scalar.Bit(i) != 0 {
+ sum.Add(t, a, pool)
+ } else {
+ sum.Set(t)
+ }
+ }
+
+ c.Set(sum)
+ sum.Put(pool)
+ t.Put(pool)
+ return c
+}
+
+// MakeAffine converts c to affine form and returns c. If c is ∞, then it sets
+// c to 0 : 1 : 0.
+func (c *twistPoint) MakeAffine(pool *bnPool) *twistPoint {
+ if c.z.IsOne() {
+ return c
+ }
+ if c.IsInfinity() {
+ c.x.SetZero()
+ c.y.SetOne()
+ c.z.SetZero()
+ c.t.SetZero()
+ return c
+ }
+ zInv := newGFp2(pool).Invert(c.z, pool)
+ t := newGFp2(pool).Mul(c.y, zInv, pool)
+ zInv2 := newGFp2(pool).Square(zInv, pool)
+ c.y.Mul(t, zInv2, pool)
+ t.Mul(c.x, zInv2, pool)
+ c.x.Set(t)
+ c.z.SetOne()
+ c.t.SetOne()
+
+ zInv.Put(pool)
+ t.Put(pool)
+ zInv2.Put(pool)
+
+ return c
+}
+
+func (c *twistPoint) Negative(a *twistPoint, pool *bnPool) {
+ c.x.Set(a.x)
+ c.y.SetZero()
+ c.y.Sub(c.y, a.y)
+ c.z.Set(a.z)
+ c.t.SetZero()
+}
diff --git a/common/crypto/crypto.go b/common/crypto/crypto.go
new file mode 100644
index 0000000..c059006
--- /dev/null
+++ b/common/crypto/crypto.go
@@ -0,0 +1,338 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package crypto
+
+import (
+ "bufio"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/common/u256"
+ "math/big"
+
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/math"
+ "github.com/astranetworld/ast/internal/avm/rlp"
+ "hash"
+ "io"
+ "os"
+
+ "github.com/holiman/uint256"
+ "golang.org/x/crypto/sha3"
+)
+
+// SignatureLength indicates the byte length required to carry a signature with recovery id.
+const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id
+
+// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id.
+const RecoveryIDOffset = 64
+
+// DigestLength sets the signature digest exact length
+const DigestLength = 32
+
+var (
+ secp256k1N = new(uint256.Int).SetBytes(hexutil.MustDecode("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"))
+ secp256k1NBig = secp256k1N.ToBig()
+ secp256k1halfN = new(uint256.Int).Div(secp256k1N, u256.Num2)
+ EmptyCodeHash = Keccak256Hash(nil)
+)
+
+var errInvalidPubkey = errors.New("invalid secp256k1 public key")
+
+// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
+// Read to get a variable amount of data from the hash state. Read is faster than Sum
+// because it doesn't copy the internal state, but also modifies the internal state.
+type KeccakState interface {
+ hash.Hash
+ Read([]byte) (int, error)
+}
+
+// NewKeccakState creates a new KeccakState
+func NewKeccakState() KeccakState {
+ return sha3.NewLegacyKeccak256().(KeccakState)
+}
+
+// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
+func HashData(kh KeccakState, data []byte) (h types.Hash) {
+ kh.Reset()
+ //nolint:errcheck
+ kh.Write(data)
+ //nolint:errcheck
+ kh.Read(h[:])
+ return h
+}
+
+// Keccak256 calculates and returns the Keccak256 hash of the input data.
+func Keccak256(data ...[]byte) []byte {
+ b := make([]byte, 32)
+ d := NewKeccakState()
+ for _, b := range data {
+ d.Write(b)
+ }
+ d.Read(b) //nolint:errcheck
+ return b
+}
+
+// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
+// converting it to an internal Hash data structure.
+func Keccak256Hash(data ...[]byte) (h types.Hash) {
+ d := NewKeccakState()
+ for _, b := range data {
+ d.Write(b)
+ }
+ d.Read(h[:]) //nolint:errcheck
+ return h
+}
+
+// Keccak512 calculates and returns the Keccak512 hash of the input data.
+func Keccak512(data ...[]byte) []byte {
+ d := sha3.NewLegacyKeccak512()
+ for _, b := range data {
+ d.Write(b)
+ }
+ return d.Sum(nil)
+}
+
+// CreateAddress creates an ethereum address given the bytes and the nonce
+// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
+func CreateAddress(b types.Address, nonce uint64) types.Address {
+ data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
+ return types.BytesToAddress(Keccak256(data)[12:])
+}
+
+// CreateAddress2 creates an ethereum address given the address bytes, initial
+// contract code hash and a salt.
+// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
+func CreateAddress2(b types.Address, salt [32]byte, inithash []byte) types.Address {
+ return types.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
+}
+
+// ToECDSA creates a private key with the given D value.
+func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
+ return toECDSA(d, true)
+}
+
+// ToECDSAUnsafe blindly converts a binary blob to a private key. It should almost
+// never be used unless you are sure the input is valid and want to avoid hitting
+// errors due to bad origin encoding (0 prefixes cut off).
+func ToECDSAUnsafe(d []byte) *ecdsa.PrivateKey {
+ priv, _ := toECDSA(d, false)
+ return priv
+}
+
+// toECDSA creates a private key with the given D value. The strict parameter
+// controls whether the key's length should be enforced at the curve size or
+// it can also accept legacy encodings (0 prefixes).
+func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
+ priv := new(ecdsa.PrivateKey)
+ priv.PublicKey.Curve = S256()
+ if strict && 8*len(d) != priv.Params().BitSize {
+ return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize)
+ }
+ priv.D = new(big.Int).SetBytes(d)
+
+ // The priv.D must < N
+ if priv.D.Cmp(secp256k1NBig) >= 0 {
+ return nil, fmt.Errorf("invalid private key, >=N")
+ }
+ // The priv.D must not be zero or negative.
+ if priv.D.Sign() <= 0 {
+ return nil, fmt.Errorf("invalid private key, zero or negative")
+ }
+
+ priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
+ if priv.PublicKey.X == nil {
+ return nil, errors.New("invalid private key")
+ }
+ return priv, nil
+}
+
+// FromECDSA exports a private key into a binary dump.
+func FromECDSA(priv *ecdsa.PrivateKey) []byte {
+ if priv == nil {
+ return nil
+ }
+ return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
+}
+
+// UnmarshalPubkey converts bytes to a secp256k1 public key.
+func UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
+ x, y := elliptic.Unmarshal(S256(), pub)
+ if x == nil {
+ return nil, errInvalidPubkey
+ }
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
+}
+
+func FromECDSAPub(pub *ecdsa.PublicKey) []byte {
+ if pub == nil || pub.X == nil || pub.Y == nil {
+ return nil
+ }
+ return elliptic.Marshal(S256(), pub.X, pub.Y)
+}
+
+// UnmarshalPubkeyStd parses a public key from the given bytes in the standard "uncompressed" format.
+// The input slice must be 65 bytes long and have this format: [4, X..., Y...]
+// See MarshalPubkeyStd.
+func UnmarshalPubkeyStd(pub []byte) (*ecdsa.PublicKey, error) {
+ x, y := elliptic.Unmarshal(S256(), pub)
+ if x == nil {
+ return nil, errInvalidPubkey
+ }
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
+}
+
+// MarshalPubkeyStd converts a public key into the standard "uncompressed" format.
+// It returns a 65 bytes long slice that contains: [4, X..., Y...]
+// Returns nil if the given public key is not initialized.
+// See UnmarshalPubkeyStd.
+func MarshalPubkeyStd(pub *ecdsa.PublicKey) []byte {
+ if pub == nil || pub.X == nil || pub.Y == nil {
+ return nil
+ }
+ return elliptic.Marshal(S256(), pub.X, pub.Y)
+}
+
+// UnmarshalPubkey parses a public key from the given bytes in the 64 bytes "uncompressed" format.
+// The input slice must be 64 bytes long and have this format: [X..., Y...]
+// See MarshalPubkey.
+//func UnmarshalPubkey(keyBytes []byte) (*ecdsa.PublicKey, error) {
+// keyBytes = append([]byte{0x4}, keyBytes...)
+// return UnmarshalPubkeyStd(keyBytes)
+//}
+
+// MarshalPubkey converts a public key into a 64 bytes "uncompressed" format.
+// It returns a 64 bytes long slice that contains: [X..., Y...]
+// In the standard 65 bytes format the first byte is always constant (equal to 4),
+// so it can be cut off and trivially recovered later.
+// Returns nil if the given public key is not initialized.
+// See UnmarshalPubkey.
+func MarshalPubkey(pubkey *ecdsa.PublicKey) []byte {
+ keyBytes := MarshalPubkeyStd(pubkey)
+ if keyBytes == nil {
+ return nil
+ }
+ return keyBytes[1:]
+}
+
+// HexToECDSA parses a secp256k1 private key.
+func HexToECDSA(hexkey string) (*ecdsa.PrivateKey, error) {
+ b, err := hex.DecodeString(hexkey)
+ if byteErr, ok := err.(hex.InvalidByteError); ok {
+ return nil, fmt.Errorf("invalid hex character %q in private key", byte(byteErr))
+ } else if err != nil {
+ return nil, errors.New("invalid hex data for private key")
+ }
+ return ToECDSA(b)
+}
+
+// LoadECDSA loads a secp256k1 private key from the given file.
+func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
+ fd, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer fd.Close()
+
+ r := bufio.NewReader(fd)
+ buf := make([]byte, 64)
+ n, err := readASCII(buf, r)
+ if err != nil {
+ return nil, err
+ } else if n != len(buf) {
+ return nil, fmt.Errorf("key file too short, want 64 hex characters")
+ }
+ if err := checkKeyFileEnd(r); err != nil {
+ return nil, err
+ }
+
+ return HexToECDSA(string(buf))
+}
+
+// readASCII reads into 'buf', stopping when the buffer is full or
+// when a non-printable control character is encountered.
+func readASCII(buf []byte, r *bufio.Reader) (n int, err error) {
+ for ; n < len(buf); n++ {
+ buf[n], err = r.ReadByte()
+ switch {
+ case errors.Is(err, io.EOF) || buf[n] < '!':
+ return n, nil
+ case err != nil:
+ return n, err
+ }
+ }
+ return n, nil
+}
+
+// checkKeyFileEnd skips over additional newlines at the end of a key file.
+func checkKeyFileEnd(r *bufio.Reader) error {
+ for i := 0; ; i++ {
+ b, err := r.ReadByte()
+ switch {
+ case errors.Is(err, io.EOF):
+ return nil
+ case err != nil:
+ return err
+ case b != '\n' && b != '\r':
+ return fmt.Errorf("invalid character %q at end of key file", b)
+ case i >= 2:
+ return errors.New("key file too long, want 64 hex characters")
+ }
+ }
+}
+
+// SaveECDSA saves a secp256k1 private key to the given file with
+// restrictive permissions. The key data is saved hex-encoded.
+func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
+ k := hex.EncodeToString(FromECDSA(key))
+ return os.WriteFile(file, []byte(k), 0600)
+}
+
+// GenerateKey generates a new private key.
+func GenerateKey() (*ecdsa.PrivateKey, error) {
+ return ecdsa.GenerateKey(S256(), rand.Reader)
+}
+
+// ValidateSignatureValues verifies whether the signature values are valid with
+// the given chain rules. The v value is assumed to be either 0 or 1.
+func ValidateSignatureValues(v byte, r, s *uint256.Int, homestead bool) bool {
+ if r.IsZero() || s.IsZero() {
+ return false
+ }
+ // reject upper range of s values (ECDSA malleability)
+ // see discussion in secp256k1/libsecp256k1/include/secp256k1.h
+ if homestead && s.Gt(secp256k1halfN) {
+ return false
+ }
+ // Frontier: allow s to be in full N range
+ return r.Lt(secp256k1N) && s.Lt(secp256k1N) && (v == 0 || v == 1)
+}
+
+// DESCRIBED: docs/programmers_guide/guide.md#address---identifier-of-an-account
+func PubkeyToAddress(p ecdsa.PublicKey) types.Address {
+ pubBytes := MarshalPubkey(&p)
+ return types.BytesToAddress(Keccak256(pubBytes)[12:])
+}
+
+func zeroBytes(bytes []byte) {
+ for i := range bytes {
+ bytes[i] = 0
+ }
+}
diff --git a/common/crypto/cryptopool/pool.go b/common/crypto/cryptopool/pool.go
new file mode 100644
index 0000000..d2faa8e
--- /dev/null
+++ b/common/crypto/cryptopool/pool.go
@@ -0,0 +1,38 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package cryptopool
+
+import (
+ "hash"
+ "sync"
+
+ "golang.org/x/crypto/sha3"
+)
+
+// hasherPool holds LegacyKeccak hashers.
+var hasherPool = sync.Pool{
+ New: func() interface{} {
+ return sha3.NewLegacyKeccak256()
+ },
+}
+
+func NewLegacyKeccak256() hash.Hash {
+ h := hasherPool.Get().(hash.Hash)
+ h.Reset()
+ return h
+}
+func ReturnToPoolKeccak256(h hash.Hash) { hasherPool.Put(h) }
diff --git a/common/crypto/ecies/.gitignore b/common/crypto/ecies/.gitignore
new file mode 100644
index 0000000..802b674
--- /dev/null
+++ b/common/crypto/ecies/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+*~
diff --git a/common/crypto/ecies/LICENSE b/common/crypto/ecies/LICENSE
new file mode 100644
index 0000000..e1ed19a
--- /dev/null
+++ b/common/crypto/ecies/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2013 Kyle Isom
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of Google Inc. 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
+OWNER 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/common/crypto/ecies/README b/common/crypto/ecies/README
new file mode 100644
index 0000000..2650c7b
--- /dev/null
+++ b/common/crypto/ecies/README
@@ -0,0 +1,94 @@
+# NOTE
+
+This implementation is direct fork of Kylom's implementation. I claim no authorship over this code apart from some minor modifications.
+Please be aware this code **has not yet been reviewed**.
+
+ecies implements the Elliptic Curve Integrated Encryption Scheme.
+
+The package is designed to be compliant with the appropriate NIST
+standards, and therefore doesn't support the full SEC 1 algorithm set.
+
+
+STATUS:
+
+ecies should be ready for use. The ASN.1 support is only complete so
+far as to supported the listed algorithms before.
+
+
+CAVEATS
+
+1. CMAC support is currently not present.
+
+
+SUPPORTED ALGORITHMS
+
+ SYMMETRIC CIPHERS HASH FUNCTIONS
+ AES128 SHA-1
+ AES192 SHA-224
+ AES256 SHA-256
+ SHA-384
+ ELLIPTIC CURVE SHA-512
+ P256
+ P384 KEY DERIVATION FUNCTION
+ P521 NIST SP 800-65a Concatenation KDF
+
+Curve P224 isn't supported because it does not provide a minimum security
+level of AES128 with HMAC-SHA1. According to NIST SP 800-57, the security
+level of P224 is 112 bits of security. Symmetric ciphers use CTR-mode;
+message tags are computed using HMAC- function.
+
+
+CURVE SELECTION
+
+According to NIST SP 800-57, the following curves should be selected:
+
+ +----------------+-------+
+ | SYMMETRIC SIZE | CURVE |
+ +----------------+-------+
+ | 128-bit | P256 |
+ +----------------+-------+
+ | 192-bit | P384 |
+ +----------------+-------+
+ | 256-bit | P521 |
+ +----------------+-------+
+
+
+TODO
+
+1. Look at serialising the parameters with the SEC 1 ASN.1 module.
+2. Validate ASN.1 formats with SEC 1.
+
+
+TEST VECTORS
+
+The only test vectors I've found so far date from 1993, predating AES
+and including only 163-bit curves. Therefore, there are no published
+test vectors to compare to.
+
+
+LICENSE
+
+ecies is released under the same license as the Go source code. See the
+LICENSE file for details.
+
+
+REFERENCES
+
+* SEC (Standard for Efficient Cryptography) 1, version 2.0: Elliptic
+ Curve Cryptography; Certicom, May 2009.
+ http://www.secg.org/sec1-v2.pdf
+* GEC (Guidelines for Efficient Cryptography) 2, version 0.3: Test
+ Vectors for SEC 1; Certicom, September 1999.
+ http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf
+* NIST SP 800-56a: Recommendation for Pair-Wise Key Establishment Schemes
+ Using Discrete Logarithm Cryptography. National Institute of Standards
+ and Technology, May 2007.
+ http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
+* Suite B Implementer’s Guide to NIST SP 800-56A. National Security
+ Agency, July 28, 2009.
+ http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf
+* NIST SP 800-57: Recommendation for Key Management – Part 1: General
+ (Revision 3). National Institute of Standards and Technology, July
+ 2012.
+ http://csrc.nist.gov/publications/nistpubs/800-57/sp800-57_part1_rev3_general.pdf
+
diff --git a/common/crypto/ecies/ecies.go b/common/crypto/ecies/ecies.go
new file mode 100644
index 0000000..64b5a99
--- /dev/null
+++ b/common/crypto/ecies/ecies.go
@@ -0,0 +1,317 @@
+// Copyright (c) 2013 Kyle Isom
+// Copyright (c) 2012 The Go Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+package ecies
+
+import (
+ "crypto/cipher"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/hmac"
+ "crypto/subtle"
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "io"
+ "math/big"
+)
+
+var (
+ ErrImport = fmt.Errorf("ecies: failed to import key")
+ ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve")
+ ErrInvalidPublicKey = fmt.Errorf("ecies: invalid public key")
+ ErrSharedKeyIsPointAtInfinity = fmt.Errorf("ecies: shared key is point at infinity")
+ ErrSharedKeyTooBig = fmt.Errorf("ecies: shared key params are too big")
+)
+
+// PublicKey is a representation of an elliptic curve public key.
+type PublicKey struct {
+ X *big.Int
+ Y *big.Int
+ elliptic.Curve
+ Params *ECIESParams
+}
+
+// Export an ECIES public key as an ECDSA public key.
+func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey {
+ return &ecdsa.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y}
+}
+
+// Import an ECDSA public key as an ECIES public key.
+func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey {
+ return &PublicKey{
+ X: pub.X,
+ Y: pub.Y,
+ Curve: pub.Curve,
+ Params: ParamsFromCurve(pub.Curve),
+ }
+}
+
+// PrivateKey is a representation of an elliptic curve private key.
+type PrivateKey struct {
+ PublicKey
+ D *big.Int
+}
+
+// Export an ECIES private key as an ECDSA private key.
+func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey {
+ pub := &prv.PublicKey
+ pubECDSA := pub.ExportECDSA()
+ return &ecdsa.PrivateKey{PublicKey: *pubECDSA, D: prv.D}
+}
+
+// Import an ECDSA private key as an ECIES private key.
+func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
+ pub := ImportECDSAPublic(&prv.PublicKey)
+ return &PrivateKey{*pub, prv.D}
+}
+
+// Generate an elliptic curve public / private keypair. If params is nil,
+// the recommended default parameters for the key will be chosen.
+func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
+ pb, x, y, err := elliptic.GenerateKey(curve, rand)
+ if err != nil {
+ return
+ }
+ prv = new(PrivateKey)
+ prv.PublicKey.X = x
+ prv.PublicKey.Y = y
+ prv.PublicKey.Curve = curve
+ prv.D = new(big.Int).SetBytes(pb)
+ if params == nil {
+ params = ParamsFromCurve(curve)
+ }
+ prv.PublicKey.Params = params
+ return
+}
+
+// MaxSharedKeyLength returns the maximum length of the shared key the
+// public key can produce.
+func MaxSharedKeyLength(pub *PublicKey) int {
+ return (pub.Curve.Params().BitSize + 7) / 8
+}
+
+// ECDH key agreement method used to establish secret keys for encryption.
+func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) {
+ if prv.PublicKey.Curve != pub.Curve {
+ return nil, ErrInvalidCurve
+ }
+ if skLen+macLen > MaxSharedKeyLength(pub) {
+ return nil, ErrSharedKeyTooBig
+ }
+
+ x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
+ if x == nil {
+ return nil, ErrSharedKeyIsPointAtInfinity
+ }
+
+ sk = make([]byte, skLen+macLen)
+ skBytes := x.Bytes()
+ copy(sk[len(sk)-len(skBytes):], skBytes)
+ return sk, nil
+}
+
+var (
+ ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long")
+ ErrInvalidMessage = fmt.Errorf("ecies: invalid message")
+)
+
+// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
+func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) []byte {
+ counterBytes := make([]byte, 4)
+ k := make([]byte, 0, roundup(kdLen, hash.Size()))
+ for counter := uint32(1); len(k) < kdLen; counter++ {
+ binary.BigEndian.PutUint32(counterBytes, counter)
+ hash.Reset()
+ hash.Write(counterBytes)
+ hash.Write(z)
+ hash.Write(s1)
+ k = hash.Sum(k)
+ }
+ return k[:kdLen]
+}
+
+// roundup rounds size up to the next multiple of blocksize.
+func roundup(size, blocksize int) int {
+ return size + blocksize - (size % blocksize)
+}
+
+// deriveKeys creates the encryption and MAC keys using concatKDF.
+func deriveKeys(hash hash.Hash, z, s1 []byte, keyLen int) (Ke, Km []byte) {
+ K := concatKDF(hash, z, s1, 2*keyLen)
+ Ke = K[:keyLen]
+ Km = K[keyLen:]
+ hash.Reset()
+ hash.Write(Km)
+ Km = hash.Sum(Km[:0])
+ return Ke, Km
+}
+
+// messageTag computes the MAC of a message (called the tag) as per
+// SEC 1, 3.5.
+func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte {
+ mac := hmac.New(hash, km)
+ mac.Write(msg)
+ mac.Write(shared)
+ tag := mac.Sum(nil)
+ return tag
+}
+
+// Generate an initialisation vector for CTR mode.
+func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
+ iv = make([]byte, params.BlockSize)
+ _, err = io.ReadFull(rand, iv)
+ return
+}
+
+// symEncrypt carries out CTR encryption using the block cipher specified in the
+func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
+ c, err := params.Cipher(key)
+ if err != nil {
+ return
+ }
+
+ iv, err := generateIV(params, rand)
+ if err != nil {
+ return
+ }
+ ctr := cipher.NewCTR(c, iv)
+
+ ct = make([]byte, len(m)+params.BlockSize)
+ copy(ct, iv)
+ ctr.XORKeyStream(ct[params.BlockSize:], m)
+ return
+}
+
+// symDecrypt carries out CTR decryption using the block cipher specified in
+// the parameters
+func symDecrypt(params *ECIESParams, key, ct []byte) (m []byte, err error) {
+ c, err := params.Cipher(key)
+ if err != nil {
+ return
+ }
+
+ ctr := cipher.NewCTR(c, ct[:params.BlockSize])
+
+ m = make([]byte, len(ct)-params.BlockSize)
+ ctr.XORKeyStream(m, ct[params.BlockSize:])
+ return
+}
+
+// Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1.
+//
+// s1 and s2 contain shared information that is not part of the resulting
+// ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
+// shared information parameters aren't being used, they should be nil.
+func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
+ params, err := pubkeyParams(pub)
+ if err != nil {
+ return nil, err
+ }
+
+ R, err := GenerateKey(rand, pub.Curve, params)
+ if err != nil {
+ return nil, err
+ }
+
+ z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
+ if err != nil {
+ return nil, err
+ }
+
+ hash := params.Hash()
+ Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
+
+ em, err := symEncrypt(rand, params, Ke, m)
+ if err != nil || len(em) <= params.BlockSize {
+ return nil, err
+ }
+
+ d := messageTag(params.Hash, Km, em, s2)
+
+ Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y)
+ ct = make([]byte, len(Rb)+len(em)+len(d))
+ copy(ct, Rb)
+ copy(ct[len(Rb):], em)
+ copy(ct[len(Rb)+len(em):], d)
+ return ct, nil
+}
+
+// Decrypt decrypts an ECIES ciphertext.
+func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
+ if len(c) == 0 {
+ return nil, ErrInvalidMessage
+ }
+ params, err := pubkeyParams(&prv.PublicKey)
+ if err != nil {
+ return nil, err
+ }
+
+ hash := params.Hash()
+
+ var (
+ rLen int
+ hLen int = hash.Size()
+ mStart int
+ mEnd int
+ )
+
+ switch c[0] {
+ case 2, 3, 4:
+ rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
+ if len(c) < (rLen + hLen + 1) {
+ return nil, ErrInvalidMessage
+ }
+ default:
+ return nil, ErrInvalidPublicKey
+ }
+
+ mStart = rLen
+ mEnd = len(c) - hLen
+
+ R := new(PublicKey)
+ R.Curve = prv.PublicKey.Curve
+ R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
+ if R.X == nil {
+ return nil, ErrInvalidPublicKey
+ }
+
+ z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
+ if err != nil {
+ return nil, err
+ }
+ Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
+
+ d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
+ if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
+ return nil, ErrInvalidMessage
+ }
+
+ return symDecrypt(params, Ke, c[mStart:mEnd])
+}
diff --git a/common/crypto/ecies/ecies_test.go b/common/crypto/ecies/ecies_test.go
new file mode 100644
index 0000000..dc54ec6
--- /dev/null
+++ b/common/crypto/ecies/ecies_test.go
@@ -0,0 +1,429 @@
+// Copyright (c) 2013 Kyle Isom
+// Copyright (c) 2012 The Go Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+package ecies
+
+import (
+ "bytes"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "testing"
+
+ "github.com/astranetworld/ast/common/crypto"
+)
+
+func TestKDF(t *testing.T) {
+ tests := []struct {
+ length int
+ output []byte
+ }{
+ {6, decode("858b192fa2ed")},
+ {32, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0")},
+ {48, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955")},
+ {64, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955f3467fd6672cce1024c5b1effccc0f61")},
+ }
+
+ for _, test := range tests {
+ h := sha256.New()
+ k := concatKDF(h, []byte("input"), nil, test.length)
+ if !bytes.Equal(k, test.output) {
+ t.Fatalf("KDF: generated key %x does not match expected output %x", k, test.output)
+ }
+ }
+}
+
+var ErrBadSharedKeys = fmt.Errorf("ecies: shared keys don't match")
+
+// cmpParams compares a set of ECIES parameters. We assume, as per the
+// docs, that AES is the only supported symmetric encryption algorithm.
+func cmpParams(p1, p2 *ECIESParams) bool {
+ return p1.hashAlgo == p2.hashAlgo &&
+ p1.KeyLen == p2.KeyLen &&
+ p1.BlockSize == p2.BlockSize
+}
+
+// Validate the ECDH component.
+func TestSharedKey(t *testing.T) {
+ prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2
+
+ prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !bytes.Equal(sk1, sk2) {
+ t.Fatal(ErrBadSharedKeys)
+ }
+}
+
+func TestSharedKeyPadding(t *testing.T) {
+ // sanity checks
+ prv0 := hexKey("1adf5c18167d96a1f9a0b1ef63be8aa27eaf6032c233b2b38f7850cf5b859fd9")
+ prv1 := hexKey("0097a076fc7fcd9208240668e31c9abee952cbb6e375d1b8febc7499d6e16f1a")
+ x0, _ := new(big.Int).SetString("1a8ed022ff7aec59dc1b440446bdda5ff6bcb3509a8b109077282b361efffbd8", 16)
+ x1, _ := new(big.Int).SetString("6ab3ac374251f638d0abb3ef596d1dc67955b507c104e5f2009724812dc027b8", 16)
+ y0, _ := new(big.Int).SetString("e040bd480b1deccc3bc40bd5b1fdcb7bfd352500b477cb9471366dbd4493f923", 16)
+ y1, _ := new(big.Int).SetString("8ad915f2b503a8be6facab6588731fefeb584fd2dfa9a77a5e0bba1ec439e4fa", 16)
+
+ if prv0.PublicKey.X.Cmp(x0) != 0 {
+ t.Errorf("mismatched prv0.X:\nhave: %x\nwant: %x\n", prv0.PublicKey.X.Bytes(), x0.Bytes())
+ }
+ if prv0.PublicKey.Y.Cmp(y0) != 0 {
+ t.Errorf("mismatched prv0.Y:\nhave: %x\nwant: %x\n", prv0.PublicKey.Y.Bytes(), y0.Bytes())
+ }
+ if prv1.PublicKey.X.Cmp(x1) != 0 {
+ t.Errorf("mismatched prv1.X:\nhave: %x\nwant: %x\n", prv1.PublicKey.X.Bytes(), x1.Bytes())
+ }
+ if prv1.PublicKey.Y.Cmp(y1) != 0 {
+ t.Errorf("mismatched prv1.Y:\nhave: %x\nwant: %x\n", prv1.PublicKey.Y.Bytes(), y1.Bytes())
+ }
+
+ // test shared secret generation
+ sk1, err := prv0.GenerateShared(&prv1.PublicKey, 16, 16)
+ if err != nil {
+ t.Log(err.Error())
+ }
+
+ sk2, err := prv1.GenerateShared(&prv0.PublicKey, 16, 16)
+ if err != nil {
+ t.Fatal(err.Error())
+ }
+
+ if !bytes.Equal(sk1, sk2) {
+ t.Fatal(ErrBadSharedKeys.Error())
+ }
+}
+
+// Verify that the key generation code fails when too much key data is
+// requested.
+func TestTooBigSharedKey(t *testing.T) {
+ prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = prv1.GenerateShared(&prv2.PublicKey, 32, 32)
+ if err != ErrSharedKeyTooBig {
+ t.Fatal("ecdh: shared key should be too large for curve")
+ }
+
+ _, err = prv2.GenerateShared(&prv1.PublicKey, 32, 32)
+ if err != ErrSharedKeyTooBig {
+ t.Fatal("ecdh: shared key should be too large for curve")
+ }
+}
+
+// Benchmark the generation of P256 keys.
+func BenchmarkGenerateKeyP256(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if _, err := GenerateKey(rand.Reader, elliptic.P256(), nil); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// Benchmark the generation of P256 shared keys.
+func BenchmarkGenSharedKeyP256(b *testing.B) {
+ prv, err := GenerateKey(rand.Reader, elliptic.P256(), nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// Benchmark the generation of S256 shared keys.
+func BenchmarkGenSharedKeyS256(b *testing.B) {
+ prv, err := GenerateKey(rand.Reader, crypto.S256(), nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := prv.GenerateShared(&prv.PublicKey, 16, 16)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// Verify that an encrypted message can be successfully decrypted.
+func TestEncryptDecrypt(t *testing.T) {
+ prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ message := []byte("Hello, world.")
+ ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pt, err := prv2.Decrypt(ct, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !bytes.Equal(pt, message) {
+ t.Fatal("ecies: plaintext doesn't match message")
+ }
+
+ _, err = prv1.Decrypt(ct, nil, nil)
+ if err == nil {
+ t.Fatal("ecies: encryption should not have succeeded")
+ }
+}
+
+func TestDecryptShared2(t *testing.T) {
+ prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ message := []byte("Hello, world.")
+ shared2 := []byte("shared data 2")
+ ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, shared2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Check that decrypting with correct shared data works.
+ pt, err := prv.Decrypt(ct, nil, shared2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(pt, message) {
+ t.Fatal("ecies: plaintext doesn't match message")
+ }
+
+ // Decrypting without shared data or incorrect shared data fails.
+ if _, err = prv.Decrypt(ct, nil, nil); err == nil {
+ t.Fatal("ecies: decrypting without shared data didn't fail")
+ }
+ if _, err = prv.Decrypt(ct, nil, []byte("garbage")); err == nil {
+ t.Fatal("ecies: decrypting with incorrect shared data didn't fail")
+ }
+}
+
+type testCase struct {
+ Curve elliptic.Curve
+ Name string
+ Expected *ECIESParams
+}
+
+var testCases = []testCase{
+ {
+ Curve: elliptic.P256(),
+ Name: "P256",
+ Expected: ECIES_AES128_SHA256,
+ },
+ {
+ Curve: elliptic.P384(),
+ Name: "P384",
+ Expected: ECIES_AES192_SHA384,
+ },
+ {
+ Curve: elliptic.P521(),
+ Name: "P521",
+ Expected: ECIES_AES256_SHA512,
+ },
+}
+
+// Test parameter selection for each curve, and that P224 fails automatic
+// parameter selection (see README for a discussion of P224). Ensures that
+// selecting a set of parameters automatically for the given curve works.
+func TestParamSelection(t *testing.T) {
+ for _, c := range testCases {
+ testParamSelection(t, c)
+ }
+}
+
+func testParamSelection(t *testing.T, c testCase) {
+ params := ParamsFromCurve(c.Curve)
+ if params == nil {
+ t.Fatal("ParamsFromCurve returned nil")
+ } else if params != nil && !cmpParams(params, c.Expected) {
+ t.Fatalf("ecies: parameters should be invalid (%s)\n", c.Name)
+ }
+
+ prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatalf("%s (%s)\n", err.Error(), c.Name)
+ }
+
+ prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatalf("%s (%s)\n", err.Error(), c.Name)
+ }
+
+ message := []byte("Hello, world.")
+ ct, err := Encrypt(rand.Reader, &prv2.PublicKey, message, nil, nil)
+ if err != nil {
+ t.Fatalf("%s (%s)\n", err.Error(), c.Name)
+ }
+
+ pt, err := prv2.Decrypt(ct, nil, nil)
+ if err != nil {
+ t.Fatalf("%s (%s)\n", err.Error(), c.Name)
+ }
+
+ if !bytes.Equal(pt, message) {
+ t.Fatalf("ecies: plaintext doesn't match message (%s)\n", c.Name)
+ }
+
+ _, err = prv1.Decrypt(ct, nil, nil)
+ if err == nil {
+ t.Fatalf("ecies: encryption should not have succeeded (%s)\n", c.Name)
+ }
+}
+
+// Ensure that the basic public key validation in the decryption operation
+// works.
+func TestBasicKeyValidation(t *testing.T) {
+ badBytes := []byte{0, 1, 5, 6, 7, 8, 9}
+
+ prv, err := GenerateKey(rand.Reader, DefaultCurve, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ message := []byte("Hello, world.")
+ ct, err := Encrypt(rand.Reader, &prv.PublicKey, message, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, b := range badBytes {
+ ct[0] = b
+ _, err := prv.Decrypt(ct, nil, nil)
+ if err != ErrInvalidPublicKey {
+ t.Fatal("ecies: validated an invalid key")
+ }
+ }
+}
+
+func TestBox(t *testing.T) {
+ prv1 := hexKey("4b50fa71f5c3eeb8fdc452224b2395af2fcc3d125e06c32c82e048c0559db03f")
+ prv2 := hexKey("d0b043b4c5d657670778242d82d68a29d25d7d711127d17b8e299f156dad361a")
+ pub2 := &prv2.PublicKey
+
+ message := []byte("Hello, world.")
+ ct, err := Encrypt(rand.Reader, pub2, message, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ pt, err := prv2.Decrypt(ct, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(pt, message) {
+ t.Fatal("ecies: plaintext doesn't match message")
+ }
+ if _, err = prv1.Decrypt(ct, nil, nil); err == nil {
+ t.Fatal("ecies: encryption should not have succeeded")
+ }
+}
+
+// Verify GenerateShared against static values - useful when
+// debugging changes in underlying libs
+func TestSharedKeyStatic(t *testing.T) {
+ prv1 := hexKey("7ebbc6a8358bc76dd73ebc557056702c8cfc34e5cfcd90eb83af0347575fd2ad")
+ prv2 := hexKey("6a3d6396903245bba5837752b9e0348874e72db0c4e11e9c485a81b4ea4353b9")
+
+ skLen := MaxSharedKeyLength(&prv1.PublicKey) / 2
+
+ sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !bytes.Equal(sk1, sk2) {
+ t.Fatal(ErrBadSharedKeys)
+ }
+
+ sk := decode("167ccc13ac5e8a26b131c3446030c60fbfac6aa8e31149d0869f93626a4cdf62")
+ if !bytes.Equal(sk1, sk) {
+ t.Fatalf("shared secret mismatch: want: %x have: %x", sk, sk1)
+ }
+}
+
+func hexKey(prv string) *PrivateKey {
+ key, err := crypto.HexToECDSA(prv)
+ if err != nil {
+ panic(err)
+ }
+ return ImportECDSA(key)
+}
+
+func decode(s string) []byte {
+ bytes, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return bytes
+}
diff --git a/common/crypto/ecies/params.go b/common/crypto/ecies/params.go
new file mode 100644
index 0000000..ecc85f5
--- /dev/null
+++ b/common/crypto/ecies/params.go
@@ -0,0 +1,144 @@
+// Copyright (c) 2013 Kyle Isom
+// Copyright (c) 2012 The Go Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. 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
+// OWNER 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.
+
+package ecies
+
+// This file contains parameters for ECIES encryption, specifying the
+// symmetric encryption and HMAC parameters.
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/elliptic"
+ "crypto/sha256"
+ "crypto/sha512"
+ "fmt"
+ "hash"
+
+ ethcrypto "github.com/astranetworld/ast/common/crypto"
+)
+
+var (
+ DefaultCurve = ethcrypto.S256()
+ ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
+ ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
+ ErrInvalidKeyLen = fmt.Errorf("ecies: invalid key size (> %d) in ECIESParams", maxKeyLen)
+)
+
+// KeyLen is limited to prevent overflow of the counter
+// in concatKDF. While the theoretical limit is much higher,
+// no known cipher uses keys larger than 512 bytes.
+const maxKeyLen = 512
+
+type ECIESParams struct {
+ Hash func() hash.Hash // hash function
+ hashAlgo crypto.Hash
+ Cipher func([]byte) (cipher.Block, error) // symmetric cipher
+ BlockSize int // block size of symmetric cipher
+ KeyLen int // length of symmetric key
+}
+
+// Standard ECIES parameters:
+// * ECIES using AES128 and HMAC-SHA-256-16
+// * ECIES using AES256 and HMAC-SHA-256-32
+// * ECIES using AES256 and HMAC-SHA-384-48
+// * ECIES using AES256 and HMAC-SHA-512-64
+
+var (
+ ECIES_AES128_SHA256 = &ECIESParams{
+ Hash: sha256.New,
+ hashAlgo: crypto.SHA256,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 16,
+ }
+
+ ECIES_AES192_SHA384 = &ECIESParams{
+ Hash: sha512.New384,
+ hashAlgo: crypto.SHA384,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 24,
+ }
+
+ ECIES_AES256_SHA256 = &ECIESParams{
+ Hash: sha256.New,
+ hashAlgo: crypto.SHA256,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 32,
+ }
+
+ ECIES_AES256_SHA384 = &ECIESParams{
+ Hash: sha512.New384,
+ hashAlgo: crypto.SHA384,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 32,
+ }
+
+ ECIES_AES256_SHA512 = &ECIESParams{
+ Hash: sha512.New,
+ hashAlgo: crypto.SHA512,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 32,
+ }
+)
+
+var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
+ ethcrypto.S256(): ECIES_AES128_SHA256,
+ elliptic.P256(): ECIES_AES128_SHA256,
+ elliptic.P384(): ECIES_AES192_SHA384,
+ elliptic.P521(): ECIES_AES256_SHA512,
+}
+
+func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {
+ paramsFromCurve[curve] = params
+}
+
+// ParamsFromCurve selects parameters optimal for the selected elliptic curve.
+// Only the curves P256, P384, and P512 are supported.
+func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
+ return paramsFromCurve[curve]
+}
+
+func pubkeyParams(key *PublicKey) (*ECIESParams, error) {
+ params := key.Params
+ if params == nil {
+ if params = ParamsFromCurve(key.Curve); params == nil {
+ return nil, ErrUnsupportedECIESParameters
+ }
+ }
+ if params.KeyLen > maxKeyLen {
+ return nil, ErrInvalidKeyLen
+ }
+ return params, nil
+}
diff --git a/common/crypto/rand/BUILD.bazel b/common/crypto/rand/BUILD.bazel
new file mode 100644
index 0000000..c8039ec
--- /dev/null
+++ b/common/crypto/rand/BUILD.bazel
@@ -0,0 +1,14 @@
+load("@prysm//tools/go:def.bzl", "go_library", "go_test")
+
+go_library(
+ name = "go_default_library",
+ srcs = ["rand.go"],
+ importpath = "github.com/prysmaticlabs/prysm/v3/crypto/rand",
+ visibility = ["//visibility:public"],
+)
+
+go_test(
+ name = "go_default_test",
+ srcs = ["rand_test.go"],
+ embed = [":go_default_library"],
+)
diff --git a/common/crypto/rand/rand.go b/common/crypto/rand/rand.go
new file mode 100644
index 0000000..5e7ecdb
--- /dev/null
+++ b/common/crypto/rand/rand.go
@@ -0,0 +1,86 @@
+/*
+Package rand defines methods of obtaining random number generators.
+
+One is expected to use randomness from this package only, without introducing any other packages.
+This limits the scope of code that needs to be hardened.
+
+There are two modes, one for deterministic and another non-deterministic randomness:
+1. If deterministic pseudo-random generator is enough, use:
+
+ import "github.com/prysmaticlabs/prysm/v3/crypto/rand"
+ randGen := rand.NewDeterministicGenerator()
+ randGen.Intn(32) // or any other func defined in math.rand API
+
+ In this mode, only seed is generated using cryptographically secure source (crypto/rand). So,
+ once seed is obtained, and generator is seeded, the next generations are deterministic, thus fast.
+ However given that we only seed this 63 bits from crypto/rand and use math/rand to generate the outputs,
+ this method is not cryptographically secure. This is directly stated in the math/rand package,
+ https://github.com/golang/go/blob/release-branch.go1.17/src/math/rand/rand.go#L15. For any security
+ sensitive work this particular generator is NOT to be used.
+
+2. For cryptographically secure non-deterministic mode (CSPRNG), use:
+
+ import "github.com/prysmaticlabs/prysm/v3/crypto/rand"
+ randGen := rand.NewGenerator()
+ randGen.Intn(32) // or any other func defined in math.rand API
+
+ Again, any of the functions from `math/rand` can be used, however, they all use custom source
+ of randomness (crypto/rand), on every step. This makes randomness non-deterministic. However,
+ you take a performance hit -- as it is an order of magnitude slower.
+*/
+package rand
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ mrand "math/rand"
+ "sync"
+)
+
+type source struct{}
+
+var lock sync.RWMutex
+var _ mrand.Source64 = (*source)(nil) // #nosec G404 -- This ensures we meet the interface
+
+// Seed does nothing when crypto/rand is used as source.
+func (_ *source) Seed(_ int64) {}
+
+// Int63 returns uniformly-distributed random (as in CSPRNG) int64 value within [0, 1<<63) range.
+// Panics if random generator reader cannot return data.
+func (s *source) Int63() int64 {
+ return int64(s.Uint64() & ^uint64(1<<63))
+}
+
+// Uint64 returns uniformly-distributed random (as in CSPRNG) uint64 value within [0, 1<<64) range.
+// Panics if random generator reader cannot return data.
+func (_ *source) Uint64() (val uint64) {
+ lock.RLock()
+ defer lock.RUnlock()
+ if err := binary.Read(rand.Reader, binary.BigEndian, &val); err != nil {
+ panic(err)
+ }
+ return
+}
+
+// Rand is alias for underlying random generator.
+type Rand = mrand.Rand // #nosec G404
+
+// NewGenerator returns a new generator that uses random values from crypto/rand as a source
+// (cryptographically secure random number generator).
+// Panics if crypto/rand input cannot be read.
+// Use it for everything where crypto secure non-deterministic randomness is required. Performance
+// takes a hit, so use sparingly.
+func NewGenerator() *Rand {
+ return mrand.New(&source{}) // #nosec G404 -- excluded
+}
+
+// NewDeterministicGenerator returns a random generator which is only seeded with crypto/rand,
+// but is deterministic otherwise (given seed, produces given results, deterministically).
+// Panics if crypto/rand input cannot be read.
+// Use this method for performance, where deterministic pseudo-random behaviour is enough.
+// Otherwise, rely on NewGenerator(). This method is not cryptographically secure as outputs
+// can be potentially predicted even without knowledge of the underlying seed.
+func NewDeterministicGenerator() *Rand {
+ randGen := NewGenerator()
+ return mrand.New(mrand.NewSource(randGen.Int63())) // #nosec G404 -- excluded
+}
diff --git a/common/crypto/rand/rand_test.go b/common/crypto/rand/rand_test.go
new file mode 100644
index 0000000..599006d
--- /dev/null
+++ b/common/crypto/rand/rand_test.go
@@ -0,0 +1,24 @@
+package rand
+
+import (
+ "math/rand"
+ "testing"
+)
+
+func TestNewGenerator(_ *testing.T) {
+ // Make sure that generation works, no panics.
+ randGen := NewGenerator()
+ _ = randGen.Int63()
+ _ = randGen.Uint64()
+ _ = randGen.Intn(32)
+ var _ = rand.Source64(randGen)
+}
+
+func TestNewDeterministicGenerator(_ *testing.T) {
+ // Make sure that generation works, no panics.
+ randGen := NewDeterministicGenerator()
+ _ = randGen.Int63()
+ _ = randGen.Uint64()
+ _ = randGen.Intn(32)
+ var _ = rand.Source64(randGen)
+}
diff --git a/common/crypto/signature_cgo.go b/common/crypto/signature_cgo.go
new file mode 100644
index 0000000..f4c803c
--- /dev/null
+++ b/common/crypto/signature_cgo.go
@@ -0,0 +1,92 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+//go:build !nacl && !js && cgo && !gofuzz
+
+package crypto
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "fmt"
+
+ "github.com/astranetworld/ast/common/math"
+ "github.com/ledgerwatch/secp256k1"
+)
+
+// Ecrecover returns the uncompressed public key that created the given signature.
+func Ecrecover(hash, sig []byte) ([]byte, error) {
+ return secp256k1.RecoverPubkey(hash, sig)
+}
+
+// Ecrecover returns the uncompressed public key that created the given signature.
+func EcrecoverWithContext(context *secp256k1.Context, hash, sig []byte) ([]byte, error) {
+ return secp256k1.RecoverPubkeyWithContext(context, hash, sig, nil)
+}
+
+// SigToPub returns the public key that created the given signature.
+func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
+ s, err := Ecrecover(hash, sig)
+ if err != nil {
+ return nil, err
+ }
+
+ x, y := elliptic.Unmarshal(S256(), s)
+ return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
+}
+
+// Sign calculates an ECDSA signature.
+//
+// This function is susceptible to chosen plaintext attacks that can leak
+// information about the private key that is used for signing. Callers must
+// be aware that the given digest cannot be chosen by an adversery. Common
+// solution is to hash any input before calculating the signature.
+//
+// The produced signature is in the [R || S || V] format where V is 0 or 1.
+func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
+ if len(digestHash) != DigestLength {
+ return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
+ }
+ seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
+ defer zeroBytes(seckey)
+ return secp256k1.Sign(digestHash, seckey)
+}
+
+// VerifySignature checks that the given public key created signature over digest.
+// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
+// The signature should have the 64 byte [R || S] format.
+func VerifySignature(pubkey, digestHash, signature []byte) bool {
+ return secp256k1.VerifySignature(pubkey, digestHash, signature)
+}
+
+// DecompressPubkey parses a public key in the 33-byte compressed format.
+func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
+ x, y := secp256k1.DecompressPubkey(pubkey)
+ if x == nil {
+ return nil, fmt.Errorf("invalid public key")
+ }
+ return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
+}
+
+// CompressPubkey encodes a public key to the 33-byte compressed format.
+func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
+ return secp256k1.CompressPubkey(pubkey.X, pubkey.Y)
+}
+
+// S256 returns an instance of the secp256k1 curve.
+func S256() elliptic.Curve {
+ return secp256k1.S256()
+}
diff --git a/common/db/database.go b/common/db/database.go
new file mode 100644
index 0000000..4674aa4
--- /dev/null
+++ b/common/db/database.go
@@ -0,0 +1,73 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package db
+
+type IDBReader interface {
+ Get(key []byte) (value []byte, err error)
+ Gets(key []byte, count uint) (keys [][]byte, value [][]byte, err error)
+ GetIterator(key []byte) (iterator IIterator, err error)
+}
+
+type IDBWriter interface {
+ Put(key []byte, value []byte) (err error)
+ Puts(keys [][]byte, values [][]byte) (err error)
+ Delete(key []byte) (err error)
+ Drop() (err error)
+}
+
+/*
+IIterator mdbx must close
+*/
+type IIterator interface {
+ Next() (err error)
+ Prev() (err error)
+ Key() (key []byte, err error)
+ Value() (value []byte, err error)
+ Close()
+}
+
+type IDBReaderWriter interface {
+ IDBReader
+ IDBWriter
+}
+
+type IDatabaseReader interface {
+ IDBReader
+}
+
+type IDatabaseWriter interface {
+ IDBWriter
+}
+
+type IDatabaseWriterReader interface {
+ IDBReaderWriter
+}
+
+type IDatabase interface {
+ OpenReader(dbName string) (reader IDatabaseReader, err error)
+ OpenWriter(dbName string) (writer IDatabaseWriter, err error)
+ Open(dbName string) (rw IDatabaseWriterReader, err error)
+ Snapshot() (ISnapshot, error)
+ Close() (err error)
+}
+
+type ISnapshot interface {
+ Put(dbName string, key []byte, value []byte) (err error)
+ Get(dbName string, key []byte) (value []byte, err error)
+ Commit() error
+ Rollback()
+}
diff --git a/common/events.go b/common/events.go
new file mode 100644
index 0000000..97780dc
--- /dev/null
+++ b/common/events.go
@@ -0,0 +1,59 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/modules/state"
+ "github.com/libp2p/go-libp2p/core/peer"
+)
+
+// NewLocalTxsEvent local txs
+type NewLocalTxsEvent struct{ Txs []*transaction.Transaction }
+
+// NewTxsEvent txs
+type NewTxsEvent struct{ Txs []*transaction.Transaction }
+
+// NewLogsEvent new logs
+type NewLogsEvent struct{ Logs []*block.Log }
+
+// RemovedLogsEvent is posted when a reorg happens // todo blockchain v2
+type RemovedLogsEvent struct{ Logs []*block.Log }
+
+// NewPendingLogsEvent is posted when a reorg happens // todo miner v2
+type NewPendingLogsEvent struct{ Logs []*block.Log }
+
+// PeerJoinEvent Peer join
+type PeerJoinEvent struct{ Peer peer.ID }
+
+// PeerDropEvent Peer drop
+type PeerDropEvent struct{ Peer peer.ID }
+
+// DownloaderStartEvent start download
+type DownloaderStartEvent struct{}
+
+// DownloaderFinishEvent finish download
+type DownloaderFinishEvent struct{}
+
+type ChainHighestBlock struct {
+ Block block.Block
+ Inserted bool
+}
+type MinedEntireEvent struct {
+ Entire state.EntireCode
+}
diff --git a/common/format.go b/common/format.go
new file mode 100644
index 0000000..ef82196
--- /dev/null
+++ b/common/format.go
@@ -0,0 +1,82 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+ "time"
+)
+
+// PrettyDuration is a pretty printed version of a time.Duration value that cuts
+// the unnecessary precision off from the formatted textual representation.
+type PrettyDuration time.Duration
+
+var prettyDurationRe = regexp.MustCompile(`\.[0-9]+`)
+
+// String implements the Stringer interface, allowing pretty printing of duration
+// values rounded to three decimals.
+func (d PrettyDuration) String() string {
+ label := fmt.Sprintf("%v", time.Duration(d))
+ if match := prettyDurationRe.FindString(label); len(match) > 4 {
+ label = strings.Replace(label, match, match[:4], 1)
+ }
+ return label
+}
+
+// PrettyAge is a pretty printed version of a time.Duration value that rounds
+// the values up to a single most significant unit, days/weeks/years included.
+type PrettyAge time.Time
+
+// ageUnits is a list of units the age pretty printing uses.
+var ageUnits = []struct {
+ Size time.Duration
+ Symbol string
+}{
+ {12 * 30 * 24 * time.Hour, "y"},
+ {30 * 24 * time.Hour, "mo"},
+ {7 * 24 * time.Hour, "w"},
+ {24 * time.Hour, "d"},
+ {time.Hour, "h"},
+ {time.Minute, "m"},
+ {time.Second, "s"},
+}
+
+// String implements the Stringer interface, allowing pretty printing of duration
+// values rounded to the most significant time unit.
+func (t PrettyAge) String() string {
+ // Calculate the time difference and handle the 0 cornercase
+ diff := time.Since(time.Time(t))
+ if diff < time.Second {
+ return "0"
+ }
+ // Accumulate a precision of 3 components before returning
+ result, prec := "", 0
+
+ for _, unit := range ageUnits {
+ if diff > unit.Size {
+ result = fmt.Sprintf("%s%d%s", result, diff/unit.Size, unit.Symbol)
+ diff %= unit.Size
+
+ if prec += 1; prec >= 3 {
+ break
+ }
+ }
+ }
+ return result
+}
diff --git a/common/gaspool.go b/common/gaspool.go
new file mode 100644
index 0000000..f50e494
--- /dev/null
+++ b/common/gaspool.go
@@ -0,0 +1,55 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "fmt"
+ "github.com/astranetworld/ast/core"
+ "math"
+)
+
+// GasPool tracks the amount of gas available during execution of the transactions
+// in a block. The zero value is a pool with zero gas available.
+type GasPool uint64
+
+// AddGas makes gas available for execution.
+func (gp *GasPool) AddGas(amount uint64) *GasPool {
+ if uint64(*gp) > math.MaxUint64-amount {
+ panic("gas pool pushed above uint64")
+ }
+ *(*uint64)(gp) += amount
+ return gp
+}
+
+// SubGas deducts the given amount from the pool if enough gas is
+// available and returns an error otherwise.
+func (gp *GasPool) SubGas(amount uint64) error {
+ if uint64(*gp) < amount {
+ return core.ErrGasLimitReached
+ }
+ *(*uint64)(gp) -= amount
+ return nil
+}
+
+// Gas returns the amount of gas remaining in the pool.
+func (gp *GasPool) Gas() uint64 {
+ return uint64(*gp)
+}
+
+func (gp *GasPool) String() string {
+ return fmt.Sprintf("%d", *gp)
+}
diff --git a/common/hash/hash.go b/common/hash/hash.go
new file mode 100644
index 0000000..8005048
--- /dev/null
+++ b/common/hash/hash.go
@@ -0,0 +1,120 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package hash
+
+import (
+ "bytes"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/avm/rlp"
+ "sync"
+
+ "golang.org/x/crypto/sha3"
+)
+
+// hasherPool holds LegacyKeccak256 hashers for rlpHash.
+var HasherPool = sync.Pool{
+ New: func() interface{} { return sha3.NewLegacyKeccak256() },
+}
+
+// encodeBufferPool holds temporary encoder buffers for DeriveSha and TX encoding.
+var encodeBufferPool = sync.Pool{
+ New: func() interface{} { return new(bytes.Buffer) },
+}
+
+// DerivableList is the input to DeriveSha.
+// It is implemented by the 'Transactions' and 'Receipts' types.
+// This is internal, do not use these methods.
+type DerivableList interface {
+ Len() int
+ EncodeIndex(int, *bytes.Buffer)
+}
+
+func encodeForDerive(list DerivableList, i int, buf *bytes.Buffer) []byte {
+ buf.Reset()
+ list.EncodeIndex(i, buf)
+ // It's really unfortunate that we need to do perform this copy.
+ // StackTrie holds onto the values until Hash is called, so the values
+ // written to it must not alias.
+ return types.CopyBytes(buf.Bytes())
+}
+
+// DeriveSha creates the tree hashes of transactions and receipts in a block header.
+func DeriveSha(list DerivableList) (h types.Hash) {
+
+ sha := HasherPool.Get().(crypto.KeccakState)
+ defer HasherPool.Put(sha)
+ sha.Reset()
+
+ valueBuf := encodeBufferPool.Get().(*bytes.Buffer)
+ defer encodeBufferPool.Put(valueBuf)
+
+ for i := 0; i < list.Len(); i++ {
+ value := encodeForDerive(list, i, valueBuf)
+ sha.Write(value)
+ }
+ sha.Read(h[:])
+ return h
+}
+
+var (
+ // NilHash sum(nil)
+ NilHash = types.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
+ // EmptyUncleHash rlpHash([]*Header(nil))
+ EmptyUncleHash = types.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")
+)
+
+func RlpHash(x interface{}) (h types.Hash) {
+ sha := HasherPool.Get().(crypto.KeccakState)
+ defer HasherPool.Put(sha)
+ sha.Reset()
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
+ return h
+}
+
+// PrefixedRlpHash writes the prefix into the hasher before rlp-encoding x.
+// It's used for typed transactions.
+func PrefixedRlpHash(prefix byte, x interface{}) (h types.Hash) {
+ sha := HasherPool.Get().(crypto.KeccakState)
+ defer HasherPool.Put(sha)
+ sha.Reset()
+ sha.Write([]byte{prefix})
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
+ return h
+}
+
+// Hash defines a function that returns the sha256 checksum of the data passed in.
+// https://github.com/ethereum/consensus-specs/blob/v0.9.3/specs/core/0_beacon-chain.md#hash
+func Hash(data []byte) [32]byte {
+ h, _ := HasherPool.Get().(crypto.KeccakState)
+ defer HasherPool.Put(h)
+ h.Reset()
+
+ var b [32]byte
+
+ // The hash interface never returns an error, for that reason
+ // we are not handling the error below. For reference, it is
+ // stated here https://golang.org/pkg/hash/#Hash
+
+ // #nosec G104
+ h.Write(data)
+ h.Read(b[:])
+
+ return b
+}
diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go
new file mode 100644
index 0000000..9080f03
--- /dev/null
+++ b/common/hexutil/hexutil.go
@@ -0,0 +1,265 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+/*
+Package hexutil implements hex encoding with 0x prefix.
+This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
+
+# Encoding Rules
+
+All hex data must have prefix "0x".
+
+For byte slices, the hex data must be of even length. An empty byte slice
+encodes as "0x".
+
+Integers are encoded using the least amount of digits (no leading zero digits). Their
+encoding may be of uneven length. The number zero encodes as "0x0".
+*/
+package hexutil
+
+import (
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "strconv"
+)
+
+const uintBits = 32 << (uint64(^uint(0)) >> 63)
+
+// Errors
+var (
+ ErrEmptyString = &decError{"empty hex string"}
+ ErrSyntax = &decError{"invalid hex string"}
+ ErrMissingPrefix = &decError{"hex string without 0x prefix"}
+ ErrOddLength = &decError{"hex string of odd length"}
+ ErrEmptyNumber = &decError{"hex string \"0x\""}
+ ErrLeadingZero = &decError{"hex number with leading zero digits"}
+ ErrUint64Range = &decError{"hex number > 64 bits"}
+ ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
+ ErrBig256Range = &decError{"hex number > 256 bits"}
+)
+
+type decError struct{ msg string }
+
+func (err decError) Error() string { return err.msg }
+
+// Decode decodes a hex string with 0x prefix.
+func Decode(input string) ([]byte, error) {
+ if len(input) == 0 {
+ return nil, ErrEmptyString
+ }
+ if !has0xPrefix(input) {
+ return nil, ErrMissingPrefix
+ }
+ b, err := hex.DecodeString(input[2:])
+ if err != nil {
+ err = mapError(err)
+ }
+ return b, err
+}
+
+// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
+func MustDecode(input string) []byte {
+ dec, err := Decode(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// Encode encodes b as a hex string with 0x prefix.
+func Encode(b []byte) string {
+ enc := make([]byte, len(b)*2+2)
+ copy(enc, "0x")
+ hex.Encode(enc[2:], b)
+ return string(enc)
+}
+
+// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
+func DecodeUint64(input string) (uint64, error) {
+ raw, err := checkNumber(input)
+ if err != nil {
+ return 0, err
+ }
+ dec, err := strconv.ParseUint(raw, 16, 64)
+ if err != nil {
+ err = mapError(err)
+ }
+ return dec, err
+}
+
+// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeUint64(input string) uint64 {
+ dec, err := DecodeUint64(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// EncodeUint64 encodes i as a hex string with 0x prefix.
+func EncodeUint64(i uint64) string {
+ enc := make([]byte, 2, 10)
+ copy(enc, "0x")
+ return string(strconv.AppendUint(enc, i, 16))
+}
+
+var bigWordNibbles int
+
+func init() {
+ // This is a weird way to compute the number of nibbles required for big.Word.
+ // The usual way would be to use constant arithmetic but go vet can't handle that.
+ b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
+ switch len(b.Bits()) {
+ case 1:
+ bigWordNibbles = 16
+ case 2:
+ bigWordNibbles = 8
+ default:
+ panic("weird big.Word size")
+ }
+}
+
+// DecodeBig decodes a hex string with 0x prefix as a quantity.
+// Numbers larger than 256 bits are not accepted.
+func DecodeBig(input string) (*big.Int, error) {
+ raw, err := checkNumber(input)
+ if err != nil {
+ return nil, err
+ }
+ if len(raw) > 64 {
+ return nil, ErrBig256Range
+ }
+ words := make([]big.Word, len(raw)/bigWordNibbles+1)
+ end := len(raw)
+ for i := range words {
+ start := end - bigWordNibbles
+ if start < 0 {
+ start = 0
+ }
+ for ri := start; ri < end; ri++ {
+ nib := decodeNibble(raw[ri])
+ if nib == badNibble {
+ return nil, ErrSyntax
+ }
+ words[i] *= 16
+ words[i] += big.Word(nib)
+ }
+ end = start
+ }
+ dec := new(big.Int).SetBits(words)
+ return dec, nil
+}
+
+// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeBig(input string) *big.Int {
+ dec, err := DecodeBig(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// EncodeBig encodes bigint as a hex string with 0x prefix.
+// The sign of the integer is ignored.
+func EncodeBig(bigint *big.Int) string {
+ nbits := bigint.BitLen()
+ if nbits == 0 {
+ return "0x0"
+ }
+ return fmt.Sprintf("%#x", bigint)
+}
+
+func has0xPrefix(input string) bool {
+ return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkNumber(input string) (raw string, err error) {
+ if len(input) == 0 {
+ return "", ErrEmptyString
+ }
+ if !has0xPrefix(input) {
+ return "", ErrMissingPrefix
+ }
+ input = input[2:]
+ if len(input) == 0 {
+ return "", ErrEmptyNumber
+ }
+ if len(input) > 1 && input[0] == '0' {
+ return "", ErrLeadingZero
+ }
+ return input, nil
+}
+
+const badNibble = ^uint64(0)
+
+func decodeNibble(in byte) uint64 {
+ switch {
+ case in >= '0' && in <= '9':
+ return uint64(in - '0')
+ case in >= 'A' && in <= 'F':
+ return uint64(in - 'A' + 10)
+ case in >= 'a' && in <= 'f':
+ return uint64(in - 'a' + 10)
+ default:
+ return badNibble
+ }
+}
+
+func mapError(err error) error {
+ if err, ok := err.(*strconv.NumError); ok {
+ switch err.Err {
+ case strconv.ErrRange:
+ return ErrUint64Range
+ case strconv.ErrSyntax:
+ return ErrSyntax
+ }
+ }
+ if _, ok := err.(hex.InvalidByteError); ok {
+ return ErrSyntax
+ }
+ if err == hex.ErrLength {
+ return ErrOddLength
+ }
+ return err
+}
+
+// CompressNibbles - supports only even number of nibbles
+// This method supports only arrays of even nibbles
+//
+// HI_NIBBLE(b) = (b >> 4) & 0x0F
+// LO_NIBBLE(b) = b & 0x0F
+func CompressNibbles(nibbles []byte, out *[]byte) {
+ tmp := (*out)[:0]
+ for i := 0; i < len(nibbles); i += 2 {
+ tmp = append(tmp, nibbles[i]<<4|nibbles[i+1])
+ }
+ *out = tmp
+}
+
+// DecompressNibbles - supports only even number of nibbles
+//
+// HI_NIBBLE(b) = (b >> 4) & 0x0F
+// LO_NIBBLE(b) = b & 0x0F
+func DecompressNibbles(in []byte, out *[]byte) {
+ tmp := (*out)[:0]
+ for i := 0; i < len(in); i++ {
+ tmp = append(tmp, (in[i]>>4)&0x0F, in[i]&0x0F)
+ }
+ *out = tmp
+}
diff --git a/common/hexutil/hexutil_test.go b/common/hexutil/hexutil_test.go
new file mode 100644
index 0000000..3ce535e
--- /dev/null
+++ b/common/hexutil/hexutil_test.go
@@ -0,0 +1,203 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package hexutil
+
+import (
+ "bytes"
+ "math/big"
+ "testing"
+)
+
+type marshalTest struct {
+ input interface{}
+ want string
+}
+
+type unmarshalTest struct {
+ input string
+ want interface{}
+ wantErr error // if set, decoding must fail on any platform
+ wantErr32bit error // if set, decoding must fail on 32bit platforms (used for Uint tests)
+}
+
+var (
+ encodeBytesTests = []marshalTest{
+ {[]byte{}, "0x"},
+ {[]byte{0}, "0x00"},
+ {[]byte{0, 0, 1, 2}, "0x00000102"},
+ }
+
+ encodeBigTests = []marshalTest{
+ {referenceBig("0"), "0x0"},
+ {referenceBig("1"), "0x1"},
+ {referenceBig("ff"), "0xff"},
+ {referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
+ {referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"},
+ {referenceBig("-80a7f2c1bcc396c00"), "-0x80a7f2c1bcc396c00"},
+ }
+
+ encodeUint64Tests = []marshalTest{
+ {uint64(0), "0x0"},
+ {uint64(1), "0x1"},
+ {uint64(0xff), "0xff"},
+ {uint64(0x1122334455667788), "0x1122334455667788"},
+ }
+
+ encodeUintTests = []marshalTest{
+ {uint(0), "0x0"},
+ {uint(1), "0x1"},
+ {uint(0xff), "0xff"},
+ {uint(0x11223344), "0x11223344"},
+ }
+
+ decodeBytesTests = []unmarshalTest{
+ // invalid
+ {input: ``, wantErr: ErrEmptyString},
+ {input: `0`, wantErr: ErrMissingPrefix},
+ {input: `0x0`, wantErr: ErrOddLength},
+ {input: `0x023`, wantErr: ErrOddLength},
+ {input: `0xxx`, wantErr: ErrSyntax},
+ {input: `0x01zz01`, wantErr: ErrSyntax},
+ // valid
+ {input: `0x`, want: []byte{}},
+ {input: `0X`, want: []byte{}},
+ {input: `0x02`, want: []byte{0x02}},
+ {input: `0X02`, want: []byte{0x02}},
+ {input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
+ {
+ input: `0xffffffffffffffffffffffffffffffffffff`,
+ want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ },
+ }
+
+ decodeBigTests = []unmarshalTest{
+ // invalid
+ {input: `0`, wantErr: ErrMissingPrefix},
+ {input: `0x`, wantErr: ErrEmptyNumber},
+ {input: `0x01`, wantErr: ErrLeadingZero},
+ {input: `0xx`, wantErr: ErrSyntax},
+ {input: `0x1zz01`, wantErr: ErrSyntax},
+ {
+ input: `0x10000000000000000000000000000000000000000000000000000000000000000`,
+ wantErr: ErrBig256Range,
+ },
+ // valid
+ {input: `0x0`, want: big.NewInt(0)},
+ {input: `0x2`, want: big.NewInt(0x2)},
+ {input: `0x2F2`, want: big.NewInt(0x2f2)},
+ {input: `0X2F2`, want: big.NewInt(0x2f2)},
+ {input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
+ {input: `0xbBb`, want: big.NewInt(0xbbb)},
+ {input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
+ {
+ input: `0x112233445566778899aabbccddeeff`,
+ want: referenceBig("112233445566778899aabbccddeeff"),
+ },
+ {
+ input: `0xffffffffffffffffffffffffffffffffffff`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
+ },
+ {
+ input: `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
+ },
+ }
+
+ decodeUint64Tests = []unmarshalTest{
+ // invalid
+ {input: `0`, wantErr: ErrMissingPrefix},
+ {input: `0x`, wantErr: ErrEmptyNumber},
+ {input: `0x01`, wantErr: ErrLeadingZero},
+ {input: `0xfffffffffffffffff`, wantErr: ErrUint64Range},
+ {input: `0xx`, wantErr: ErrSyntax},
+ {input: `0x1zz01`, wantErr: ErrSyntax},
+ // valid
+ {input: `0x0`, want: uint64(0)},
+ {input: `0x2`, want: uint64(0x2)},
+ {input: `0x2F2`, want: uint64(0x2f2)},
+ {input: `0X2F2`, want: uint64(0x2f2)},
+ {input: `0x1122aaff`, want: uint64(0x1122aaff)},
+ {input: `0xbbb`, want: uint64(0xbbb)},
+ {input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
+ }
+)
+
+func TestEncode(t *testing.T) {
+ for _, test := range encodeBytesTests {
+ enc := Encode(test.input.([]byte))
+ if enc != test.want {
+ t.Errorf("input %x: wrong encoding %s", test.input, enc)
+ }
+ }
+}
+
+func TestDecode(t *testing.T) {
+ for _, test := range decodeBytesTests {
+ dec, err := Decode(test.input)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if !bytes.Equal(test.want.([]byte), dec) {
+ t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+ continue
+ }
+ }
+}
+
+func TestEncodeBig(t *testing.T) {
+ for _, test := range encodeBigTests {
+ enc := EncodeBig(test.input.(*big.Int))
+ if enc != test.want {
+ t.Errorf("input %x: wrong encoding %s", test.input, enc)
+ }
+ }
+}
+
+func TestDecodeBig(t *testing.T) {
+ for _, test := range decodeBigTests {
+ dec, err := DecodeBig(test.input)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if dec.Cmp(test.want.(*big.Int)) != 0 {
+ t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+ continue
+ }
+ }
+}
+
+func TestEncodeUint64(t *testing.T) {
+ for _, test := range encodeUint64Tests {
+ enc := EncodeUint64(test.input.(uint64))
+ if enc != test.want {
+ t.Errorf("input %x: wrong encoding %s", test.input, enc)
+ }
+ }
+}
+
+func TestDecodeUint64(t *testing.T) {
+ for _, test := range decodeUint64Tests {
+ dec, err := DecodeUint64(test.input)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if dec != test.want.(uint64) {
+ t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
+ continue
+ }
+ }
+}
diff --git a/common/hexutil/json.go b/common/hexutil/json.go
new file mode 100644
index 0000000..ccf9cb5
--- /dev/null
+++ b/common/hexutil/json.go
@@ -0,0 +1,323 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package hexutil
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "math/big"
+ "reflect"
+ "strconv"
+)
+
+var (
+ bytesT = reflect.TypeOf(Bytes(nil))
+ bigT = reflect.TypeOf((*Big)(nil))
+ uintT = reflect.TypeOf(Uint(0))
+ uint64T = reflect.TypeOf(Uint64(0))
+)
+
+// Bytes marshals/unmarshals as a JSON string with 0x prefix.
+// The empty slice marshals as "0x".
+type Bytes []byte
+
+const hexPrefix = `0x`
+
+// MarshalText implements encoding.TextMarshaler
+func (b Bytes) MarshalText() ([]byte, error) {
+ result := make([]byte, len(b)*2+2)
+ copy(result, hexPrefix)
+ hex.Encode(result[2:], b)
+ return result, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Bytes) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(bytesT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (b *Bytes) UnmarshalText(input []byte) error {
+ raw, err := checkText(input, true)
+ if err != nil {
+ return err
+ }
+ dec := make([]byte, len(raw)/2)
+ if _, err = hex.Decode(dec, raw); err != nil {
+ err = mapError(err)
+ } else {
+ *b = dec
+ }
+ return err
+}
+
+// String returns the hex encoding of b.
+func (b Bytes) String() string {
+ return Encode(b)
+}
+
+// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
+// determines the required input length. This function is commonly used to implement the
+// UnmarshalJSON method for fixed-size types.
+func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
+ if !isString(input) {
+ return errNonString(typ)
+ }
+ return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ)
+}
+
+// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
+// determines the required input length. This function is commonly used to implement the
+// UnmarshalText method for fixed-size types.
+func UnmarshalFixedText(typname string, input, out []byte) error {
+ raw, err := checkText(input, true)
+ if err != nil {
+ return err
+ }
+ if len(raw)/2 != len(out) {
+ return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
+ }
+ // Pre-verify syntax before modifying out.
+ for _, b := range raw {
+ if decodeNibble(b) == badNibble {
+ return ErrSyntax
+ }
+ }
+ hex.Decode(out, raw)
+ return nil
+}
+
+// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The
+// length of out determines the required input length. This function is commonly used to
+// implement the UnmarshalText method for fixed-size types.
+func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error {
+ raw, err := checkText(input, false)
+ if err != nil {
+ return err
+ }
+ if len(raw)/2 != len(out) {
+ return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
+ }
+ // Pre-verify syntax before modifying out.
+ for _, b := range raw {
+ if decodeNibble(b) == badNibble {
+ return ErrSyntax
+ }
+ }
+ hex.Decode(out, raw)
+ return nil
+}
+
+// Big marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+//
+// Negative integers are not supported at this time. Attempting to marshal them will
+// return an error. Values larger than 256bits are rejected by Unmarshal but will be
+// marshaled without error.
+type Big big.Int
+
+// MarshalText implements encoding.TextMarshaler
+func (b Big) MarshalText() ([]byte, error) {
+ return []byte(EncodeBig((*big.Int)(&b))), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Big) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(bigT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *Big) UnmarshalText(input []byte) error {
+ raw, err := checkNumberText(input)
+ if err != nil {
+ return err
+ }
+ if len(raw) > 64 {
+ return ErrBig256Range
+ }
+ words := make([]big.Word, len(raw)/bigWordNibbles+1)
+ end := len(raw)
+ for i := range words {
+ start := end - bigWordNibbles
+ if start < 0 {
+ start = 0
+ }
+ for ri := start; ri < end; ri++ {
+ nib := decodeNibble(raw[ri])
+ if nib == badNibble {
+ return ErrSyntax
+ }
+ words[i] *= 16
+ words[i] += big.Word(nib)
+ }
+ end = start
+ }
+ var dec big.Int
+ dec.SetBits(words)
+ *b = (Big)(dec)
+ return nil
+}
+
+// ToInt converts b to a big.Int.
+func (b *Big) ToInt() *big.Int {
+ return (*big.Int)(b)
+}
+
+// String returns the hex encoding of b.
+func (b *Big) String() string {
+ return EncodeBig(b.ToInt())
+}
+
+// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint64 uint64
+
+// MarshalText implements encoding.TextMarshaler.
+func (b Uint64) MarshalText() ([]byte, error) {
+ buf := make([]byte, 2, 10)
+ copy(buf, `0x`)
+ buf = strconv.AppendUint(buf, uint64(b), 16)
+ return buf, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint64) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(uint64T)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *Uint64) UnmarshalText(input []byte) error {
+ raw, err := checkNumberText(input)
+ if err != nil {
+ return err
+ }
+ if len(raw) > 16 {
+ return ErrUint64Range
+ }
+ var dec uint64
+ for _, byte := range raw {
+ nib := decodeNibble(byte)
+ if nib == badNibble {
+ return ErrSyntax
+ }
+ dec *= 16
+ dec += nib
+ }
+ *b = Uint64(dec)
+ return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint64) String() string {
+ return EncodeUint64(uint64(b))
+}
+
+// Uint marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint uint
+
+// MarshalText implements encoding.TextMarshaler.
+func (b Uint) MarshalText() ([]byte, error) {
+ return Uint64(b).MarshalText()
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(uintT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (b *Uint) UnmarshalText(input []byte) error {
+ var u64 Uint64
+ err := u64.UnmarshalText(input)
+ if u64 > Uint64(^uint(0)) || err == ErrUint64Range {
+ return ErrUintRange
+ } else if err != nil {
+ return err
+ }
+ *b = Uint(u64)
+ return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint) String() string {
+ return EncodeUint64(uint64(b))
+}
+
+func isString(input []byte) bool {
+ return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
+}
+
+func bytesHave0xPrefix(input []byte) bool {
+ return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkText(input []byte, wantPrefix bool) ([]byte, error) {
+ if len(input) == 0 {
+ return nil, nil // empty strings are allowed
+ }
+ if bytesHave0xPrefix(input) {
+ input = input[2:]
+ } else if wantPrefix {
+ return nil, ErrMissingPrefix
+ }
+ if len(input)%2 != 0 {
+ return nil, ErrOddLength
+ }
+ return input, nil
+}
+
+func checkNumberText(input []byte) (raw []byte, err error) {
+ if len(input) == 0 {
+ return nil, nil // empty strings are allowed
+ }
+ if !bytesHave0xPrefix(input) {
+ return nil, ErrMissingPrefix
+ }
+ input = input[2:]
+ if len(input) == 0 {
+ return nil, ErrEmptyNumber
+ }
+ if len(input) > 1 && input[0] == '0' {
+ return nil, ErrLeadingZero
+ }
+ return input, nil
+}
+
+func wrapTypeError(err error, typ reflect.Type) error {
+ if _, ok := err.(*decError); ok {
+ return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
+ }
+ return err
+}
+
+func errNonString(typ reflect.Type) error {
+ return &json.UnmarshalTypeError{Value: "non-string", Type: typ}
+}
diff --git a/common/hexutil/json_test.go b/common/hexutil/json_test.go
new file mode 100644
index 0000000..accc7a4
--- /dev/null
+++ b/common/hexutil/json_test.go
@@ -0,0 +1,374 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package hexutil
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "math/big"
+ "testing"
+)
+
+func checkError(t *testing.T, input string, got, want error) bool {
+ if got == nil {
+ if want != nil {
+ t.Errorf("input %s: got no error, want %q", input, want)
+ return false
+ }
+ return true
+ }
+ if want == nil {
+ t.Errorf("input %s: unexpected error %q", input, got)
+ } else if got.Error() != want.Error() {
+ t.Errorf("input %s: got error %q, want %q", input, got, want)
+ }
+ return false
+}
+
+func referenceBig(s string) *big.Int {
+ b, ok := new(big.Int).SetString(s, 16)
+ if !ok {
+ panic("invalid")
+ }
+ return b
+}
+
+func referenceBytes(s string) []byte {
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+var errJSONEOF = errors.New("unexpected end of JSON input")
+
+var unmarshalBytesTests = []unmarshalTest{
+ // invalid encoding
+ {input: "", wantErr: errJSONEOF},
+ {input: "null", wantErr: errNonString(bytesT)},
+ {input: "10", wantErr: errNonString(bytesT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bytesT)},
+ {input: `"0x0"`, wantErr: wrapTypeError(ErrOddLength, bytesT)},
+ {input: `"0xxx"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
+ {input: `"0x01zz01"`, wantErr: wrapTypeError(ErrSyntax, bytesT)},
+
+ // valid encoding
+ {input: `""`, want: referenceBytes("")},
+ {input: `"0x"`, want: referenceBytes("")},
+ {input: `"0x02"`, want: referenceBytes("02")},
+ {input: `"0X02"`, want: referenceBytes("02")},
+ {input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
+ {
+ input: `"0xffffffffffffffffffffffffffffffffffff"`,
+ want: referenceBytes("ffffffffffffffffffffffffffffffffffff"),
+ },
+}
+
+func TestUnmarshalBytes(t *testing.T) {
+ for _, test := range unmarshalBytesTests {
+ var v Bytes
+ err := json.Unmarshal([]byte(test.input), &v)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if !bytes.Equal(test.want.([]byte), v) {
+ t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
+ continue
+ }
+ }
+}
+
+func BenchmarkUnmarshalBytes(b *testing.B) {
+ input := []byte(`"0x123456789abcdef123456789abcdef"`)
+ for i := 0; i < b.N; i++ {
+ var v Bytes
+ if err := v.UnmarshalJSON(input); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalBytes(t *testing.T) {
+ for _, test := range encodeBytesTests {
+ in := test.input.([]byte)
+ out, err := json.Marshal(Bytes(in))
+ if err != nil {
+ t.Errorf("%x: %v", in, err)
+ continue
+ }
+ if want := `"` + test.want + `"`; string(out) != want {
+ t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+ continue
+ }
+ if out := Bytes(in).String(); out != test.want {
+ t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+ continue
+ }
+ }
+}
+
+var unmarshalBigTests = []unmarshalTest{
+ // invalid encoding
+ {input: "", wantErr: errJSONEOF},
+ {input: "null", wantErr: errNonString(bigT)},
+ {input: "10", wantErr: errNonString(bigT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, bigT)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, bigT)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, bigT)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, bigT)},
+ {
+ input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
+ wantErr: wrapTypeError(ErrBig256Range, bigT),
+ },
+
+ // valid encoding
+ {input: `""`, want: big.NewInt(0)},
+ {input: `"0x0"`, want: big.NewInt(0)},
+ {input: `"0x2"`, want: big.NewInt(0x2)},
+ {input: `"0x2F2"`, want: big.NewInt(0x2f2)},
+ {input: `"0X2F2"`, want: big.NewInt(0x2f2)},
+ {input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
+ {input: `"0xbBb"`, want: big.NewInt(0xbbb)},
+ {input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
+ {
+ input: `"0x112233445566778899aabbccddeeff"`,
+ want: referenceBig("112233445566778899aabbccddeeff"),
+ },
+ {
+ input: `"0xffffffffffffffffffffffffffffffffffff"`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
+ },
+ {
+ input: `"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`,
+ want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
+ },
+}
+
+func TestUnmarshalBig(t *testing.T) {
+ for _, test := range unmarshalBigTests {
+ var v Big
+ err := json.Unmarshal([]byte(test.input), &v)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
+ t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
+ continue
+ }
+ }
+}
+
+func BenchmarkUnmarshalBig(b *testing.B) {
+ input := []byte(`"0x123456789abcdef123456789abcdef"`)
+ for i := 0; i < b.N; i++ {
+ var v Big
+ if err := v.UnmarshalJSON(input); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalBig(t *testing.T) {
+ for _, test := range encodeBigTests {
+ in := test.input.(*big.Int)
+ out, err := json.Marshal((*Big)(in))
+ if err != nil {
+ t.Errorf("%d: %v", in, err)
+ continue
+ }
+ if want := `"` + test.want + `"`; string(out) != want {
+ t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+ continue
+ }
+ if out := (*Big)(in).String(); out != test.want {
+ t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+ continue
+ }
+ }
+}
+
+var unmarshalUint64Tests = []unmarshalTest{
+ // invalid encoding
+ {input: "", wantErr: errJSONEOF},
+ {input: "null", wantErr: errNonString(uint64T)},
+ {input: "10", wantErr: errNonString(uint64T)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uint64T)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uint64T)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uint64T)},
+ {input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUint64Range, uint64T)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uint64T)},
+
+ // valid encoding
+ {input: `""`, want: uint64(0)},
+ {input: `"0x0"`, want: uint64(0)},
+ {input: `"0x2"`, want: uint64(0x2)},
+ {input: `"0x2F2"`, want: uint64(0x2f2)},
+ {input: `"0X2F2"`, want: uint64(0x2f2)},
+ {input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
+ {input: `"0xbbb"`, want: uint64(0xbbb)},
+ {input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
+}
+
+func TestUnmarshalUint64(t *testing.T) {
+ for _, test := range unmarshalUint64Tests {
+ var v Uint64
+ err := json.Unmarshal([]byte(test.input), &v)
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if uint64(v) != test.want.(uint64) {
+ t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
+ continue
+ }
+ }
+}
+
+func BenchmarkUnmarshalUint64(b *testing.B) {
+ input := []byte(`"0x123456789abcdf"`)
+ for i := 0; i < b.N; i++ {
+ var v Uint64
+ v.UnmarshalJSON(input)
+ }
+}
+
+func TestMarshalUint64(t *testing.T) {
+ for _, test := range encodeUint64Tests {
+ in := test.input.(uint64)
+ out, err := json.Marshal(Uint64(in))
+ if err != nil {
+ t.Errorf("%d: %v", in, err)
+ continue
+ }
+ if want := `"` + test.want + `"`; string(out) != want {
+ t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+ continue
+ }
+ if out := (Uint64)(in).String(); out != test.want {
+ t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+ continue
+ }
+ }
+}
+
+func TestMarshalUint(t *testing.T) {
+ for _, test := range encodeUintTests {
+ in := test.input.(uint)
+ out, err := json.Marshal(Uint(in))
+ if err != nil {
+ t.Errorf("%d: %v", in, err)
+ continue
+ }
+ if want := `"` + test.want + `"`; string(out) != want {
+ t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
+ continue
+ }
+ if out := (Uint)(in).String(); out != test.want {
+ t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
+ continue
+ }
+ }
+}
+
+var (
+ // These are variables (not constants) to avoid constant overflow
+ // checks in the compiler on 32bit platforms.
+ maxUint33bits = uint64(^uint32(0)) + 1
+ maxUint64bits = ^uint64(0)
+)
+
+var unmarshalUintTests = []unmarshalTest{
+ // invalid encoding
+ {input: "", wantErr: errJSONEOF},
+ {input: "null", wantErr: errNonString(uintT)},
+ {input: "10", wantErr: errNonString(uintT)},
+ {input: `"0"`, wantErr: wrapTypeError(ErrMissingPrefix, uintT)},
+ {input: `"0x"`, wantErr: wrapTypeError(ErrEmptyNumber, uintT)},
+ {input: `"0x01"`, wantErr: wrapTypeError(ErrLeadingZero, uintT)},
+ {input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
+ {input: `"0xfffffffffffffffff"`, wantErr: wrapTypeError(ErrUintRange, uintT)},
+ {input: `"0xx"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
+ {input: `"0x1zz01"`, wantErr: wrapTypeError(ErrSyntax, uintT)},
+
+ // valid encoding
+ {input: `""`, want: uint(0)},
+ {input: `"0x0"`, want: uint(0)},
+ {input: `"0x2"`, want: uint(0x2)},
+ {input: `"0x2F2"`, want: uint(0x2f2)},
+ {input: `"0X2F2"`, want: uint(0x2f2)},
+ {input: `"0x1122aaff"`, want: uint(0x1122aaff)},
+ {input: `"0xbbb"`, want: uint(0xbbb)},
+ {input: `"0xffffffff"`, want: uint(0xffffffff)},
+ {input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: wrapTypeError(ErrUintRange, uintT)},
+}
+
+func TestUnmarshalUint(t *testing.T) {
+ for _, test := range unmarshalUintTests {
+ var v Uint
+ err := json.Unmarshal([]byte(test.input), &v)
+ if uintBits == 32 && test.wantErr32bit != nil {
+ checkError(t, test.input, err, test.wantErr32bit)
+ continue
+ }
+ if !checkError(t, test.input, err, test.wantErr) {
+ continue
+ }
+ if uint(v) != test.want.(uint) {
+ t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
+ continue
+ }
+ }
+}
+
+func TestUnmarshalFixedUnprefixedText(t *testing.T) {
+ tests := []struct {
+ input string
+ want []byte
+ wantErr error
+ }{
+ {input: "0x2", wantErr: ErrOddLength},
+ {input: "2", wantErr: ErrOddLength},
+ {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
+ {input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
+ // check that output is not modified for partially correct input
+ {input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
+ {input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
+ // valid inputs
+ {input: "44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
+ {input: "0x44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
+ }
+
+ for _, test := range tests {
+ out := make([]byte, 4)
+ err := UnmarshalFixedUnprefixedText("x", []byte(test.input), out)
+ switch {
+ case err == nil && test.wantErr != nil:
+ t.Errorf("%q: got no error, expected %q", test.input, test.wantErr)
+ case err != nil && test.wantErr == nil:
+ t.Errorf("%q: unexpected error %q", test.input, err)
+ case err != nil && err.Error() != test.wantErr.Error():
+ t.Errorf("%q: error mismatch: got %q, want %q", test.input, err, test.wantErr)
+ }
+ if test.want != nil && !bytes.Equal(out, test.want) {
+ t.Errorf("%q: output mismatch: got %x, want %x", test.input, out, test.want)
+ }
+ }
+}
diff --git a/common/interface/Transaction.go b/common/interface/Transaction.go
new file mode 100644
index 0000000..3c27442
--- /dev/null
+++ b/common/interface/Transaction.go
@@ -0,0 +1,34 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package _interface
+
+//import (
+// "github.com/astranetworld/ast/common/types"
+// "github.com/gogo/protobuf/proto"
+//)
+//
+//type ITransaction interface {
+// MarshalTo(data []byte) (n int, err error)
+// ToProtoMessage() proto.Message
+// Unmarshal(data []byte) error
+// Marshal() ([]byte, error)
+// Type() uint8
+// ChainId() uint256.Int
+// Data() []byte
+//}
+//
+//type Transactions []ITransaction
diff --git a/common/interfaces.go b/common/interfaces.go
new file mode 100644
index 0000000..ab95f4b
--- /dev/null
+++ b/common/interfaces.go
@@ -0,0 +1,150 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "context"
+ "github.com/astranetworld/ast/common/block"
+ "github.com/astranetworld/ast/common/message"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+ pubsub "github.com/libp2p/go-libp2p-pubsub"
+ "github.com/libp2p/go-libp2p/core/host"
+ "github.com/libp2p/go-libp2p/core/peer"
+ "google.golang.org/protobuf/proto"
+)
+
+// Service is a struct that can be registered into a ServiceRegistry for
+// easy dependency management.
+type Service interface {
+ // Start spawns any goroutines required by the service.
+ //Start()
+ // Stop terminates all goroutines belonging to the service,
+ // blocking until they are all terminated.
+ Stop() error
+ // Status returns error if the service is not considered healthy.
+ //Status() error
+}
+
+type IDownloader interface {
+ SyncHeader() error
+ SyncBody() error
+ SyncTx() error
+ Start() error
+ Close() error
+ IsDownloading() bool
+ ConnHandler([]byte, peer.ID) error
+}
+
+type ConnHandler func([]byte, peer.ID) error
+
+type ProtocolHandshakeFn func(peer IPeer, genesisHash types.Hash, currentHeight *uint256.Int) (Peer, bool)
+type ProtocolHandshakeInfo func() (types.Hash, *uint256.Int, error)
+
+type INetwork interface {
+ WriterMessage(messageType message.MessageType, payload []byte, peer peer.ID) error
+ //BroadcastMessage(messageType message.MessageType, payload []byte) (int, error)
+ SetHandler(message.MessageType, ConnHandler) error
+ ClosePeer(id peer.ID) error
+ Start() error
+ Host() host.Host
+ PeerCount() int
+ Bootstrapped() bool
+}
+
+type IPeer interface {
+ ID() peer.ID
+ Write(msg message.IMessage) error
+ WriteMsg(messageType message.MessageType, payload []byte) error
+ SetHandler(message.MessageType, ConnHandler) error
+ ClearHandler(message.MessageType) error
+ Close() error
+}
+
+type IPubSub interface {
+ JoinTopic(topic string) (*pubsub.Topic, error)
+ Publish(topic string, msg proto.Message) error
+ GetTopics() []string
+ Start() error
+}
+
+type IStateDB interface {
+ CreateAccount(types.Address)
+
+ SubBalance(addr types.Address, amount uint256.Int)
+ AddBalance(addr types.Address, amount uint256.Int)
+ GetBalance(addr types.Address) uint256.Int
+
+ GetNonce(addr types.Address) uint64
+ SetNonce(addr types.Address, nonce uint64)
+
+ GetCodeHash(addr types.Address) types.Hash
+ GetCode(addr types.Address) []byte
+ SetCode(addr types.Address, code []byte)
+ GetCodeSize(addr types.Address) int
+
+ AddRefund(uint64)
+ SubRefund(uint64)
+ GetRefund() uint64
+
+ GetCommittedState(types.Address, types.Hash) types.Hash
+ GetState(types.Address, types.Hash) types.Hash
+ SetState(types.Address, types.Hash, types.Hash)
+
+ Suicide(types.Address) bool
+ HasSuicided(types.Address) bool
+
+ Exist(types.Address) bool
+ Empty(types.Address) bool
+
+ PrepareAccessList(sender types.Address, dest *types.Address, precompiles []types.Address, list transaction.AccessList)
+ AddressInAccessList(addr types.Address) bool
+ SlotInAccessList(addr types.Address, slot types.Hash) (addressOk bool, slotOk bool)
+ AddAddressToAccessList(addr types.Address)
+ AddSlotToAccessList(addr types.Address, slot types.Hash)
+
+ RevertToSnapshot(int)
+ Snapshot() int
+
+ AddLog(*block.Log)
+ GetLogs(hash types.Hash, blockHash types.Hash) []*block.Log
+
+ TxIndex() int
+ Prepare(thash types.Hash, ti int)
+
+ Error() error
+}
+type ChainStateReader interface {
+ BalanceAt(ctx context.Context, account types.Address, blockNumber uint256.Int) (uint256.Int, error)
+ StorageAt(ctx context.Context, account types.Address, key types.Hash, blockNumber uint256.Int) ([]byte, error)
+ CodeAt(ctx context.Context, account types.Address, blockNumber uint256.Int) ([]byte, error)
+ NonceAt(ctx context.Context, account types.Address, blockNumber uint256.Int) (uint64, error)
+}
+
+type ITxsPool interface {
+ Service
+ Has(hash types.Hash) bool
+ Pending(enforceTips bool) map[types.Address][]*transaction.Transaction
+ GetTransaction() ([]*transaction.Transaction, error)
+ GetTx(hash types.Hash) *transaction.Transaction
+ AddRemotes(txs []*transaction.Transaction) []error
+ AddLocal(tx *transaction.Transaction) error
+ Stats() (int, int, int, int)
+ Nonce(addr types.Address) uint64
+ Content() (map[types.Address][]*transaction.Transaction, map[types.Address][]*transaction.Transaction)
+}
diff --git a/common/math/big.go b/common/math/big.go
new file mode 100644
index 0000000..e88ce40
--- /dev/null
+++ b/common/math/big.go
@@ -0,0 +1,277 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package math provides integer math utilities.
+package math
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/holiman/uint256"
+)
+
+// Various big integer limit values.
+var (
+ tt255 = BigPow(2, 255)
+ tt256 = BigPow(2, 256)
+ tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
+ tt63 = BigPow(2, 63)
+ MaxBig256 = new(big.Int).Set(tt256m1)
+ MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1))
+)
+
+const (
+ // number of bits in a big.Word
+ wordBits = 32 << (uint64(^big.Word(0)) >> 63)
+ // number of bytes in a big.Word
+ wordBytes = wordBits / 8
+)
+
+// HexOrDecimal256 marshals big.Int as hex or decimal.
+type HexOrDecimal256 big.Int
+
+// NewHexOrDecimal256 creates a new HexOrDecimal256
+func NewHexOrDecimal256(x int64) *HexOrDecimal256 {
+ b := big.NewInt(x)
+ h := HexOrDecimal256(*b)
+ return &h
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
+ bigint, ok := ParseBig256(string(input))
+ if !ok {
+ return fmt.Errorf("invalid hex or decimal integer %q", input)
+ }
+ *i = HexOrDecimal256(*bigint)
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
+ if i == nil {
+ return []byte("0x0"), nil
+ }
+ return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
+}
+
+// Decimal256 unmarshals big.Int as a decimal string. When unmarshalling,
+// it however accepts either "0x"-prefixed (hex encoded) or non-prefixed (decimal)
+type Decimal256 big.Int
+
+// NewHexOrDecimal256 creates a new Decimal256
+func NewDecimal256(x int64) *Decimal256 {
+ b := big.NewInt(x)
+ d := Decimal256(*b)
+ return &d
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (i *Decimal256) UnmarshalText(input []byte) error {
+ bigint, ok := ParseBig256(string(input))
+ if !ok {
+ return fmt.Errorf("invalid hex or decimal integer %q", input)
+ }
+ *i = Decimal256(*bigint)
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (i *Decimal256) MarshalText() ([]byte, error) {
+ return []byte(i.String()), nil
+}
+
+// String implements Stringer.
+func (i *Decimal256) String() string {
+ if i == nil {
+ return "0"
+ }
+ return fmt.Sprintf("%#d", (*big.Int)(i))
+}
+
+// ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax.
+// Leading zeros are accepted. The empty string parses as zero.
+func ParseBig256(s string) (*big.Int, bool) {
+ if s == "" {
+ return new(big.Int), true
+ }
+ var bigint *big.Int
+ var ok bool
+ if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
+ bigint, ok = new(big.Int).SetString(s[2:], 16)
+ } else {
+ bigint, ok = new(big.Int).SetString(s, 10)
+ }
+ if ok && bigint.BitLen() > 256 {
+ bigint, ok = nil, false
+ }
+ return bigint, ok
+}
+
+// MustParseBig256 parses s as a 256 bit big integer and panics if the string is invalid.
+func MustParseBig256(s string) *big.Int {
+ v, ok := ParseBig256(s)
+ if !ok {
+ panic("invalid 256 bit integer: " + s)
+ }
+ return v
+}
+
+// BigPow returns a ** b as a big integer.
+func BigPow(a, b int64) *big.Int {
+ r := big.NewInt(a)
+ return r.Exp(r, big.NewInt(b), nil)
+}
+
+// BigMax returns the larger of x or y.
+func BigMax(x, y *big.Int) *big.Int {
+ if x.Cmp(y) < 0 {
+ return y
+ }
+ return x
+}
+
+// BigMin returns the smaller of x or y.
+func BigMin(x, y *big.Int) *big.Int {
+ if x.Cmp(y) > 0 {
+ return y
+ }
+ return x
+}
+
+// U256Min returns the smaller of x or y.
+func U256Min(x, y *uint256.Int) *uint256.Int {
+ if x.Cmp(y) > 0 {
+ return y
+ }
+ return x
+}
+
+// Min256 returns the smaller of x or y.
+func Min256(x, y *uint256.Int) *uint256.Int {
+ if x.Cmp(y) > 0 {
+ return y
+ }
+ return x
+}
+
+// FirstBitSet returns the index of the first 1 bit in v, counting from LSB.
+func FirstBitSet(v *big.Int) int {
+ for i := 0; i < v.BitLen(); i++ {
+ if v.Bit(i) > 0 {
+ return i
+ }
+ }
+ return v.BitLen()
+}
+
+// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length
+// of the slice is at least n bytes.
+func PaddedBigBytes(bigint *big.Int, n int) []byte {
+ if bigint.BitLen()/8 >= n {
+ return bigint.Bytes()
+ }
+ ret := make([]byte, n)
+ ReadBits(bigint, ret)
+ return ret
+}
+
+// bigEndianByteAt returns the byte at position n,
+// in Big-Endian encoding
+// So n==0 returns the least significant byte
+func bigEndianByteAt(bigint *big.Int, n int) byte {
+ words := bigint.Bits()
+ // Check word-bucket the byte will reside in
+ i := n / wordBytes
+ if i >= len(words) {
+ return byte(0)
+ }
+ word := words[i]
+ // Offset of the byte
+ shift := 8 * uint(n%wordBytes)
+
+ return byte(word >> shift)
+}
+
+// Byte returns the byte at position n,
+// with the supplied padlength in Little-Endian encoding.
+// n==0 returns the MSB
+// Example: bigint '5', padlength 32, n=31 => 5
+func Byte(bigint *big.Int, padlength, n int) byte {
+ if n >= padlength {
+ return byte(0)
+ }
+ return bigEndianByteAt(bigint, padlength-1-n)
+}
+
+// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
+// that buf has enough space. If buf is too short the result will be incomplete.
+func ReadBits(bigint *big.Int, buf []byte) {
+ i := len(buf)
+ for _, d := range bigint.Bits() {
+ for j := 0; j < wordBytes && i > 0; j++ {
+ i--
+ buf[i] = byte(d)
+ d >>= 8
+ }
+ }
+}
+
+// U256 encodes as a 256 bit two's complement number. This operation is destructive.
+func U256(x *big.Int) *big.Int {
+ return x.And(x, tt256m1)
+}
+
+// U256Bytes converts a big Int into a 256bit EVM number.
+// This operation is destructive.
+func U256Bytes(n *big.Int) []byte {
+ return PaddedBigBytes(U256(n), 32)
+}
+
+// S256 interprets x as a two's complement number.
+// x must not exceed 256 bits (the result is undefined if it does) and is not modified.
+//
+// S256(0) = 0
+// S256(1) = 1
+// S256(2**255) = -2**255
+// S256(2**256-1) = -1
+func S256(x *big.Int) *big.Int {
+ if x.Cmp(tt255) < 0 {
+ return x
+ }
+ return new(big.Int).Sub(x, tt256)
+}
+
+// Exp implements exponentiation by squaring.
+// Exp returns a newly-allocated big integer and does not change
+// base or exponent. The result is truncated to 256 bits.
+//
+// Courtesy @karalabe and @chfast
+func Exp(base, exponent *big.Int) *big.Int {
+ result := big.NewInt(1)
+
+ for _, word := range exponent.Bits() {
+ for i := 0; i < wordBits; i++ {
+ if word&1 == 1 {
+ U256(result.Mul(result, base))
+ }
+ U256(base.Mul(base, base))
+ word >>= 1
+ }
+ }
+ return result
+}
diff --git a/common/math/integer.go b/common/math/integer.go
new file mode 100644
index 0000000..a6a2695
--- /dev/null
+++ b/common/math/integer.go
@@ -0,0 +1,117 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package math
+
+import (
+ "crypto/rand"
+ "fmt"
+ "math"
+ "math/big"
+ "math/bits"
+ "strconv"
+)
+
+// Integer limit values.
+const (
+ MaxInt8 = 1<<7 - 1
+ MinInt8 = -1 << 7
+ MaxInt16 = 1<<15 - 1
+ MinInt16 = -1 << 15
+ MaxInt32 = 1<<31 - 1
+ MinInt32 = -1 << 31
+ MaxInt64 = 1<<63 - 1
+ MinInt64 = -1 << 63
+ MaxUint8 = 1<<8 - 1
+ MaxUint16 = 1<<16 - 1
+ MaxUint32 = 1<<32 - 1
+ MaxUint64 = 1<<64 - 1
+)
+
+// HexOrDecimal64 marshals uint64 as hex or decimal.
+type HexOrDecimal64 uint64
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (i *HexOrDecimal64) UnmarshalText(input []byte) error {
+ int, ok := ParseUint64(string(input))
+ if !ok {
+ return fmt.Errorf("invalid hex or decimal integer %q", input)
+ }
+ *i = HexOrDecimal64(int)
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (i HexOrDecimal64) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf("%#x", uint64(i))), nil
+}
+
+// ParseUint64 parses s as an integer in decimal or hexadecimal syntax.
+// Leading zeros are accepted. The empty string parses as zero.
+func ParseUint64(s string) (uint64, bool) {
+ if s == "" {
+ return 0, true
+ }
+ if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
+ v, err := strconv.ParseUint(s[2:], 16, 64)
+ return v, err == nil
+ }
+ v, err := strconv.ParseUint(s, 10, 64)
+ return v, err == nil
+}
+
+// MustParseUint64 parses s as an integer and panics if the string is invalid.
+func MustParseUint64(s string) uint64 {
+ v, ok := ParseUint64(s)
+ if !ok {
+ panic("invalid unsigned 64 bit integer: " + s)
+ }
+ return v
+}
+
+// SafeSub returns x-y and checks for overflow.
+func SafeSub(x, y uint64) (uint64, bool) {
+ diff, borrowOut := bits.Sub64(x, y, 0)
+ return diff, borrowOut != 0
+}
+
+// SafeAdd returns x+y and checks for overflow.
+func SafeAdd(x, y uint64) (uint64, bool) {
+ sum, carryOut := bits.Add64(x, y, 0)
+ return sum, carryOut != 0
+}
+
+// SafeMul returns x*y and checks for overflow.
+func SafeMul(x, y uint64) (uint64, bool) {
+ hi, lo := bits.Mul64(x, y)
+ return lo, hi != 0
+}
+
+// AbsoluteDifference is a utility method that given 2 int64, it returns the absolute value of their difference in uint64 format.
+func AbsoluteDifference(x, y uint64) uint64 {
+ if x > y {
+ return x - y
+ }
+ return y - x
+}
+
+func RandInt64() (int64, error) {
+ n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
+ if err != nil {
+ return 0, err
+ }
+ return n.Int64(), nil
+}
diff --git a/common/math/integer_test.go b/common/math/integer_test.go
new file mode 100644
index 0000000..a9e3fae
--- /dev/null
+++ b/common/math/integer_test.go
@@ -0,0 +1,125 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package math
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type operation byte
+
+const (
+ sub operation = iota
+ add
+ mul
+)
+
+func TestOverflow(t *testing.T) {
+ for i, test := range []struct {
+ x uint64
+ y uint64
+ overflow bool
+ op operation
+ }{
+ // add operations
+ {MaxUint64, 1, true, add},
+ {MaxUint64 - 1, 1, false, add},
+
+ // sub operations
+ {0, 1, true, sub},
+ {0, 0, false, sub},
+
+ // mul operations
+ {0, 0, false, mul},
+ {10, 10, false, mul},
+ {MaxUint64, 2, true, mul},
+ {MaxUint64, 1, false, mul},
+ } {
+ var overflows bool
+ switch test.op {
+ case sub:
+ _, overflows = SafeSub(test.x, test.y)
+ case add:
+ _, overflows = SafeAdd(test.x, test.y)
+ case mul:
+ _, overflows = SafeMul(test.x, test.y)
+ }
+
+ if test.overflow != overflows {
+ t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows)
+ }
+ }
+}
+
+func TestHexOrDecimal64(t *testing.T) {
+ tests := []struct {
+ input string
+ num uint64
+ ok bool
+ }{
+ {"", 0, true},
+ {"0", 0, true},
+ {"0x0", 0, true},
+ {"12345678", 12345678, true},
+ {"0x12345678", 0x12345678, true},
+ {"0X12345678", 0x12345678, true},
+ // Tests for leading zero behaviour:
+ {"0123456789", 123456789, true}, // note: not octal
+ {"0x00", 0, true},
+ {"0x012345678abc", 0x12345678abc, true},
+ // Invalid syntax:
+ {"abcdef", 0, false},
+ {"0xgg", 0, false},
+ // Doesn't fit into 64 bits:
+ {"18446744073709551617", 0, false},
+ }
+ for _, test := range tests {
+ var num HexOrDecimal64
+ err := num.UnmarshalText([]byte(test.input))
+ if (err == nil) != test.ok {
+ t.Errorf("ParseUint64(%q) -> (err == nil) = %t, want %t", test.input, err == nil, test.ok)
+ continue
+ }
+ if err == nil && uint64(num) != test.num {
+ t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num)
+ }
+ }
+}
+
+func TestMustParseUint64(t *testing.T) {
+ if v := MustParseUint64("12345"); v != 12345 {
+ t.Errorf(`MustParseUint64("12345") = %d, want 12345`, v)
+ }
+}
+
+func TestMustParseUint64Panic(t *testing.T) {
+ defer func() {
+ if recover() == nil {
+ t.Error("MustParseBig should've panicked")
+ }
+ }()
+ MustParseUint64("ggg")
+}
+
+func TestAbsoluteDifference(t *testing.T) {
+ x1 := uint64(99)
+ x2 := uint64(45)
+ assert.Equal(t, AbsoluteDifference(x1, x2), x1-x2)
+ assert.Equal(t, AbsoluteDifference(x2, x1), x1-x2)
+}
diff --git a/common/mclock/mclock.go b/common/mclock/mclock.go
new file mode 100644
index 0000000..e07af28
--- /dev/null
+++ b/common/mclock/mclock.go
@@ -0,0 +1,127 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+// Package mclock is a wrapper for a monotonic clock source
+package mclock
+
+import (
+ "time"
+
+ _ "unsafe" // for go:linkname
+)
+
+//go:noescape
+//go:linkname nanotime runtime.nanotime
+func nanotime() int64
+
+// AbsTime represents absolute monotonic time.
+type AbsTime int64
+
+// Now returns the current absolute monotonic time.
+func Now() AbsTime {
+ return AbsTime(nanotime())
+}
+
+// Add returns t + d as absolute time.
+func (t AbsTime) Add(d time.Duration) AbsTime {
+ return t + AbsTime(d)
+}
+
+// Sub returns t - t2 as a duration.
+func (t AbsTime) Sub(t2 AbsTime) time.Duration {
+ return time.Duration(t - t2)
+}
+
+// The Clock interface makes it possible to replace the monotonic system clock with
+// a simulated clock.
+type Clock interface {
+ Now() AbsTime
+ Sleep(time.Duration)
+ NewTimer(time.Duration) ChanTimer
+ After(time.Duration) <-chan AbsTime
+ AfterFunc(d time.Duration, f func()) Timer
+}
+
+// Timer is a cancellable event created by AfterFunc.
+type Timer interface {
+ // Stop cancels the timer. It returns false if the timer has already
+ // expired or been stopped.
+ Stop() bool
+}
+
+// ChanTimer is a cancellable event created by NewTimer.
+type ChanTimer interface {
+ Timer
+
+ // The channel returned by C receives a value when the timer expires.
+ C() <-chan AbsTime
+ // Reset reschedules the timer with a new timeout.
+ // It should be invoked only on stopped or expired timers with drained channels.
+ Reset(time.Duration)
+}
+
+// System implements Clock using the system clock.
+type System struct{}
+
+// Now returns the current monotonic time.
+func (c System) Now() AbsTime {
+ return Now()
+}
+
+// Sleep blocks for the given duration.
+func (c System) Sleep(d time.Duration) {
+ time.Sleep(d)
+}
+
+// NewTimer creates a timer which can be rescheduled.
+func (c System) NewTimer(d time.Duration) ChanTimer {
+ ch := make(chan AbsTime, 1)
+ t := time.AfterFunc(d, func() {
+ // This send is non-blocking because that's how time.Timer
+ // behaves. It doesn't matter in the happy case, but does
+ // when Reset is misused.
+ select {
+ case ch <- c.Now():
+ default:
+ }
+ })
+ return &systemTimer{t, ch}
+}
+
+// After returns a channel which receives the current time after d has elapsed.
+func (c System) After(d time.Duration) <-chan AbsTime {
+ ch := make(chan AbsTime, 1)
+ time.AfterFunc(d, func() { ch <- c.Now() })
+ return ch
+}
+
+// AfterFunc runs f on a new goroutine after the duration has elapsed.
+func (c System) AfterFunc(d time.Duration, f func()) Timer {
+ return time.AfterFunc(d, f)
+}
+
+type systemTimer struct {
+ *time.Timer
+ ch <-chan AbsTime
+}
+
+func (st *systemTimer) Reset(d time.Duration) {
+ st.Timer.Reset(d)
+}
+
+func (st *systemTimer) C() <-chan AbsTime {
+ return st.ch
+}
diff --git a/common/mclock/mclock.s b/common/mclock/mclock.s
new file mode 100644
index 0000000..99a7a87
--- /dev/null
+++ b/common/mclock/mclock.s
@@ -0,0 +1 @@
+// This file exists in order to be able to use go:linkname.
diff --git a/common/mclock/simclock.go b/common/mclock/simclock.go
new file mode 100644
index 0000000..6dbe7fe
--- /dev/null
+++ b/common/mclock/simclock.go
@@ -0,0 +1,209 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package mclock
+
+import (
+ "container/heap"
+ "sync"
+ "time"
+)
+
+// Simulated implements a virtual Clock for reproducible time-sensitive tests. It
+// simulates a scheduler on a virtual timescale where actual processing takes zero time.
+//
+// The virtual clock doesn't advance on its own, call Run to advance it and execute timers.
+// Since there is no way to influence the Go scheduler, testing timeout behaviour involving
+// goroutines needs special care. A good way to test such timeouts is as follows: First
+// perform the action that is supposed to time out. Ensure that the timer you want to test
+// is created. Then run the clock until after the timeout. Finally observe the effect of
+// the timeout using a channel or semaphore.
+type Simulated struct {
+ now AbsTime
+ scheduled simTimerHeap
+ mu sync.RWMutex
+ cond *sync.Cond
+}
+
+// simTimer implements ChanTimer on the virtual clock.
+type simTimer struct {
+ at AbsTime
+ index int // position in s.scheduled
+ s *Simulated
+ do func()
+ ch <-chan AbsTime
+}
+
+func (s *Simulated) init() {
+ if s.cond == nil {
+ s.cond = sync.NewCond(&s.mu)
+ }
+}
+
+// Run moves the clock by the given duration, executing all timers before that duration.
+func (s *Simulated) Run(d time.Duration) {
+ s.mu.Lock()
+ s.init()
+
+ end := s.now + AbsTime(d)
+ var do []func()
+ for len(s.scheduled) > 0 && s.scheduled[0].at <= end {
+ ev := heap.Pop(&s.scheduled).(*simTimer)
+ do = append(do, ev.do)
+ }
+ s.now = end
+ s.mu.Unlock()
+
+ for _, fn := range do {
+ fn()
+ }
+}
+
+// ActiveTimers returns the number of timers that haven't fired.
+func (s *Simulated) ActiveTimers() int {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return len(s.scheduled)
+}
+
+// WaitForTimers waits until the clock has at least n scheduled timers.
+func (s *Simulated) WaitForTimers(n int) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.init()
+
+ for len(s.scheduled) < n {
+ s.cond.Wait()
+ }
+}
+
+// Now returns the current virtual time.
+func (s *Simulated) Now() AbsTime {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return s.now
+}
+
+// Sleep blocks until the clock has advanced by d.
+func (s *Simulated) Sleep(d time.Duration) {
+ <-s.After(d)
+}
+
+// NewTimer creates a timer which fires when the clock has advanced by d.
+func (s *Simulated) NewTimer(d time.Duration) ChanTimer {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ ch := make(chan AbsTime, 1)
+ var timer *simTimer
+ timer = s.schedule(d, func() { ch <- timer.at })
+ timer.ch = ch
+ return timer
+}
+
+// After returns a channel which receives the current time after the clock
+// has advanced by d.
+func (s *Simulated) After(d time.Duration) <-chan AbsTime {
+ return s.NewTimer(d).C()
+}
+
+// AfterFunc runs fn after the clock has advanced by d. Unlike with the system
+// clock, fn runs on the goroutine that calls Run.
+func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ return s.schedule(d, fn)
+}
+
+func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer {
+ s.init()
+
+ at := s.now + AbsTime(d)
+ ev := &simTimer{do: fn, at: at, s: s}
+ heap.Push(&s.scheduled, ev)
+ s.cond.Broadcast()
+ return ev
+}
+
+func (ev *simTimer) Stop() bool {
+ ev.s.mu.Lock()
+ defer ev.s.mu.Unlock()
+
+ if ev.index < 0 {
+ return false
+ }
+ heap.Remove(&ev.s.scheduled, ev.index)
+ ev.s.cond.Broadcast()
+ ev.index = -1
+ return true
+}
+
+func (ev *simTimer) Reset(d time.Duration) {
+ if ev.ch == nil {
+ panic("mclock: Reset() on timer created by AfterFunc")
+ }
+
+ ev.s.mu.Lock()
+ defer ev.s.mu.Unlock()
+ ev.at = ev.s.now.Add(d)
+ if ev.index < 0 {
+ heap.Push(&ev.s.scheduled, ev) // already expired
+ } else {
+ heap.Fix(&ev.s.scheduled, ev.index) // hasn't fired yet, reschedule
+ }
+ ev.s.cond.Broadcast()
+}
+
+func (ev *simTimer) C() <-chan AbsTime {
+ if ev.ch == nil {
+ panic("mclock: C() on timer created by AfterFunc")
+ }
+ return ev.ch
+}
+
+type simTimerHeap []*simTimer
+
+func (h *simTimerHeap) Len() int {
+ return len(*h)
+}
+
+func (h *simTimerHeap) Less(i, j int) bool {
+ return (*h)[i].at < (*h)[j].at
+}
+
+func (h *simTimerHeap) Swap(i, j int) {
+ (*h)[i], (*h)[j] = (*h)[j], (*h)[i]
+ (*h)[i].index = i
+ (*h)[j].index = j
+}
+
+func (h *simTimerHeap) Push(x interface{}) {
+ t := x.(*simTimer)
+ t.index = len(*h)
+ *h = append(*h, t)
+}
+
+func (h *simTimerHeap) Pop() interface{} {
+ end := len(*h) - 1
+ t := (*h)[end]
+ t.index = -1
+ (*h)[end] = nil
+ *h = (*h)[:end]
+ return t
+}
diff --git a/common/mclock/simclock_test.go b/common/mclock/simclock_test.go
new file mode 100644
index 0000000..ffd02d5
--- /dev/null
+++ b/common/mclock/simclock_test.go
@@ -0,0 +1,162 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package mclock
+
+import (
+ "testing"
+ "time"
+)
+
+var _ Clock = System{}
+var _ Clock = new(Simulated)
+
+func TestSimulatedAfter(t *testing.T) {
+ var (
+ timeout = 30 * time.Minute
+ offset = 99 * time.Hour
+ adv = 11 * time.Minute
+ c Simulated
+ )
+ c.Run(offset)
+
+ end := c.Now().Add(timeout)
+ ch := c.After(timeout)
+ for c.Now() < end.Add(-adv) {
+ c.Run(adv)
+ select {
+ case <-ch:
+ t.Fatal("Timer fired early")
+ default:
+ }
+ }
+
+ c.Run(adv)
+ select {
+ case stamp := <-ch:
+ want := AbsTime(0).Add(offset).Add(timeout)
+ if stamp != want {
+ t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want)
+ }
+ default:
+ t.Fatal("Timer didn't fire")
+ }
+}
+
+func TestSimulatedAfterFunc(t *testing.T) {
+ var c Simulated
+
+ called1 := false
+ timer1 := c.AfterFunc(100*time.Millisecond, func() { called1 = true })
+ if c.ActiveTimers() != 1 {
+ t.Fatalf("%d active timers, want one", c.ActiveTimers())
+ }
+ if fired := timer1.Stop(); !fired {
+ t.Fatal("Stop returned false even though timer didn't fire")
+ }
+ if c.ActiveTimers() != 0 {
+ t.Fatalf("%d active timers, want zero", c.ActiveTimers())
+ }
+ if called1 {
+ t.Fatal("timer 1 called")
+ }
+ if fired := timer1.Stop(); fired {
+ t.Fatal("Stop returned true after timer was already stopped")
+ }
+
+ called2 := false
+ timer2 := c.AfterFunc(100*time.Millisecond, func() { called2 = true })
+ c.Run(50 * time.Millisecond)
+ if called2 {
+ t.Fatal("timer 2 called")
+ }
+ c.Run(51 * time.Millisecond)
+ if !called2 {
+ t.Fatal("timer 2 not called")
+ }
+ if fired := timer2.Stop(); fired {
+ t.Fatal("Stop returned true after timer has fired")
+ }
+}
+
+func TestSimulatedSleep(t *testing.T) {
+ var (
+ c Simulated
+ timeout = 1 * time.Hour
+ done = make(chan AbsTime, 1)
+ )
+ go func() {
+ c.Sleep(timeout)
+ done <- c.Now()
+ }()
+
+ c.WaitForTimers(1)
+ c.Run(2 * timeout)
+ select {
+ case stamp := <-done:
+ want := AbsTime(2 * timeout)
+ if stamp != want {
+ t.Errorf("Wrong time after sleep: got %v, want %v", stamp, want)
+ }
+ case <-time.After(5 * time.Second):
+ t.Fatal("Sleep didn't return in time")
+ }
+}
+
+func TestSimulatedTimerReset(t *testing.T) {
+ var (
+ c Simulated
+ timeout = 1 * time.Hour
+ )
+ timer := c.NewTimer(timeout)
+ c.Run(2 * timeout)
+ select {
+ case ftime := <-timer.C():
+ if ftime != AbsTime(timeout) {
+ t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(timeout))
+ }
+ default:
+ t.Fatal("timer didn't fire")
+ }
+
+ timer.Reset(timeout)
+ c.Run(2 * timeout)
+ select {
+ case ftime := <-timer.C():
+ if ftime != AbsTime(3*timeout) {
+ t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(3*timeout))
+ }
+ default:
+ t.Fatal("timer didn't fire again")
+ }
+}
+
+func TestSimulatedTimerStop(t *testing.T) {
+ var (
+ c Simulated
+ timeout = 1 * time.Hour
+ )
+ timer := c.NewTimer(timeout)
+ c.Run(2 * timeout)
+ if timer.Stop() {
+ t.Errorf("Stop returned true for fired timer")
+ }
+ select {
+ case <-timer.C():
+ default:
+ t.Fatal("timer didn't fire")
+ }
+}
diff --git a/common/message/message.go b/common/message/message.go
new file mode 100644
index 0000000..a14c4c8
--- /dev/null
+++ b/common/message/message.go
@@ -0,0 +1,71 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package message
+
+import (
+ "github.com/libp2p/go-libp2p/core/peer"
+)
+
+const (
+ MsgConnect = MessageType(iota + 1)
+ MsgPingReq
+ MsgPingResp
+ MsgSystem
+ MsgAppHandshake
+ MsgApplication
+ MsgDownloader
+ MsgNewBlock
+ MsgDisconnect
+ MsgTransaction
+ MsgTypeFirstInvalid
+)
+
+type MessageType uint8
+
+func (mt MessageType) IsValid() bool {
+ return mt >= MsgConnect && mt < MsgTypeFirstInvalid
+}
+
+// String implements the stringer interface.
+func (mt MessageType) String() string {
+ switch mt {
+ case MsgConnect:
+ return "Connect"
+ case MsgPingReq:
+ return "PingRequest"
+ case MsgPingResp:
+ return "PingResponse"
+ case MsgAppHandshake:
+ return "AppHandshake"
+ case MsgApplication:
+ return "MsgApplication"
+ case MsgDownloader:
+ return "MsgDownloader"
+ case MsgTransaction:
+ return "MsgTransaction"
+ default:
+ return "unknown"
+ }
+}
+
+type IMessage interface {
+ Type() MessageType
+ Peer() peer.ID
+ Broadcast() bool
+ Encode() ([]byte, error)
+ Decode(MessageType, []byte) error
+}
diff --git a/common/message/topic_default.go b/common/message/topic_default.go
new file mode 100644
index 0000000..dbd1156
--- /dev/null
+++ b/common/message/topic_default.go
@@ -0,0 +1,30 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package message
+
+var TopicMappings = map[string]interface{}{
+ GossipBlockMessage: struct{}{},
+ GossipSyncState: struct{}{},
+ GossipTransactionMessage: struct{}{},
+}
+
+const (
+ GossipPrefix = "ast/1/"
+ GossipBlockMessage = GossipPrefix + "new_block"
+ GossipSyncState = GossipPrefix + "sync_state"
+ GossipTransactionMessage = GossipPrefix + "new_transaction"
+)
diff --git a/common/paths/paths.go b/common/paths/paths.go
new file mode 100644
index 0000000..9ccc839
--- /dev/null
+++ b/common/paths/paths.go
@@ -0,0 +1,88 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package paths
+
+import (
+ "os"
+ "os/user"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+const dirname = "ast"
+
+// DefaultDataDir is the default data directory to use for the databases and other
+// persistence requirements.
+func DefaultDataDir() string {
+ // Try to place the data folder in the user's home dir
+ home := homeDir()
+ if home != "" {
+ switch runtime.GOOS {
+ case "darwin":
+ return filepath.Join(home, "Library", dirname)
+ case "windows":
+ // We used to put everything in %HOME%\AppData\Roaming, but this caused
+ // problems with non-typical setups. If this fallback location exists and
+ // is non-empty, use it, otherwise DTRT and check %LOCALAPPDATA%.
+ fallback := filepath.Join(home, "AppData", "Roaming", dirname)
+ appdata := windowsAppData()
+ if appdata == "" || isNonEmptyDir(fallback) {
+ return fallback
+ }
+ return filepath.Join(appdata, dirname)
+ default:
+ if xdgDataDir := os.Getenv("XDG_DATA_HOME"); xdgDataDir != "" {
+ return filepath.Join(xdgDataDir, strings.ToLower(dirname))
+ }
+ return filepath.Join(home, ".local/share", strings.ToLower(dirname))
+ }
+ }
+ // As we cannot guess a stable location, return empty and handle later
+ return ""
+}
+
+func windowsAppData() string {
+ v := os.Getenv("LOCALAPPDATA")
+ if v == "" {
+ // Windows XP and below don't have LocalAppData. Crash here because
+ // we don't support Windows XP and undefining the variable will cause
+ // other issues.
+ panic("environment variable LocalAppData is undefined")
+ }
+ return v
+}
+
+func isNonEmptyDir(dir string) bool {
+ f, err := os.Open(dir)
+ if err != nil {
+ return false
+ }
+ names, _ := f.Readdir(1)
+ f.Close()
+ return len(names) > 0
+}
+
+func homeDir() string {
+ if home := os.Getenv("HOME"); home != "" {
+ return home
+ }
+ if usr, err := user.Current(); err == nil {
+ return usr.HomeDir
+ }
+ return ""
+}
diff --git a/common/peer_set.go b/common/peer_set.go
new file mode 100644
index 0000000..fa72141
--- /dev/null
+++ b/common/peer_set.go
@@ -0,0 +1,56 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package common
+
+import (
+ "github.com/holiman/uint256"
+ "github.com/libp2p/go-libp2p/core/peer"
+ "time"
+)
+
+type Peer struct {
+ IPeer
+ CurrentHeight *uint256.Int
+ AddTimer time.Time
+}
+
+type PeerSet []Peer
+type PeerMap map[peer.ID]Peer
+
+func (pm PeerMap) ToSlice() PeerSet {
+ peerSet := PeerSet{}
+ for k, _ := range pm {
+ peerSet = append(peerSet, pm[k])
+ }
+
+ return peerSet
+}
+
+func (ps PeerSet) Len() int {
+ return len(ps)
+}
+
+func (ps PeerSet) Less(i, j int) bool {
+ if ps[i].CurrentHeight.Cmp(ps[j].CurrentHeight) == 1 {
+ return true
+ }
+ return false
+}
+
+func (ps PeerSet) Swap(i, j int) {
+ ps[i], ps[j] = ps[j], ps[i]
+}
diff --git a/common/prque/lazyqueue.go b/common/prque/lazyqueue.go
new file mode 100644
index 0000000..7d55686
--- /dev/null
+++ b/common/prque/lazyqueue.go
@@ -0,0 +1,196 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package prque
+
+import (
+ "container/heap"
+ "github.com/astranetworld/ast/common/mclock"
+ "time"
+)
+
+// LazyQueue is a priority queue data structure where priorities can change over
+// time and are only evaluated on demand.
+// Two callbacks are required:
+// - priority evaluates the actual priority of an item
+// - maxPriority gives an upper estimate for the priority in any moment between
+// now and the given absolute time
+//
+// If the upper estimate is exceeded then Update should be called for that item.
+// A global Refresh function should also be called periodically.
+type LazyQueue struct {
+ clock mclock.Clock
+ // Items are stored in one of two internal queues ordered by estimated max
+ // priority until the next and the next-after-next refresh. Update and Refresh
+ // always places items in queue[1].
+ queue [2]*sstack
+ popQueue *sstack
+ period time.Duration
+ maxUntil mclock.AbsTime
+ indexOffset int
+ setIndex SetIndexCallback
+ priority PriorityCallback
+ maxPriority MaxPriorityCallback
+ lastRefresh1, lastRefresh2 mclock.AbsTime
+}
+
+type (
+ PriorityCallback func(data interface{}) int64 // actual priority callback
+ MaxPriorityCallback func(data interface{}, until mclock.AbsTime) int64 // estimated maximum priority callback
+)
+
+// NewLazyQueue creates a new lazy queue
+func NewLazyQueue(setIndex SetIndexCallback, priority PriorityCallback, maxPriority MaxPriorityCallback, clock mclock.Clock, refreshPeriod time.Duration) *LazyQueue {
+ q := &LazyQueue{
+ popQueue: newSstack(nil, false),
+ setIndex: setIndex,
+ priority: priority,
+ maxPriority: maxPriority,
+ clock: clock,
+ period: refreshPeriod,
+ lastRefresh1: clock.Now(),
+ lastRefresh2: clock.Now(),
+ }
+ q.Reset()
+ q.refresh(clock.Now())
+ return q
+}
+
+// Reset clears the contents of the queue
+func (q *LazyQueue) Reset() {
+ q.queue[0] = newSstack(q.setIndex0, false)
+ q.queue[1] = newSstack(q.setIndex1, false)
+}
+
+// Refresh performs queue re-evaluation if necessary
+func (q *LazyQueue) Refresh() {
+ now := q.clock.Now()
+ for time.Duration(now-q.lastRefresh2) >= q.period*2 {
+ q.refresh(now)
+ q.lastRefresh2 = q.lastRefresh1
+ q.lastRefresh1 = now
+ }
+}
+
+// refresh re-evaluates items in the older queue and swaps the two queues
+func (q *LazyQueue) refresh(now mclock.AbsTime) {
+ q.maxUntil = now + mclock.AbsTime(q.period)
+ for q.queue[0].Len() != 0 {
+ q.Push(heap.Pop(q.queue[0]).(*item).value)
+ }
+ q.queue[0], q.queue[1] = q.queue[1], q.queue[0]
+ q.indexOffset = 1 - q.indexOffset
+ q.maxUntil += mclock.AbsTime(q.period)
+}
+
+// Push adds an item to the queue
+func (q *LazyQueue) Push(data interface{}) {
+ heap.Push(q.queue[1], &item{data, q.maxPriority(data, q.maxUntil)})
+}
+
+// Update updates the upper priority estimate for the item with the given queue index
+func (q *LazyQueue) Update(index int) {
+ q.Push(q.Remove(index))
+}
+
+// Pop removes and returns the item with the greatest actual priority
+func (q *LazyQueue) Pop() (interface{}, int64) {
+ var (
+ resData interface{}
+ resPri int64
+ )
+ q.MultiPop(func(data interface{}, priority int64) bool {
+ resData = data
+ resPri = priority
+ return false
+ })
+ return resData, resPri
+}
+
+// peekIndex returns the index of the internal queue where the item with the
+// highest estimated priority is or -1 if both are empty
+func (q *LazyQueue) peekIndex() int {
+ if q.queue[0].Len() != 0 {
+ if q.queue[1].Len() != 0 && q.queue[1].blocks[0][0].priority > q.queue[0].blocks[0][0].priority {
+ return 1
+ }
+ return 0
+ }
+ if q.queue[1].Len() != 0 {
+ return 1
+ }
+ return -1
+}
+
+// MultiPop pops multiple items from the queue and is more efficient than calling
+// Pop multiple times. Popped items are passed to the callback. MultiPop returns
+// when the callback returns false or there are no more items to pop.
+func (q *LazyQueue) MultiPop(callback func(data interface{}, priority int64) bool) {
+ nextIndex := q.peekIndex()
+ for nextIndex != -1 {
+ data := heap.Pop(q.queue[nextIndex]).(*item).value
+ heap.Push(q.popQueue, &item{data, q.priority(data)})
+ nextIndex = q.peekIndex()
+ for q.popQueue.Len() != 0 && (nextIndex == -1 || q.queue[nextIndex].blocks[0][0].priority < q.popQueue.blocks[0][0].priority) {
+ i := heap.Pop(q.popQueue).(*item)
+ if !callback(i.value, i.priority) {
+ for q.popQueue.Len() != 0 {
+ q.Push(heap.Pop(q.popQueue).(*item).value)
+ }
+ return
+ }
+ nextIndex = q.peekIndex() // re-check because callback is allowed to push items back
+ }
+ }
+}
+
+// PopItem pops the item from the queue only, dropping the associated priority value.
+func (q *LazyQueue) PopItem() interface{} {
+ i, _ := q.Pop()
+ return i
+}
+
+// Remove removes removes the item with the given index.
+func (q *LazyQueue) Remove(index int) interface{} {
+ if index < 0 {
+ return nil
+ }
+ return heap.Remove(q.queue[index&1^q.indexOffset], index>>1).(*item).value
+}
+
+// Empty checks whether the priority queue is empty.
+func (q *LazyQueue) Empty() bool {
+ return q.queue[0].Len() == 0 && q.queue[1].Len() == 0
+}
+
+// Size returns the number of items in the priority queue.
+func (q *LazyQueue) Size() int {
+ return q.queue[0].Len() + q.queue[1].Len()
+}
+
+// setIndex0 translates internal queue item index to the virtual index space of LazyQueue
+func (q *LazyQueue) setIndex0(data interface{}, index int) {
+ if index == -1 {
+ q.setIndex(data, -1)
+ } else {
+ q.setIndex(data, index+index)
+ }
+}
+
+// setIndex1 translates internal queue item index to the virtual index space of LazyQueue
+func (q *LazyQueue) setIndex1(data interface{}, index int) {
+ q.setIndex(data, index+index+1)
+}
diff --git a/common/prque/lazyqueue_test.go b/common/prque/lazyqueue_test.go
new file mode 100644
index 0000000..acb7a3e
--- /dev/null
+++ b/common/prque/lazyqueue_test.go
@@ -0,0 +1,123 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package prque
+
+import (
+ "github.com/astranetworld/ast/common/mclock"
+ "math/rand"
+ "sync"
+ "testing"
+ "time"
+)
+
+const (
+ testItems = 1000
+ testPriorityStep = 100
+ testSteps = 1000000
+ testStepPeriod = time.Millisecond
+ testQueueRefresh = time.Second
+ testAvgRate = float64(testPriorityStep) / float64(testItems) / float64(testStepPeriod)
+)
+
+type lazyItem struct {
+ p, maxp int64
+ last mclock.AbsTime
+ index int
+}
+
+func testPriority(a interface{}) int64 {
+ return a.(*lazyItem).p
+}
+
+func testMaxPriority(a interface{}, until mclock.AbsTime) int64 {
+ i := a.(*lazyItem)
+ dt := until - i.last
+ i.maxp = i.p + int64(float64(dt)*testAvgRate)
+ return i.maxp
+}
+
+func testSetIndex(a interface{}, i int) {
+ a.(*lazyItem).index = i
+}
+
+func TestLazyQueue(t *testing.T) {
+ rand.Seed(time.Now().UnixNano())
+ clock := &mclock.Simulated{}
+ q := NewLazyQueue(testSetIndex, testPriority, testMaxPriority, clock, testQueueRefresh)
+
+ var (
+ items [testItems]lazyItem
+ maxPri int64
+ )
+
+ for i := range items[:] {
+ items[i].p = rand.Int63n(testPriorityStep * 10)
+ if items[i].p > maxPri {
+ maxPri = items[i].p
+ }
+ items[i].index = -1
+ q.Push(&items[i])
+ }
+
+ var (
+ lock sync.Mutex
+ wg sync.WaitGroup
+ stopCh = make(chan chan struct{})
+ )
+ defer wg.Wait()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case <-clock.After(testQueueRefresh):
+ lock.Lock()
+ q.Refresh()
+ lock.Unlock()
+ case <-stopCh:
+ return
+ }
+ }
+ }()
+
+ for c := 0; c < testSteps; c++ {
+ i := rand.Intn(testItems)
+ lock.Lock()
+ items[i].p += rand.Int63n(testPriorityStep*2-1) + 1
+ if items[i].p > maxPri {
+ maxPri = items[i].p
+ }
+ items[i].last = clock.Now()
+ if items[i].p > items[i].maxp {
+ q.Update(items[i].index)
+ }
+ if rand.Intn(100) == 0 {
+ p := q.PopItem().(*lazyItem)
+ if p.p != maxPri {
+ lock.Unlock()
+ close(stopCh)
+ t.Fatalf("incorrect item (best known priority %d, popped %d)", maxPri, p.p)
+ }
+ q.Push(p)
+ }
+ lock.Unlock()
+ clock.Run(testStepPeriod)
+ clock.WaitForTimers(1)
+ }
+
+ close(stopCh)
+}
diff --git a/common/prque/prque.go b/common/prque/prque.go
new file mode 100755
index 0000000..54c78b5
--- /dev/null
+++ b/common/prque/prque.go
@@ -0,0 +1,83 @@
+// CookieJar - A contestant's algorithm toolbox
+// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
+//
+// CookieJar is dual licensed: use of this source code is governed by a BSD
+// license that can be found in the LICENSE file. Alternatively, the CookieJar
+// toolbox may be used in accordance with the terms and conditions contained
+// in a signed written agreement between you and the author(s).
+
+// This is a duplicated and slightly modified version of "gopkg.in/karalabe/cookiejar.v2/collections/prque".
+
+// Package prque implements a priority queue data structure supporting arbitrary
+// value types and int64 priorities.
+//
+// If you would like to use a min-priority queue, simply negate the priorities.
+//
+// Internally the queue is based on the standard heap package working on a
+// sortable version of the block based stack.
+package prque
+
+import (
+ "container/heap"
+)
+
+// Priority queue data structure.
+type Prque struct {
+ cont *sstack
+}
+
+// New creates a new priority queue.
+func New(setIndex SetIndexCallback) *Prque {
+ return &Prque{newSstack(setIndex, false)}
+}
+
+// NewWrapAround creates a new priority queue with wrap-around priority handling.
+func NewWrapAround(setIndex SetIndexCallback) *Prque {
+ return &Prque{newSstack(setIndex, true)}
+}
+
+// Pushes a value with a given priority into the queue, expanding if necessary.
+func (p *Prque) Push(data interface{}, priority int64) {
+ heap.Push(p.cont, &item{data, priority})
+}
+
+// Peek returns the value with the greates priority but does not pop it off.
+func (p *Prque) Peek() (interface{}, int64) {
+ item := p.cont.blocks[0][0]
+ return item.value, item.priority
+}
+
+// Pops the value with the greates priority off the stack and returns it.
+// Currently no shrinking is done.
+func (p *Prque) Pop() (interface{}, int64) {
+ item := heap.Pop(p.cont).(*item)
+ return item.value, item.priority
+}
+
+// Pops only the item from the queue, dropping the associated priority value.
+func (p *Prque) PopItem() interface{} {
+ return heap.Pop(p.cont).(*item).value
+}
+
+// Remove removes the element with the given index.
+func (p *Prque) Remove(i int) interface{} {
+ if i < 0 {
+ return nil
+ }
+ return heap.Remove(p.cont, i)
+}
+
+// Checks whether the priority queue is empty.
+func (p *Prque) Empty() bool {
+ return p.cont.Len() == 0
+}
+
+// Returns the number of element in the priority queue.
+func (p *Prque) Size() int {
+ return p.cont.Len()
+}
+
+// Clears the contents of the priority queue.
+func (p *Prque) Reset() {
+ *p = *New(p.cont.setIndex)
+}
diff --git a/common/prque/prque_test.go b/common/prque/prque_test.go
new file mode 100644
index 0000000..1cffceb
--- /dev/null
+++ b/common/prque/prque_test.go
@@ -0,0 +1,130 @@
+// CookieJar - A contestant's algorithm toolbox
+// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
+//
+// CookieJar is dual licensed: use of this source code is governed by a BSD
+// license that can be found in the LICENSE file. Alternatively, the CookieJar
+// toolbox may be used in accordance with the terms and conditions contained
+// in a signed written agreement between you and the author(s).
+
+package prque
+
+import (
+ "math/rand"
+ "testing"
+)
+
+func TestPrque(t *testing.T) {
+ // Generate a batch of random data and a specific priority order
+ size := 16 * blockSize
+ prio := rand.Perm(size)
+ data := make([]int, size)
+ for i := 0; i < size; i++ {
+ data[i] = rand.Int()
+ }
+ queue := New(nil)
+ for rep := 0; rep < 2; rep++ {
+ // Fill a priority queue with the above data
+ for i := 0; i < size; i++ {
+ queue.Push(data[i], int64(prio[i]))
+ if queue.Size() != i+1 {
+ t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1)
+ }
+ }
+ // Create a map the values to the priorities for easier verification
+ dict := make(map[int64]int)
+ for i := 0; i < size; i++ {
+ dict[int64(prio[i])] = data[i]
+ }
+ // Pop out the elements in priority order and verify them
+ prevPrio := int64(size + 1)
+ for !queue.Empty() {
+ val, prio := queue.Pop()
+ if prio > prevPrio {
+ t.Errorf("invalid priority order: %v after %v.", prio, prevPrio)
+ }
+ prevPrio = prio
+ if val != dict[prio] {
+ t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio])
+ }
+ delete(dict, prio)
+ }
+ }
+}
+
+func TestReset(t *testing.T) {
+ // Generate a batch of random data and a specific priority order
+ size := 16 * blockSize
+ prio := rand.Perm(size)
+ data := make([]int, size)
+ for i := 0; i < size; i++ {
+ data[i] = rand.Int()
+ }
+ queue := New(nil)
+ for rep := 0; rep < 2; rep++ {
+ // Fill a priority queue with the above data
+ for i := 0; i < size; i++ {
+ queue.Push(data[i], int64(prio[i]))
+ if queue.Size() != i+1 {
+ t.Errorf("queue size mismatch: have %v, want %v.", queue.Size(), i+1)
+ }
+ }
+ // Create a map the values to the priorities for easier verification
+ dict := make(map[int64]int)
+ for i := 0; i < size; i++ {
+ dict[int64(prio[i])] = data[i]
+ }
+ // Pop out half the elements in priority order and verify them
+ prevPrio := int64(size + 1)
+ for i := 0; i < size/2; i++ {
+ val, prio := queue.Pop()
+ if prio > prevPrio {
+ t.Errorf("invalid priority order: %v after %v.", prio, prevPrio)
+ }
+ prevPrio = prio
+ if val != dict[prio] {
+ t.Errorf("push/pop mismatch: have %v, want %v.", val, dict[prio])
+ }
+ delete(dict, prio)
+ }
+ // Reset and ensure it's empty
+ queue.Reset()
+ if !queue.Empty() {
+ t.Errorf("priority queue not empty after reset: %v", queue)
+ }
+ }
+}
+
+func BenchmarkPush(b *testing.B) {
+ // Create some initial data
+ data := make([]int, b.N)
+ prio := make([]int64, b.N)
+ for i := 0; i < len(data); i++ {
+ data[i] = rand.Int()
+ prio[i] = rand.Int63()
+ }
+ // Execute the benchmark
+ b.ResetTimer()
+ queue := New(nil)
+ for i := 0; i < len(data); i++ {
+ queue.Push(data[i], prio[i])
+ }
+}
+
+func BenchmarkPop(b *testing.B) {
+ // Create some initial data
+ data := make([]int, b.N)
+ prio := make([]int64, b.N)
+ for i := 0; i < len(data); i++ {
+ data[i] = rand.Int()
+ prio[i] = rand.Int63()
+ }
+ queue := New(nil)
+ for i := 0; i < len(data); i++ {
+ queue.Push(data[i], prio[i])
+ }
+ // Execute the benchmark
+ b.ResetTimer()
+ for !queue.Empty() {
+ queue.Pop()
+ }
+}
diff --git a/common/prque/sstack.go b/common/prque/sstack.go
new file mode 100755
index 0000000..b06a954
--- /dev/null
+++ b/common/prque/sstack.go
@@ -0,0 +1,120 @@
+// CookieJar - A contestant's algorithm toolbox
+// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
+//
+// CookieJar is dual licensed: use of this source code is governed by a BSD
+// license that can be found in the LICENSE file. Alternatively, the CookieJar
+// toolbox may be used in accordance with the terms and conditions contained
+// in a signed written agreement between you and the author(s).
+
+// This is a duplicated and slightly modified version of "gopkg.in/karalabe/cookiejar.v2/collections/prque".
+
+package prque
+
+// The size of a block of data
+const blockSize = 4096
+
+// A prioritized item in the sorted stack.
+//
+// Note: priorities can "wrap around" the int64 range, a comes before b if (a.priority - b.priority) > 0.
+// The difference between the lowest and highest priorities in the queue at any point should be less than 2^63.
+type item struct {
+ value interface{}
+ priority int64
+}
+
+// SetIndexCallback is called when the element is moved to a new index.
+// Providing SetIndexCallback is optional, it is needed only if the application needs
+// to delete elements other than the top one.
+type SetIndexCallback func(data interface{}, index int)
+
+// Internal sortable stack data structure. Implements the Push and Pop ops for
+// the stack (heap) functionality and the Len, Less and Swap methods for the
+// sortability requirements of the heaps.
+type sstack struct {
+ setIndex SetIndexCallback
+ size int
+ capacity int
+ offset int
+ wrapAround bool
+
+ blocks [][]*item
+ active []*item
+}
+
+// Creates a new, empty stack.
+func newSstack(setIndex SetIndexCallback, wrapAround bool) *sstack {
+ result := new(sstack)
+ result.setIndex = setIndex
+ result.active = make([]*item, blockSize)
+ result.blocks = [][]*item{result.active}
+ result.capacity = blockSize
+ result.wrapAround = wrapAround
+ return result
+}
+
+// Pushes a value onto the stack, expanding it if necessary. Required by
+// heap.Interface.
+func (s *sstack) Push(data interface{}) {
+ if s.size == s.capacity {
+ s.active = make([]*item, blockSize)
+ s.blocks = append(s.blocks, s.active)
+ s.capacity += blockSize
+ s.offset = 0
+ } else if s.offset == blockSize {
+ s.active = s.blocks[s.size/blockSize]
+ s.offset = 0
+ }
+ if s.setIndex != nil {
+ s.setIndex(data.(*item).value, s.size)
+ }
+ s.active[s.offset] = data.(*item)
+ s.offset++
+ s.size++
+}
+
+// Pops a value off the stack and returns it. Currently no shrinking is done.
+// Required by heap.Interface.
+func (s *sstack) Pop() (res interface{}) {
+ s.size--
+ s.offset--
+ if s.offset < 0 {
+ s.offset = blockSize - 1
+ s.active = s.blocks[s.size/blockSize]
+ }
+ res, s.active[s.offset] = s.active[s.offset], nil
+ if s.setIndex != nil {
+ s.setIndex(res.(*item).value, -1)
+ }
+ return
+}
+
+// Returns the length of the stack. Required by sort.Interface.
+func (s *sstack) Len() int {
+ return s.size
+}
+
+// Compares the priority of two elements of the stack (higher is first).
+// Required by sort.Interface.
+func (s *sstack) Less(i, j int) bool {
+ a, b := s.blocks[i/blockSize][i%blockSize].priority, s.blocks[j/blockSize][j%blockSize].priority
+ if s.wrapAround {
+ return a-b > 0
+ }
+ return a > b
+}
+
+// Swaps two elements in the stack. Required by sort.Interface.
+func (s *sstack) Swap(i, j int) {
+ ib, io, jb, jo := i/blockSize, i%blockSize, j/blockSize, j%blockSize
+ a, b := s.blocks[jb][jo], s.blocks[ib][io]
+ if s.setIndex != nil {
+ s.setIndex(a.value, i)
+ s.setIndex(b.value, j)
+ }
+ s.blocks[ib][io], s.blocks[jb][jo] = a, b
+}
+
+// Resets the stack, effectively clearing its contents.
+func (s *sstack) Reset() {
+ *s = *newSstack(s.setIndex, false)
+}
diff --git a/common/prque/sstack_test.go b/common/prque/sstack_test.go
new file mode 100644
index 0000000..bc62989
--- /dev/null
+++ b/common/prque/sstack_test.go
@@ -0,0 +1,100 @@
+// CookieJar - A contestant's algorithm toolbox
+// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
+//
+// CookieJar is dual licensed: use of this source code is governed by a BSD
+// license that can be found in the LICENSE file. Alternatively, the CookieJar
+// toolbox may be used in accordance with the terms and conditions contained
+// in a signed written agreement between you and the author(s).
+
+package prque
+
+import (
+ "math/rand"
+ "sort"
+ "testing"
+)
+
+func TestSstack(t *testing.T) {
+ // Create some initial data
+ size := 16 * blockSize
+ data := make([]*item, size)
+ for i := 0; i < size; i++ {
+ data[i] = &item{rand.Int(), rand.Int63()}
+ }
+ stack := newSstack(nil, false)
+ for rep := 0; rep < 2; rep++ {
+ // Push all the data into the stack, pop out every second
+ secs := []*item{}
+ for i := 0; i < size; i++ {
+ stack.Push(data[i])
+ if i%2 == 0 {
+ secs = append(secs, stack.Pop().(*item))
+ }
+ }
+ rest := []*item{}
+ for stack.Len() > 0 {
+ rest = append(rest, stack.Pop().(*item))
+ }
+ // Make sure the contents of the resulting slices are ok
+ for i := 0; i < size; i++ {
+ if i%2 == 0 && data[i] != secs[i/2] {
+ t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i])
+ }
+ if i%2 == 1 && data[i] != rest[len(rest)-i/2-1] {
+ t.Errorf("push/pop mismatch: have %v, want %v.", rest[len(rest)-i/2-1], data[i])
+ }
+ }
+ }
+}
+
+func TestSstackSort(t *testing.T) {
+ // Create some initial data
+ size := 16 * blockSize
+ data := make([]*item, size)
+ for i := 0; i < size; i++ {
+ data[i] = &item{rand.Int(), int64(i)}
+ }
+ // Push all the data into the stack
+ stack := newSstack(nil, false)
+ for _, val := range data {
+ stack.Push(val)
+ }
+ // Sort and pop the stack contents (should reverse the order)
+ sort.Sort(stack)
+ for _, val := range data {
+ out := stack.Pop()
+ if out != val {
+ t.Errorf("push/pop mismatch after sort: have %v, want %v.", out, val)
+ }
+ }
+}
+
+func TestSstackReset(t *testing.T) {
+ // Create some initial data
+ size := 16 * blockSize
+ data := make([]*item, size)
+ for i := 0; i < size; i++ {
+ data[i] = &item{rand.Int(), rand.Int63()}
+ }
+ stack := newSstack(nil, false)
+ for rep := 0; rep < 2; rep++ {
+ // Push all the data into the stack, pop out every second
+ secs := []*item{}
+ for i := 0; i < size; i++ {
+ stack.Push(data[i])
+ if i%2 == 0 {
+ secs = append(secs, stack.Pop().(*item))
+ }
+ }
+ // Reset and verify both pulled and stack contents
+ stack.Reset()
+ if stack.Len() != 0 {
+ t.Errorf("stack not empty after reset: %v", stack)
+ }
+ for i := 0; i < size; i++ {
+ if i%2 == 0 && data[i] != secs[i/2] {
+ t.Errorf("push/pop mismatch: have %v, want %v.", secs[i/2], data[i])
+ }
+ }
+ }
+}
diff --git a/common/transaction/access_list.go b/common/transaction/access_list.go
new file mode 100644
index 0000000..b001639
--- /dev/null
+++ b/common/transaction/access_list.go
@@ -0,0 +1,131 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "github.com/astranetworld/ast/common/hash"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+)
+
+type AccessList []AccessTuple
+
+// AccessTuple is the element type of an access list.
+type AccessTuple struct {
+ Address types.Address `json:"address" gencodec:"required"`
+ StorageKeys []types.Hash `json:"storageKeys" gencodec:"required"`
+}
+
+// StorageKeys returns the total number of storage keys in the access list.
+func (al AccessList) StorageKeys() int {
+ sum := 0
+ for _, tuple := range al {
+ sum += len(tuple.StorageKeys)
+ }
+ return sum
+}
+
+type AccessListTx struct {
+ ChainID *uint256.Int // destination chain ID
+ Nonce uint64 // nonce of sender account
+ GasPrice *uint256.Int // wei per gas
+ Gas uint64 // gas limit
+ To *types.Address `rlp:"nil"` // nil means contract creation
+ From *types.Address `rlp:"nil"` // nil means contract creation
+ Value *uint256.Int // wei amount
+ Data []byte // contract invocation input data
+ AccessList AccessList // EIP-2930 access list
+ Sign []byte // signature values
+ V, R, S *uint256.Int // signature values
+}
+
+func (tx *AccessListTx) copy() TxData {
+ cpy := &AccessListTx{
+ Nonce: tx.Nonce,
+ To: copyAddressPtr(tx.To),
+ From: copyAddressPtr(tx.From),
+ Data: types.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are copied below.
+ AccessList: make(AccessList, len(tx.AccessList)),
+ Value: new(uint256.Int),
+ ChainID: new(uint256.Int),
+ GasPrice: new(uint256.Int),
+ }
+ copy(cpy.AccessList, tx.AccessList)
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.ChainID != nil {
+ cpy.ChainID.Set(tx.ChainID)
+ }
+ if tx.GasPrice != nil {
+ cpy.GasPrice.Set(tx.GasPrice)
+ }
+ if tx.V != nil {
+ cpy.V.Set(tx.V)
+ }
+ if tx.R != nil {
+ cpy.R.Set(tx.R)
+ }
+ if tx.S != nil {
+ cpy.S.Set(tx.S)
+ }
+
+ if tx.Sign != nil {
+ copy(cpy.Sign, tx.Sign)
+ }
+ return cpy
+}
+
+// accessors for innerTx.
+func (tx *AccessListTx) txType() byte { return AccessListTxType }
+func (tx *AccessListTx) chainID() *uint256.Int { return tx.ChainID }
+func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
+func (tx *AccessListTx) data() []byte { return tx.Data }
+func (tx *AccessListTx) gas() uint64 { return tx.Gas }
+func (tx *AccessListTx) gasPrice() *uint256.Int { return tx.GasPrice }
+func (tx *AccessListTx) gasTipCap() *uint256.Int { return tx.GasPrice }
+func (tx *AccessListTx) gasFeeCap() *uint256.Int { return tx.GasPrice }
+func (tx *AccessListTx) value() *uint256.Int { return tx.Value }
+func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
+func (tx *AccessListTx) to() *types.Address { return tx.To }
+func (tx *AccessListTx) from() *types.Address { return tx.From }
+func (tx *AccessListTx) sign() []byte { return tx.Sign }
+
+func (tx *AccessListTx) hash() types.Hash {
+ hash := hash.PrefixedRlpHash(AccessListTxType, []interface{}{
+ tx.ChainID,
+ tx.Nonce,
+ tx.GasPrice,
+ tx.Gas,
+ tx.To,
+ tx.Value,
+ tx.Data,
+ tx.AccessList,
+ tx.V, tx.R, tx.S,
+ })
+ return hash
+}
+
+func (tx *AccessListTx) rawSignatureValues() (v, r, s *uint256.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *AccessListTx) setSignatureValues(chainID, v, r, s *uint256.Int) {
+ tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
+}
diff --git a/common/transaction/dynamic_fee_tx.go b/common/transaction/dynamic_fee_tx.go
new file mode 100644
index 0000000..c77fbde
--- /dev/null
+++ b/common/transaction/dynamic_fee_tx.go
@@ -0,0 +1,125 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "github.com/astranetworld/ast/common/hash"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/internal/avm/common"
+ "github.com/holiman/uint256"
+)
+
+type DynamicFeeTx struct {
+ ChainID *uint256.Int
+ Nonce uint64
+ GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
+ GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
+ Gas uint64
+ To *types.Address `rlp:"nil"` // nil means contract creation
+ From *types.Address `rlp:"nil"` // nil means contract creation
+ Value *uint256.Int
+ Data []byte
+ AccessList AccessList
+ Sign []byte // Signature values
+ V, R, S *uint256.Int // signature values
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *DynamicFeeTx) copy() TxData {
+ cpy := &DynamicFeeTx{
+ Nonce: tx.Nonce,
+ To: copyAddressPtr(tx.To),
+ From: copyAddressPtr(tx.From),
+ Data: common.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are copied below.
+ AccessList: make(AccessList, len(tx.AccessList)),
+ Value: new(uint256.Int),
+ ChainID: new(uint256.Int),
+ GasTipCap: new(uint256.Int),
+ GasFeeCap: new(uint256.Int),
+ V: new(uint256.Int),
+ R: new(uint256.Int),
+ S: new(uint256.Int),
+ }
+ copy(cpy.AccessList, tx.AccessList)
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.ChainID != nil {
+ cpy.ChainID.Set(tx.ChainID)
+ }
+ if tx.GasTipCap != nil {
+ cpy.GasTipCap.Set(tx.GasTipCap)
+ }
+ if tx.GasFeeCap != nil {
+ cpy.GasFeeCap.Set(tx.GasFeeCap)
+ }
+ if tx.V != nil {
+ cpy.V.Set(tx.V)
+ }
+ if tx.R != nil {
+ cpy.R.Set(tx.R)
+ }
+ if tx.S != nil {
+ cpy.S.Set(tx.S)
+ }
+ if tx.Sign != nil {
+ copy(cpy.Sign, tx.Sign)
+ }
+ return cpy
+}
+
+// accessors for innerTx.
+func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
+func (tx *DynamicFeeTx) chainID() *uint256.Int { return tx.ChainID }
+func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
+func (tx *DynamicFeeTx) data() []byte { return tx.Data }
+func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
+func (tx *DynamicFeeTx) gasFeeCap() *uint256.Int { return tx.GasFeeCap }
+func (tx *DynamicFeeTx) gasTipCap() *uint256.Int { return tx.GasTipCap }
+func (tx *DynamicFeeTx) gasPrice() *uint256.Int { return tx.GasFeeCap }
+func (tx *DynamicFeeTx) value() *uint256.Int { return tx.Value }
+func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
+func (tx *DynamicFeeTx) to() *types.Address { return tx.To }
+func (tx *DynamicFeeTx) from() *types.Address { return tx.From }
+func (tx *DynamicFeeTx) sign() []byte { return tx.Sign }
+
+// Hash computes the hash (but not for signatures!)
+func (tx *DynamicFeeTx) hash() types.Hash {
+ hash := hash.PrefixedRlpHash(DynamicFeeTxType, []interface{}{
+ tx.ChainID,
+ tx.Nonce,
+ tx.GasTipCap,
+ tx.GasFeeCap,
+ tx.Gas,
+ tx.To,
+ tx.Value,
+ tx.Data,
+ tx.AccessList,
+ tx.V, tx.R, tx.S,
+ })
+ return hash
+}
+
+func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *uint256.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *uint256.Int) {
+ tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
+}
diff --git a/common/transaction/legacy_tx.go b/common/transaction/legacy_tx.go
new file mode 100644
index 0000000..5f16e77
--- /dev/null
+++ b/common/transaction/legacy_tx.go
@@ -0,0 +1,137 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "github.com/astranetworld/ast/common/hash"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+)
+
+// LegacyTx is the transaction data of regular Ethereum transactions.
+type LegacyTx struct {
+ Nonce uint64 // nonce of sender account
+ GasPrice *uint256.Int // wei per gas
+ Gas uint64 // gas limit
+ To *types.Address `rlp:"nil"` // nil means contract creation
+ From *types.Address `rlp:"nil"` // nil means contract creation
+ Value *uint256.Int // wei amount
+ Data []byte // contract invocation input data
+ V, R, S *uint256.Int // signature values
+ Sign []byte
+}
+
+// NewTransaction creates an unsigned legacy transaction.
+// Deprecated: use NewTx instead.
+func NewTransaction(nonce uint64, from types.Address, to *types.Address, amount *uint256.Int, gasLimit uint64, gasPrice *uint256.Int, data []byte) *Transaction {
+ return NewTx(&LegacyTx{
+ Nonce: nonce,
+ To: to,
+ From: &from,
+ Value: amount,
+ Gas: gasLimit,
+ GasPrice: gasPrice,
+ Data: data,
+ })
+}
+
+// NewContractCreation creates an unsigned legacy transaction.
+// Deprecated: use NewTx instead.
+func NewContractCreation(nonce uint64, amount *uint256.Int, gasLimit uint64, gasPrice *uint256.Int, data []byte) *Transaction {
+ return NewTx(&LegacyTx{
+ Nonce: nonce,
+ Value: amount,
+ Gas: gasLimit,
+ GasPrice: gasPrice,
+ Data: data,
+ })
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *LegacyTx) copy() TxData {
+ cpy := &LegacyTx{
+ Nonce: tx.Nonce,
+ To: copyAddressPtr(tx.To),
+ From: copyAddressPtr(tx.From),
+ Data: types.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are initialized below.
+ Value: new(uint256.Int),
+ GasPrice: new(uint256.Int),
+ V: new(uint256.Int),
+ R: new(uint256.Int),
+ S: new(uint256.Int),
+ }
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.GasPrice != nil {
+ cpy.GasPrice.Set(tx.GasPrice)
+ }
+ if tx.V != nil {
+ cpy.V.Set(tx.V)
+ }
+ if tx.R != nil {
+ cpy.R.Set(tx.R)
+ }
+ if tx.S != nil {
+ cpy.S.Set(tx.S)
+ }
+ if tx.sign() != nil {
+ copy(cpy.Sign, tx.Sign)
+ }
+
+ return cpy
+}
+
+// accessors for innerTx.
+func (tx *LegacyTx) txType() byte { return LegacyTxType }
+func (tx *LegacyTx) chainID() *uint256.Int {
+ return DeriveChainId(tx.V)
+}
+func (tx *LegacyTx) accessList() AccessList { return nil }
+func (tx *LegacyTx) data() []byte { return tx.Data }
+func (tx *LegacyTx) gas() uint64 { return tx.Gas }
+func (tx *LegacyTx) gasPrice() *uint256.Int { return tx.GasPrice }
+func (tx *LegacyTx) gasTipCap() *uint256.Int { return tx.GasPrice }
+func (tx *LegacyTx) gasFeeCap() *uint256.Int { return tx.GasPrice }
+func (tx *LegacyTx) value() *uint256.Int { return tx.Value }
+func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
+func (tx *LegacyTx) to() *types.Address { return tx.To }
+func (tx *LegacyTx) from() *types.Address { return tx.From }
+func (tx *LegacyTx) sign() []byte { return tx.Sign }
+
+func (tx *LegacyTx) rawSignatureValues() (v, r, s *uint256.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *LegacyTx) setSignatureValues(chainID, v, r, s *uint256.Int) {
+ tx.V, tx.R, tx.S = v, r, s
+}
+
+func (tx *LegacyTx) hash() types.Hash {
+ hash := hash.RlpHash([]interface{}{
+ tx.Nonce,
+ tx.GasPrice,
+ tx.Gas,
+ tx.To,
+ tx.Value,
+ tx.Data,
+ tx.V, tx.R, tx.S,
+ })
+ return hash
+}
diff --git a/common/transaction/sakuragi_tx.go b/common/transaction/sakuragi_tx.go
new file mode 100644
index 0000000..63a443c
--- /dev/null
+++ b/common/transaction/sakuragi_tx.go
@@ -0,0 +1,33 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+)
+
+type SakuragiTx struct {
+ Nonce uint64 // nonce of sender account
+ GasPrice uint256.Int // wei per gas
+ Gas uint64 // gas limit
+ To *types.Address
+ From *types.Address
+ Value uint256.Int // wei amount
+ Data []byte // contract invocation input data
+ Sign []byte // signature values
+}
diff --git a/common/transaction/transaction.go b/common/transaction/transaction.go
new file mode 100644
index 0000000..f714bc8
--- /dev/null
+++ b/common/transaction/transaction.go
@@ -0,0 +1,695 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/astranetworld/ast/utils"
+ "github.com/holiman/uint256"
+ "google.golang.org/protobuf/proto"
+ "math/big"
+ "sync/atomic"
+ "time"
+
+ "github.com/astranetworld/ast/api/protocol/types_pb"
+ "github.com/astranetworld/ast/common/types"
+)
+
+var (
+ ErrGasFeeCapTooLow = fmt.Errorf("fee cap less than base fee")
+ ErrUnmarshalHash = fmt.Errorf("hash verify falied")
+)
+
+// Transaction types.
+const (
+ LegacyTxType = iota
+ AccessListTxType
+ DynamicFeeTxType
+)
+
+type TxData interface {
+ txType() byte // returns the type ID
+ copy() TxData // creates a deep copy and initializes all fields
+
+ chainID() *uint256.Int
+ accessList() AccessList
+ data() []byte
+ gas() uint64
+ gasPrice() *uint256.Int
+ gasTipCap() *uint256.Int
+ gasFeeCap() *uint256.Int
+ value() *uint256.Int
+ nonce() uint64
+ to() *types.Address
+ from() *types.Address
+ sign() []byte
+ hash() types.Hash
+
+ rawSignatureValues() (v, r, s *uint256.Int)
+ setSignatureValues(chainID, v, r, s *uint256.Int)
+
+ //Marshal() ([]byte, error)
+ //MarshalTo(data []byte) (n int, err error)
+ //Unmarshal(data []byte) error
+ //Size() int
+}
+
+// Transactions implements DerivableList for transactions.
+type Transactions []*Transaction
+
+// Len returns the length of s.
+func (s Transactions) Len() int { return len(s) }
+
+// EncodeIndex encodes the i'th transaction to w. Note that this does not check for errors
+// because we assume that *Transaction will only ever contain valid txs that were either
+// constructed by decoding or via public API in this package.
+func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) {
+ tx := s[i]
+ proto, _ := tx.Marshal()
+ w.Write(proto)
+}
+
+type Transaction struct {
+ inner TxData // Consensus contents of a transaction
+ time time.Time // Time first seen locally (spam avoidance)
+
+ // caches
+ hash atomic.Value
+ size atomic.Value
+ from atomic.Value
+}
+
+// NewTx creates a new transaction.
+func NewTx(inner TxData) *Transaction {
+ tx := new(Transaction)
+ tx.setDecoded(inner.copy(), 0)
+ return tx
+}
+
+func txDataFromProtoMessage(message proto.Message) (TxData, error) {
+ var (
+ pbTx *types_pb.Transaction
+ ok bool
+ inner TxData
+ )
+
+ if pbTx, ok = message.(*types_pb.Transaction); !ok {
+ return nil, fmt.Errorf("aa")
+ }
+
+ switch pbTx.Type {
+ case LegacyTxType:
+ var itx LegacyTx
+ if nil != pbTx.To {
+ itx.To = utils.ConvertH160ToPAddress(pbTx.To)
+ if *itx.To == (types.Address{}) {
+ itx.To = nil
+ }
+ }
+ itx.From = utils.ConvertH160ToPAddress(pbTx.From)
+ itx.Sign = pbTx.Sign
+ itx.Nonce = pbTx.Nonce
+ itx.Gas = pbTx.Gas
+ itx.GasPrice = utils.ConvertH256ToUint256Int(pbTx.GasPrice)
+ itx.Value = utils.ConvertH256ToUint256Int(pbTx.Value)
+ if nil != pbTx.V {
+ itx.V = utils.ConvertH256ToUint256Int(pbTx.V)
+ }
+ if nil != pbTx.R {
+ itx.R = utils.ConvertH256ToUint256Int(pbTx.R)
+ }
+ if nil != pbTx.S {
+ itx.S = utils.ConvertH256ToUint256Int(pbTx.S)
+ }
+ itx.Data = pbTx.Data
+ inner = &itx
+ case AccessListTxType:
+ var altt AccessListTx
+ altt.ChainID = uint256.NewInt(pbTx.ChainID)
+ altt.Nonce = pbTx.Nonce
+ altt.Gas = pbTx.Gas
+ altt.GasPrice = utils.ConvertH256ToUint256Int(pbTx.GasPrice)
+ altt.Value = utils.ConvertH256ToUint256Int(pbTx.Value)
+ if nil != pbTx.V {
+ altt.V = utils.ConvertH256ToUint256Int(pbTx.V)
+ }
+ if nil != pbTx.R {
+ altt.R = utils.ConvertH256ToUint256Int(pbTx.R)
+ }
+ if nil != pbTx.S {
+ altt.S = utils.ConvertH256ToUint256Int(pbTx.S)
+ }
+ altt.Data = pbTx.Data
+ if nil != pbTx.To {
+ altt.To = utils.ConvertH160ToPAddress(pbTx.To)
+ if *altt.To == (types.Address{}) {
+ altt.To = nil
+ }
+ }
+ //altt.To = utils.ConvertH160ToPAddress(pbTx.To)
+ altt.From = utils.ConvertH160ToPAddress(pbTx.From)
+ altt.Sign = pbTx.Sign
+ inner = &altt
+ case DynamicFeeTxType:
+ var dftt DynamicFeeTx
+ dftt.ChainID = uint256.NewInt(pbTx.ChainID)
+ dftt.Nonce = pbTx.Nonce
+ dftt.Gas = pbTx.Gas
+ dftt.GasFeeCap = utils.ConvertH256ToUint256Int(pbTx.FeePerGas)
+ dftt.GasTipCap = utils.ConvertH256ToUint256Int(pbTx.PriorityFeePerGas)
+ dftt.Value = utils.ConvertH256ToUint256Int(pbTx.Value)
+ if nil != pbTx.V {
+ dftt.V = utils.ConvertH256ToUint256Int(pbTx.V)
+ }
+ if nil != pbTx.R {
+ dftt.R = utils.ConvertH256ToUint256Int(pbTx.R)
+ }
+ if nil != pbTx.S {
+ dftt.S = utils.ConvertH256ToUint256Int(pbTx.S)
+ }
+ dftt.Data = pbTx.Data
+ if nil != pbTx.To {
+ dftt.To = utils.ConvertH160ToPAddress(pbTx.To)
+ if *dftt.To == (types.Address{}) {
+ dftt.To = nil
+ }
+ }
+
+ //dftt.To = utils.ConvertH160ToPAddress(pbTx.To)
+ dftt.From = utils.ConvertH160ToPAddress(pbTx.From)
+ dftt.Sign = pbTx.Sign
+ inner = &dftt
+ }
+
+ // todo
+ protoHash := utils.ConvertH256ToHash(pbTx.Hash)
+ innerHash := inner.hash()
+ if bytes.Compare(protoHash[:], innerHash[:]) != 0 {
+ //return nil, ErrUnmarshalHash
+ }
+
+ return inner, nil
+}
+
+func FromProtoMessage(message proto.Message) (*Transaction, error) {
+ inner, err := txDataFromProtoMessage(message)
+ if err != nil {
+ return nil, err
+ }
+ return NewTx(inner), nil
+}
+
+func (tx *Transaction) ToProtoMessage() proto.Message {
+ var pbTx types_pb.Transaction
+ pbTx.Type = uint64(tx.inner.txType())
+
+ switch t := tx.inner.(type) {
+ case *AccessListTx:
+ pbTx.ChainID = t.ChainID.Uint64()
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ case *LegacyTx:
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ //pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ case *DynamicFeeTx:
+ pbTx.ChainID = t.ChainID.Uint64()
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ //pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ pbTx.FeePerGas = utils.ConvertUint256IntToH256(t.GasFeeCap)
+ pbTx.PriorityFeePerGas = utils.ConvertUint256IntToH256(t.GasTipCap)
+ }
+ if tx.To() != nil {
+ pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ }
+
+ pbTx.Hash = utils.ConvertHashToH256(tx.Hash())
+
+ v, r, s := tx.RawSignatureValues()
+ if nil != v {
+ pbTx.V = utils.ConvertUint256IntToH256(v)
+ }
+ if nil != r {
+ pbTx.R = utils.ConvertUint256IntToH256(r)
+ }
+ if nil != s {
+ pbTx.S = utils.ConvertUint256IntToH256(s)
+ }
+
+ return &pbTx
+}
+
+func (tx *Transaction) setDecoded(inner TxData, size int) {
+ tx.inner = inner
+ tx.time = time.Now()
+ //if size > 0 {
+ // tx.size.Store(common.StorageSize(size))
+ //}
+}
+
+func (tx *Transaction) RawSignatureValues() (v, r, s *uint256.Int) {
+ return tx.inner.rawSignatureValues()
+}
+
+// WithSignature todo
+func (tx *Transaction) WithSignatureValues(v, r, s *uint256.Int) (*Transaction, error) {
+ //todo
+ tx.inner.setSignatureValues(uint256.NewInt(100100100), v, r, s)
+ return tx, nil
+}
+
+func (tx Transaction) Marshal() ([]byte, error) {
+ var pbTx types_pb.Transaction
+ pbTx.Type = uint64(tx.inner.txType())
+
+ switch t := tx.inner.(type) {
+ case *AccessListTx:
+ pbTx.ChainID = t.ChainID.Uint64()
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ case *LegacyTx:
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ //pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ case *DynamicFeeTx:
+ pbTx.ChainID = t.ChainID.Uint64()
+ pbTx.Nonce = tx.Nonce()
+ pbTx.Gas = tx.Gas()
+ pbTx.GasPrice = utils.ConvertUint256IntToH256(tx.GasPrice())
+ pbTx.Value = utils.ConvertUint256IntToH256(tx.Value())
+ pbTx.Data = tx.Data()
+ //pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ pbTx.From = utils.ConvertAddressToH160(*tx.From())
+ pbTx.Sign = t.Sign
+ pbTx.FeePerGas = utils.ConvertUint256IntToH256(t.GasFeeCap)
+ pbTx.PriorityFeePerGas = utils.ConvertUint256IntToH256(t.GasTipCap)
+ }
+ if tx.To() != nil {
+ pbTx.To = utils.ConvertAddressToH160(*tx.To())
+ }
+ v, r, s := tx.RawSignatureValues()
+ if nil != v {
+ pbTx.V = utils.ConvertUint256IntToH256(v)
+ }
+ if nil != r {
+ pbTx.R = utils.ConvertUint256IntToH256(r)
+ }
+ if nil != s {
+ pbTx.S = utils.ConvertUint256IntToH256(s)
+ }
+ return proto.Marshal(&pbTx)
+}
+
+func (tx *Transaction) MarshalTo(data []byte) (n int, err error) {
+ b, err := tx.Marshal()
+ if err != nil {
+ return 0, err
+ }
+
+ copy(data, b)
+ return len(b), nil
+}
+
+func (tx *Transaction) Unmarshal(data []byte) error {
+ var pbTx types_pb.Transaction
+ var err error
+ var inner TxData
+ err = proto.Unmarshal(data, &pbTx)
+ if err != nil {
+ return err
+ }
+
+ inner, err = txDataFromProtoMessage(&pbTx)
+ if err != nil {
+ return err
+ }
+
+ tx.setDecoded(inner, 0)
+ return err
+}
+
+func (tx *Transaction) Type() uint8 {
+ return tx.inner.txType()
+}
+
+func (tx *Transaction) ChainId() *uint256.Int {
+ return tx.inner.chainID()
+}
+
+func (tx *Transaction) Data() []byte {
+ return tx.inner.data()
+}
+
+func (tx *Transaction) AccessList() AccessList {
+ return tx.inner.accessList()
+}
+
+func (tx *Transaction) Gas() uint64 {
+ return tx.inner.gas()
+}
+
+func (tx *Transaction) GasPrice() *uint256.Int {
+ return tx.inner.gasPrice()
+}
+
+func (tx *Transaction) GasTipCap() *uint256.Int {
+ return tx.inner.gasTipCap()
+}
+
+func (tx *Transaction) GasFeeCap() *uint256.Int {
+ return tx.inner.gasFeeCap()
+}
+
+func (tx *Transaction) Value() *uint256.Int {
+ return tx.inner.value()
+}
+
+func (tx *Transaction) Nonce() uint64 {
+ return tx.inner.nonce()
+}
+
+func (tx *Transaction) To() *types.Address {
+ return copyAddressPtr(tx.inner.to())
+}
+
+func (tx *Transaction) From() *types.Address {
+ return tx.inner.from()
+}
+
+func (tx *Transaction) SetFrom(addr types.Address) {
+ switch t := tx.inner.(type) {
+ case *AccessListTx:
+ t.From = &addr
+ case *LegacyTx:
+ t.From = &addr
+ case *DynamicFeeTx:
+ t.From = &addr
+ }
+}
+
+func (tx *Transaction) SetNonce(nonce uint64) {
+ switch t := tx.inner.(type) {
+ case *AccessListTx:
+ t.Nonce = nonce
+ case *LegacyTx:
+ t.Nonce = nonce
+ case *DynamicFeeTx:
+ t.Nonce = nonce
+ }
+}
+
+func (tx *Transaction) Sign() []byte {
+ return tx.inner.sign()
+}
+
+func (tx *Transaction) Cost() *uint256.Int {
+ price := tx.inner.gasPrice()
+ gas := uint256.NewInt(tx.inner.gas())
+ total := new(uint256.Int).Mul(price, gas)
+ total = total.Add(total, tx.Value())
+ return total
+}
+
+func (tx *Transaction) Hash() types.Hash {
+ if hash := tx.hash.Load(); hash != nil {
+ return hash.(types.Hash)
+ }
+ h := tx.inner.hash()
+ tx.hash.Store(h)
+ return h
+}
+
+// GasFeeCapCmp compares the fee cap of two transactions.
+func (tx *Transaction) GasFeeCapCmp(other *Transaction) int {
+ return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap())
+}
+
+// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
+func (tx *Transaction) EffectiveGasTipIntCmp(other *uint256.Int, baseFee *uint256.Int) int {
+ if baseFee == nil {
+ return tx.GasTipCapIntCmp(other)
+ }
+ return tx.EffectiveGasTipValue(baseFee).Cmp(other)
+}
+
+// GasTipCapCmp compares the gasTipCap of two transactions.
+func (tx *Transaction) GasTipCapCmp(other *Transaction) int {
+ return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap())
+}
+
+// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap.
+func (tx *Transaction) GasFeeCapIntCmp(other *uint256.Int) int {
+ return tx.inner.gasFeeCap().Cmp(other)
+}
+
+// GasTipCapIntCmp compares the gasTipCap of the transaction against the given gasTipCap.
+func (tx *Transaction) GasTipCapIntCmp(other *uint256.Int) int {
+ return tx.inner.gasTipCap().Cmp(other)
+}
+
+// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
+// error in case the effective gasTipCap is negative
+func (tx *Transaction) EffectiveGasTipValue(baseFee *uint256.Int) *uint256.Int {
+ effectiveTip, _ := tx.EffectiveGasTip(baseFee)
+ return effectiveTip
+}
+
+// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
+func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *uint256.Int) int {
+ if baseFee == nil {
+ return tx.GasTipCapCmp(other)
+ }
+ return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
+}
+
+// EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
+// Note: if the effective gasTipCap is negative, this method returns both error
+// the actual negative value, _and_ ErrGasFeeCapTooLow
+func (tx *Transaction) EffectiveGasTip(baseFee *uint256.Int) (*uint256.Int, error) {
+ if baseFee == nil {
+ return tx.GasTipCap(), nil
+ }
+ var err error
+ gasFeeCap := tx.GasFeeCap()
+ if gasFeeCap.Cmp(baseFee) == -1 {
+ err = ErrGasFeeCapTooLow
+ }
+ return uint256Min(tx.GasTipCap(), new(uint256.Int).Sub(gasFeeCap, baseFee)), err
+}
+
+func uint256Min(x, y *uint256.Int) *uint256.Int {
+ if x.Cmp(y) == 1 {
+ return x
+ }
+ return y
+}
+
+func isProtectedV(V *big.Int) bool {
+ if V.BitLen() <= 8 {
+ v := V.Uint64()
+ return v != 27 && v != 28 && v != 1 && v != 0
+ }
+ // anything not 27 or 28 is considered protected
+ return true
+}
+
+// Protected says whether the transaction is replay-protected.
+func (tx *Transaction) Protected() bool {
+ switch tx := tx.inner.(type) {
+ case *LegacyTx:
+ return tx.V.ToBig() != nil && isProtectedV(tx.V.ToBig())
+ default:
+ return true
+ }
+}
+
+// WithSignature returns a new transaction with the given signature.
+// This signature needs to be in the [R || S || V] format where V is 0 or 1.
+func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
+ r, s, v, err := signer.SignatureValues(tx, sig)
+ if err != nil {
+ return nil, err
+ }
+ cpy := tx.inner.copy()
+ var chainID *uint256.Int
+ if signer.ChainID() == nil {
+ chainID = nil
+ } else {
+ chainID, _ = uint256.FromBig(signer.ChainID())
+ }
+
+ v1, _ := uint256.FromBig(v)
+ r1, _ := uint256.FromBig(r)
+ s1, _ := uint256.FromBig(s)
+ cpy.setSignatureValues(chainID, v1, r1, s1)
+ return &Transaction{inner: cpy, time: tx.time}, nil
+}
+
+//type Transaction struct {
+// to types.Address
+// from types.Address
+// nonce uint64
+// amount uint256.Int
+// gasLimit uint64
+// gasPrice uint256.Int
+// gasFeeCap uint256.Int
+// payload []byte
+//}
+
+//func (t *Transaction) ToProtoMessage() proto.Message {
+// pbTx := types_pb.Transaction{
+// From: types.Address(t.from),
+// To: types.Address(t.to),
+// Value: t.amount,
+// Data: t.payload,
+// Nonce: t.nonce,
+// S: nil,
+// V: nil,
+// R: nil,
+// }
+//
+// return &pbTx
+//}
+
+func copyAddressPtr(a *types.Address) *types.Address {
+ if a == nil {
+ return nil
+ }
+ cpy := *a
+ return &cpy
+}
+
+// Message is a fully derived transaction and implements core.Message
+type Message struct {
+ to *types.Address
+ from types.Address
+ nonce uint64
+ amount uint256.Int
+ gasLimit uint64
+ gasPrice uint256.Int
+ feeCap uint256.Int
+ tip uint256.Int
+ data []byte
+ accessList AccessList
+ checkNonce bool
+ isFree bool
+}
+
+func NewMessage(from types.Address, to *types.Address, nonce uint64, amount *uint256.Int, gasLimit uint64, gasPrice *uint256.Int, feeCap, tip *uint256.Int, data []byte, accessList AccessList, checkNonce bool, isFree bool) Message {
+ m := Message{
+ from: from,
+ to: to,
+ nonce: nonce,
+ amount: *amount,
+ gasLimit: gasLimit,
+ data: data,
+ accessList: accessList,
+ checkNonce: checkNonce,
+ isFree: isFree,
+ }
+ if gasPrice != nil {
+ m.gasPrice.Set(gasPrice)
+ }
+ if tip != nil {
+ m.tip.Set(tip)
+ }
+ if feeCap != nil {
+ m.feeCap.Set(feeCap)
+ }
+ return m
+}
+
+// AsMessage returns the transaction as a core.Message.
+func (tx *Transaction) AsMessage(s Signer, baseFee *uint256.Int) (Message, error) {
+ msg := Message{
+ nonce: tx.Nonce(),
+ gasLimit: tx.Gas(),
+ gasPrice: *new(uint256.Int).Set(tx.GasPrice()),
+ feeCap: *new(uint256.Int).Set(tx.GasFeeCap()),
+ tip: *new(uint256.Int).Set(tx.GasTipCap()),
+ //gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
+ //gasTipCap: new(big.Int).Set(tx.GasTipCap()),
+ to: tx.To(),
+ amount: *tx.Value(),
+ data: tx.Data(),
+ accessList: tx.AccessList(),
+ checkNonce: false,
+ //isFake: false,
+ }
+
+ // If baseFee provided, set gasPrice to effectiveGasPrice.
+ if baseFee != nil {
+ msg.gasPrice.Add(&msg.tip, baseFee)
+
+ if msg.gasPrice.Gt(&msg.feeCap) {
+ msg.gasPrice = msg.feeCap
+ }
+ }
+ //var err error
+ //msg.from, err = Sender(s, tx)
+ //if nil != err {
+ // return msg, err
+ //}
+ msg.from = *tx.From()
+
+ return msg, nil
+}
+func (m Message) From() types.Address { return m.from }
+func (m Message) To() *types.Address { return m.to }
+func (m Message) GasPrice() *uint256.Int { return &m.gasPrice }
+func (m Message) FeeCap() *uint256.Int { return &m.feeCap }
+func (m Message) Tip() *uint256.Int { return &m.tip }
+func (m Message) Value() *uint256.Int { return &m.amount }
+func (m Message) Gas() uint64 { return m.gasLimit }
+func (m Message) Nonce() uint64 { return m.nonce }
+func (m Message) Data() []byte { return m.data }
+func (m Message) AccessList() AccessList { return m.accessList }
+func (m Message) CheckNonce() bool { return m.checkNonce }
+func (m *Message) SetCheckNonce(checkNonce bool) {
+ m.checkNonce = checkNonce
+}
+func (m Message) IsFree() bool { return m.isFree }
+func (m *Message) SetIsFree(isFree bool) {
+ m.isFree = isFree
+}
diff --git a/common/transaction/transaction_signing.go b/common/transaction/transaction_signing.go
new file mode 100644
index 0000000..93ef3ef
--- /dev/null
+++ b/common/transaction/transaction_signing.go
@@ -0,0 +1,506 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/hash"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/common/u256"
+ "github.com/astranetworld/ast/params"
+ "github.com/holiman/uint256"
+ "math/big"
+)
+
+var (
+ ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+ ErrTxTypeNotSupported = errors.New("transaction type not supported")
+ ErrInvalidChainId = errors.New("invalid chain id for signer")
+)
+
+// sigCache is used to cache the derived sender and contains
+// the signer used to derive it.
+type sigCache struct {
+ signer Signer
+ from types.Address
+}
+
+// MakeSigner returns a Signer based on the given chain config and block number.
+func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
+ var signer Signer
+ switch {
+ case config.IsLondon(blockNumber.Uint64()):
+ signer = NewLondonSigner(config.ChainID)
+ case config.IsBerlin(blockNumber.Uint64()):
+ signer = NewEIP2930Signer(config.ChainID)
+ case config.IsEip1559FeeCollector(blockNumber.Uint64()):
+ signer = NewEIP155Signer(config.ChainID)
+ case config.IsHomestead(blockNumber.Uint64()):
+ signer = HomesteadSigner{}
+ default:
+ signer = FrontierSigner{}
+ }
+ return signer
+}
+
+// LatestSignerForChainID returns the 'most permissive' Signer available. Specifically,
+// this enables support for EIP-155 replay protection and all implemented EIP-2718
+// transaction types if chainID is non-nil.
+//
+// Use this in transaction-handling code where the current block number and fork
+// configuration are unknown. If you have a ChainConfig, use LatestSigner instead.
+// If you have a ChainConfig and know the current block number, use MakeSigner instead.
+func LatestSignerForChainID(chainID *big.Int) Signer {
+ if chainID == nil {
+ return HomesteadSigner{}
+ }
+ return NewLondonSigner(chainID)
+}
+
+// SignNewTx creates a transaction and signs it.
+func SignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) (*Transaction, error) {
+ tx := NewTx(txdata)
+ h := s.Hash(tx)
+ sig, err := crypto.Sign(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(s, sig)
+}
+
+// SignTx signs the transaction using the given signer and private key.
+func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := s.Hash(tx)
+ sig, err := crypto.Sign(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return tx.WithSignature(s, sig)
+}
+
+// Sender returns the address derived from the signature (V, R, S) using secp256k1
+// elliptic curve and an error if it failed deriving or upon an incorrect
+// signature.
+//
+// Sender may cache the address, allowing it to be used regardless of
+// signing method. The cache is invalidated if the cached signer does
+// not match the signer used in the current call.
+func Sender(signer Signer, tx *Transaction) (types.Address, error) {
+ if sc := tx.from.Load(); sc != nil {
+ sigCache := sc.(sigCache)
+ // If the signer used to derive from in a previous
+ // call is not the same as used current, invalidate
+ // the cache.
+ if sigCache.signer.Equal(signer) {
+ return sigCache.from, nil
+ }
+ }
+
+ addr, err := signer.Sender(tx)
+ if err != nil {
+ return types.Address{}, err
+ }
+ tx.from.Store(sigCache{signer: signer, from: addr})
+ return addr, nil
+}
+
+// Signer encapsulates transaction signature handling. The name of this type is slightly
+// misleading because Signers don't actually sign, they're just for validating and
+// processing of signatures.
+//
+// Note that this interface is not a stable API and may change at any time to accommodate
+// new protocol rules.
+type Signer interface {
+ // Sender returns the sender address of the transaction.
+ Sender(tx *Transaction) (types.Address, error)
+
+ // SignatureValues returns the raw R, S, V values corresponding to the
+ // given signature.
+ SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error)
+ ChainID() *big.Int
+
+ // Hash returns 'signature hash', i.e. the transaction hash that is signed by the
+ // private key. This hash does not uniquely identify the transaction.
+ Hash(tx *Transaction) types.Hash
+
+ // Equal returns true if the given signer is the same as the receiver.
+ Equal(Signer) bool
+}
+
+type londonSigner struct{ eip2930Signer }
+
+// NewLondonSigner returns a signer that accepts
+// - EIP-1559 dynamic fee transactions
+// - EIP-2930 access list transactions,
+// - EIP-155 replay protected transactions, and
+// - legacy Homestead transactions.
+func NewLondonSigner(chainId *big.Int) Signer {
+ return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}
+}
+
+func (s londonSigner) Sender(tx *Transaction) (types.Address, error) {
+ if tx.Type() != DynamicFeeTxType {
+ return s.eip2930Signer.Sender(tx)
+ }
+ V, R, S := tx.RawSignatureValues()
+ // DynamicFee txs are defined to use 0 and 1 as their recovery
+ // id, add 27 to become equivalent to unprotected Homestead signatures.
+ V1 := new(big.Int).Add(V.ToBig(), big.NewInt(27))
+ chainId, _ := uint256.FromBig(s.chainId)
+ id := tx.ChainId()
+ if id.Cmp(chainId) != 0 {
+ return types.Address{}, ErrInvalidChainId
+ }
+ return recoverPlain(s.Hash(tx), R.ToBig(), S.ToBig(), V1, true)
+}
+
+func (s londonSigner) Equal(s2 Signer) bool {
+ x, ok := s2.(londonSigner)
+ return ok && x.chainId.Cmp(s.chainId) == 0
+}
+
+func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ txdata, ok := tx.inner.(*DynamicFeeTx)
+ if !ok {
+ return s.eip2930Signer.SignatureValues(tx, sig)
+ }
+ // Check that chain ID of tx matches the signer. We also accept ID zero here,
+ // because it indicates that the chain ID was not specified in the tx.
+ chainId, _ := uint256.FromBig(s.chainId)
+ if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(chainId) != 0 {
+ return nil, nil, nil, ErrInvalidChainId
+ }
+ R, S, _ = decodeSignature(sig)
+ V = big.NewInt(int64(sig[64]))
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s londonSigner) Hash(tx *Transaction) types.Hash {
+ if tx.Type() != DynamicFeeTxType {
+ return s.eip2930Signer.Hash(tx)
+ }
+ return hash.PrefixedRlpHash(
+ tx.Type(),
+ []interface{}{
+ s.chainId,
+ tx.Nonce(),
+ tx.GasTipCap(),
+ tx.GasFeeCap(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ tx.AccessList(),
+ })
+}
+
+type eip2930Signer struct{ EIP155Signer }
+
+// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
+// EIP-155 replay protected transactions, and legacy Homestead transactions.
+func NewEIP2930Signer(chainId *big.Int) Signer {
+ return eip2930Signer{NewEIP155Signer(chainId)}
+}
+
+func (s eip2930Signer) ChainID() *big.Int {
+ return s.chainId
+}
+
+func (s eip2930Signer) Equal(s2 Signer) bool {
+ x, ok := s2.(eip2930Signer)
+ return ok && x.chainId.Cmp(s.chainId) == 0
+}
+
+func (s eip2930Signer) Sender(tx *Transaction) (types.Address, error) {
+ V, R, S := tx.RawSignatureValues()
+ V1 := V.ToBig()
+ switch tx.Type() {
+ case LegacyTxType:
+ if !tx.Protected() {
+ return HomesteadSigner{}.Sender(tx)
+ }
+ V1 = new(big.Int).Sub(V1, s.chainIdMul)
+ V1.Sub(V1, big8)
+ case AccessListTxType:
+ // AL txs are defined to use 0 and 1 as their recovery
+ // id, add 27 to become equivalent to unprotected Homestead signatures.
+ V1 = new(big.Int).Add(V1, big.NewInt(27))
+ default:
+ return types.Address{}, ErrTxTypeNotSupported
+ }
+ chainId, _ := uint256.FromBig(s.chainId)
+ id := tx.ChainId()
+ if id.Cmp(chainId) != 0 {
+ return types.Address{}, ErrInvalidChainId
+ }
+ return recoverPlain(s.Hash(tx), R.ToBig(), S.ToBig(), V1, true)
+}
+
+func (s eip2930Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ switch txdata := tx.inner.(type) {
+ case *LegacyTx:
+ return s.EIP155Signer.SignatureValues(tx, sig)
+ case *AccessListTx:
+ // Check that chain ID of tx matches the signer. We also accept ID zero here,
+ // because it indicates that the chain ID was not specified in the tx.
+ chainId, _ := uint256.FromBig(s.chainId)
+ if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(chainId) != 0 {
+ return nil, nil, nil, ErrInvalidChainId
+ }
+ R, S, _ = decodeSignature(sig)
+ V = big.NewInt(int64(sig[64]))
+ default:
+ return nil, nil, nil, ErrTxTypeNotSupported
+ }
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s eip2930Signer) Hash(tx *Transaction) types.Hash {
+ switch tx.Type() {
+ case LegacyTxType:
+ return hash.RlpHash([]interface{}{
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ s.chainId, uint(0), uint(0),
+ })
+ case AccessListTxType:
+ return hash.PrefixedRlpHash(
+ tx.Type(),
+ []interface{}{
+ s.chainId,
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ tx.AccessList(),
+ })
+ default:
+ // This _should_ not happen, but in case someone sends in a bad
+ // json struct via RPC, it's probably more prudent to return an
+ // empty hash instead of killing the node with a panic
+ //panic("Unsupported transaction type: %d", tx.typ)
+ return types.Hash{}
+ }
+}
+
+// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which
+// are replay-protected as well as unprotected homestead transactions.
+type EIP155Signer struct {
+ chainId, chainIdMul *big.Int
+}
+
+func NewEIP155Signer(chainId *big.Int) EIP155Signer {
+ if chainId == nil {
+ chainId = new(big.Int)
+ }
+ return EIP155Signer{
+ chainId: chainId,
+ chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
+ }
+}
+
+func (s EIP155Signer) ChainID() *big.Int {
+ return s.chainId
+}
+
+func (s EIP155Signer) Equal(s2 Signer) bool {
+ eip155, ok := s2.(EIP155Signer)
+ return ok && eip155.chainId.Cmp(s.chainId) == 0
+}
+
+var big8 = big.NewInt(8)
+
+func (s EIP155Signer) Sender(tx *Transaction) (types.Address, error) {
+ if tx.Type() != LegacyTxType {
+ return types.Address{}, ErrTxTypeNotSupported
+ }
+ if !tx.Protected() {
+ return HomesteadSigner{}.Sender(tx)
+ }
+ chainId, _ := uint256.FromBig(s.chainId)
+ id := tx.ChainId()
+ if id.Cmp(chainId) != 0 {
+ return types.Address{}, ErrInvalidChainId
+ }
+ V, R, S := tx.RawSignatureValues()
+ V1 := V.ToBig()
+ V1 = new(big.Int).Sub(V1, s.chainIdMul)
+ V1.Sub(V1, big8)
+ return recoverPlain(s.Hash(tx), R.ToBig(), S.ToBig(), V1, true)
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ if tx.Type() != LegacyTxType {
+ return nil, nil, nil, ErrTxTypeNotSupported
+ }
+ R, S, V = decodeSignature(sig)
+ if s.chainId.Sign() != 0 {
+ V = big.NewInt(int64(sig[64] + 35))
+ V.Add(V, s.chainIdMul)
+ }
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s EIP155Signer) Hash(tx *Transaction) types.Hash {
+ return hash.RlpHash([]interface{}{
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ s.chainId, uint(0), uint(0),
+ })
+}
+
+// HomesteadTransaction implements TransactionInterface using the
+// homestead rules.
+type HomesteadSigner struct{ FrontierSigner }
+
+func (s HomesteadSigner) ChainID() *big.Int {
+ return nil
+}
+
+func (s HomesteadSigner) Equal(s2 Signer) bool {
+ _, ok := s2.(HomesteadSigner)
+ return ok
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ return hs.FrontierSigner.SignatureValues(tx, sig)
+}
+
+func (hs HomesteadSigner) Sender(tx *Transaction) (types.Address, error) {
+ if tx.Type() != LegacyTxType {
+ return types.Address{}, ErrTxTypeNotSupported
+ }
+ v, r, s := tx.RawSignatureValues()
+ return recoverPlain(hs.Hash(tx), r.ToBig(), s.ToBig(), v.ToBig(), true)
+}
+
+type FrontierSigner struct{}
+
+func (s FrontierSigner) ChainID() *big.Int {
+ return nil
+}
+
+func (s FrontierSigner) Equal(s2 Signer) bool {
+ _, ok := s2.(FrontierSigner)
+ return ok
+}
+
+func (fs FrontierSigner) Sender(tx *Transaction) (types.Address, error) {
+ if tx.Type() != LegacyTxType {
+ return types.Address{}, ErrTxTypeNotSupported
+ }
+ v, r, s := tx.RawSignatureValues()
+ return recoverPlain(fs.Hash(tx), r.ToBig(), s.ToBig(), v.ToBig(), false)
+}
+
+// SignatureValues returns signature values. This signature
+// needs to be in the [R || S || V] format where V is 0 or 1.
+func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ if tx.Type() != LegacyTxType {
+ return nil, nil, nil, ErrTxTypeNotSupported
+ }
+ r, s, v = decodeSignature(sig)
+ return r, s, v, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (fs FrontierSigner) Hash(tx *Transaction) types.Hash {
+ return hash.RlpHash([]interface{}{
+ tx.Nonce(),
+ tx.GasPrice(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ })
+}
+
+func decodeSignature(sig []byte) (r, s, v *big.Int) {
+ if len(sig) != crypto.SignatureLength {
+ panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength))
+ }
+ r = new(big.Int).SetBytes(sig[:32])
+ s = new(big.Int).SetBytes(sig[32:64])
+ v = new(big.Int).SetBytes([]byte{sig[64] + 27})
+ return r, s, v
+}
+
+func recoverPlain(sighash types.Hash, R, S, Vb *big.Int, homestead bool) (types.Address, error) {
+ if Vb.BitLen() > 8 {
+ return types.Address{}, ErrInvalidSig
+ }
+ V := byte(Vb.Uint64() - 27)
+ lr, _ := uint256.FromBig(R)
+ ls, _ := uint256.FromBig(S)
+ if !crypto.ValidateSignatureValues(V, lr, ls, homestead) {
+ return types.Address{}, ErrInvalidSig
+ }
+ // encode the signature in uncompressed format
+ r, s := R.Bytes(), S.Bytes()
+ sig := make([]byte, crypto.SignatureLength)
+ copy(sig[32-len(r):32], r)
+ copy(sig[64-len(s):64], s)
+ sig[64] = V
+ // recover the public key from the signature
+ pub, err := crypto.Ecrecover(sighash[:], sig)
+ if err != nil {
+ return types.Address{}, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return types.Address{}, errors.New("invalid public key")
+ }
+ var addr types.Address
+ copy(addr[:], crypto.Keccak256(pub[1:])[12:])
+ return addr, nil
+}
+
+// deriveChainID derives the chain id from the given v parameter
+func DeriveChainId(v *uint256.Int) *uint256.Int {
+ if v.IsUint64() {
+ v := v.Uint64()
+ if v == 27 || v == 28 {
+ return new(uint256.Int)
+ }
+ return new(uint256.Int).SetUint64((v - 35) / 2)
+ }
+ r := new(uint256.Int).Sub(v, u256.Num35)
+ return r.Div(r, u256.Num2)
+}
diff --git a/common/transaction/transaction_test.go b/common/transaction/transaction_test.go
new file mode 100644
index 0000000..f494484
--- /dev/null
+++ b/common/transaction/transaction_test.go
@@ -0,0 +1,88 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package transaction
+
+import (
+ "crypto/rand"
+ "encoding/json"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+ "github.com/libp2p/go-libp2p/core/crypto"
+ "testing"
+)
+
+func TestNewLegacyTx(t *testing.T) {
+ _, pub, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := types.PublicToAddress(pub)
+
+ tx := NewTransaction(1, addr, &addr, uint256.NewInt(10000), 21000, uint256.NewInt(10000000), []byte("hello"))
+ t.Logf("tx: %v", tx)
+
+ buf1, err := json.Marshal(tx)
+ t.Log(types.BytesHash(buf1).String())
+
+ switch txt := tx.inner.(type) {
+ case *LegacyTx:
+ buf, err := json.Marshal(txt)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ txHash := types.BytesHash(buf)
+ t.Log(txHash.String())
+ }
+
+ hash := tx.Hash()
+
+ t.Log(hash.String())
+
+}
+
+func TestTDin(t *testing.T) {
+ _, pub, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := types.PublicToAddress(pub)
+
+ tx := NewTransaction(1, addr, &addr, uint256.NewInt(10000), 21000, uint256.NewInt(10000000), []byte("hello"))
+ t.Logf("tx: %v", tx)
+
+ b, err := tx.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tx.Unmarshal(b)
+
+ t.Log(b)
+}
+
+func TestNewDynamicTx(t *testing.T) {
+ //_, pub, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+ //if err != nil {
+ // t.Fatal(err)
+ //}
+ //
+ //addr := types.PublicToAddress(pub)
+
+}
diff --git a/common/types/address.go b/common/types/address.go
new file mode 100644
index 0000000..249b97b
--- /dev/null
+++ b/common/types/address.go
@@ -0,0 +1,272 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/hex"
+ "fmt"
+ "strings"
+
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/libp2p/go-libp2p/core/crypto"
+ "golang.org/x/crypto/sha3"
+)
+
+const (
+ AddressLength = 20
+ // IncarnationLength length of uint64 for contract incarnations
+ IncarnationLength = 2
+ // Address32Length is the expected length of the Starknet address (in bytes)
+ Address32Length = 32
+)
+
+var (
+ prefixAddress = "ast"
+ nullAddress = Address{0}
+)
+
+type Address [AddressLength]byte
+
+// BytesToAddress returns Address with value b.
+// If b is larger than len(h), b will be cropped from the left.
+func BytesToAddress(b []byte) Address {
+ var a Address
+ a.SetBytes(b)
+ return a
+}
+
+// HexToAddress returns Address with byte values of s.
+// If s is larger than len(h), s will be cropped from the left.
+func HexToAddress(s string) Address { return BytesToAddress(FromHex1(s)) }
+
+func PublicToAddress(key crypto.PubKey) Address {
+ bPub, err := crypto.MarshalPublicKey(key)
+ if err != nil {
+ return Address{0}
+ }
+
+ h := sha3.New256()
+ h.Write(bPub)
+ hash := h.Sum(nil)
+ var addr Address
+ copy(addr[:], hash[:AddressLength])
+ return addr
+}
+
+func PrivateToAddress(key crypto.PrivKey) Address {
+ return PublicToAddress(key.GetPublic())
+}
+
+func HexToString(hexs string) (Address, error) {
+ a := Address{0}
+ if !strings.HasPrefix(strings.ToUpper(hexs), prefixAddress) {
+ return a, fmt.Errorf("invalid prefix address")
+ }
+
+ b, err := hex.DecodeString(hexs[len(prefixAddress):])
+ if err != nil {
+ return a, err
+ }
+
+ copy(a[:], b)
+
+ return a, nil
+}
+
+// IsHexAddress verifies whether a string can represent a valid hex-encoded
+// Ethereum address or not.
+func IsHexAddress(s string) bool {
+ if has0xPrefix(s) {
+ s = s[2:]
+ }
+ return len(s) == 2*AddressLength && isHex(s)
+}
+
+// Bytes gets the string representation of the underlying address.
+func (a Address) Bytes() []byte { return a[:] }
+
+// Hash converts an address to a hash by left-padding it with zeros.
+func (a Address) Hash() Hash { return BytesToHash(a[:]) }
+
+// Hex returns an EIP55-compliant hex string representation of the address.
+func (a Address) Hex() string {
+ return string(a.checksumHex())
+}
+
+// String implements fmt.Stringer.
+func (a Address) String() string {
+ return a.Hex()
+}
+
+// Addresses is a slice of common.Address, implementing sort.Interface
+type Addresses []Address
+
+func (addrs Addresses) Len() int {
+ return len(addrs)
+}
+func (addrs Addresses) Less(i, j int) bool {
+ return bytes.Compare(addrs[i][:], addrs[j][:]) == -1
+}
+func (addrs Addresses) Swap(i, j int) {
+ addrs[i], addrs[j] = addrs[j], addrs[i]
+}
+
+func (a *Address) checksumHex() []byte {
+ buf := a.hex()
+
+ // compute checksum
+ sha := sha3.NewLegacyKeccak256()
+ //nolint:errcheck
+ sha.Write(buf[2:])
+ hash := sha.Sum(nil)
+ for i := 2; i < len(buf); i++ {
+ hashByte := hash[(i-2)/2]
+ if i%2 == 0 {
+ hashByte = hashByte >> 4
+ } else {
+ hashByte &= 0xf
+ }
+ if buf[i] > '9' && hashByte > 7 {
+ buf[i] -= 32
+ }
+ }
+ return buf
+}
+
+func (a Address) hex() []byte {
+ var buf [len(a)*2 + 2]byte
+ copy(buf[:2], "0x")
+ hex.Encode(buf[2:], a[:])
+ return buf[:]
+}
+
+func (a *Address) DecodeBytes(b []byte) bool {
+ if len(b) != AddressLength {
+ return false
+ }
+
+ copy(a[:], b)
+ return true
+}
+
+func (a *Address) DecodeString(s string) bool {
+ if !strings.HasPrefix(strings.ToUpper(s), prefixAddress) {
+ return false
+ }
+
+ b, err := hex.DecodeString(s[len(prefixAddress):])
+ if err != nil {
+ a = &Address{0}
+ return false
+ }
+
+ copy(a[:], b)
+ return true
+}
+
+func (a Address) Equal(other Address) bool {
+ return bytes.Equal(a[:], other[:])
+}
+
+func (a *Address) IsNull() bool {
+ return bytes.Equal(a[:], nullAddress[:])
+}
+
+func (a Address) Marshal() ([]byte, error) {
+ return a.Bytes(), nil
+}
+
+func (a *Address) MarshalTo(data []byte) (n int, err error) {
+ copy(data, a[:])
+ return len(data), nil
+}
+
+func (a *Address) Unmarshal(data []byte) error {
+ if len(data) != AddressLength {
+ return fmt.Errorf("invalid bytes len: %d, hex: %s", len(data), a.Hex())
+ }
+
+ copy(a[:], data)
+ return nil
+}
+
+// SetBytes sets the address to the value of b.
+// If b is larger than len(a), b will be cropped from the left.
+func (a *Address) SetBytes(b []byte) *Address {
+ if len(b) > len(a) {
+ b = b[len(b)-AddressLength:]
+ }
+ copy(a[AddressLength-len(b):], b)
+ return a
+}
+
+// MarshalText returns the hex representation of a.
+func (a Address) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(a[:]).MarshalText()
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (a *Address) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Address", input, a[:])
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (a *Address) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
+}
+
+// Scan implements Scanner for database/sql.
+func (a *Address) Scan(src interface{}) error {
+ srcB, ok := src.([]byte)
+ if !ok {
+ return fmt.Errorf("can't scan %T into Address", src)
+ }
+ if len(srcB) != AddressLength {
+ return fmt.Errorf("can't scan []byte of len %d into Address, want %d", len(srcB), AddressLength)
+ }
+ copy(a[:], srcB)
+ return nil
+}
+
+// Value implements valuer for database/sql.
+func (a Address) Value() (driver.Value, error) {
+ return a[:], nil
+}
+
+func (a Address) Size() int {
+ return AddressLength
+}
+
+// isHex validates whether each byte is valid hexadecimal string.
+func isHex(str string) bool {
+ if len(str)%2 != 0 {
+ return false
+ }
+ for _, c := range []byte(str) {
+ if !isHexCharacter(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// isHexCharacter returns bool of c being a valid hexadecimal.
+func isHexCharacter(c byte) bool {
+ return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
+}
diff --git a/common/types/address_test.go b/common/types/address_test.go
new file mode 100644
index 0000000..eba667d
--- /dev/null
+++ b/common/types/address_test.go
@@ -0,0 +1,194 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "crypto/rand"
+ "encoding/json"
+ "github.com/holiman/uint256"
+ "reflect"
+
+ // "github.com/astranetworld/ast/utils" cycle import
+ "strings"
+ "testing"
+
+ "github.com/libp2p/go-libp2p/core/crypto"
+)
+
+func TestAddress_ed25519(t *testing.T) {
+ _, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := PublicToAddress(pub)
+ t.Log(addr.String())
+}
+
+func TestFromECDSAPub(t *testing.T) {
+ _, pub, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := PublicToAddress(pub)
+ t.Log(addr.String())
+}
+
+// func TestAddress_DecodeString(t *testing.T) {
+// _, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+// if err != nil {
+// t.Fatal(err)
+// }
+
+// addr := PublicToAddress(pub)
+// t.Log(addr.String())
+// t.Log(addr.Bytes())
+
+// h := addr.HexBytes()
+// var c Address
+// if c.DecodeHexBytes(h) {
+// t.Log(h)
+// t.Logf("c: %s", c.String())
+// }
+
+// var a, b Address
+// if a.DecodeString(addr.String()) {
+// t.Logf("a: %s", a.String())
+// }
+// if b.DecodeBytes(addr.Bytes()) {
+// t.Logf("b: %s", b.String())
+// }
+
+// t.Log("done!")
+// }
+
+func TestPrivateToAddress(t *testing.T) {
+ priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := PublicToAddress(pub)
+ t.Log(addr.String())
+ addr2 := PrivateToAddress(priv)
+ t.Log(addr2.String())
+}
+
+func TestAddress_null(t *testing.T) {
+ a := Address{}
+ if a.IsNull() {
+ t.Log("null address")
+ } else {
+ t.Log(a)
+ }
+
+ _, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := PublicToAddress(pub)
+
+ if addr.IsNull() {
+ t.Log("null address")
+ } else {
+ t.Log(strings.ToUpper(addr.String()))
+ }
+}
+
+func TestAddress(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ _, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ addr := PublicToAddress(pub)
+
+ if addr.IsNull() {
+ t.Log("null address")
+ } else {
+ t.Log(strings.ToUpper(addr.String()))
+ }
+ }
+}
+
+func TestSign(t *testing.T) {
+ data := []byte("hello")
+ //priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
+ priv, pub, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ s, err := priv.Sign(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("sign len (%d)", len(s))
+
+ ok, err := pub.Verify(data, s)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Logf("sign len %d", len(s))
+ if !ok {
+ t.Log("verify sign failed")
+ } else {
+ t.Log("verify success")
+ }
+}
+
+// func TestAddressToInt(t *testing.T) {
+// priv, _, err := crypto.GenerateECDSAKeyPair(rand.Reader)
+// if err != nil {
+// t.Fatal(err)
+// }
+
+// sp, err := utils.PrivateToString(priv)
+
+// addr := PrivateToAddress(priv)
+// t.Logf("address: %s", addr)
+// t.Logf("private: %s", sp)
+// }
+
+func TestAddressMarshal(t *testing.T) {
+ aa := make(map[Address]*uint256.Int)
+ var a1, a2 Address
+ a1.DecodeString("ast4541Fc1CCB4e042a3BaDFE46904F9D22d127B682")
+ a2.DecodeString("ast9BA336835422BAeFc537d75642959d2a866500a3")
+ aa[a1] = uint256.NewInt(1)
+ aa[a2] = uint256.NewInt(2)
+
+ b, err := json.Marshal(aa)
+ if nil != err {
+ t.Fatal(err)
+ }
+
+ bb := make(map[Address]*uint256.Int)
+
+ if err := json.Unmarshal(b, &bb); nil != err {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(aa, bb) {
+ t.Error("failed")
+ }
+}
diff --git a/common/types/bloom.go b/common/types/bloom.go
new file mode 100644
index 0000000..12c6803
--- /dev/null
+++ b/common/types/bloom.go
@@ -0,0 +1,71 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "encoding/binary"
+ "fmt"
+ bloomfilter "github.com/holiman/bloomfilter/v2"
+)
+
+// Collide rate
+const probCollide = 0.0000001
+
+type hasher []byte
+
+func (f hasher) Write(p []byte) (n int, err error) { panic("not implemented") }
+func (f hasher) Sum(b []byte) []byte { panic("not implemented") }
+func (f hasher) Reset() { panic("not implemented") }
+func (f hasher) BlockSize() int { panic("not implemented") }
+func (f hasher) Size() int { return 8 }
+func (f hasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) }
+
+type Bloom struct {
+ bloom *bloomfilter.Filter
+}
+
+func NewBloom(size uint64) (*Bloom, error) {
+ bloom, err := bloomfilter.NewOptimal(size, probCollide)
+ if err != nil {
+ return nil, err
+ }
+ return &Bloom{bloom: bloom}, nil
+}
+
+func (b *Bloom) UnMarshalBloom(data []byte) error {
+ err := b.bloom.UnmarshalBinary(data)
+ return err
+}
+
+func (b *Bloom) Add(key []byte) error {
+ if len(key) != HashLength {
+ return fmt.Errorf("key length is not 32 ")
+ }
+ b.bloom.Add(hasher(key))
+ return nil
+}
+
+// Contain
+// - true maybe in the set
+// - false must not in the set
+func (b *Bloom) Contain(key []byte) bool {
+ return b.bloom.Contains(hasher(key))
+}
+
+func (b *Bloom) Marshal() ([]byte, error) {
+ return b.bloom.MarshalBinary()
+}
diff --git a/common/types/bloom_test.go b/common/types/bloom_test.go
new file mode 100644
index 0000000..641aaa2
--- /dev/null
+++ b/common/types/bloom_test.go
@@ -0,0 +1,41 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import "testing"
+
+func TestBloom_Contain(t *testing.T) {
+ bloom, _ := NewBloom(10)
+ hashes := []Hash{{0x01}, {0x02}, {0x03}, {0x04}, {0x05}, {0x06}, {0x07}, {0x08}, {0x09}, {0x0a}}
+ for _, hash := range hashes {
+ bloom.Add(hash.Bytes())
+ }
+
+ searchHash := []Hash{{0x10}, {0x11}, {0x01}}
+
+ for _, hash := range searchHash {
+ if bloom.Contain(hash.Bytes()) {
+ t.Logf("hash %d is in hashes %d", hash, hashes)
+ } else {
+ t.Logf("hash %d is not in hashes %d", hash, hashes)
+ }
+ }
+
+ b, _ := bloom.Marshal()
+ t.Logf("bloom Marshal: %+v", b)
+
+}
diff --git a/common/types/bytes.go b/common/types/bytes.go
new file mode 100644
index 0000000..1b0d3bb
--- /dev/null
+++ b/common/types/bytes.go
@@ -0,0 +1,118 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "encoding/hex"
+)
+
+// FromHex returns the bytes represented by the hexadecimal string s.
+// s may be prefixed with "0x".
+func FromHex1(s string) []byte {
+ if has0xPrefix(s) {
+ s = s[2:]
+ }
+ if len(s)%2 == 1 {
+ s = "0" + s
+ }
+ return Hex2Bytes(s)
+}
+
+// has0xPrefix validates str begins with '0x' or '0X'.
+func has0xPrefix(str string) bool {
+ return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
+}
+
+// Hex2Bytes returns the bytes represented by the hexadecimal string str.
+func Hex2Bytes(str string) []byte {
+ h, _ := hex.DecodeString(str)
+ return h
+}
+
+func KeyCmp(key1, key2 []byte) (int, bool) {
+ switch {
+ //both keys are empty
+ case len(key1) == 0 && len(key2) == 0:
+ return 0, true
+ // key1 is empty
+ case len(key1) == 0 && len(key2) != 0:
+ return 1, false
+ // key2 is empty
+ case len(key1) != 0 && len(key2) == 0:
+ return -1, false
+ default:
+ return bytes.Compare(key1, key2), false
+ }
+}
+
+// CopyBytes returns an exact copy of the provided bytes.
+func CopyBytes(b []byte) (copiedBytes []byte) {
+ if b == nil {
+ return nil
+ }
+ copiedBytes = make([]byte, len(b))
+ copy(copiedBytes, b)
+
+ return
+}
+
+// RightPadBytes zero-pads slice to the right up to length l.
+func RightPadBytes(slice []byte, l int) []byte {
+ if l <= len(slice) {
+ return slice
+ }
+
+ padded := make([]byte, l)
+ copy(padded, slice)
+
+ return padded
+}
+
+// LeftPadBytes zero-pads slice to the left up to length l.
+func LeftPadBytes(slice []byte, l int) []byte {
+ if l <= len(slice) {
+ return slice
+ }
+
+ padded := make([]byte, l)
+ copy(padded[l-len(slice):], slice)
+
+ return padded
+}
+
+// TrimLeftZeroes returns a subslice of s without leading zeroes
+func TrimLeftZeroes(s []byte) []byte {
+ idx := 0
+ for ; idx < len(s); idx++ {
+ if s[idx] != 0 {
+ break
+ }
+ }
+ return s[idx:]
+}
+
+// TrimRightZeroes returns a subslice of s without trailing zeroes
+func TrimRightZeroes(s []byte) []byte {
+ idx := len(s)
+ for ; idx > 0; idx-- {
+ if s[idx-1] != 0 {
+ break
+ }
+ }
+ return s[:idx]
+}
diff --git a/common/types/hash.go b/common/types/hash.go
new file mode 100644
index 0000000..2608ba8
--- /dev/null
+++ b/common/types/hash.go
@@ -0,0 +1,313 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/astranetworld/ast/common/hexutil"
+
+ "hash"
+ "math/big"
+ "math/rand"
+ "reflect"
+
+ "golang.org/x/crypto/sha3"
+)
+
+const (
+ HashLength = 32
+)
+
+var (
+ hashT = reflect.TypeOf(Hash{})
+ addressT = reflect.TypeOf(Address{})
+ //addressSt = reflect.TypeOf(Address32{})
+)
+
+type Hash [HashLength]byte
+
+// Bytes gets the byte representation of the underlying hash.
+func (h Hash) Bytes() []byte { return h[:] }
+
+// Big converts a hash to a big integer.
+func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
+
+// Hex converts a hash to a hex string.
+func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
+
+// TerminalString implements log.TerminalStringer, formatting a string for console
+// output during logging.
+func (h Hash) TerminalString() string {
+ return fmt.Sprintf("%x…%x", h[:3], h[29:])
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (h *Hash) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Hash", input, h[:])
+}
+
+// MarshalText returns the hex representation of h.
+func (h Hash) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(h[:]).MarshalText()
+}
+
+// SetBytes sets the hash to the value of b.
+func (h *Hash) SetBytes(b []byte) error {
+ if len(b) != HashLength {
+ return fmt.Errorf("invalid bytes len %d", len(b))
+ }
+
+ copy(h[:], b[:HashLength])
+ return nil
+}
+
+// Generate implements testing/quick.Generator.
+func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
+ m := rand.Intn(len(h))
+ for i := len(h) - 1; i > m; i-- {
+ h[i] = byte(rand.Uint32())
+ }
+ return reflect.ValueOf(h)
+}
+
+// Scan implements Scanner for database/sql.
+func (h *Hash) Scan(src interface{}) error {
+ srcB, ok := src.([]byte)
+ if !ok {
+ return fmt.Errorf("can't scan %T into Hash", src)
+ }
+ if len(srcB) != HashLength {
+ return fmt.Errorf("can't scan []byte of len %d into Hash, want %d", len(srcB), HashLength)
+ }
+ copy(h[:], srcB)
+ return nil
+}
+
+// Value implements valuer for database/sql.
+func (h Hash) Value() (driver.Value, error) {
+ return h[:], nil
+}
+
+func BytesHash(b []byte) Hash {
+ h3 := sha3.New256()
+ h3.Write(b)
+ r := h3.Sum(nil)
+ var h Hash
+ copy(h[:], r[:HashLength])
+ return h
+}
+
+func BytesToHash(b []byte) Hash {
+ var h Hash
+ h.SetBytes(b)
+ return h
+}
+
+func StringToHash(s string) Hash {
+ var h Hash
+ b, err := hex.DecodeString(s)
+ if err == nil {
+ //copy(h[:], b[:HashLength])
+ return BytesToHash(b)
+ }
+
+ return h
+}
+
+func (h Hash) String() string {
+ return hex.EncodeToString(h[:])
+}
+
+func (h Hash) HexBytes() []byte {
+ s := h.String()
+ return []byte(s)
+}
+
+func (h *Hash) SetString(s string) error {
+ if len(s) != HashLength*2 {
+ return fmt.Errorf("invalid string len %v, len(%d)", string(s), len(s))
+ }
+
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ return err
+ }
+
+ return h.SetBytes(b)
+}
+
+func (h Hash) Marshal() ([]byte, error) {
+ return h.Bytes(), nil
+}
+
+func (h *Hash) MarshalTo(data []byte) (n int, err error) {
+ copy(data, h.Bytes())
+ return len(h.Bytes()), err
+}
+
+func (h *Hash) Unmarshal(data []byte) error {
+ return h.SetBytes(data)
+}
+
+func (h *Hash) Size() int {
+ return len(h.Bytes())
+}
+
+// Format implements fmt.Formatter.
+// Hash supports the %v, %s, %v, %x, %X and %d format verbs.
+func (h Hash) Format(s fmt.State, c rune) {
+ hexb := make([]byte, 2+len(h)*2)
+ copy(hexb, "0x")
+ hex.Encode(hexb[2:], h[:])
+
+ switch c {
+ case 'x', 'X':
+ if !s.Flag('#') {
+ hexb = hexb[2:]
+ }
+ if c == 'X' {
+ hexb = bytes.ToUpper(hexb)
+ }
+ fallthrough
+ case 'v', 's':
+ s.Write(hexb)
+ case 'q':
+ q := []byte{'"'}
+ s.Write(q)
+ s.Write(hexb)
+ s.Write(q)
+ case 'd':
+ fmt.Fprint(s, ([len(h)]byte)(h))
+ default:
+ fmt.Fprintf(s, "%%!%c(hash=%x)", c, h)
+ }
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (h *Hash) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
+}
+
+func (h Hash) Equal(other Hash) bool {
+ return bytes.Equal(h.Bytes(), other.Bytes())
+}
+
+// HashDifference returns a new set which is the difference between a and b.
+func HashDifference(a, b []Hash) []Hash {
+ keep := make([]Hash, 0, len(a))
+
+ remove := make(map[Hash]struct{})
+ for _, hash := range b {
+ remove[hash] = struct{}{}
+ }
+
+ for _, hash := range a {
+ if _, ok := remove[hash]; !ok {
+ keep = append(keep, hash)
+ }
+ }
+
+ return keep
+}
+
+// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
+// Read to get a variable amount of data from the hash state. Read is faster than Sum
+// because it doesn't copy the internal state, but also modifies the internal state.
+type keccakState interface {
+ hash.Hash
+ Read([]byte) (int, error)
+}
+
+type Hasher struct {
+ Sha keccakState
+}
+
+var hasherPools = make(chan *Hasher, 128)
+
+func NewHasher() *Hasher {
+ var h *Hasher
+ select {
+ case h = <-hasherPools:
+ default:
+ h = &Hasher{Sha: sha3.NewLegacyKeccak256().(keccakState)}
+ }
+ return h
+}
+
+func ReturnHasherToPool(h *Hasher) {
+ select {
+ case hasherPools <- h:
+ default:
+ fmt.Printf("Allowing Hasher to be garbage collected, pool is full\n")
+ }
+}
+
+func HashData(data []byte) (Hash, error) {
+ h := NewHasher()
+ defer ReturnHasherToPool(h)
+ h.Sha.Reset()
+
+ _, err := h.Sha.Write(data)
+ if err != nil {
+ return Hash{}, err
+ }
+
+ var buf Hash
+ _, err = h.Sha.Read(buf[:])
+ if err != nil {
+ return Hash{}, err
+ }
+ return buf, nil
+}
+
+// Bytes2Hex returns the hexadecimal encoding of d.
+func Bytes2Hex(d []byte) string {
+ return hex.EncodeToString(d)
+}
+
+// HexToHash sets byte representation of s to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func HexToHash(s string) Hash { return BytesToHash(FromHex2Bytes(s)) }
+
+// FromHex returns the bytes represented by the hexadecimal string s.
+// s may be prefixed with "0x".
+func FromHex2Bytes(s string) []byte {
+ if has0xPrefix(s) {
+ s = s[2:]
+ }
+ if len(s)%2 == 1 {
+ s = "0" + s
+ }
+ return Hex2Bytes(s)
+}
+
+// Hashes is a slice of common.Hash, implementing sort.Interface
+type Hashes []Hash
+
+func (hashes Hashes) Len() int {
+ return len(hashes)
+}
+func (hashes Hashes) Less(i, j int) bool {
+ return bytes.Compare(hashes[i][:], hashes[j][:]) == -1
+}
+func (hashes Hashes) Swap(i, j int) {
+ hashes[i], hashes[j] = hashes[j], hashes[i]
+}
diff --git a/common/types/int256.go b/common/types/int256.go
new file mode 100644
index 0000000..d106794
--- /dev/null
+++ b/common/types/int256.go
@@ -0,0 +1,33 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "github.com/holiman/uint256"
+ "math/big"
+)
+
+func Int256Min(a, b *uint256.Int) *uint256.Int {
+ if a.Cmp(b) > 0 {
+ return b
+ }
+ return a
+}
+
+// BigToHash sets byte representation of b to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
diff --git a/common/types/signature.go b/common/types/signature.go
new file mode 100644
index 0000000..19cba22
--- /dev/null
+++ b/common/types/signature.go
@@ -0,0 +1,183 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "github.com/astranetworld/ast/common/hexutil"
+ "reflect"
+)
+
+const SignatureLength = 96
+const PublicKeyLength = 48
+
+var (
+ signatureT = reflect.TypeOf(Signature{})
+ publicKeyT = reflect.TypeOf(PublicKey{})
+)
+
+type Signature [SignatureLength]byte
+
+func (h Signature) Hex() string { return hexutil.Encode(h[:]) }
+func (h Signature) String() string {
+ return h.Hex()
+}
+
+func (h Signature) Size() int {
+ return SignatureLength
+}
+
+func (h Signature) Bytes() []byte { return h[:] }
+
+func (h *Signature) SetBytes(data []byte) error {
+ if len(data) != SignatureLength {
+ return fmt.Errorf("invalid bytes len %d", len(data))
+ }
+ copy(h[:], data[:SignatureLength])
+ return nil
+}
+
+func (h Signature) Marshal() ([]byte, error) {
+ return h.Bytes(), nil
+}
+
+func (h *Signature) Unmarshal(data []byte) error {
+ return h.SetBytes(data)
+}
+
+// Hash supports the %v, %s, %q, %x, %X and %d format verbs.
+func (h Signature) Format(s fmt.State, c rune) {
+ hexb := make([]byte, 2+len(h)*2)
+ copy(hexb, "0x")
+ hex.Encode(hexb[2:], h[:])
+
+ switch c {
+ case 'x', 'X':
+ if !s.Flag('#') {
+ hexb = hexb[2:]
+ }
+ if c == 'X' {
+ hexb = bytes.ToUpper(hexb)
+ }
+ fallthrough
+ case 'v', 's':
+ s.Write(hexb)
+ case 'q':
+ q := []byte{'"'}
+ s.Write(q)
+ s.Write(hexb)
+ s.Write(q)
+ case 'd':
+ fmt.Fprint(s, ([len(h)]byte)(h))
+ default:
+ fmt.Fprintf(s, "%%!%c(signature=%x)", c, h)
+ }
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (h *Signature) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Signature", input, h[:])
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (h *Signature) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(signatureT, input, h[:])
+}
+
+// MarshalText returns the hex representation of h.
+func (h Signature) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(h[:]).MarshalText()
+}
+
+type PublicKey [PublicKeyLength]byte
+
+func (h PublicKey) Hex() string { return hexutil.Encode(h[:]) }
+func (h PublicKey) String() string {
+ return h.Hex()
+}
+
+func (h PublicKey) Bytes() []byte {
+ return h[:]
+}
+
+func (h *PublicKey) SetBytes(b []byte) error {
+ if len(b) != PublicKeyLength {
+ return fmt.Errorf("invalid bytes len %d", len(b))
+ }
+
+ copy(h[:], b[:PublicKeyLength])
+ return nil
+}
+
+func (h PublicKey) Size() int {
+ return PublicKeyLength
+}
+
+func (h PublicKey) Marshal() ([]byte, error) {
+ return h.Bytes(), nil
+}
+
+func (h *PublicKey) Unmarshal(data []byte) error {
+ return h.SetBytes(data)
+}
+
+// MarshalText returns the hex representation of a.
+func (a PublicKey) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(a[:]).MarshalText()
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (a *PublicKey) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("PublicKey", input, a[:])
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (a *PublicKey) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(publicKeyT, input, a[:])
+}
+
+// Format implements fmt.Formatter.
+// supports the %v, %s, %q, %x, %X and %d format verbs.
+func (h PublicKey) Format(s fmt.State, c rune) {
+ hexb := make([]byte, 2+len(h)*2)
+ copy(hexb, "0x")
+ hex.Encode(hexb[2:], h[:])
+
+ switch c {
+ case 'x', 'X':
+ if !s.Flag('#') {
+ hexb = hexb[2:]
+ }
+ if c == 'X' {
+ hexb = bytes.ToUpper(hexb)
+ }
+ fallthrough
+ case 'v', 's':
+ s.Write(hexb)
+ case 'q':
+ q := []byte{'"'}
+ s.Write(q)
+ s.Write(hexb)
+ s.Write(q)
+ case 'd':
+ fmt.Fprint(s, ([len(h)]byte)(h))
+ default:
+ fmt.Fprintf(s, "%%!%c(publickey=%x)", c, h)
+ }
+}
diff --git a/common/types/size.go b/common/types/size.go
new file mode 100644
index 0000000..4fa0cb0
--- /dev/null
+++ b/common/types/size.go
@@ -0,0 +1,56 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package types
+
+import (
+ "fmt"
+)
+
+// StorageSize is a wrapper around a float value that supports user friendly
+// formatting.
+type StorageSize float64
+
+// String implements the stringer interface.
+func (s StorageSize) String() string {
+ if s > 1099511627776 {
+ return fmt.Sprintf("%.2f TiB", s/1099511627776)
+ } else if s > 1073741824 {
+ return fmt.Sprintf("%.2f GiB", s/1073741824)
+ } else if s > 1048576 {
+ return fmt.Sprintf("%.2f MiB", s/1048576)
+ } else if s > 1024 {
+ return fmt.Sprintf("%.2f KiB", s/1024)
+ } else {
+ return fmt.Sprintf("%.2f B", s)
+ }
+}
+
+// TerminalString implements log.TerminalStringer, formatting a string for console
+// output during logging.
+func (s StorageSize) TerminalString() string {
+ if s > 1099511627776 {
+ return fmt.Sprintf("%.2fTiB", s/1099511627776)
+ } else if s > 1073741824 {
+ return fmt.Sprintf("%.2fGiB", s/1073741824)
+ } else if s > 1048576 {
+ return fmt.Sprintf("%.2fMiB", s/1048576)
+ } else if s > 1024 {
+ return fmt.Sprintf("%.2fKiB", s/1024)
+ } else {
+ return fmt.Sprintf("%.2fB", s)
+ }
+}
diff --git a/common/types/ssz/sszuint64.go b/common/types/ssz/sszuint64.go
new file mode 100644
index 0000000..e417349
--- /dev/null
+++ b/common/types/ssz/sszuint64.go
@@ -0,0 +1,57 @@
+package ssztype
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ fssz "github.com/prysmaticlabs/fastssz"
+)
+
+// SSZUint64 --
+type SSZUint64 uint64
+
+// SizeSSZ --
+func (s *SSZUint64) SizeSSZ() int {
+ return 8
+}
+
+// MarshalSSZTo --
+func (s *SSZUint64) MarshalSSZTo(dst []byte) ([]byte, error) {
+ marshalled, err := s.MarshalSSZ()
+ if err != nil {
+ return nil, err
+ }
+ return append(dst, marshalled...), nil
+}
+
+// MarshalSSZ --
+func (s *SSZUint64) MarshalSSZ() ([]byte, error) {
+ marshalled := fssz.MarshalUint64([]byte{}, uint64(*s))
+ return marshalled, nil
+}
+
+// UnmarshalSSZ --
+func (s *SSZUint64) UnmarshalSSZ(buf []byte) error {
+ if len(buf) != s.SizeSSZ() {
+ return fmt.Errorf("expected buffer of length %d received %d", s.SizeSSZ(), len(buf))
+ }
+ *s = SSZUint64(fssz.UnmarshallUint64(buf))
+ return nil
+}
+
+// HashTreeRoot --
+func (s *SSZUint64) HashTreeRoot() ([32]byte, error) {
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, uint64(*s))
+ var root [32]byte
+ copy(root[:], buf)
+ return root, nil
+}
+
+// HashTreeRootWith --
+func (s *SSZUint64) HashTreeRootWith(hh *fssz.Hasher) error {
+ indx := hh.Index()
+ hh.PutUint64(uint64(*s))
+ hh.Merkleize(indx)
+ return nil
+}
diff --git a/common/u256/big.go b/common/u256/big.go
new file mode 100644
index 0000000..0abc9b7
--- /dev/null
+++ b/common/u256/big.go
@@ -0,0 +1,32 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+package u256
+
+import (
+ "github.com/holiman/uint256"
+)
+
+// Common big integers often used
+var (
+ Num0 = uint256.NewInt(0)
+ Num1 = uint256.NewInt(1)
+ Num2 = uint256.NewInt(2)
+ Num4 = uint256.NewInt(4)
+ Num8 = uint256.NewInt(8)
+ Num27 = uint256.NewInt(27)
+ Num32 = uint256.NewInt(32)
+ Num35 = uint256.NewInt(35)
+)
diff --git a/conf/account_config.go b/conf/account_config.go
new file mode 100644
index 0000000..62b771c
--- /dev/null
+++ b/conf/account_config.go
@@ -0,0 +1,21 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type AccountConfig struct {
+ PasswordFile string `json:"password_file" yaml:"password_file"`
+}
diff --git a/conf/config.go b/conf/config.go
new file mode 100644
index 0000000..5066f91
--- /dev/null
+++ b/conf/config.go
@@ -0,0 +1,71 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import (
+ "bufio"
+ "fmt"
+ "github.com/astranetworld/ast/params"
+ "os"
+
+ "gopkg.in/yaml.v2"
+)
+
+type Config struct {
+ NodeCfg NodeConfig `json:"node" yaml:"node"`
+ NetworkCfg NetWorkConfig `json:"network" yaml:"network"`
+ LoggerCfg LoggerConfig `json:"logger" yaml:"logger"`
+ DatabaseCfg DatabaseConfig `json:"database" yaml:"database"`
+ PprofCfg PprofConfig `json:"pprof" yaml:"pprof"`
+ ChainCfg *params.ChainConfig `json:"chain" yaml:"chain"`
+ AccountCfg AccountConfig `json:"account" yaml:"account"`
+ MetricsCfg MetricsConfig `json:"metrics" yaml:"metrics"`
+ P2PCfg *P2PConfig `json:"p2p" yaml:"p2p"`
+ // Gas Price Oracle options
+ GPO GpoConfig `json:"gpo" yaml:"gpo"`
+ Miner MinerConfig `json:"miner"`
+}
+
+func SaveConfigToFile(file string, config Config) error {
+ if len(file) == 0 {
+ file = "./config2.yaml"
+ }
+
+ fd, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
+ if err != nil {
+ //log.Errorf("filed open file %v, err %v", file, err)
+ return err
+ }
+ defer fd.Close()
+ return yaml.NewEncoder(fd).Encode(config)
+ //return toml.NewEncoder(fd).Encode(blockchain)
+}
+
+func LoadConfigFromFile(file string, config *Config) error {
+ if len(file) <= 0 {
+ return fmt.Errorf("failed to load blockchain from file, file is nil")
+ }
+ //_, err := toml.DecodeFile(file, blockchain)
+ fd, err := os.Open(file)
+ if err != nil {
+ return err
+ }
+ defer fd.Close()
+ reader := bufio.NewReader(fd)
+ //return toml.NewDecoder(reader).Decode(blockchain)
+ return yaml.NewDecoder(reader).Decode(config)
+}
diff --git a/conf/consensus_config.go b/conf/consensus_config.go
new file mode 100644
index 0000000..0adf595
--- /dev/null
+++ b/conf/consensus_config.go
@@ -0,0 +1,50 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import "math/big"
+
+type ConsensusConfig struct {
+ EngineName string `json:"name" yaml:"name"`
+ Etherbase string `json:"etherbase" yaml:"etherbase"`
+ Period uint64 `json:"period" yaml:"period"`
+ APoa *APoaConfig `json:"apoa" yaml:"poa"`
+ GasFloor uint64 `json:"gasFloor" yaml:"gasFloor"` // Target gas floor for mined blocks.
+ GasCeil uint64 `json:"gasCeil" yaml:"gasCeil"` // Target gas ceiling for mined blocks.
+ APos *APosConfig `json:"apos" yaml:"pos"`
+}
+
+type APoaConfig struct {
+ Epoch uint64 `json:"epoch" yaml:"epoch"`
+ CheckpointInterval uint64 `json:"checkpointInterval" yaml:"checkpointInterval"`
+ InmemorySnapshots int `json:"inmemorySnapshots" yaml:"inmemorySnapshots"`
+ InmemorySignatures int `json:"inmemorySignatures" yaml:"inmemorySignatures"`
+ InMemory bool `json:"inMemory" yaml:"inMemory"`
+}
+
+type APosConfig struct {
+ Epoch uint64 `json:"epoch" yaml:"epoch"`
+ CheckpointInterval uint64 `json:"checkpointInterval" yaml:"checkpointInterval"`
+ InmemorySnapshots int `json:"inmemorySnapshots" yaml:"inmemorySnapshots"`
+ InmemorySignatures int `json:"inmemorySignatures" yaml:"inmemorySignatures"`
+ InMemory bool `json:"inMemory" yaml:"inMemory"`
+ RewardEpoch uint64 `json:"rewardEpoch" yaml:"rewardEpoch"`
+ RewardLimit *big.Int `json:"rewardLimit" yaml:"rewardLimit"`
+
+ DepositContract string `json:"depositContract" yaml:"depositContract"` // Deposit contract
+ DepositNFTContract string `json:"depositNFTContract" yaml:"depositNFTContract"` // Deposit NFT contract
+}
diff --git a/conf/databae_config.go b/conf/databae_config.go
new file mode 100644
index 0000000..b53012d
--- /dev/null
+++ b/conf/databae_config.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type DatabaseConfig struct {
+ DBType string `json:"db_type" yaml:"db_type"`
+ DBPath string `json:"path" yaml:"path"`
+ DBName string `json:"name" yaml:"name"`
+ SubDB []string `json:"sub_name" yaml:"sub_name"`
+ Debug bool `json:"debug" yaml:"debug"`
+ IsMem bool `json:"memory" yaml:"memory"`
+ MaxDB uint64 `json:"max_db" yaml:"max_db"`
+ MaxReaders uint64 `json:"max_readers" yaml:"max_readers"`
+}
diff --git a/conf/gasprice.go b/conf/gasprice.go
new file mode 100644
index 0000000..61b9455
--- /dev/null
+++ b/conf/gasprice.go
@@ -0,0 +1,57 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import (
+ "github.com/astranetworld/ast/params"
+ "math/big"
+)
+
+var (
+ DefaultMaxPrice = big.NewInt(500 * params.GWei)
+ DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+)
+
+type GpoConfig struct {
+ Blocks int
+ Percentile int
+ MaxHeaderHistory int
+ MaxBlockHistory int
+ Default *big.Int `toml:",omitempty"`
+ MaxPrice *big.Int `toml:",omitempty"`
+ IgnorePrice *big.Int `toml:",omitempty"`
+}
+
+// FullNodeGPO contains default gasprice oracle settings for full node.
+var FullNodeGPO = GpoConfig{
+ Blocks: 20,
+ Percentile: 60,
+ MaxHeaderHistory: 1024,
+ MaxBlockHistory: 1024,
+ MaxPrice: DefaultMaxPrice,
+ IgnorePrice: DefaultIgnorePrice,
+}
+
+// LightClientGPO contains default gasprice oracle settings for light client.
+var LightClientGPO = GpoConfig{
+ Blocks: 2,
+ Percentile: 60,
+ MaxHeaderHistory: 300,
+ MaxBlockHistory: 5,
+ MaxPrice: DefaultMaxPrice,
+ IgnorePrice: DefaultIgnorePrice,
+}
diff --git a/conf/genesis_config.go b/conf/genesis_config.go
new file mode 100644
index 0000000..9279acb
--- /dev/null
+++ b/conf/genesis_config.go
@@ -0,0 +1,57 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import (
+ "github.com/astranetworld/ast/common/types"
+ "github.com/holiman/uint256"
+
+ "github.com/astranetworld/ast/params"
+)
+
+type Genesis struct {
+ Config *params.ChainConfig `json:"config" yaml:"config"`
+ Nonce uint64 `json:"nonce"`
+ Timestamp uint64 `json:"timestamp"`
+ ExtraData []byte `json:"extraData"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *uint256.Int `json:"difficulty" gencodec:"required"`
+ Mixhash types.Hash `json:"mixHash"`
+ Coinbase types.Address `json:"coinbase"`
+
+ //Engine *ConsensusConfig `json:"engine" yaml:"engine"`
+ Miners []string `json:"miners" yaml:"miners"`
+ Alloc GenesisAlloc `json:"alloc" yaml:"alloc" gencodec:"required"`
+
+ // These fields are used for consensus tests. Please don't use them
+ // in actual genesis blocks.
+ Number uint64 `json:"number"`
+ GasUsed uint64 `json:"gasUsed"`
+ ParentHash types.Hash `json:"parentHash"`
+ BaseFee *uint256.Int `json:"baseFeePerGas"`
+}
+
+// GenesisAlloc specifies the initial state that is part of the genesis block.
+type GenesisAlloc map[types.Address]GenesisAccount
+
+type GenesisAccount struct {
+ //Address string `json:"address" toml:"address"`
+ Balance string `json:"balance"`
+ Code []byte `json:"code,omitempty"`
+ Storage map[types.Hash]types.Hash `json:"storage,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+}
diff --git a/conf/logger_config.go b/conf/logger_config.go
new file mode 100644
index 0000000..ea310bb
--- /dev/null
+++ b/conf/logger_config.go
@@ -0,0 +1,26 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type LoggerConfig struct {
+ LogFile string `json:"name" yaml:"name"`
+ Level string `json:"level" yaml:"level"`
+ MaxSize int `json:"max_size" yaml:"max_size"`
+ MaxBackups int `json:"max_count" yaml:"max_count"`
+ MaxAge int `json:"max_day" yaml:"max_day"`
+ Compress bool `json:"compress" yaml:"compress"`
+}
diff --git a/conf/metrics_config.go b/conf/metrics_config.go
new file mode 100644
index 0000000..6bfb360
--- /dev/null
+++ b/conf/metrics_config.go
@@ -0,0 +1,23 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type MetricsConfig struct {
+ Enable bool `json:"enable" yaml:"enable"`
+ Port int `json:"port" yaml:"port"`
+ HTTP string `json:"http" yaml:"http"`
+}
diff --git a/conf/miner.go b/conf/miner.go
new file mode 100644
index 0000000..fe5cc4c
--- /dev/null
+++ b/conf/miner.go
@@ -0,0 +1,29 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import (
+ "math/big"
+ "time"
+)
+
+type MinerConfig struct {
+ Etherbase string // Public address for block mining rewards
+ GasCeil uint64 // Target gas ceiling for mined blocks.
+ GasPrice *big.Int // Minimum gas price for mining a transaction
+ Recommit time.Duration // The time interval for miner to re-create mining work
+}
diff --git a/conf/node_config.go b/conf/node_config.go
new file mode 100644
index 0000000..9389f6b
--- /dev/null
+++ b/conf/node_config.go
@@ -0,0 +1,138 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+import (
+ "os"
+ "path/filepath"
+)
+
+const (
+ datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore
+)
+
+type NodeConfig struct {
+ NodePrivate string `json:"private" yaml:"private"`
+ HTTP bool `json:"http" yaml:"http" `
+ HTTPHost string `json:"http_host" yaml:"http_host" `
+ HTTPPort string `json:"http_port" yaml:"http_port"`
+ HTTPApi string `json:"http_api" yaml:"http_api"`
+ // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
+ // clients. Please be aware that CORS is a browser enforced security, it's fully
+ // useless for custom HTTP clients.
+ HTTPCors string `json:"http_cors" yaml:"http_cors"`
+
+ WS bool `json:"ws" yaml:"ws" `
+ WSHost string `json:"ws_host" yaml:"ws_host" `
+ WSPort string `json:"ws_port" yaml:"ws_port"`
+ WSApi string `json:"ws_api" yaml:"ws_api"`
+ // WSOrigins is the list of domain to accept websocket requests from. Please be
+ // aware that the server can only act upon the HTTP request the client sends and
+ // cannot verify the validity of the request header.
+ WSOrigins string `toml:",omitempty"`
+ IPCPath string `json:"ipc_path" yaml:"ipc_path"`
+ DataDir string `json:"data_dir" yaml:"data_dir"`
+ MinFreeDiskSpace int `json:"min_free_disk_space" yaml:"min_free_disk_space"`
+ Chain string `json:"chain" yaml:"chain"`
+ Miner bool `json:"miner" yaml:"miner"`
+
+ AuthRPC bool `json:"auth_rpc" yaml:"auth_rpc"`
+ // AuthAddr is the listening address on which authenticated APIs are provided.
+ AuthAddr string `json:"auth_addr" yaml:"auth_addr"`
+
+ // AuthPort is the port number on which authenticated APIs are provided.
+ AuthPort int `json:"auth_port" yaml:"auth_port"`
+
+ // AuthVirtualHosts is the list of virtual hostnames which are allowed on incoming requests
+ // for the authenticated api. This is by default {'localhost'}.
+ AuthVirtualHosts []string `json:"auth_virtual_hosts" yaml:"auth_virtual_hosts"`
+
+ // JWTSecret is the path to the hex-encoded jwt secret.
+ JWTSecret string `json:"jwt_secret" yaml:"jwt_secret"`
+
+ // KeyStoreDir is the file system folder that contains private keys. The directory can
+ // be specified as a relative path, in which case it is resolved relative to the
+ // current directory.
+ //
+ // If KeyStoreDir is empty, the default location is the "keystore" subdirectory of
+ // DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory
+ // is created by New and destroyed when the node is stopped.
+ KeyStoreDir string `json:"key_store_dir" yaml:"key_store_dir"`
+
+ // ExternalSigner specifies an external URI for a clef-type signer
+ ExternalSigner string `json:"external_signer" yaml:"external_signer"`
+
+ // UseLightweightKDF lowers the memory and CPU requirements of the key store
+ // scrypt KDF at the expense of security.
+ UseLightweightKDF bool `json:"use_lightweight_kdf" yaml:"use_lightweight_kdf"`
+
+ // InsecureUnlockAllowed allows user to unlock accounts in unsafe http environment.
+ InsecureUnlockAllowed bool `json:"insecure_unlock_allowed" yaml:"insecure_unlock_allowed"`
+
+ PasswordFile string `json:"password_file" yaml:"password_file"`
+}
+
+// KeyDirConfig determines the settings for keydirectory
+func (c *NodeConfig) KeyDirConfig() (string, error) {
+ var (
+ keydir string
+ err error
+ )
+ switch {
+ case filepath.IsAbs(c.KeyStoreDir):
+ keydir = c.KeyStoreDir
+ case c.DataDir != "":
+ if c.KeyStoreDir == "" {
+ keydir = filepath.Join(c.DataDir, datadirDefaultKeyStore)
+ } else {
+ keydir, err = filepath.Abs(c.KeyStoreDir)
+ }
+ case c.KeyStoreDir != "":
+ keydir, err = filepath.Abs(c.KeyStoreDir)
+ }
+ return keydir, err
+}
+
+// getKeyStoreDir retrieves the key directory and will create
+// and ephemeral one if necessary.
+func getKeyStoreDir(conf *NodeConfig) (string, bool, error) {
+ keydir, err := conf.KeyDirConfig()
+ if err != nil {
+ return "", false, err
+ }
+ isEphemeral := false
+ if keydir == "" {
+ // There is no datadir.
+ keydir, err = os.MkdirTemp("", "astranet-keystore")
+ isEphemeral = true
+ }
+
+ if err != nil {
+ return "", false, err
+ }
+ if err := os.MkdirAll(keydir, 0700); err != nil {
+ return "", false, err
+ }
+
+ return keydir, isEphemeral, nil
+}
+
+// ExtRPCEnabled returns the indicator whether node enables the external
+// RPC(http, ws or graphql).
+func (c *NodeConfig) ExtRPCEnabled() bool {
+ return c.HTTPHost != "" || c.WSHost != ""
+}
diff --git a/conf/p2p_config.go b/conf/p2p_config.go
new file mode 100644
index 0000000..a7c1204
--- /dev/null
+++ b/conf/p2p_config.go
@@ -0,0 +1,54 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type NetWorkConfig struct {
+ ListenersAddress []string `json:"listeners" yaml:"listeners"`
+ BootstrapPeers []string `json:"bootstraps" yaml:"bootstraps"`
+ LocalPeerKey string `json:"private" yaml:"network_private"`
+ Bootstrapped bool `json:"discover" yaml:"discover"`
+}
+
+type P2PConfig struct {
+ NoDiscovery bool `json:"no_discovery" yaml:"no_discovery"`
+ EnableUPnP bool `json:"enable_upnp" yaml:"enable_upnp"`
+ StaticPeerID bool `json:"static_peer_id" yaml:"static_peer_id"`
+ StaticPeers []string `json:"static_peers" yaml:"static_peers"`
+ BootstrapNodeAddr []string `json:"bootstrap_node_addr" yaml:"bootstrap_node_addr"`
+ Discv5BootStrapAddr []string `json:"discv5_bootstrap_addr" yaml:"discv5_bootstrap_addr"`
+ RelayNodeAddr string `json:"relay_node_addr" yaml:"relay_node_addr"`
+ LocalIP string `json:"local_ip" yaml:"local_ip"`
+ HostAddress string `json:"host_address" yaml:"host_address"`
+ HostDNS string `json:"host_dns" yaml:"host_dns"`
+ PrivateKey string `json:"private_key" yaml:"private_key"`
+ DataDir string `json:"data_dir" yaml:"data_dir"`
+ MetaDataDir string `json:"metadata_dir" yaml:"metadata_dir"`
+ TCPPort int `json:"tcp_port" yaml:"tcp_port"`
+ UDPPort int `json:"udp_port" yaml:"udp_port"`
+ MaxPeers int `json:"max_peers" yaml:"max_peers"`
+ AllowListCIDR string `json:"allow_list_cidr" yaml:"allow_list_cidr"`
+ DenyListCIDR []string `json:"deny_list_cidr" yaml:"deny_list_cidr"`
+ MinSyncPeers int `json:"min_sync_peers" yaml:"min_sync_peers"`
+
+ P2PLimit *P2PLimit
+}
+
+type P2PLimit struct {
+ BlockBatchLimit int `json:"block_batch_limit" yaml:"block_batch_limit"`
+ BlockBatchLimitBurstFactor int `json:"block_batch_limit_burst_factor" yaml:"block_batch_limit_burst_factor"`
+ BlockBatchLimiterPeriod int `json:"block_batch_limiter_period" yaml:"block_batch_limiter_period"`
+}
diff --git a/conf/pprof_config.go b/conf/pprof_config.go
new file mode 100644
index 0000000..842b6ce
--- /dev/null
+++ b/conf/pprof_config.go
@@ -0,0 +1,25 @@
+// Copyright 2022 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package conf
+
+type PprofConfig struct {
+ MaxCpu int `json:"cpu" yaml:"cpu"`
+ Port int `json:"port" yaml:"port"`
+ TraceMutex bool `json:"trace_mutex" yaml:"trace_mutex"`
+ TraceBlock bool `json:"trace_block" yaml:"trace_block"`
+ Pprof bool `json:"pprof" yaml:"pprof"`
+}
diff --git a/console/prompt/prompter.go b/console/prompt/prompter.go
new file mode 100644
index 0000000..49640b4
--- /dev/null
+++ b/console/prompt/prompter.go
@@ -0,0 +1,172 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package prompt
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/peterh/liner"
+)
+
+// Stdin holds the stdin line reader (also using stdout for printing prompts).
+// Only this reader may be used for input because it keeps an internal buffer.
+var Stdin = newTerminalPrompter()
+
+// UserPrompter defines the methods needed by the console to prompt the user for
+// various types of inputs.
+type UserPrompter interface {
+ // PromptInput displays the given prompt to the user and requests some textual
+ // data to be entered, returning the input of the user.
+ PromptInput(prompt string) (string, error)
+
+ // PromptPassword displays the given prompt to the user and requests some textual
+ // data to be entered, but one which must not be echoed out into the terminal.
+ // The method returns the input provided by the user.
+ PromptPassword(prompt string) (string, error)
+
+ // PromptConfirm displays the given prompt to the user and requests a boolean
+ // choice to be made, returning that choice.
+ PromptConfirm(prompt string) (bool, error)
+
+ // SetHistory sets the input scrollback history that the prompter will allow
+ // the user to scroll back to.
+ SetHistory(history []string)
+
+ // AppendHistory appends an entry to the scrollback history. It should be called
+ // if and only if the prompt to append was a valid command.
+ AppendHistory(command string)
+
+ // ClearHistory clears the entire history
+ ClearHistory()
+
+ // SetWordCompleter sets the completion function that the prompter will call to
+ // fetch completion candidates when the user presses tab.
+ SetWordCompleter(completer WordCompleter)
+}
+
+// WordCompleter takes the currently edited line with the cursor position and
+// returns the completion candidates for the partial word to be completed. If
+// the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello,
+// wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world",
+// "Word"}, "!!!") to have "Hello, world!!!".
+type WordCompleter func(line string, pos int) (string, []string, string)
+
+// terminalPrompter is a UserPrompter backed by the liner package. It supports
+// prompting the user for various input, among others for non-echoing password
+// input.
+type terminalPrompter struct {
+ *liner.State
+ warned bool
+ supported bool
+ normalMode liner.ModeApplier
+ rawMode liner.ModeApplier
+}
+
+// newTerminalPrompter creates a liner based user input prompter working off the
+// standard input and output streams.
+func newTerminalPrompter() *terminalPrompter {
+ p := new(terminalPrompter)
+ // Get the original mode before calling NewLiner.
+ // This is usually regular "cooked" mode where characters echo.
+ normalMode, _ := liner.TerminalMode()
+ // Turn on liner. It switches to raw mode.
+ p.State = liner.NewLiner()
+ rawMode, err := liner.TerminalMode()
+ if err != nil || !liner.TerminalSupported() {
+ p.supported = false
+ } else {
+ p.supported = true
+ p.normalMode = normalMode
+ p.rawMode = rawMode
+ // Switch back to normal mode while we're not prompting.
+ normalMode.ApplyMode()
+ }
+ p.SetCtrlCAborts(true)
+ p.SetTabCompletionStyle(liner.TabPrints)
+ p.SetMultiLineMode(true)
+ return p
+}
+
+// PromptInput displays the given prompt to the user and requests some textual
+// data to be entered, returning the input of the user.
+func (p *terminalPrompter) PromptInput(prompt string) (string, error) {
+ if p.supported {
+ p.rawMode.ApplyMode()
+ defer p.normalMode.ApplyMode()
+ } else {
+ // liner tries to be smart about printing the prompt
+ // and doesn't print anything if input is redirected.
+ // Un-smart it by printing the prompt always.
+ fmt.Print(prompt)
+ prompt = ""
+ defer fmt.Println()
+ }
+ return p.State.Prompt(prompt)
+}
+
+// PromptPassword displays the given prompt to the user and requests some textual
+// data to be entered, but one which must not be echoed out into the terminal.
+// The method returns the input provided by the user.
+func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) {
+ if p.supported {
+ p.rawMode.ApplyMode()
+ defer p.normalMode.ApplyMode()
+ return p.State.PasswordPrompt(prompt)
+ }
+ if !p.warned {
+ fmt.Println("!! Unsupported terminal, password will be echoed.")
+ p.warned = true
+ }
+ // Just as in Prompt, handle printing the prompt here instead of relying on liner.
+ fmt.Print(prompt)
+ passwd, err = p.State.Prompt("")
+ fmt.Println()
+ return passwd, err
+}
+
+// PromptConfirm displays the given prompt to the user and requests a boolean
+// choice to be made, returning that choice.
+func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) {
+ input, err := p.Prompt(prompt + " [y/n] ")
+ if len(input) > 0 && strings.EqualFold(input[:1], "y") {
+ return true, nil
+ }
+ return false, err
+}
+
+// SetHistory sets the input scrollback history that the prompter will allow
+// the user to scroll back to.
+func (p *terminalPrompter) SetHistory(history []string) {
+ p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n")))
+}
+
+// AppendHistory appends an entry to the scrollback history.
+func (p *terminalPrompter) AppendHistory(command string) {
+ p.State.AppendHistory(command)
+}
+
+// ClearHistory clears the entire history
+func (p *terminalPrompter) ClearHistory() {
+ p.State.ClearHistory()
+}
+
+// SetWordCompleter sets the completion function that the prompter will call to
+// fetch completion candidates when the user presses tab.
+func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) {
+ p.State.SetWordCompleter(liner.WordCompleter(completer))
+}
diff --git a/contracts/deposit/AMT/AMT.go b/contracts/deposit/AMT/AMT.go
new file mode 100644
index 0000000..3408141
--- /dev/null
+++ b/contracts/deposit/AMT/AMT.go
@@ -0,0 +1,96 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package amtdeposit
+
+import (
+ "bytes"
+ "embed"
+ "github.com/astranetworld/ast/accounts/abi"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "github.com/holiman/uint256"
+ "github.com/pkg/errors"
+ "math/big"
+)
+
+//go:embed abi.json
+var abiJson embed.FS
+var contractAbi abi.ABI
+var depositSignature = crypto.Keccak256Hash([]byte("DepositEvent(bytes,uint256,bytes)"))
+var withdrawnSignature = crypto.Keccak256Hash([]byte("WithdrawnEvent(uint256)"))
+
+func init() {
+ var (
+ depositAbiCode []byte
+ err error
+ )
+ if depositAbiCode, err = abiJson.ReadFile("abi.json"); err != nil {
+ panic("Could not open abi.json")
+ }
+
+ if contractAbi, err = abi.JSON(bytes.NewReader(depositAbiCode)); err != nil {
+ panic("unable to parse AMT deposit contract abi")
+ }
+}
+
+type Contract struct {
+}
+
+func (Contract) WithdrawnSignature() types.Hash {
+ return withdrawnSignature
+}
+
+func (Contract) DepositSignature() types.Hash {
+ return depositSignature
+}
+
+func (c Contract) IsDepositAction(sigdata [4]byte) bool {
+ var (
+ method *abi.Method
+ err error
+ )
+ if method, err = contractAbi.MethodById(sigdata[:]); err != nil {
+ return false
+ }
+
+ if !bytes.Equal(method.ID, contractAbi.Methods["deposit"].ID) {
+ return false
+ }
+ return true
+}
+
+func (Contract) UnpackDepositLogData(data []byte) (publicKey []byte, signature []byte, depositAmount *uint256.Int, err error) {
+ var (
+ unpackedLogs []interface{}
+ overflow bool
+ )
+ //
+ if unpackedLogs, err = contractAbi.Unpack("DepositEvent", data); err != nil {
+ err = errors.Wrap(err, "unable to unpack logs")
+ return
+ }
+ //
+ if depositAmount, overflow = uint256.FromBig(unpackedLogs[1].(*big.Int)); overflow {
+ err = errors.New("unable to unpack amount")
+ return
+ }
+ publicKey, signature = unpackedLogs[0].([]byte), unpackedLogs[2].([]byte)
+ log.Debug("unpacked DepositEvent Logs", "publicKey", hexutil.Encode(unpackedLogs[0].([]byte)), "signature", hexutil.Encode(unpackedLogs[2].([]byte)), "message", hexutil.Encode(depositAmount.Bytes()))
+ return
+}
diff --git a/contracts/deposit/AMT/Deposit.sol b/contracts/deposit/AMT/Deposit.sol
new file mode 100644
index 0000000..dd46f5e
--- /dev/null
+++ b/contracts/deposit/AMT/Deposit.sol
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+
+import "@openzeppelin/contracts@v4.9.0/access/Ownable.sol";
+import "./IDeposit.sol";
+
+
+contract Deposit is Ownable, IDeposit {
+ // using Address for address payable;
+
+ mapping(address => uint256) private deposits;
+ mapping(address => uint64) private depositTime;
+ uint64 private depositLockingTime;
+ uint64 private fiftyDepositLimit;
+ uint64 private oneHundredDepositLimit;
+ uint64 private fiveHundredDepositLimit;
+ //
+ uint256 private allDeposits = 0;
+ uint64 private fiftyDepositCount = 0;
+ uint64 private oneHundredDepositCount = 0;
+ uint64 private fiveHundredDepositCount = 0;
+
+ uint256 constant private fiftyDeposit = 50 ether;
+ uint256 constant private oneHundredDeposit = 100 ether;
+ uint256 constant private fiveHundredDeposit = 500 ether;
+
+
+ modifier onlyOperator() {
+ require(msg.sender == owner(), "Caller is not Operator");
+ _;
+ }
+
+ modifier onlyDeposit() {
+ require(deposits[msg.sender] > 0, "Do not have deposit");
+ _;
+ }
+
+ constructor (uint64 _depositLockingTime, uint64 _fiftyDepositLimit, uint64 _oneHundredDepositLimit, uint64 _fiveHundredDepositLimit) Ownable() {
+ depositLockingTime = _depositLockingTime;
+ fiftyDepositLimit = _fiftyDepositLimit;
+ oneHundredDepositLimit = _oneHundredDepositLimit;
+ fiveHundredDepositLimit = _fiveHundredDepositLimit;
+ }
+
+ function depositsOf(address payee) public view override returns (uint256) {
+ return deposits[payee];
+ }
+
+ function depositUnlockingTimestamp(address payee) public view override returns (uint64) {
+ require(depositTime[payee] > 0, "DepositContract: do not have deposit");
+ return depositTime[payee] + depositLockingTime;
+ }
+
+ function getDepositCount() public view override returns (uint256) {
+ return allDeposits;
+ }
+
+ function depositAllowed(uint256 amount) public view returns (bool) {
+ require(deposits[msg.sender] == 0, "DepositContract: you already deposited");
+ require(amount == oneHundredDeposit || amount == fiveHundredDeposit || amount == fiftyDeposit, "DepositContract: payee is not allowed to deposit");
+ //
+ if (amount == fiftyDeposit) {
+ require(fiftyDepositLimit > fiftyDepositCount, "10 ast Deposit Limit has been reached");
+ }
+ //
+ if (amount == oneHundredDeposit) {
+ require(oneHundredDepositLimit > oneHundredDepositCount , "100 ast Deposit Limit has been reached");
+ }
+ //
+ if (amount == fiveHundredDeposit) {
+ require(fiveHundredDepositLimit > fiveHundredDepositCount, "500 ast Deposit Limit has been reached");
+ }
+ return true;
+ }
+
+ function deposit(bytes calldata pubkey, bytes calldata signature) public payable virtual override {
+
+ uint256 amount = msg.value;
+ require(depositAllowed(amount), "DepositContract: payee is not allowed to deposit");
+ require(pubkey.length == 48, "DepositContract: invalid pubkey length");
+ require(signature.length == 96, "DepositContract: invalid signature length");
+
+ //
+ deposits[msg.sender] = amount;
+ depositTime[msg.sender] = uint64(block.timestamp);
+ allDeposits += amount;
+ //
+ if (amount == fiftyDeposit) {
+ fiftyDepositCount++;
+ }
+ //
+ if (amount == oneHundredDeposit) {
+ oneHundredDepositCount++;
+ }
+ //
+ if (amount == fiveHundredDeposit) {
+ fiveHundredDepositCount++;
+ }
+
+ emit DepositEvent(pubkey, amount, signature);
+ }
+
+ function withdrawalAllowed(uint64 timestamp) public view returns (bool) {
+ require(deposits[msg.sender] > 0 && depositTime[msg.sender] > 0, "Do not have deposits");
+ require(address(this).balance >= deposits[msg.sender], "Insufficient balance");
+ require(depositTime[msg.sender] + depositLockingTime <= timestamp, "Deposit is locking");
+ return true;
+ }
+
+ function withdraw() public payable virtual override onlyDeposit {
+
+ require(withdrawalAllowed(uint64(block.timestamp)), "DepositContract: payee is not allowed to deposit");
+ uint256 amount = deposits[msg.sender];
+ //
+ sendValue(payable(msg.sender), amount);
+ delete deposits[msg.sender];
+ delete depositTime[msg.sender];
+
+ //
+ if (amount == fiftyDeposit) {
+ fiftyDepositCount--;
+ }
+ //
+ if (amount == oneHundredDeposit) {
+ oneHundredDepositCount--;
+ }
+ //
+ if (amount == fiveHundredDeposit) {
+ fiveHundredDepositCount--;
+ }
+
+ allDeposits -= amount;
+ emit WithdrawnEvent(amount);
+ }
+
+ function addDepositLimit(uint256 amount, uint64 limit) public virtual onlyOperator {
+ //
+ if (amount == fiftyDeposit) {
+ fiftyDepositLimit += limit;
+ }
+ //
+ if (amount == oneHundredDeposit) {
+ oneHundredDepositLimit += limit;
+ }
+ //
+ if (amount == fiveHundredDeposit) {
+ fiveHundredDepositLimit += limit;
+ }
+ }
+ function getDepositLimit(uint256 amount) public view returns(uint64) {
+ //
+ if (amount == fiftyDeposit) {
+ return fiftyDepositLimit;
+ }
+ //
+ if (amount == oneHundredDeposit) {
+ return oneHundredDepositLimit;
+ }
+ //
+ if (amount == fiveHundredDeposit) {
+ return fiveHundredDepositLimit;
+ }
+
+ return 0;
+ }
+
+ function getDepositRemain() public view returns(uint64, uint64, uint64) {
+ uint64 fiftyDepositRemain = fiftyDepositLimit - fiftyDepositCount;
+ uint64 oneHundredDepositRemain = oneHundredDepositLimit - oneHundredDepositCount;
+ uint64 fiveHundredDepositRemain = fiveHundredDepositLimit - fiveHundredDepositCount;
+ return (fiftyDepositRemain, oneHundredDepositRemain, fiveHundredDepositRemain);
+ }
+
+ function sendValue(address payable recipient, uint256 amount) private {
+ require(address(this).balance >= amount, "Insufficient balance");
+ (bool success, ) = recipient.call{value: amount}("");
+ require(success, "Unable to send value, recipient may have reverted");
+ }
+
+}
+
+
diff --git a/contracts/deposit/AMT/IDeposit.sol b/contracts/deposit/AMT/IDeposit.sol
new file mode 100644
index 0000000..ed3011f
--- /dev/null
+++ b/contracts/deposit/AMT/IDeposit.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+
+interface IDeposit{
+ event DepositEvent(
+ bytes pubkey,
+ uint256 weiAmount,
+ bytes signature
+ );
+ event WithdrawnEvent(uint256 weiAmount);
+
+ function deposit(
+ bytes calldata pubkey,
+ bytes calldata signature
+ ) external payable;
+
+ function withdraw() external payable;
+ function depositsOf(address payee) external view returns (uint256);
+ function depositUnlockingTimestamp(address payee) external view returns (uint64);
+ function getDepositCount() external view returns (uint256);
+}
\ No newline at end of file
diff --git a/contracts/deposit/AMT/abi.json b/contracts/deposit/AMT/abi.json
new file mode 100644
index 0000000..9e41870
--- /dev/null
+++ b/contracts/deposit/AMT/abi.json
@@ -0,0 +1,292 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint64",
+ "name": "limit",
+ "type": "uint64"
+ }
+ ],
+ "name": "addDepositLimit",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes",
+ "name": "pubkey",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ }
+ ],
+ "name": "deposit",
+ "outputs": [],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint64",
+ "name": "_depositLockingTime",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint64",
+ "name": "_fiftyDepositLimit",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint64",
+ "name": "_oneHundredDepositLimit",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint64",
+ "name": "_fiveHundredDepositLimit",
+ "type": "uint64"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "pubkey",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "weiAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ }
+ ],
+ "name": "DepositEvent",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "withdraw",
+ "outputs": [],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "weiAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "WithdrawnEvent",
+ "type": "event"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "depositAllowed",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "payee",
+ "type": "address"
+ }
+ ],
+ "name": "depositsOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "payee",
+ "type": "address"
+ }
+ ],
+ "name": "depositUnlockingTimestamp",
+ "outputs": [
+ {
+ "internalType": "uint64",
+ "name": "",
+ "type": "uint64"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getDepositCount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getDepositLimit",
+ "outputs": [
+ {
+ "internalType": "uint64",
+ "name": "",
+ "type": "uint64"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getDepositRemain",
+ "outputs": [
+ {
+ "internalType": "uint64",
+ "name": "",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint64",
+ "name": "",
+ "type": "uint64"
+ },
+ {
+ "internalType": "uint64",
+ "name": "",
+ "type": "uint64"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint64",
+ "name": "timestamp",
+ "type": "uint64"
+ }
+ ],
+ "name": "withdrawalAllowed",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/contracts/deposit/AMT/bytecode.bin b/contracts/deposit/AMT/bytecode.bin
new file mode 100644
index 0000000..447ce42
--- /dev/null
+++ b/contracts/deposit/AMT/bytecode.bin
@@ -0,0 +1 @@
+608060405260006004556000600560006101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000600560086101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000600560106101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055503480156200009457600080fd5b50604051620026e4380380620026e48339818101604052810190620000ba91906200029e565b620000da620000ce6200018860201b60201c565b6200019060201b60201c565b83600360006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555082600360086101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555081600360106101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555080600360186101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505050505062000310565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600080fd5b600067ffffffffffffffff82169050919050565b620002788162000259565b81146200028457600080fd5b50565b60008151905062000298816200026d565b92915050565b60008060008060808587031215620002bb57620002ba62000254565b5b6000620002cb8782880162000287565b9450506020620002de8782880162000287565b9350506040620002f18782880162000287565b9250506060620003048782880162000287565b91505092959194509250565b6123c480620003206000396000f3fe6080604052600436106100c25760003560e01c80638da5cb5b1161007f578063d680292f11610059578063d680292f1461024e578063e3a9db1a14610277578063ee7bc641146102b4578063f2fde38b146102e1576100c2565b80638da5cb5b146101bb5780639363a141146101e6578063973bbea014610211576100c2565b80630e4a72ff146100c7578063164af1df146101045780633ccfd60b14610120578063715018a61461012a5780637bf7c807146101415780638d3043b01461017e575b600080fd5b3480156100d357600080fd5b506100ee60048036038101906100e991906115b6565b61030a565b6040516100fb91906115fe565b60405180910390f35b61011e6004803603810190610119919061167e565b6105c0565b005b6101286108bc565b005b34801561013657600080fd5b5061013f610bda565b005b34801561014d57600080fd5b506101686004803603810190610163919061173f565b610bee565b60405161017591906115fe565b60405180910390f35b34801561018a57600080fd5b506101a560048036038101906101a091906115b6565b610e30565b6040516101b2919061177b565b60405180910390f35b3480156101c757600080fd5b506101d0610ec6565b6040516101dd91906117d7565b60405180910390f35b3480156101f257600080fd5b506101fb610eef565b6040516102089190611801565b60405180910390f35b34801561021d57600080fd5b5061023860048036038101906102339190611848565b610ef9565b604051610245919061177b565b60405180910390f35b34801561025a57600080fd5b5061027560048036038101906102709190611875565b611016565b005b34801561028357600080fd5b5061029e60048036038101906102999190611848565b6111a3565b6040516102ab9190611801565b60405180910390f35b3480156102c057600080fd5b506102c96111ec565b6040516102d8939291906118b5565b60405180910390f35b3480156102ed57600080fd5b5061030860048036038101906103039190611848565b6112b5565b005b600080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541461038d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103849061196f565b60405180910390fd5b68056bc75e2d631000008214806103ac5750681b1ae4d6e2ef50000082145b806103bf57506802b5e3af16b188000082145b6103fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103f590611a01565b60405180910390fd5b6802b5e3af16b1880000820361049157600560009054906101000a900467ffffffffffffffff1667ffffffffffffffff16600360089054906101000a900467ffffffffffffffff1667ffffffffffffffff1611610490576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161048790611a93565b60405180910390fd5b5b68056bc75e2d63100000820361052457600560089054906101000a900467ffffffffffffffff1667ffffffffffffffff16600360109054906101000a900467ffffffffffffffff1667ffffffffffffffff1611610523576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161051a90611b25565b60405180910390fd5b5b681b1ae4d6e2ef50000082036105b757600560109054906101000a900467ffffffffffffffff1667ffffffffffffffff16600360189054906101000a900467ffffffffffffffff1667ffffffffffffffff16116105b6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ad90611bb7565b60405180910390fd5b5b60019050919050565b60003490506105ce8161030a565b61060d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060490611a01565b60405180910390fd5b60308585905014610653576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064a90611c49565b60405180910390fd5b60608383905014610699576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161069090611cdb565b60405180910390fd5b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555042600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555080600460008282546107559190611d2a565b925050819055506802b5e3af16b188000081036107ba576005600081819054906101000a900467ffffffffffffffff168092919061079290611d5e565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b68056bc75e2d631000008103610818576005600881819054906101000a900467ffffffffffffffff16809291906107f090611d5e565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b681b1ae4d6e2ef5000008103610876576005601081819054906101000a900467ffffffffffffffff168092919061084e90611d5e565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b7fb8d51a6e65c24b720301201860c41bedb147444aa7a59c1c6402493d56595f5885858386866040516108ad959493929190611dec565b60405180910390a15050505050565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541161093e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161093590611e81565b60405180910390fd5b61094742610bee565b610986576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161097d90611a01565b60405180910390fd5b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490506109d43382611338565b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549067ffffffffffffffff02191690556802b5e3af16b18800008103610acb576005600081819054906101000a900467ffffffffffffffff1680929190610aa390611ea1565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b68056bc75e2d631000008103610b29576005600881819054906101000a900467ffffffffffffffff1680929190610b0190611ea1565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b681b1ae4d6e2ef5000008103610b87576005601081819054906101000a900467ffffffffffffffff1680929190610b5f90611ea1565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505b8060046000828254610b999190611eca565b925050819055507f58799390987ee1f6c91da03f668142478dbf60938ef1355203cceb37caea106b81604051610bcf9190611801565b60405180910390a150565b610be261142c565b610bec60006114aa565b565b600080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054118015610c9b57506000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1667ffffffffffffffff16115b610cda576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cd190611f4a565b60405180910390fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054471015610d5c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d5390611fb6565b60405180910390fd5b8167ffffffffffffffff16600360009054906101000a900467ffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff16610ddc9190611fd6565b67ffffffffffffffff161115610e27576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610e1e9061205e565b60405180910390fd5b60019050919050565b60006802b5e3af16b18800008203610e6057600360089054906101000a900467ffffffffffffffff169050610ec1565b68056bc75e2d631000008203610e8e57600360109054906101000a900467ffffffffffffffff169050610ec1565b681b1ae4d6e2ef5000008203610ebc57600360189054906101000a900467ffffffffffffffff169050610ec1565b600090505b919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000600454905090565b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1667ffffffffffffffff1611610f9a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f91906120f0565b60405180910390fd5b600360009054906101000a900467ffffffffffffffff16600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900467ffffffffffffffff1661100f9190611fd6565b9050919050565b61101e610ec6565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461108b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110829061215c565b60405180910390fd5b6802b5e3af16b188000082036110e75780600360088282829054906101000a900467ffffffffffffffff166110c09190611fd6565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b68056bc75e2d6310000082036111435780600360108282829054906101000a900467ffffffffffffffff1661111c9190611fd6565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b681b1ae4d6e2ef500000820361119f5780600360188282829054906101000a900467ffffffffffffffff166111789190611fd6565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b5050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080600080600560009054906101000a900467ffffffffffffffff16600360089054906101000a900467ffffffffffffffff1661122a919061217c565b90506000600560089054906101000a900467ffffffffffffffff16600360109054906101000a900467ffffffffffffffff16611266919061217c565b90506000600560109054906101000a900467ffffffffffffffff16600360189054906101000a900467ffffffffffffffff166112a2919061217c565b9050828282955095509550505050909192565b6112bd61142c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361132c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016113239061222a565b60405180910390fd5b611335816114aa565b50565b8047101561137b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161137290611fb6565b60405180910390fd5b60008273ffffffffffffffffffffffffffffffffffffffff16826040516113a19061227b565b60006040518083038185875af1925050503d80600081146113de576040519150601f19603f3d011682016040523d82523d6000602084013e6113e3565b606091505b5050905080611427576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161141e90612302565b60405180910390fd5b505050565b61143461156e565b73ffffffffffffffffffffffffffffffffffffffff16611452610ec6565b73ffffffffffffffffffffffffffffffffffffffff16146114a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161149f9061236e565b60405180910390fd5b565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600033905090565b600080fd5b600080fd5b6000819050919050565b61159381611580565b811461159e57600080fd5b50565b6000813590506115b08161158a565b92915050565b6000602082840312156115cc576115cb611576565b5b60006115da848285016115a1565b91505092915050565b60008115159050919050565b6115f8816115e3565b82525050565b600060208201905061161360008301846115ef565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261163e5761163d611619565b5b8235905067ffffffffffffffff81111561165b5761165a61161e565b5b60208301915083600182028301111561167757611676611623565b5b9250929050565b6000806000806040858703121561169857611697611576565b5b600085013567ffffffffffffffff8111156116b6576116b561157b565b5b6116c287828801611628565b9450945050602085013567ffffffffffffffff8111156116e5576116e461157b565b5b6116f187828801611628565b925092505092959194509250565b600067ffffffffffffffff82169050919050565b61171c816116ff565b811461172757600080fd5b50565b60008135905061173981611713565b92915050565b60006020828403121561175557611754611576565b5b60006117638482850161172a565b91505092915050565b611775816116ff565b82525050565b6000602082019050611790600083018461176c565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006117c182611796565b9050919050565b6117d1816117b6565b82525050565b60006020820190506117ec60008301846117c8565b92915050565b6117fb81611580565b82525050565b600060208201905061181660008301846117f2565b92915050565b611825816117b6565b811461183057600080fd5b50565b6000813590506118428161181c565b92915050565b60006020828403121561185e5761185d611576565b5b600061186c84828501611833565b91505092915050565b6000806040838503121561188c5761188b611576565b5b600061189a858286016115a1565b92505060206118ab8582860161172a565b9150509250929050565b60006060820190506118ca600083018661176c565b6118d7602083018561176c565b6118e4604083018461176c565b949350505050565b600082825260208201905092915050565b7f4465706f736974436f6e74726163743a20796f7520616c72656164792064657060008201527f6f73697465640000000000000000000000000000000000000000000000000000602082015250565b60006119596026836118ec565b9150611964826118fd565b604082019050919050565b600060208201905081810360008301526119888161194c565b9050919050565b7f4465706f736974436f6e74726163743a207061796565206973206e6f7420616c60008201527f6c6f77656420746f206465706f73697400000000000000000000000000000000602082015250565b60006119eb6030836118ec565b91506119f68261198f565b604082019050919050565b60006020820190508181036000830152611a1a816119de565b9050919050565b7f313020414d43204465706f736974204c696d697420686173206265656e20726560008201527f6163686564000000000000000000000000000000000000000000000000000000602082015250565b6000611a7d6025836118ec565b9150611a8882611a21565b604082019050919050565b60006020820190508181036000830152611aac81611a70565b9050919050565b7f31303020414d43204465706f736974204c696d697420686173206265656e207260008201527f6561636865640000000000000000000000000000000000000000000000000000602082015250565b6000611b0f6026836118ec565b9150611b1a82611ab3565b604082019050919050565b60006020820190508181036000830152611b3e81611b02565b9050919050565b7f35303020414d43204465706f736974204c696d697420686173206265656e207260008201527f6561636865640000000000000000000000000000000000000000000000000000602082015250565b6000611ba16026836118ec565b9150611bac82611b45565b604082019050919050565b60006020820190508181036000830152611bd081611b94565b9050919050565b7f4465706f736974436f6e74726163743a20696e76616c6964207075626b65792060008201527f6c656e6774680000000000000000000000000000000000000000000000000000602082015250565b6000611c336026836118ec565b9150611c3e82611bd7565b604082019050919050565b60006020820190508181036000830152611c6281611c26565b9050919050565b7f4465706f736974436f6e74726163743a20696e76616c6964207369676e61747560008201527f7265206c656e6774680000000000000000000000000000000000000000000000602082015250565b6000611cc56029836118ec565b9150611cd082611c69565b604082019050919050565b60006020820190508181036000830152611cf481611cb8565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000611d3582611580565b9150611d4083611580565b9250828201905080821115611d5857611d57611cfb565b5b92915050565b6000611d69826116ff565b915067ffffffffffffffff8203611d8357611d82611cfb565b5b600182019050919050565b600082825260208201905092915050565b82818337600083830152505050565b6000601f19601f8301169050919050565b6000611dcb8385611d8e565b9350611dd8838584611d9f565b611de183611dae565b840190509392505050565b60006060820190508181036000830152611e07818789611dbf565b9050611e1660208301866117f2565b8181036040830152611e29818486611dbf565b90509695505050505050565b7f446f206e6f742068617665206465706f73697400000000000000000000000000600082015250565b6000611e6b6013836118ec565b9150611e7682611e35565b602082019050919050565b60006020820190508181036000830152611e9a81611e5e565b9050919050565b6000611eac826116ff565b915060008203611ebf57611ebe611cfb565b5b600182039050919050565b6000611ed582611580565b9150611ee083611580565b9250828203905081811115611ef857611ef7611cfb565b5b92915050565b7f446f206e6f742068617665206465706f73697473000000000000000000000000600082015250565b6000611f346014836118ec565b9150611f3f82611efe565b602082019050919050565b60006020820190508181036000830152611f6381611f27565b9050919050565b7f496e73756666696369656e742062616c616e6365000000000000000000000000600082015250565b6000611fa06014836118ec565b9150611fab82611f6a565b602082019050919050565b60006020820190508181036000830152611fcf81611f93565b9050919050565b6000611fe1826116ff565b9150611fec836116ff565b9250828201905067ffffffffffffffff81111561200c5761200b611cfb565b5b92915050565b7f4465706f736974206973206c6f636b696e670000000000000000000000000000600082015250565b60006120486012836118ec565b915061205382612012565b602082019050919050565b600060208201905081810360008301526120778161203b565b9050919050565b7f4465706f736974436f6e74726163743a20646f206e6f7420686176652064657060008201527f6f73697400000000000000000000000000000000000000000000000000000000602082015250565b60006120da6024836118ec565b91506120e58261207e565b604082019050919050565b60006020820190508181036000830152612109816120cd565b9050919050565b7f43616c6c6572206973206e6f74204f70657261746f7200000000000000000000600082015250565b60006121466016836118ec565b915061215182612110565b602082019050919050565b6000602082019050818103600083015261217581612139565b9050919050565b6000612187826116ff565b9150612192836116ff565b9250828203905067ffffffffffffffff8111156121b2576121b1611cfb565b5b92915050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006122146026836118ec565b915061221f826121b8565b604082019050919050565b6000602082019050818103600083015261224381612207565b9050919050565b600081905092915050565b50565b600061226560008361224a565b915061227082612255565b600082019050919050565b600061228682612258565b9150819050919050565b7f556e61626c6520746f2073656e642076616c75652c20726563697069656e742060008201527f6d61792068617665207265766572746564000000000000000000000000000000602082015250565b60006122ec6031836118ec565b91506122f782612290565b604082019050919050565b6000602082019050818103600083015261231b816122df565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b60006123586020836118ec565b915061236382612322565b602082019050919050565b600060208201905081810360008301526123878161234b565b905091905056fea2646970667358221220b62c2fec2b83d4d4dfd96ba7114dc2e4110afaa2e05b2f08d56984d0693be50464736f6c63430008110033
\ No newline at end of file
diff --git a/contracts/deposit/FUJI/FUJI.go b/contracts/deposit/FUJI/FUJI.go
new file mode 100644
index 0000000..3e19c6d
--- /dev/null
+++ b/contracts/deposit/FUJI/FUJI.go
@@ -0,0 +1,96 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package fujideposit
+
+import (
+ "bytes"
+ "embed"
+ "github.com/astranetworld/ast/accounts/abi"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "github.com/holiman/uint256"
+ "github.com/pkg/errors"
+ "math/big"
+)
+
+//go:embed abi.json
+var abiJson embed.FS
+var contractAbi abi.ABI
+var depositSignature = crypto.Keccak256Hash([]byte("DepositEvent(bytes,uint256,bytes)"))
+var withdrawnSignature = crypto.Keccak256Hash([]byte("WithdrawnEvent(uint256)"))
+
+func init() {
+ var (
+ depositAbiCode []byte
+ err error
+ )
+ if depositAbiCode, err = abiJson.ReadFile("abi.json"); err != nil {
+ panic("Could not open abi.json")
+ }
+
+ if contractAbi, err = abi.JSON(bytes.NewReader(depositAbiCode)); err != nil {
+ panic("unable to parse AMT deposit contract abi")
+ }
+}
+
+type Contract struct {
+}
+
+func (c Contract) IsDepositAction(sigdata [4]byte) bool {
+ var (
+ method *abi.Method
+ err error
+ )
+ if method, err = contractAbi.MethodById(sigdata[:]); err != nil {
+ return false
+ }
+
+ if !bytes.Equal(method.ID, contractAbi.Methods["deposit"].ID) {
+ return false
+ }
+ return true
+}
+
+func (Contract) WithdrawnSignature() types.Hash {
+ return withdrawnSignature
+}
+
+func (Contract) DepositSignature() types.Hash {
+ return depositSignature
+}
+
+func (Contract) UnpackDepositLogData(data []byte) (publicKey []byte, signature []byte, depositAmount *uint256.Int, err error) {
+ var (
+ unpackedLogs []interface{}
+ overflow bool
+ )
+ //
+ if unpackedLogs, err = contractAbi.Unpack("DepositEvent", data); err != nil {
+ err = errors.Wrap(err, "unable to unpack logs")
+ return
+ }
+ //
+ if depositAmount, overflow = uint256.FromBig(unpackedLogs[1].(*big.Int)); overflow {
+ err = errors.New("unable to unpack amount")
+ return
+ }
+ publicKey, signature = unpackedLogs[0].([]byte), unpackedLogs[2].([]byte)
+ log.Debug("unpacked DepositEvent Logs", "publicKey", hexutil.Encode(unpackedLogs[0].([]byte)), "signature", hexutil.Encode(unpackedLogs[2].([]byte)), "message", hexutil.Encode(depositAmount.Bytes()))
+ return
+}
diff --git a/contracts/deposit/FUJI/IDeposit.sol b/contracts/deposit/FUJI/IDeposit.sol
new file mode 100644
index 0000000..e48fc11
--- /dev/null
+++ b/contracts/deposit/FUJI/IDeposit.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+interface IDeposit {
+ event DepositEvent(bytes pubkey, uint256 weiAmount, bytes signature);
+
+ event WithdrawnEvent(uint256 weiAmount);
+
+ function deposit(
+ bytes calldata pubkey,
+ bytes calldata signature,
+ uint256 tokenID
+ ) external;
+
+ function withdraw() external;
+
+ function depositsOf(address account) external view returns (uint256);
+
+ function depositUnlockingTimestamp(
+ address payee
+ ) external view returns (uint256);
+}
\ No newline at end of file
diff --git a/contracts/deposit/FUJI/abi.json b/contracts/deposit/FUJI/abi.json
new file mode 100644
index 0000000..e29334e
--- /dev/null
+++ b/contracts/deposit/FUJI/abi.json
@@ -0,0 +1,366 @@
+[
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "fujiAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "fujiAdminAddress",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "constructor"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnableInvalidOwner",
+ "type": "error"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "OwnableUnauthorizedAccount",
+ "type": "error"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "pubkey",
+ "type": "bytes"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "weiAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ }
+ ],
+ "name": "DepositEvent",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "weiAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "WithdrawnEvent",
+ "type": "event"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "T2000TokenIDs",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "addT2000TokendIDs",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "T200TokenIDs",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "addT200TokendIDs",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256[]",
+ "name": "T800TokenIDs",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "addT800TokendIDs",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes",
+ "name": "pubkey",
+ "type": "bytes"
+ },
+ {
+ "internalType": "bytes",
+ "name": "signature",
+ "type": "bytes"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenID",
+ "type": "uint256"
+ }
+ ],
+ "name": "deposit",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "depositUnlockingTimestamp",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "depositsOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "depositsOfBalance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "sender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tokenID",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "onERC721Received",
+ "outputs": [
+ {
+ "internalType": "bytes4",
+ "name": "",
+ "type": "bytes4"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "renounceOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "bytes4",
+ "name": "interfaceId",
+ "type": "bytes4"
+ }
+ ],
+ "name": "supportsInterface",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "transferOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "transferOfBalance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "withdraw",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "withdrawOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/contracts/deposit/FUJI/staking.sol b/contracts/deposit/FUJI/staking.sol
new file mode 100644
index 0000000..10cc999
--- /dev/null
+++ b/contracts/deposit/FUJI/staking.sol
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import "@openzeppelin/contracts@v4.9.0/access/Ownable.sol";
+import "@openzeppelin/contracts@v4.9.0/token/ERC721/ERC721.sol";
+import "@openzeppelin/contracts@v4.9.0/token/ERC721/IERC721Receiver.sol";
+import "@openzeppelin/contracts@v4.9.0/token/ERC721/IERC721.sol";
+import "@openzeppelin/contracts-upgradeable@v4.9.0/proxy/utils/Initializable.sol";
+import "./IDeposit.sol";
+
+contract StakingFUJI is IDeposit, IERC721Receiver, Ownable {
+ IERC721 token;
+ mapping(uint256 => bool) T200NFT;
+ mapping(uint256 => bool) T800NFT;
+ mapping(uint256 => bool) T2000NFT;
+
+ mapping(address => uint256) depoistNFT;
+ mapping(address => uint256) depositExpire;
+
+ mapping(uint256 => address) withdrawIDToAddress;
+ mapping(address => uint256) withdrawAddressToID;
+ // mapping(address => uint256) withdrawNFT;
+
+ constructor(address fujiAddr, address fujiAdminAddress) Ownable() {
+ token = IERC721(fujiAddr);
+ token.setApprovalForAll(fujiAdminAddress, true);
+ }
+
+ function addT200TokendIDs(uint256[] calldata T200TokenIDs) public virtual onlyOwner {
+ for (uint i = 0; i < T200TokenIDs.length; i++) {
+ T200NFT[T200TokenIDs[i]] = true;
+ }
+ }
+
+ function addT800TokendIDs(uint256[] calldata T800TokenIDs) public virtual onlyOwner {
+ for (uint i = 0; i < T800TokenIDs.length; i++) {
+ T800NFT[T800TokenIDs[i]] = true;
+ }
+ }
+
+ function addT2000TokendIDs(uint256[] calldata T2000TokenIDs) public virtual onlyOwner {
+ for (uint i = 0; i < T2000TokenIDs.length; i++) {
+ T2000NFT[T2000TokenIDs[i]] = true;
+ }
+ }
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) external pure returns (bool) {
+ return
+ interfaceId == type(IDeposit).interfaceId ||
+ interfaceId == type(IERC721Receiver).interfaceId;
+ }
+
+ function deposit(
+ bytes calldata pubkey,
+ bytes calldata signature,
+ uint256 tokenID
+ ) external {
+ require(pubkey.length == 48, "Staking: invalid public key");
+ require(signature.length == 96, "Staking: invalid signature");
+ require(
+ T200NFT[tokenID] || T800NFT[tokenID] || T2000NFT[tokenID],
+ "Staking: not allowed token"
+ );
+ require(depoistNFT[msg.sender] == tokenID, "Staking: tokenID do not have transfed");
+
+ depositExpire[msg.sender] = block.timestamp + 90 days;
+ emit DepositEvent(pubkey, nftToAmount(tokenID), signature);
+ }
+
+ function withdraw() external {
+
+ uint256 tokenID = depoistNFT[msg.sender];
+
+ require(tokenID > 0, "Staking: not deposited");
+ require(
+ depositExpire[msg.sender] <= block.timestamp,
+ "Staking: token is locked"
+ );
+ require(
+ token.ownerOf(tokenID) == address(this),
+ "Staking: Insuficient Allowance"
+ );
+
+ withdrawIDToAddress[tokenID] = msg.sender;
+ withdrawAddressToID[msg.sender] = tokenID;
+ // withdrawNFT[msg.sender] = tokenID;
+ delete depoistNFT[msg.sender];
+ delete depositExpire[msg.sender];
+
+ emit WithdrawnEvent(nftToAmount(tokenID));
+ }
+
+ function depositsOf(address account) external view returns (uint256) {
+ if (depositExpire[account] == 0) {
+ return 0;
+ }
+ return depoistNFT[account];
+ }
+
+ function depositsOfBalance(address account) external view returns (uint256) {
+ if (depositExpire[account] == 0) {
+ return 0;
+ }
+ return nftToAmount(depoistNFT[account]);
+ }
+ function transferOf(address account) external view returns (uint256) {
+ return depoistNFT[account];
+ }
+
+ function transferOfBalance(address account) external view returns (uint256) {
+ return nftToAmount(depoistNFT[account]);
+ }
+
+ function depositUnlockingTimestamp(
+ address account
+ ) external view returns (uint256) {
+ return depositExpire[account];
+ }
+
+ function withdrawOf(address account) external view returns (uint256) {
+ return withdrawAddressToID[account];
+ }
+
+
+ function nftToAmount(uint256 id) internal view returns (uint256) {
+ if (T200NFT[id] == true) {
+ return 200 ether;
+ }
+
+ if (T800NFT[id] == true) {
+ return 800 ether;
+ }
+
+ if (T2000NFT[id] == true) {
+ return 2000 ether;
+ }
+
+ return 0 ether;
+ }
+
+ function onERC721Received(
+ address /*operator*/,
+ address sender /*from*/,
+ uint256 tokenID /*id*/,
+ bytes calldata /*data*/
+ ) external returns (bytes4) {
+ require(
+ T200NFT[tokenID] || T800NFT[tokenID] || T2000NFT[tokenID],
+ "Staking: not allowed token"
+ );
+
+ // A deposit T1 , withdraw T1, A send T1 to B, B deposit T1, A deposit T2
+ if (withdrawIDToAddress[tokenID] != address(0)) {
+ address withdrawAddress = withdrawIDToAddress[tokenID];
+ delete withdrawAddressToID[withdrawAddress];
+ delete withdrawIDToAddress[tokenID];
+ }
+
+
+ if (withdrawAddressToID[sender] > 0) {
+// require(
+// token.ownerOf(withdrawAddressToID[sender]) != address(this),
+// "Staking: Have not withdraw"
+// );
+
+ uint256 withdrawTokenID = withdrawAddressToID[sender];
+ delete withdrawAddressToID[sender];
+ delete withdrawIDToAddress[withdrawTokenID];
+ }
+
+ depoistNFT[sender] = tokenID;
+ return
+ bytes4(
+ keccak256(
+ "onERC721Received(address,address,uint256,bytes)"
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/contracts/deposit/NFT/IDeposit.sol b/contracts/deposit/NFT/IDeposit.sol
new file mode 100644
index 0000000..e48fc11
--- /dev/null
+++ b/contracts/deposit/NFT/IDeposit.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+interface IDeposit {
+ event DepositEvent(bytes pubkey, uint256 weiAmount, bytes signature);
+
+ event WithdrawnEvent(uint256 weiAmount);
+
+ function deposit(
+ bytes calldata pubkey,
+ bytes calldata signature,
+ uint256 tokenID
+ ) external;
+
+ function withdraw() external;
+
+ function depositsOf(address account) external view returns (uint256);
+
+ function depositUnlockingTimestamp(
+ address payee
+ ) external view returns (uint256);
+}
\ No newline at end of file
diff --git a/contracts/deposit/NFT/NFT.go b/contracts/deposit/NFT/NFT.go
new file mode 100644
index 0000000..12af999
--- /dev/null
+++ b/contracts/deposit/NFT/NFT.go
@@ -0,0 +1,96 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package nftdeposit
+
+import (
+ "bytes"
+ "embed"
+ "github.com/astranetworld/ast/accounts/abi"
+ "github.com/astranetworld/ast/common/crypto"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ "github.com/holiman/uint256"
+ "github.com/pkg/errors"
+ "math/big"
+)
+
+//go:embed abi.json
+var abiJson embed.FS
+var contractAbi abi.ABI
+var depositSignature = crypto.Keccak256Hash([]byte("DepositEvent(bytes,uint256,bytes)"))
+var withdrawnSignature = crypto.Keccak256Hash([]byte("WithdrawnEvent(uint256)"))
+
+func init() {
+ var (
+ depositAbiCode []byte
+ err error
+ )
+ if depositAbiCode, err = abiJson.ReadFile("abi.json"); err != nil {
+ panic("Could not open abi.json")
+ }
+
+ if contractAbi, err = abi.JSON(bytes.NewReader(depositAbiCode)); err != nil {
+ panic("unable to parse AMT deposit contract abi")
+ }
+}
+
+type Contract struct {
+}
+
+func (c Contract) IsDepositAction(sigdata [4]byte) bool {
+ var (
+ method *abi.Method
+ err error
+ )
+ if method, err = contractAbi.MethodById(sigdata[:]); err != nil {
+ return false
+ }
+
+ if !bytes.Equal(method.ID, contractAbi.Methods["deposit"].ID) {
+ return false
+ }
+ return true
+}
+
+func (Contract) WithdrawnSignature() types.Hash {
+ return withdrawnSignature
+}
+
+func (Contract) DepositSignature() types.Hash {
+ return depositSignature
+}
+
+func (Contract) UnpackDepositLogData(data []byte) (publicKey []byte, signature []byte, depositAmount *uint256.Int, err error) {
+ var (
+ unpackedLogs []interface{}
+ overflow bool
+ )
+ //
+ if unpackedLogs, err = contractAbi.Unpack("DepositEvent", data); err != nil {
+ err = errors.Wrap(err, "unable to unpack logs")
+ return
+ }
+ //
+ if depositAmount, overflow = uint256.FromBig(unpackedLogs[1].(*big.Int)); overflow {
+ err = errors.New("unable to unpack amount")
+ return
+ }
+ publicKey, signature = unpackedLogs[0].([]byte), unpackedLogs[2].([]byte)
+ log.Debug("unpacked DepositEvent Logs", "publicKey", hexutil.Encode(unpackedLogs[0].([]byte)), "signature", hexutil.Encode(unpackedLogs[2].([]byte)), "message", hexutil.Encode(depositAmount.Bytes()))
+ return
+}
diff --git a/contracts/deposit/NFT/abi.json b/contracts/deposit/NFT/abi.json
new file mode 100644
index 0000000..754db41
--- /dev/null
+++ b/contracts/deposit/NFT/abi.json
@@ -0,0 +1,246 @@
+[
+ {
+ "inputs":[
+
+ ],
+ "stateMutability":"nonpayable",
+ "type":"constructor"
+ },
+ {
+ "anonymous":false,
+ "inputs":[
+ {
+ "indexed":false,
+ "internalType":"bytes",
+ "name":"pubkey",
+ "type":"bytes"
+ },
+ {
+ "indexed":false,
+ "internalType":"uint256",
+ "name":"weiAmount",
+ "type":"uint256"
+ },
+ {
+ "indexed":false,
+ "internalType":"bytes",
+ "name":"signature",
+ "type":"bytes"
+ }
+ ],
+ "name":"DepositEvent",
+ "type":"event"
+ },
+ {
+ "anonymous":false,
+ "inputs":[
+ {
+ "indexed":false,
+ "internalType":"uint8",
+ "name":"version",
+ "type":"uint8"
+ }
+ ],
+ "name":"Initialized",
+ "type":"event"
+ },
+ {
+ "anonymous":false,
+ "inputs":[
+ {
+ "indexed":false,
+ "internalType":"uint256",
+ "name":"weiAmount",
+ "type":"uint256"
+ }
+ ],
+ "name":"WithdrawnEvent",
+ "type":"event"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"bytes",
+ "name":"pubkey",
+ "type":"bytes"
+ },
+ {
+ "internalType":"bytes",
+ "name":"signature",
+ "type":"bytes"
+ },
+ {
+ "internalType":"uint256",
+ "name":"tokenID",
+ "type":"uint256"
+ }
+ ],
+ "name":"deposit",
+ "outputs":[
+
+ ],
+ "stateMutability":"nonpayable",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"address",
+ "name":"account",
+ "type":"address"
+ }
+ ],
+ "name":"depositUnlockingTimestamp",
+ "outputs":[
+ {
+ "internalType":"uint256",
+ "name":"",
+ "type":"uint256"
+ }
+ ],
+ "stateMutability":"view",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"address",
+ "name":"account",
+ "type":"address"
+ }
+ ],
+ "name":"depositsOf",
+ "outputs":[
+ {
+ "internalType":"uint256",
+ "name":"",
+ "type":"uint256"
+ }
+ ],
+ "stateMutability":"view",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"address",
+ "name":"addr",
+ "type":"address"
+ }
+ ],
+ "name":"initialize",
+ "outputs":[
+
+ ],
+ "stateMutability":"nonpayable",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"address",
+ "name":"",
+ "type":"address"
+ },
+ {
+ "internalType":"address",
+ "name":"",
+ "type":"address"
+ },
+ {
+ "internalType":"uint256[]",
+ "name":"",
+ "type":"uint256[]"
+ },
+ {
+ "internalType":"uint256[]",
+ "name":"",
+ "type":"uint256[]"
+ },
+ {
+ "internalType":"bytes",
+ "name":"",
+ "type":"bytes"
+ }
+ ],
+ "name":"onERC1155BatchReceived",
+ "outputs":[
+ {
+ "internalType":"bytes4",
+ "name":"",
+ "type":"bytes4"
+ }
+ ],
+ "stateMutability":"nonpayable",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"address",
+ "name":"",
+ "type":"address"
+ },
+ {
+ "internalType":"address",
+ "name":"",
+ "type":"address"
+ },
+ {
+ "internalType":"uint256",
+ "name":"",
+ "type":"uint256"
+ },
+ {
+ "internalType":"uint256",
+ "name":"",
+ "type":"uint256"
+ },
+ {
+ "internalType":"bytes",
+ "name":"",
+ "type":"bytes"
+ }
+ ],
+ "name":"onERC1155Received",
+ "outputs":[
+ {
+ "internalType":"bytes4",
+ "name":"",
+ "type":"bytes4"
+ }
+ ],
+ "stateMutability":"pure",
+ "type":"function"
+ },
+ {
+ "inputs":[
+ {
+ "internalType":"bytes4",
+ "name":"interfaceId",
+ "type":"bytes4"
+ }
+ ],
+ "name":"supportsInterface",
+ "outputs":[
+ {
+ "internalType":"bool",
+ "name":"",
+ "type":"bool"
+ }
+ ],
+ "stateMutability":"pure",
+ "type":"function"
+ },
+ {
+ "inputs":[
+
+ ],
+ "name":"withdraw",
+ "outputs":[
+
+ ],
+ "stateMutability":"nonpayable",
+ "type":"function"
+ }
+]
\ No newline at end of file
diff --git a/contracts/deposit/NFT/bytecode.bin b/contracts/deposit/NFT/bytecode.bin
new file mode 100644
index 0000000..4b30c52
--- /dev/null
+++ b/contracts/deposit/NFT/bytecode.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b506040518060200160405280600081525062000033816200005a60201b60201c565b5062000054620000486200006f60201b60201c565b6200007760201b60201c565b6200049e565b80600290816200006b9190620003b7565b5050565b600033905090565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620001bf57607f821691505b602082108103620001d557620001d462000177565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026200023f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000200565b6200024b868362000200565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600062000298620002926200028c8462000263565b6200026d565b62000263565b9050919050565b6000819050919050565b620002b48362000277565b620002cc620002c3826200029f565b8484546200020d565b825550505050565b600090565b620002e3620002d4565b620002f0818484620002a9565b505050565b5b8181101562000318576200030c600082620002d9565b600181019050620002f6565b5050565b601f82111562000367576200033181620001db565b6200033c84620001f0565b810160208510156200034c578190505b620003646200035b85620001f0565b830182620002f5565b50505b505050565b600082821c905092915050565b60006200038c600019846008026200036c565b1980831691505092915050565b6000620003a7838362000379565b9150826002028217905092915050565b620003c2826200013d565b67ffffffffffffffff811115620003de57620003dd62000148565b5b620003ea8254620001a6565b620003f78282856200031c565b600060209050601f8311600181146200042f57600084156200041a578287015190505b62000426858262000399565b86555062000496565b601f1984166200043f86620001db565b60005b82811015620004695784890151825560018201915060208501945060208101905062000442565b8683101562000489578489015162000485601f89168262000379565b8355505b6001600288020188555050505b505050505050565b612b3880620004ae6000396000f3fe608060405234801561001057600080fd5b50600436106100b35760003560e01c8063715018a611610071578063715018a6146101b05780638da5cb5b146101ba578063a22cb465146101d8578063e985e9c5146101f4578063f242432a14610224578063f2fde38b14610240576100b3565b8062fdd58e146100b857806301ffc9a7146100e85780630e89341c146101185780632eb2c2d61461014857806340c10f19146101645780634e1273f414610180575b600080fd5b6100d260048036038101906100cd9190611819565b61025c565b6040516100df9190611868565b60405180910390f35b61010260048036038101906100fd91906118db565b610324565b60405161010f9190611923565b60405180910390f35b610132600480360381019061012d919061193e565b610406565b60405161013f91906119fb565b60405180910390f35b610162600480360381019061015d9190611c1a565b61049a565b005b61017e60048036038101906101799190611819565b61053b565b005b61019a60048036038101906101959190611dac565b610563565b6040516101a79190611ee2565b60405180910390f35b6101b861067c565b005b6101c2610690565b6040516101cf9190611f13565b60405180910390f35b6101f260048036038101906101ed9190611f5a565b6106ba565b005b61020e60048036038101906102099190611f9a565b6106d0565b60405161021b9190611923565b60405180910390f35b61023e60048036038101906102399190611fda565b610764565b005b61025a60048036038101906102559190612071565b610805565b005b60008073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036102cc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102c390612110565b60405180910390fd5b60008083815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b60007fd9b67a26000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614806103ef57507f0e89341c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806103ff57506103fe82610888565b5b9050919050565b6060600280546104159061215f565b80601f01602080910402602001604051908101604052809291908181526020018280546104419061215f565b801561048e5780601f106104635761010080835404028352916020019161048e565b820191906000526020600020905b81548152906001019060200180831161047157829003601f168201915b50505050509050919050565b6104a26108f2565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614806104e857506104e7856104e26108f2565b6106d0565b5b610527576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161051e90612202565b60405180910390fd5b61053485858585856108fa565b5050505050565b610543610c1b565b61055f8282600160405180602001604052806000815250610c99565b5050565b606081518351146105a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105a090612294565b60405180910390fd5b6000835167ffffffffffffffff8111156105c6576105c5611a22565b5b6040519080825280602002602001820160405280156105f45781602001602082028036833780820191505090505b50905060005b845181101561067157610641858281518110610619576106186122b4565b5b6020026020010151858381518110610634576106336122b4565b5b602002602001015161025c565b828281518110610654576106536122b4565b5b6020026020010181815250508061066a90612312565b90506105fa565b508091505092915050565b610684610c1b565b61068e6000610e49565b565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6106cc6106c56108f2565b8383610f0f565b5050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b61076c6108f2565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614806107b257506107b1856107ac6108f2565b6106d0565b5b6107f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107e890612202565b60405180910390fd5b6107fe858585858561107b565b5050505050565b61080d610c1b565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361087c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610873906123cc565b60405180910390fd5b61088581610e49565b50565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b600033905090565b815183511461093e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109359061245e565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109a4906124f0565b60405180910390fd5b60006109b76108f2565b90506109c7818787878787611316565b60005b8451811015610b785760008582815181106109e8576109e76122b4565b5b602002602001015190506000858381518110610a0757610a066122b4565b5b60200260200101519050600080600084815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610aa8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a9f90612582565b60405180910390fd5b81810360008085815260200190815260200160002060008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508160008085815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b5d91906125a2565b9250508190555050505080610b7190612312565b90506109ca565b508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051610bef9291906125d6565b60405180910390a4610c0581878787878761131e565b610c13818787878787611326565b505050505050565b610c236108f2565b73ffffffffffffffffffffffffffffffffffffffff16610c41610690565b73ffffffffffffffffffffffffffffffffffffffff1614610c97576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8e90612659565b60405180910390fd5b565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603610d08576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610cff906126eb565b60405180910390fd5b6000610d126108f2565b90506000610d1f856114fd565b90506000610d2c856114fd565b9050610d3d83600089858589611316565b8460008088815260200190815260200160002060008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610d9c91906125a2565b925050819055508673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628989604051610e1a92919061270b565b60405180910390a4610e318360008985858961131e565b610e4083600089898989611577565b50505050505050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905081600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610f7d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f74906127a6565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c318360405161106e9190611923565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036110ea576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110e1906124f0565b60405180910390fd5b60006110f46108f2565b90506000611101856114fd565b9050600061110e856114fd565b905061111e838989858589611316565b600080600088815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050858110156111b5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ac90612582565b60405180910390fd5b85810360008089815260200190815260200160002060008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508560008089815260200190815260200160002060008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461126a91906125a2565b925050819055508773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f628a8a6040516112e792919061270b565b60405180910390a46112fd848a8a86868a61131e565b61130b848a8a8a8a8a611577565b505050505050505050565b505050505050565b505050505050565b6113458473ffffffffffffffffffffffffffffffffffffffff1661174e565b156114f5578373ffffffffffffffffffffffffffffffffffffffff1663bc197c8187878686866040518663ffffffff1660e01b815260040161138b95949392919061281b565b6020604051808303816000875af19250505080156113c757506040513d601f19601f820116820180604052508101906113c49190612898565b60015b61146c576113d36128d2565b806308c379a00361142f57506113e76128f4565b806113f25750611431565b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161142691906119fb565b60405180910390fd5b505b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611463906129f6565b60405180910390fd5b63bc197c8160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146114f3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114ea90612a88565b60405180910390fd5b505b505050505050565b60606000600167ffffffffffffffff81111561151c5761151b611a22565b5b60405190808252806020026020018201604052801561154a5781602001602082028036833780820191505090505b5090508281600081518110611562576115616122b4565b5b60200260200101818152505080915050919050565b6115968473ffffffffffffffffffffffffffffffffffffffff1661174e565b15611746578373ffffffffffffffffffffffffffffffffffffffff1663f23a6e6187878686866040518663ffffffff1660e01b81526004016115dc959493929190612aa8565b6020604051808303816000875af192505050801561161857506040513d601f19601f820116820180604052508101906116159190612898565b60015b6116bd576116246128d2565b806308c379a00361168057506116386128f4565b806116435750611682565b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161167791906119fb565b60405180910390fd5b505b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116b4906129f6565b60405180910390fd5b63f23a6e6160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614611744576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161173b90612a88565b60405180910390fd5b505b505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006117b082611785565b9050919050565b6117c0816117a5565b81146117cb57600080fd5b50565b6000813590506117dd816117b7565b92915050565b6000819050919050565b6117f6816117e3565b811461180157600080fd5b50565b600081359050611813816117ed565b92915050565b600080604083850312156118305761182f61177b565b5b600061183e858286016117ce565b925050602061184f85828601611804565b9150509250929050565b611862816117e3565b82525050565b600060208201905061187d6000830184611859565b92915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6118b881611883565b81146118c357600080fd5b50565b6000813590506118d5816118af565b92915050565b6000602082840312156118f1576118f061177b565b5b60006118ff848285016118c6565b91505092915050565b60008115159050919050565b61191d81611908565b82525050565b60006020820190506119386000830184611914565b92915050565b6000602082840312156119545761195361177b565b5b600061196284828501611804565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156119a557808201518184015260208101905061198a565b60008484015250505050565b6000601f19601f8301169050919050565b60006119cd8261196b565b6119d78185611976565b93506119e7818560208601611987565b6119f0816119b1565b840191505092915050565b60006020820190508181036000830152611a1581846119c2565b905092915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b611a5a826119b1565b810181811067ffffffffffffffff82111715611a7957611a78611a22565b5b80604052505050565b6000611a8c611771565b9050611a988282611a51565b919050565b600067ffffffffffffffff821115611ab857611ab7611a22565b5b602082029050602081019050919050565b600080fd5b6000611ae1611adc84611a9d565b611a82565b90508083825260208201905060208402830185811115611b0457611b03611ac9565b5b835b81811015611b2d5780611b198882611804565b845260208401935050602081019050611b06565b5050509392505050565b600082601f830112611b4c57611b4b611a1d565b5b8135611b5c848260208601611ace565b91505092915050565b600080fd5b600067ffffffffffffffff821115611b8557611b84611a22565b5b611b8e826119b1565b9050602081019050919050565b82818337600083830152505050565b6000611bbd611bb884611b6a565b611a82565b905082815260208101848484011115611bd957611bd8611b65565b5b611be4848285611b9b565b509392505050565b600082601f830112611c0157611c00611a1d565b5b8135611c11848260208601611baa565b91505092915050565b600080600080600060a08688031215611c3657611c3561177b565b5b6000611c44888289016117ce565b9550506020611c55888289016117ce565b945050604086013567ffffffffffffffff811115611c7657611c75611780565b5b611c8288828901611b37565b935050606086013567ffffffffffffffff811115611ca357611ca2611780565b5b611caf88828901611b37565b925050608086013567ffffffffffffffff811115611cd057611ccf611780565b5b611cdc88828901611bec565b9150509295509295909350565b600067ffffffffffffffff821115611d0457611d03611a22565b5b602082029050602081019050919050565b6000611d28611d2384611ce9565b611a82565b90508083825260208201905060208402830185811115611d4b57611d4a611ac9565b5b835b81811015611d745780611d6088826117ce565b845260208401935050602081019050611d4d565b5050509392505050565b600082601f830112611d9357611d92611a1d565b5b8135611da3848260208601611d15565b91505092915050565b60008060408385031215611dc357611dc261177b565b5b600083013567ffffffffffffffff811115611de157611de0611780565b5b611ded85828601611d7e565b925050602083013567ffffffffffffffff811115611e0e57611e0d611780565b5b611e1a85828601611b37565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611e59816117e3565b82525050565b6000611e6b8383611e50565b60208301905092915050565b6000602082019050919050565b6000611e8f82611e24565b611e998185611e2f565b9350611ea483611e40565b8060005b83811015611ed5578151611ebc8882611e5f565b9750611ec783611e77565b925050600181019050611ea8565b5085935050505092915050565b60006020820190508181036000830152611efc8184611e84565b905092915050565b611f0d816117a5565b82525050565b6000602082019050611f286000830184611f04565b92915050565b611f3781611908565b8114611f4257600080fd5b50565b600081359050611f5481611f2e565b92915050565b60008060408385031215611f7157611f7061177b565b5b6000611f7f858286016117ce565b9250506020611f9085828601611f45565b9150509250929050565b60008060408385031215611fb157611fb061177b565b5b6000611fbf858286016117ce565b9250506020611fd0858286016117ce565b9150509250929050565b600080600080600060a08688031215611ff657611ff561177b565b5b6000612004888289016117ce565b9550506020612015888289016117ce565b945050604061202688828901611804565b935050606061203788828901611804565b925050608086013567ffffffffffffffff81111561205857612057611780565b5b61206488828901611bec565b9150509295509295909350565b6000602082840312156120875761208661177b565b5b6000612095848285016117ce565b91505092915050565b7f455243313135353a2061646472657373207a65726f206973206e6f742061207660008201527f616c6964206f776e657200000000000000000000000000000000000000000000602082015250565b60006120fa602a83611976565b91506121058261209e565b604082019050919050565b60006020820190508181036000830152612129816120ed565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061217757607f821691505b60208210810361218a57612189612130565b5b50919050565b7f455243313135353a2063616c6c6572206973206e6f7420746f6b656e206f776e60008201527f6572206f7220617070726f766564000000000000000000000000000000000000602082015250565b60006121ec602e83611976565b91506121f782612190565b604082019050919050565b6000602082019050818103600083015261221b816121df565b9050919050565b7f455243313135353a206163636f756e747320616e6420696473206c656e67746860008201527f206d69736d617463680000000000000000000000000000000000000000000000602082015250565b600061227e602983611976565b915061228982612222565b604082019050919050565b600060208201905081810360008301526122ad81612271565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061231d826117e3565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361234f5761234e6122e3565b5b600182019050919050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b60006123b6602683611976565b91506123c18261235a565b604082019050919050565b600060208201905081810360008301526123e5816123a9565b9050919050565b7f455243313135353a2069647320616e6420616d6f756e7473206c656e6774682060008201527f6d69736d61746368000000000000000000000000000000000000000000000000602082015250565b6000612448602883611976565b9150612453826123ec565b604082019050919050565b600060208201905081810360008301526124778161243b565b9050919050565b7f455243313135353a207472616e7366657220746f20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b60006124da602583611976565b91506124e58261247e565b604082019050919050565b60006020820190508181036000830152612509816124cd565b9050919050565b7f455243313135353a20696e73756666696369656e742062616c616e636520666f60008201527f72207472616e7366657200000000000000000000000000000000000000000000602082015250565b600061256c602a83611976565b915061257782612510565b604082019050919050565b6000602082019050818103600083015261259b8161255f565b9050919050565b60006125ad826117e3565b91506125b8836117e3565b92508282019050808211156125d0576125cf6122e3565b5b92915050565b600060408201905081810360008301526125f08185611e84565b905081810360208301526126048184611e84565b90509392505050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000612643602083611976565b915061264e8261260d565b602082019050919050565b6000602082019050818103600083015261267281612636565b9050919050565b7f455243313135353a206d696e7420746f20746865207a65726f2061646472657360008201527f7300000000000000000000000000000000000000000000000000000000000000602082015250565b60006126d5602183611976565b91506126e082612679565b604082019050919050565b60006020820190508181036000830152612704816126c8565b9050919050565b60006040820190506127206000830185611859565b61272d6020830184611859565b9392505050565b7f455243313135353a2073657474696e6720617070726f76616c2073746174757360008201527f20666f722073656c660000000000000000000000000000000000000000000000602082015250565b6000612790602983611976565b915061279b82612734565b604082019050919050565b600060208201905081810360008301526127bf81612783565b9050919050565b600081519050919050565b600082825260208201905092915050565b60006127ed826127c6565b6127f781856127d1565b9350612807818560208601611987565b612810816119b1565b840191505092915050565b600060a0820190506128306000830188611f04565b61283d6020830187611f04565b818103604083015261284f8186611e84565b905081810360608301526128638185611e84565b9050818103608083015261287781846127e2565b90509695505050505050565b600081519050612892816118af565b92915050565b6000602082840312156128ae576128ad61177b565b5b60006128bc84828501612883565b91505092915050565b60008160e01c9050919050565b600060033d11156128f15760046000803e6128ee6000516128c5565b90505b90565b600060443d1061298157612906611771565b60043d036004823e80513d602482011167ffffffffffffffff8211171561292e575050612981565b808201805167ffffffffffffffff81111561294c5750505050612981565b80602083010160043d038501811115612969575050505050612981565b61297882602001850186611a51565b82955050505050505b90565b7f455243313135353a207472616e7366657220746f206e6f6e2d4552433131353560008201527f526563656976657220696d706c656d656e746572000000000000000000000000602082015250565b60006129e0603483611976565b91506129eb82612984565b604082019050919050565b60006020820190508181036000830152612a0f816129d3565b9050919050565b7f455243313135353a204552433131353552656365697665722072656a6563746560008201527f6420746f6b656e73000000000000000000000000000000000000000000000000602082015250565b6000612a72602883611976565b9150612a7d82612a16565b604082019050919050565b60006020820190508181036000830152612aa181612a65565b9050919050565b600060a082019050612abd6000830188611f04565b612aca6020830187611f04565b612ad76040830186611859565b612ae46060830185611859565b8181036080830152612af681846127e2565b9050969550505050505056fea2646970667358221220dc057c984306b0b6074ec7c11aa65ff07518e65613ff877b2ab44569f54ed28864736f6c63430008110033
\ No newline at end of file
diff --git a/contracts/deposit/NFT/staking.sol b/contracts/deposit/NFT/staking.sol
new file mode 100644
index 0000000..0cb73a1
--- /dev/null
+++ b/contracts/deposit/NFT/staking.sol
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.17;
+
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
+import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
+import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
+import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
+import "./IDeposit.sol";
+import "hardhat/console.sol";
+
+contract NFT is ERC1155, Ownable {
+ constructor() ERC1155("") Ownable() {}
+
+ function mint(address to, uint256 id) external onlyOwner {
+ //console.log(id);
+ _mint(to, id, 1, "");
+ }
+}
+
+contract Staking is IDeposit, IERC1155Receiver, Initializable {
+ uint256 constant T50 = uint256(keccak256("50AMT"));
+ uint256 constant T100 = uint256(keccak256("100AMT"));
+ uint256 constant T500 = uint256(keccak256("500AMT"));
+
+ IERC1155 token;
+ mapping(address => uint256) depoistNFT;
+ mapping(address => uint256) depositExpire;
+
+ constructor() {}
+
+ function supportsInterface(
+ bytes4 interfaceId
+ ) external pure returns (bool) {
+ return
+ interfaceId == type(IDeposit).interfaceId ||
+ interfaceId == type(IERC1155Receiver).interfaceId;
+ }
+
+ function initialize(address addr) public initializer {
+ token = IERC1155(addr);
+ }
+
+ function deposit(
+ bytes calldata pubkey,
+ bytes calldata signature,
+ uint256 tokenID
+ ) external {
+ require(pubkey.length == 48, "Staking: invalid public key");
+ require(signature.length == 96, "Staking: invalid signature");
+ require(
+ tokenID == T50 || tokenID == T100 || tokenID == T500,
+ "Staking: not allowed token"
+ );
+ require(depoistNFT[msg.sender] == 0, "Staking: have deposited");
+ require(
+ token.balanceOf(msg.sender, tokenID) > 0,
+ "Staking: Insuficient Allowance"
+ );
+ require(
+ token.isApprovedForAll(msg.sender, address(this)),
+ "Staking: caller is not approved"
+ );
+
+ token.safeTransferFrom(msg.sender, address(this), tokenID, 1, "");
+
+ depoistNFT[msg.sender] = tokenID;
+ depositExpire[msg.sender] = block.timestamp + 365 days;
+
+ emit DepositEvent(pubkey, nftToAmount(tokenID), signature);
+ }
+
+ function withdraw() external {
+ require(depoistNFT[msg.sender] > 0, "Staking: not deposited");
+ require(
+ depositExpire[msg.sender] <= block.timestamp,
+ "Staking: token is locked"
+ );
+
+ uint256 id = depoistNFT[msg.sender];
+ delete depoistNFT[msg.sender];
+ delete depositExpire[msg.sender];
+
+ token.safeTransferFrom(address(this), msg.sender, id, 1, "");
+ emit WithdrawnEvent(nftToAmount(id));
+ }
+
+ function depositsOf(address account) external view returns (uint256) {
+ return depoistNFT[account];
+ }
+
+ function depositUnlockingTimestamp(
+ address account
+ ) external view returns (uint256) {
+ return depositExpire[account];
+ }
+
+ function disperseAMT(
+ address payable[] calldata recipients,
+ uint256[] calldata amounts
+ ) external payable {
+ require(
+ recipients.length > 0 && recipients.length == amounts.length,
+ "the lenght of receipts not equal amounts or length is 0 "
+ );
+
+ uint256 totalAmount;
+ for (uint i = 0; i < amounts.length; ) {
+ require(amounts[i] <= 0.7083 ether, "beyond the max rewards");
+ unchecked {
+ totalAmount += amounts[i];
+ ++i;
+ }
+ }
+ require(msg.value >= totalAmount, "Insufficient AMT sent");
+ for (uint i = 0; i < recipients.length; i++) {
+ recipients[i].transfer(amounts[i]);
+ }
+ }
+
+ function nftToAmount(uint256 id) internal pure returns (uint256) {
+ if (id == T50) {
+ return 50 ether;
+ }
+
+ if (id == T100) {
+ return 100 ether;
+ }
+
+ if (id == T500) {
+ return 500 ether;
+ }
+
+ return 0;
+ }
+
+ function onERC1155Received(
+ address /*operator*/,
+ address /*from*/,
+ uint256 /*id*/,
+ uint256 /*value*/,
+ bytes calldata /*data*/
+ ) external pure returns (bytes4) {
+ return
+ bytes4(
+ keccak256(
+ "onERC1155Received(address,address,uint256,uint256,bytes)"
+ )
+ );
+ }
+
+ function onERC1155BatchReceived(
+ address /*operator*/,
+ address /*from*/,
+ uint256[] calldata /*ids*/,
+ uint256[] calldata /*values*/,
+ bytes calldata /*data*/
+ ) external returns (bytes4) {}
+}
\ No newline at end of file
diff --git a/contracts/deposit/contract.go b/contracts/deposit/contract.go
new file mode 100644
index 0000000..9a8f84c
--- /dev/null
+++ b/contracts/deposit/contract.go
@@ -0,0 +1,317 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package deposit
+
+import (
+ "context"
+ "github.com/astranetworld/ast/common"
+ "github.com/astranetworld/ast/common/crypto/bls"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/common/transaction"
+ "github.com/astranetworld/ast/common/types"
+ "github.com/astranetworld/ast/log"
+ event "github.com/astranetworld/ast/modules/event/v2"
+ "github.com/astranetworld/ast/modules/rawdb"
+ "github.com/astranetworld/ast/params"
+ "github.com/holiman/uint256"
+ "github.com/ledgerwatch/erigon-lib/kv"
+ "sync"
+)
+
+const (
+ //
+ DayPerMonth = 30
+ //
+ fiftyDeposit = 50
+ OneHundredDeposit = 100
+ FiveHundredDeposit = 500
+
+ fuji2000Deposit = 2000
+ fuji800Deposit = 800
+ fuji200Deposit = 200
+ //
+ fiftyDepositMaxTaskPerEpoch = 500
+ OneHundredDepositMaxTaskPerEpoch = 100
+ FiveHundredDepositMaxTaskPerEpoch = 100
+ //
+ fujiMaxTaskPerEpoch = 50
+ //
+ fiftyDepositRewardPerMonth = 0.375 * params.AMT
+ OneHundredDepositRewardPerMonth = 1 * params.AMT
+ FiveHundredDepositRewardPerMonth = 6.25 * params.AMT //max uint64 = ^uint64(0) ≈ 18.44 AMT so 15 AMT is ok
+ //
+ fuji200RewardPerEpoch = 0.025 * params.AMT
+ fuji800RewardPerEpoch = 0.1 * params.AMT
+ fuji2000RewardPerMonth = 10 * params.AMT
+)
+
+// DepositContract d
+type DepositContract interface {
+ WithdrawnSignature() types.Hash
+ DepositSignature() types.Hash
+ UnpackDepositLogData(data []byte) (publicKey []byte, signature []byte, depositAmount *uint256.Int, err error)
+ IsDepositAction(sig [4]byte) bool
+}
+
+func GetDepositInfo(tx kv.Tx, addr types.Address) *Info {
+
+ pubkey, depositAmount, err := rawdb.GetDeposit(tx, addr)
+ if err != nil {
+ return nil
+ }
+
+ var (
+ maxRewardPerEpoch *uint256.Int
+ rewardPerBlock *uint256.Int
+ )
+ depositEther := new(uint256.Int).Div(depositAmount, uint256.NewInt(params.AMT)).Uint64()
+ switch depositEther {
+ case fiftyDeposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(fiftyDepositRewardPerMonth), uint256.NewInt(DayPerMonth*fiftyDepositMaxTaskPerEpoch))
+ maxRewardPerEpoch = new(uint256.Int).Mul(rewardPerBlock, uint256.NewInt(fiftyDepositMaxTaskPerEpoch))
+ case OneHundredDeposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(OneHundredDepositRewardPerMonth), uint256.NewInt(DayPerMonth*OneHundredDepositMaxTaskPerEpoch))
+ rewardPerBlock = new(uint256.Int).Add(rewardPerBlock, uint256.NewInt(params.Wei))
+ maxRewardPerEpoch = new(uint256.Int).Mul(rewardPerBlock, uint256.NewInt(OneHundredDepositMaxTaskPerEpoch))
+ case FiveHundredDeposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(FiveHundredDepositRewardPerMonth), uint256.NewInt(DayPerMonth*FiveHundredDepositMaxTaskPerEpoch))
+ rewardPerBlock = new(uint256.Int).Add(rewardPerBlock, uint256.NewInt(params.Wei))
+ //
+ maxRewardPerEpoch = new(uint256.Int).Mul(rewardPerBlock, uint256.NewInt(FiveHundredDepositMaxTaskPerEpoch))
+ case fuji200Deposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(fuji200RewardPerEpoch), uint256.NewInt(fujiMaxTaskPerEpoch))
+ maxRewardPerEpoch = new(uint256.Int).SetUint64(fuji200RewardPerEpoch)
+ case fuji800Deposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(fuji800RewardPerEpoch), uint256.NewInt(fujiMaxTaskPerEpoch))
+ maxRewardPerEpoch = new(uint256.Int).SetUint64(fuji800RewardPerEpoch)
+ case fuji2000Deposit:
+ rewardPerBlock = new(uint256.Int).Div(uint256.NewInt(fuji2000RewardPerMonth), uint256.NewInt(DayPerMonth*fujiMaxTaskPerEpoch))
+ rewardPerBlock = new(uint256.Int).Add(rewardPerBlock, uint256.NewInt(params.Wei))
+
+ maxRewardPerEpoch = new(uint256.Int).Mul(rewardPerBlock, uint256.NewInt(fujiMaxTaskPerEpoch))
+ case 10: //todo
+ return nil
+ default:
+ panic("wrong deposit amount")
+ }
+
+ return &Info{
+ pubkey,
+ depositAmount,
+ rewardPerBlock,
+ maxRewardPerEpoch,
+ }
+}
+
+type Info struct {
+ PublicKey types.PublicKey `json:"PublicKey"`
+ DepositAmount *uint256.Int `json:"DepositAmount"`
+ RewardPerBlock *uint256.Int `json:"RewardPerBlock"`
+ MaxRewardPerEpoch *uint256.Int `json:"MaxRewardPerEpoch"`
+}
+
+//func NewInfo(depositAmount uint256.Int, publicKey bls.PublicKey) *Info {
+// return &Info{
+// PublicKey: publicKey,
+// DepositAmount: depositAmount,
+// }
+//}
+
+type Deposit struct {
+ ctx context.Context
+ cancel context.CancelFunc
+ wg sync.WaitGroup
+ blockChain common.IBlockChain
+ db kv.RwDB
+
+ logsSub event.Subscription // Subscription for new log event
+ rmLogsSub event.Subscription // Subscription for removed log event
+
+ logsCh chan common.NewLogsEvent // Channel to receive new log event
+ rmLogsCh chan common.RemovedLogsEvent // Channel to receive removed log event
+
+ depositContracts map[types.Address]DepositContract
+}
+
+func NewDeposit(ctx context.Context, bc common.IBlockChain, db kv.RwDB, depositContracts map[types.Address]DepositContract) *Deposit {
+ c, cancel := context.WithCancel(ctx)
+ d := &Deposit{
+ ctx: c,
+ cancel: cancel,
+ blockChain: bc,
+ db: db,
+ logsCh: make(chan common.NewLogsEvent),
+ rmLogsCh: make(chan common.RemovedLogsEvent),
+ depositContracts: depositContracts,
+ }
+
+ d.logsSub = event.GlobalEvent.Subscribe(d.logsCh)
+ d.rmLogsSub = event.GlobalEvent.Subscribe(d.rmLogsCh)
+
+ if d.logsSub == nil || d.rmLogsSub == nil {
+ log.Error("Subscribe for event system failed")
+ }
+ return d
+}
+
+func (d *Deposit) Start() {
+ d.wg.Add(1)
+ go d.eventLoop()
+}
+
+func (d *Deposit) Stop() error {
+ d.cancel()
+ d.wg.Wait()
+ return nil
+}
+
+func (d *Deposit) IsDepositAction(txs *transaction.Transaction) bool {
+ var (
+ depositContract DepositContract
+ foundDepositContract bool
+ )
+ to := txs.To()
+ if to == nil {
+ return false
+ }
+ if depositContract, foundDepositContract = d.depositContracts[*to]; !foundDepositContract {
+ return false
+ }
+
+ if len(txs.Data()) < 4 {
+ return false
+ }
+
+ var sig [4]byte
+ copy(sig[:], txs.Data()[:4])
+ if !depositContract.IsDepositAction(sig) {
+ return false
+ }
+
+ return true
+}
+
+func (d *Deposit) eventLoop() {
+ // Ensure all subscriptions get cleaned up
+ defer func() {
+ d.logsSub.Unsubscribe()
+ d.rmLogsSub.Unsubscribe()
+ d.wg.Done()
+ log.Info("Context closed, exiting goroutine (eventLoop)")
+ }()
+
+ for {
+ select {
+ case logEvent := <-d.logsCh:
+ for _, l := range logEvent.Logs {
+ if depositContract, found := d.depositContracts[l.Address]; found {
+ if l.Topics[0] == depositContract.DepositSignature() {
+ d.handleDepositEvent(l.TxHash, l.Data, depositContract)
+ } else if l.Topics[0] == depositContract.WithdrawnSignature() {
+ d.handleWithdrawnEvent(l.TxHash, l.Data)
+ }
+ }
+ }
+ case logRemovedEvent := <-d.rmLogsCh:
+ for _, l := range logRemovedEvent.Logs {
+ log.Info("logEvent", "address", l.Address, "data", l.Data, "")
+ }
+ case <-d.logsSub.Err():
+ return
+ case <-d.rmLogsSub.Err():
+ return
+ case <-d.ctx.Done():
+ return
+ }
+ }
+}
+
+func (d *Deposit) handleDepositEvent(txHash types.Hash, data []byte, depositContract DepositContract) {
+ // 1
+ pb, sig, amount, err := depositContract.UnpackDepositLogData(data)
+ if err != nil {
+ log.Warn("cannot unpack deposit log data", "err", err)
+ return
+ }
+ // 2
+ signature, err := bls.SignatureFromBytes(sig)
+ if err != nil {
+ log.Warn("cannot unpack BLS signature", "signature", hexutil.Encode(sig), "err", err)
+ return
+ }
+ // 3
+ publicKey, err := bls.PublicKeyFromBytes(pb)
+ if err != nil {
+ log.Warn("cannot unpack BLS publicKey", "publicKey", hexutil.Encode(pb), "err", err)
+ return
+ }
+ // 4
+ log.Trace("DepositEvent verify:", "signature", hexutil.Encode(signature.Marshal()), "publicKey", hexutil.Encode(publicKey.Marshal()), "msg", hexutil.Encode(amount.Bytes()))
+ if signature.Verify(publicKey, amount.Bytes()) {
+ var tx *transaction.Transaction
+ rwTx, err := d.db.BeginRw(d.ctx)
+ defer rwTx.Rollback()
+ if err != nil {
+ log.Error("cannot open db", "err", err)
+ return
+ }
+
+ tx, _, _, _, err = rawdb.ReadTransactionByHash(rwTx, txHash)
+ if err != nil {
+ log.Error("rawdb.ReadTransactionByHash", "err", err, "hash", txHash)
+ }
+
+ if tx != nil {
+ log.Info("add Deposit info", "address", tx.From(), "amount", amount.String())
+
+ var pub types.PublicKey
+ pub.SetBytes(publicKey.Marshal())
+ //
+ rawdb.PutDeposit(rwTx, *tx.From(), pub, *amount)
+ rwTx.Commit()
+ }
+ } else {
+ log.Error("DepositEvent cannot Verify signature", "signature", hexutil.Encode(sig), "publicKey", hexutil.Encode(pb), "message", hexutil.Encode(amount.Bytes()), "err", err)
+ }
+}
+
+func (d *Deposit) handleWithdrawnEvent(txHash types.Hash, data []byte) {
+ var tx *transaction.Transaction
+
+ rwTx, err := d.db.BeginRw(d.ctx)
+ defer rwTx.Rollback()
+ if err != nil {
+ log.Error("cannot open db", "err", err)
+ return
+ }
+ tx, _, _, _, err = rawdb.ReadTransactionByHash(rwTx, txHash)
+ if err != nil {
+ log.Error("rawdb.ReadTransactionByHash", "err", err, "hash", txHash)
+ return
+ }
+ if tx == nil {
+ log.Error("cannot find Transaction", "err", err, "hash", txHash)
+ return
+ }
+
+ err = rawdb.DeleteDeposit(rwTx, *tx.From())
+ if err != nil {
+ log.Error("cannot delete deposit", "err", err)
+ return
+ }
+ rwTx.Commit()
+}
diff --git a/contracts/deposit/contract_test.go b/contracts/deposit/contract_test.go
new file mode 100644
index 0000000..8a88ef6
--- /dev/null
+++ b/contracts/deposit/contract_test.go
@@ -0,0 +1,79 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package deposit
+
+import (
+ "github.com/astranetworld/ast/common/crypto/bls"
+ "github.com/astranetworld/ast/common/hexutil"
+ "github.com/astranetworld/ast/params"
+ "github.com/holiman/uint256"
+ "testing"
+)
+
+func TestBLS(t *testing.T) {
+ sig, _ := hexutil.Decode("0xab22c6b63e3595630ffe8ed2903dfeba2a781c2d33dc66f88442982b65c5fcce9a8078f9ae419c95eecd2a5546a06e371196855311a930a4ad404321083f4f058c41e2d6c2e1f2bf11b1cd9b73d65a0a169a81cc1e60b50164aa7b322396be67")
+ bp, _ := hexutil.Decode("0xa20699fa55487f79c1400e2be5bb6acf89b0c5880becfa4b0560b9994bd8050616886a8fb71bdf15065dd31dd2858c18")
+ msg := new(uint256.Int).Mul(uint256.NewInt(params.AMT), uint256.NewInt(50)) //50AMT
+ signature, err := bls.SignatureFromBytes(sig)
+ if err != nil {
+ t.Fatal("cannot unpack BLS signature", err)
+ }
+
+ publicKey, err := bls.PublicKeyFromBytes(bp)
+
+ if err != nil {
+ t.Fatal("cannot unpack BLS publicKey", err)
+ }
+
+ if !signature.Verify(publicKey, msg.Bytes()) {
+ t.Fatal("bls cannot verify signature")
+ }
+
+}
+
+func TestUint256(t *testing.T) {
+ amt50Hex, _ := hexutil.Decode("0x2B5E3AF16B1880000") // 50 AMT
+ amt50Uint256 := new(uint256.Int).Mul(uint256.NewInt(params.AMT), uint256.NewInt(50))
+ //
+ t.Logf("50 AMT uint256 bytes:%s, hex Bytes: %s", hexutil.Encode(amt50Uint256.Bytes()), hexutil.Encode(amt50Hex))
+
+ amt500Hex, _ := hexutil.Decode("0x1B1AE4D6E2EF500000") // 500 AMT
+ amt500Uint256 := new(uint256.Int).Mul(uint256.NewInt(params.AMT), uint256.NewInt(500))
+ //
+ t.Logf("500 AMT uint256 bytes:%s, hex Bytes: %s", hexutil.Encode(amt500Uint256.Bytes()), hexutil.Encode(amt500Hex))
+
+ amt100Hex, _ := hexutil.Decode("0x56BC75E2D63100000") // 100 AMT
+ amt100Uint256 := new(uint256.Int).Mul(uint256.NewInt(params.AMT), uint256.NewInt(100))
+ //
+ t.Logf("100 AMT uint256 bytes:%s, hex Bytes: %s", hexutil.Encode(amt100Uint256.Bytes()), hexutil.Encode(amt100Hex))
+}
+
+//func TestPrivateKey(t *testing.T) {
+// private, _ := hexutil.Decode("0xde4b76c3dca3d8e10aea7644f77b316a68a6476fbd119d441ead5c6131aa42a7")
+//
+// p, err := bls.SecretKeyFromBytes(private)
+// if err != nil {
+// t.Fatal("bls cannot import private key", err)
+// }
+//
+// pub, err := p.PublicKey().MarshalText()
+//
+// if err != nil {
+// t.Fatal("bls public key cannot MarshalText ", err)
+// }
+// t.Logf("pubkey %s", string(pub))
+//}
diff --git a/core/error.go b/core/error.go
new file mode 100644
index 0000000..d63332a
--- /dev/null
+++ b/core/error.go
@@ -0,0 +1,101 @@
+// Copyright 2023 The astranet Authors
+// This file is part of the astranet library.
+//
+// The astranet library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The astranet library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the astranet library. If not, see .
+
+package core
+
+import (
+ "errors"
+ "fmt"
+)
+
+var (
+ ErrInvalidBlock = fmt.Errorf("invalid block")
+ ErrInvalidPubSub = fmt.Errorf("PubSub is nil")
+)
+
+var (
+
+ // ErrBannedHash is returned if a block to import is on the banned list.
+ ErrBannedHash = errors.New("banned hash")
+
+ // ErrNoGenesis is returned when there is no Genesis Block.
+ ErrNoGenesis = errors.New("genesis not found in chain")
+
+ errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data")
+)
+
+// List of evm-call-message pre-checking errors. All state transition messages will
+// be pre-checked before execution. If any invalidation detected, the corresponding
+// error should be returned which is defined here.
+//
+// - If the pre-checking happens in the miner, then the transaction won't be packed.
+// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk"
+// error should be emitted.
+var (
+ // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
+ // one present in the local chain.
+ ErrNonceTooLow = errors.New("nonce too low")
+
+ // ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
+ // next one expected based on the local chain.
+ ErrNonceTooHigh = errors.New("nonce too high")
+
+ // ErrNonceMax is returned if the nonce of a transaction sender account has
+ // maximum allowed value and would become invalid if incremented.
+ ErrNonceMax = errors.New("nonce has max value")
+
+ // ErrGasLimitReached is returned by the gas pool if the amount of gas required
+ // by a transaction is higher than what's left in the block.
+ ErrGasLimitReached = errors.New("gas limit reached")
+
+ // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
+ // have enough funds for transfer(topmost call only).
+ ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
+
+ // ErrInsufficientFunds is returned if the total cost of executing a transaction
+ // is higher than the balance of the user's account.
+ ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
+
+ // ErrGasUintOverflow is returned when calculating gas usage.
+ ErrGasUintOverflow = errors.New("gas uint64 overflow")
+
+ // ErrIntrinsicGas is returned if the transaction is specified to use less gas
+ // than required to start the invocation.
+ ErrIntrinsicGas = errors.New("intrinsic gas too low")
+
+ // ErrTxTypeNotSupported is returned if a transaction is not supported in the
+ // current network configuration.
+ ErrTxTypeNotSupported = errors.New("transaction type not supported")
+
+ // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
+ // transaction with a tip higher than the total fee cap.
+ ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas")
+
+ // ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
+ // in the tip field.
+ ErrTipVeryHigh = errors.New("max priority fee per gas higher than 2^256-1")
+
+ // ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified
+ // in the fee cap field.
+ ErrFeeCapVeryHigh = errors.New("max fee per gas higher than 2^256-1")
+
+ // ErrFeeCapTooLow is returned if the transaction fee cap is less than the
+ // the base fee of the block.
+ ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
+
+ // ErrSenderNoEOA is returned if the sender of a transaction is a contract.
+ ErrSenderNoEOA = errors.New("sender not an eoa")
+)
diff --git a/deployments/explorer/config.env b/deployments/explorer/config.env
new file mode 100644
index 0000000..00b7ec1
--- /dev/null
+++ b/deployments/explorer/config.env
@@ -0,0 +1,13 @@
+NETWORK="ast astranet"
+SUBNETWORK=astranet Explorer
+ETHEREUM_JSONRPC_VARIANT=geth
+BLOCK_TRANSFORMER=clique
+ETHEREUM_JSONRPC_HTTP_URL=http://bootnode:20012
+ETHEREUM_JSONRPC_WS_URL=ws://bootnode:20013
+ECTO_USE_SSL=false
+COIN=SNX
+BLOCKSCOUT_VERSION=1.0
+COIN_NAME=ast
+LOGO=/images/blockscout_logo_ast.svg
+# INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=true
+# INDEXER_DISABLE_ADDRESS_COIN_BALANCE_FETCHER=true
\ No newline at end of file
diff --git a/deployments/influxdb/config.yml b/deployments/influxdb/config.yml
new file mode 100644
index 0000000..abd60f3
--- /dev/null
+++ b/deployments/influxdb/config.yml
@@ -0,0 +1,70 @@
+assets-path: ""
+bolt-path: /var/lib/influxdb2/influxd.bolt
+e2e-testing: false
+engine-path: /var/lib/influxdb2/engine
+feature-flags: {}
+flux-log-enabled: false
+hardening-enabled: false
+http-bind-address: :8086
+http-idle-timeout: 3m0s
+http-read-header-timeout: 10s
+http-read-timeout: 0s
+http-write-timeout: 0s
+influxql-max-select-buckets: 0
+influxql-max-select-point: 0
+influxql-max-select-series: 0
+instance-id: ""
+key-name: ""
+log-level: info
+metrics-disabled: false
+nats-max-payload-bytes: 0
+nats-port: 4222
+no-tasks: false
+pprof-disabled: false
+query-concurrency: 1024
+query-initial-memory-bytes: 0
+query-max-memory-bytes: 0
+query-memory-bytes: 0
+query-queue-size: 1024
+reporting-disabled: false
+secret-store: bolt
+session-length: 60
+session-renew-disabled: false
+sqlite-path: ""
+storage-cache-max-memory-size: 1073741824
+storage-cache-snapshot-memory-size: 26214400
+storage-cache-snapshot-write-cold-duration: 10m0s
+storage-compact-full-write-cold-duration: 4h0m0s
+storage-compact-throughput-burst: 50331648
+storage-max-concurrent-compactions: 0
+storage-max-index-log-file-size: 1048576
+storage-no-validate-field-size: false
+storage-retention-check-interval: 30m0s
+storage-series-file-max-concurrent-snapshot-compactions: 0
+storage-series-id-set-cache-size: 0
+storage-shard-precreator-advance-period: 30m0s
+storage-shard-precreator-check-interval: 10m0s
+storage-tsm-use-madv-willneed: false
+storage-validate-keys: false
+storage-wal-fsync-delay: 0s
+storage-wal-max-concurrent-writes: 0
+storage-wal-max-write-delay: 10m0s
+storage-write-timeout: 10s
+store: disk
+testing-always-allow-setup: false
+tls-cert: ""
+tls-key: ""
+tls-min-version: "1.2"
+tls-strict-ciphers: false
+tracing-type: ""
+ui-disabled: false
+vault-addr: ""
+vault-cacert: ""
+vault-capath: ""
+vault-client-cert: ""
+vault-client-key: ""
+vault-client-timeout: 0s
+vault-max-retries: 0
+vault-skip-verify: false
+vault-tls-server-name: ""
+vault-token: ""
diff --git a/deployments/influxdb/influx-configs b/deployments/influxdb/influx-configs
new file mode 100644
index 0000000..f5c64b6
--- /dev/null
+++ b/deployments/influxdb/influx-configs
@@ -0,0 +1,5 @@
+[default]
+ url = "http://influxdb:8086"
+ token = "ksLlfJ0cRCNXRtrFVpKiZ2au3-pOWL-hgVhgsCqivAlq7Vf0VdyjjVMWA__IhjkcI_GbWccpwRDpQiIGdm2FpA=="
+ org = "astranet"
+ active = true
\ No newline at end of file
diff --git a/deployments/prometheus/dashboards/amc.json b/deployments/prometheus/dashboards/amc.json
new file mode 100644
index 0000000..3443277
--- /dev/null
+++ b/deployments/prometheus/dashboards/amc.json
@@ -0,0 +1,1911 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 1,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 4,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Blockchain",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 5,
+ "x": 0,
+ "y": 1
+ },
+ "id": 110,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "sync{instance=~\"$instance\",stage=\"headers\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "header: {{instance}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "chain_head_block{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "block: {{instance}}",
+ "refId": "C"
+ }
+ ],
+ "title": "Chain head",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 5,
+ "x": 5,
+ "y": 1
+ },
+ "id": 116,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "txpool_pending{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "pending: {{instance}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "txpool_local{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "local: {{instance}}",
+ "range": true,
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "txpool_queued{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "queued: {{instance}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Transaction pool",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 7,
+ "x": 10,
+ "y": 1
+ },
+ "id": 106,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "increase(process_cpu_seconds_system_total{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "system: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "increase(process_cpu_seconds_user_total{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "iowait: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "CPU",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 7,
+ "x": 17,
+ "y": 1
+ },
+ "id": 154,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_stack_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "stack_sys: {{ instance }}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "max: {{ instance }}",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_stack_inuse_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "stack_inuse: {{ instance }}",
+ "range": true,
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_mspan_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "mspan_sys: {{ instance }}",
+ "range": true,
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_mcache_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "mcache_sys: {{ instance }}",
+ "range": true,
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_heap_alloc_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "current: {{ instance }}",
+ "range": true,
+ "refId": "F"
+ }
+ ],
+ "title": "Memory Use",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "sign number",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 9,
+ "x": 0,
+ "y": 12
+ },
+ "id": 197,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "disableTextWrap": false,
+ "editorMode": "builder",
+ "expr": "block_sign_counter{instance=~\"$instance\"}",
+ "fullMetaSearch": false,
+ "includeNullMetadata": true,
+ "instant": false,
+ "legendFormat": "sign:{{instance}}",
+ "range": true,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Block signatures",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 7,
+ "x": 10,
+ "y": 12
+ },
+ "id": 77,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull",
+ "max",
+ "min"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "p2p_peer_count{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "peers: {{instance}}-{{state}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "rate(p2p_dials{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "dials: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "rate(p2p_serves{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "serves: {{instance}}",
+ "refId": "C"
+ }
+ ],
+ "title": "Peers",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "Bps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 7,
+ "x": 17,
+ "y": 12
+ },
+ "id": 96,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(p2p_ingress{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "ingress: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(p2p_egress{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "egress: {{instance}}",
+ "refId": "C"
+ }
+ ],
+ "title": "Network Traffic",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 7,
+ "x": 10,
+ "y": 23
+ },
+ "id": 85,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_storage_read_bytes_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "read: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_storage_written_bytes_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "write: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "Disk bytes/sec",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 7,
+ "x": 17,
+ "y": 23
+ },
+ "id": 159,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.4.7",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "db_size{instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "size: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "db_mi_last_pgno{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "db_mi_last_pgno: {{instance}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "DB Size",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 31
+ },
+ "id": 183,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "RPC",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "reqps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 32
+ },
+ "id": 185,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(rpc_duration_seconds_count{instance=~\"$instance\",success=\"success\"}[1m])",
+ "interval": "",
+ "legendFormat": "success {{ method }} {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(rpc_duration_seconds_count{instance=~\"$instance\",success=\"failure\"}[1m])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "failure {{ method }} {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "RPS",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 32
+ },
+ "id": 187,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rpc_duration_seconds{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": " {{ method }} {{ instance }} {{ success }}",
+ "refId": "A"
+ }
+ ],
+ "title": "Timings",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 7,
+ "x": 12,
+ "y": 40
+ },
+ "id": 189,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_keys_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "keys: {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_list_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "list: {{ instance }} ",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_code_keys_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code_keys: {{ instance }} ",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_code_list_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code_list: {{ instance }} ",
+ "refId": "D"
+ }
+ ],
+ "title": "Cache keys",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 5,
+ "x": 19,
+ "y": 40
+ },
+ "id": 184,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "sum(delta(cache_total{result=\"hit\",name=\"rpc\",instance=~\"$instance\"}[1m]))/sum(delta(cache_total{name=\"rpc\",instance=~\"$instance\"}[1m])) ",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "hit rate: {{ instance }} ",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "sum(delta(cache_code_total{result=\"hit\",name=\"rpc\",instance=~\"$instance\"}[1m]))/sum(delta(cache_code_total{name=\"rpc\",instance=~\"$instance\"}[1m])) ",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code hit rate: {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "Cache hit-rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 48
+ },
+ "id": 138,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Private api",
+ "type": "row"
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fill": 1,
+ "fillGradient": 0,
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 49
+ },
+ "hiddenSeries": false,
+ "id": 136,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "options": {
+ "alertThreshold": true
+ },
+ "percentage": false,
+ "pluginVersion": "10.1.2",
+ "pointradius": 2,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "sum by (grpc_service, grpc_method, instance) (rate(grpc_server_started_total{instance=~\"$instance\"}[1m]))",
+ "interval": "",
+ "legendFormat": "Calls: {{grpc_service}}.{{grpc_method}}, {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "sum by (grpc_service, grpc_method, instance) (rate(grpc_server_handled_total{instance=~\"$instance\",grpc_code!=\"OK\"}[1m])) ",
+ "interval": "",
+ "legendFormat": "Errors: {{grpc_service}}.{{grpc_method}}, {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "thresholds": [],
+ "timeRegions": [],
+ "title": "gRPC call, error rates ",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "mode": "time",
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ },
+ {
+ "format": "short",
+ "logBase": 1,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false
+ }
+ }
+ ],
+ "refresh": "30s",
+ "revision": 1,
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": false,
+ "text": "0.97",
+ "value": "0.97"
+ },
+ "hide": 0,
+ "includeAll": false,
+ "multi": false,
+ "name": "quantile",
+ "options": [
+ {
+ "selected": false,
+ "text": "0.0",
+ "value": "0.0"
+ },
+ {
+ "selected": false,
+ "text": "0.25",
+ "value": "0.25"
+ },
+ {
+ "selected": false,
+ "text": "0.5",
+ "value": "0.5"
+ },
+ {
+ "selected": false,
+ "text": "0.9",
+ "value": "0.9"
+ },
+ {
+ "selected": true,
+ "text": "0.97",
+ "value": "0.97"
+ },
+ {
+ "selected": false,
+ "text": "0.99",
+ "value": "0.99"
+ },
+ {
+ "selected": false,
+ "text": "1",
+ "value": "1"
+ }
+ ],
+ "query": "0.0,0.25,0.5, 0.9, 0.97, 0.99, 1",
+ "queryValue": "",
+ "skipUrlSync": false,
+ "type": "custom"
+ },
+ {
+ "current": {
+ "selected": false,
+ "text": "All",
+ "value": "$__all"
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "go_goroutines",
+ "hide": 0,
+ "includeAll": true,
+ "label": "instance",
+ "multi": true,
+ "name": "instance",
+ "options": [],
+ "query": {
+ "query": "go_goroutines",
+ "refId": "StandardVariableQuery"
+ },
+ "refresh": 1,
+ "regex": "/.*instance=\"([^\"]*).*/",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "auto": false,
+ "auto_count": 30,
+ "auto_min": "10s",
+ "current": {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ "hide": 0,
+ "label": "Rate Interval",
+ "name": "rate_interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": true,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "30m",
+ "value": "30m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ },
+ {
+ "selected": false,
+ "text": "6h",
+ "value": "6h"
+ },
+ {
+ "selected": false,
+ "text": "12h",
+ "value": "12h"
+ },
+ {
+ "selected": false,
+ "text": "1d",
+ "value": "1d"
+ },
+ {
+ "selected": false,
+ "text": "7d",
+ "value": "7d"
+ },
+ {
+ "selected": false,
+ "text": "14d",
+ "value": "14d"
+ },
+ {
+ "selected": false,
+ "text": "30d",
+ "value": "30d"
+ }
+ ],
+ "query": "1m,10m,30m,1h,3h,6h,12h,1d,7d,14d,30d",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "astranet",
+ "uid": "FPpjH6Hik",
+ "version": 14,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/deployments/prometheus/dashboards/amc_internal.json b/deployments/prometheus/dashboards/amc_internal.json
new file mode 100644
index 0000000..c63b59d
--- /dev/null
+++ b/deployments/prometheus/dashboards/amc_internal.json
@@ -0,0 +1,4600 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 2,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 171,
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Blocks execution",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "0.025"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 1
+ },
+ "id": 199,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": " ceil(increase(chain_execution_bucket{instance=~\"$instance\", le!=\"+Inf\"}[1m]))",
+ "format": "heatmap",
+ "fullMetaSearch": false,
+ "includeNullMetadata": false,
+ "instant": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "{{le}}",
+ "range": false,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Block Execution speed ",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "0.025"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 8,
+ "y": 1
+ },
+ "id": 202,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": " ceil(increase(chain_inserts_bucket{instance=~\"$instance\", le!=\"+Inf\"}[1m]))",
+ "format": "heatmap",
+ "fullMetaSearch": false,
+ "includeNullMetadata": false,
+ "instant": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "{{le}}",
+ "range": false,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Block insert speed ",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "0.025"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 16,
+ "y": 1
+ },
+ "id": 201,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": " ceil(increase(chain_validation_bucket{instance=~\"$instance\", le!=\"+Inf\"}[1m]))",
+ "format": "heatmap",
+ "fullMetaSearch": false,
+ "includeNullMetadata": false,
+ "instant": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "{{le}}",
+ "range": false,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Block validate speed ",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "0.025"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 8,
+ "y": 6
+ },
+ "id": 200,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "disableTextWrap": false,
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": " ceil(increase(chain_write_bucket{instance=~\"$instance\", le!=\"+Inf\"}[1m]))",
+ "format": "heatmap",
+ "fullMetaSearch": false,
+ "includeNullMetadata": false,
+ "instant": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "{{le}}",
+ "range": false,
+ "refId": "A",
+ "useBackend": false
+ }
+ ],
+ "title": "Block write speed ",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 11
+ },
+ "id": 17,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Database",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "min": 0.001,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 12
+ },
+ "id": 141,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_commit_seconds_count{phase=\"total\",instance=~\"$instance\"}[$rate_interval])",
+ "interval": "",
+ "legendFormat": "commit: {{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Commit",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 2,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 16,
+ "x": 8,
+ "y": 12
+ },
+ "id": 166,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_commit_seconds{phase=\"total\",quantile=\"$quantile\",instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "total: {{instance}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_commit_seconds{phase=\"gc_wall_clock\",quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_wall_clock: {{instance}}",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_commit_seconds{phase=\"write\",quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "write: {{instance}}",
+ "range": true,
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_commit_seconds{phase=\"sync\",quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "sync: {{instance}}",
+ "range": true,
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_self_rtime_cpu{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_self_rtime_cpu: {{instance}}",
+ "range": true,
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_work_rtime_cpu{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_work_rtime_cpu: {{instance}}",
+ "range": true,
+ "refId": "F"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_work_rtime{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_work_rtime: {{instance}}",
+ "range": true,
+ "refId": "G"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_self_rtime{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_self_rtime: {{instance}}",
+ "range": true,
+ "refId": "H"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_commit_seconds{phase=\"gc_cpu_time\",quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_cpu_time: {{instance}}",
+ "range": true,
+ "refId": "I"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_self_xtime{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_self_xtime: {{instance}}",
+ "range": true,
+ "refId": "J"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_work_pnl_merge_time{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "work_pnl_merge_time: {{instance}}",
+ "range": true,
+ "refId": "K"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_slef_pnl_merge_time{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "self_pnl_merge_time: {{instance}}",
+ "range": true,
+ "refId": "L"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "db_gc_work_xtime{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_work_xtime: {{instance}}",
+ "range": true,
+ "refId": "M"
+ }
+ ],
+ "title": "Commit speed",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 17
+ },
+ "id": 159,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.4.7",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "db_size{instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "size: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "db_mi_last_pgno{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "db_mi_last_pgno: {{instance}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "DB Size",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 16,
+ "x": 8,
+ "y": 21
+ },
+ "id": 168,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"newly\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "newly: {{instance}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"cow\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "cow: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"clone\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "clone: {{instance}}",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"split\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "split: {{instance}}",
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"merge\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "merge: {{instance}}",
+ "range": true,
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"spill\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "spill: {{instance}}",
+ "refId": "F"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"wops\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "wops: {{instance}}",
+ "refId": "G"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"unspill\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "unspill: {{instance}}",
+ "refId": "H"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"gcrloops\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gcrloops: {{instance}}",
+ "range": true,
+ "refId": "I"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"gcwloops\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gcwloops: {{instance}}",
+ "range": true,
+ "refId": "J"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"gcxpages\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gcxpages: {{instance}}",
+ "range": true,
+ "refId": "K"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"msync\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "msync: {{instance}}",
+ "range": true,
+ "refId": "L"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"fsync\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "fsync: {{instance}}",
+ "range": true,
+ "refId": "M"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"minicore\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "minicore: {{instance}}",
+ "refId": "N"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(db_pgops{phase=\"prefault\", instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "prefault: {{instance}}",
+ "refId": "O"
+ }
+ ],
+ "title": "DB Pages Ops/sec",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 22
+ },
+ "id": 167,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "tx_limit{instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "limit: {{instance}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "tx_dirty{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "dirty: {{instance}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Tx Size",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": [
+ {
+ "__systemRef": "hideSeriesFrom",
+ "matcher": {
+ "id": "byNames",
+ "options": {
+ "mode": "exclude",
+ "names": [
+ "gc_overflow: mainnet2-1:6061"
+ ],
+ "prefix": "All except:",
+ "readOnly": true
+ }
+ },
+ "properties": [
+ {
+ "id": "custom.hideFrom",
+ "value": {
+ "legend": false,
+ "tooltip": false,
+ "viz": true
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 28
+ },
+ "id": 169,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "db_gc_leaf{instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "gc_leaf: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "db_gc_overflow{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "gc_overflow: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "exec_steps_in_db{instance=~\"$instance\"}/100",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "exec_steps_in_db: {{instance}}",
+ "range": true,
+ "refId": "E"
+ }
+ ],
+ "title": "GC and State",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 8,
+ "y": 28
+ },
+ "id": 150,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_minor_pagefaults_total{instance=~\"$instance\"}[$rate_interval])",
+ "interval": "",
+ "legendFormat": "soft: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_major_pagefaults_total{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "hard: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "getrusage: minflt - soft page faults (reclaims), majflt - hard faults",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 34
+ },
+ "id": 134,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Process",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 35
+ },
+ "id": 148,
+ "options": {
+ "legend": {
+ "calcs": [
+ "max"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "process_virtual_memory_bytes{instance=~\"$instance\"}",
+ "hide": true,
+ "interval": "",
+ "legendFormat": "resident virtual mem: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "process_resident_memory_anon_bytes{instance=~\"$instance\"}",
+ "hide": true,
+ "interval": "",
+ "legendFormat": "resident anon mem: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "process_resident_memory_bytes{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "resident mem: {{instance}}",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "mem_data{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "data: {{instance}}",
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "mem_stack{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "stack: {{instance}}",
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "mem_locked{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "locked: {{instance}}",
+ "refId": "F"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "mem_swap{instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "swap: {{instance}}",
+ "refId": "G"
+ }
+ ],
+ "title": "mem: resident set size",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 35
+ },
+ "id": 155,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_write_syscalls_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "in: {{instance}}",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_read_syscalls_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "out: {{instance}}",
+ "refId": "D"
+ }
+ ],
+ "title": "Read/Write syscall/sec",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "cps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 35
+ },
+ "id": 153,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(go_cgo_calls_count{instance=~\"$instance\"}[$rate_interval])",
+ "interval": "",
+ "legendFormat": "cgo_calls_count: {{instance}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "cgo calls",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 40
+ },
+ "id": 86,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(go_memstats_mallocs_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "memstats_mallocs_total: {{ instance }}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(go_memstats_frees_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "memstats_frees_total: {{ instance }}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Process Mem: allocate objects/sec, free",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 41
+ },
+ "id": 85,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_storage_read_bytes_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "read: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(process_io_storage_written_bytes_total{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "write: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "Disk bytes/sec",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 41
+ },
+ "id": 128,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "go_goroutines{instance=~\"$instance\"}",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "goroutines: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "expr": "go_threads{instance=~\"$instance\"}",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "threads: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "GO Goroutines and Threads",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 45
+ },
+ "id": 106,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "increase(process_cpu_seconds_system_total{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "system: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "increase(process_cpu_seconds_user_total{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "iowait: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "CPU",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "description": "",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "decbytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 47
+ },
+ "id": 154,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_stack_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "stack_sys: {{ instance }}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "sys: {{ instance }}",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_stack_inuse_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "stack_inuse: {{ instance }}",
+ "range": true,
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_mspan_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "mspan_sys: {{ instance }}",
+ "range": true,
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_mcache_sys_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "mcache_sys: {{ instance }}",
+ "range": true,
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "go_memstats_heap_alloc_bytes{instance=~\"$instance\"}",
+ "format": "time_series",
+ "hide": false,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "heap_alloc: {{ instance }}",
+ "range": true,
+ "refId": "F"
+ }
+ ],
+ "title": "go memstat",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 16,
+ "y": 47
+ },
+ "id": 124,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "rate(go_gc_duration_seconds{quantile=\"0.75\",instance=~\"$instance\"}[$rate_interval])",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "",
+ "refId": "A"
+ }
+ ],
+ "title": "GC Stop the World per sec",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 53
+ },
+ "id": 173,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "TxPool",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 54
+ },
+ "id": 175,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_process_remote_txs{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "process_remote_txs: {{ instance }}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_add_remote_txs{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "add_remote_txs: {{ instance }}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_new_block{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "new_block: {{ instance }}",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_write_to_db{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "write_to_db: {{ instance }}",
+ "refId": "D"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_propagate_to_new_peer{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "propagate_to_new_peer: {{ instance }}",
+ "refId": "E"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "pool_propagate_new_txs{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "propagate_new_txs: {{ instance }}",
+ "refId": "F"
+ }
+ ],
+ "title": "Timings",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "reqps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 54
+ },
+ "id": 177,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(pool_process_remote_txs_count{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "pool_process_remote_txs_count: {{ instance }}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(pool_add_remote_txs_count{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "pool_add_remote_txs_count: {{ instance }}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(pool_new_block_count{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "pool_new_block_count: {{ instance }}",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(pool_write_to_db_count{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "pool_write_to_db_count: {{ instance }}",
+ "refId": "D"
+ }
+ ],
+ "title": "RPS",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 62
+ },
+ "id": 176,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "sum(delta(cache_total{result=\"hit\",name=\"txpool\",instance=~\"$instance\"}[1m]))/sum(delta(cache_total{name=\"txpool\",instance=~\"$instance\"}[1m])) ",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "hit rate: {{ instance }} ",
+ "refId": "A"
+ }
+ ],
+ "title": "Cache hit-rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 62
+ },
+ "id": 180,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(cache_total{name=\"txpool\",instance=~\"$instance\"}[1m])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "{{ result }}: {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(cache_timeout_total{name=\"txpool\",instance=~\"$instance\"}[1m])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "timeout: {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "Cache rps",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 62
+ },
+ "id": 181,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_keys_total{name=\"txpool\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "keys: {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_list_total{name=\"txpool\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "list: {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "Cache keys",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "binBps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 0,
+ "y": 68
+ },
+ "id": 178,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(pool_write_to_db_bytes{instance=~\"$instance\"}[$rate_interval])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "pool_write_to_db_bytes: {{ instance }}",
+ "refId": "A"
+ }
+ ],
+ "title": "DB",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 74
+ },
+ "id": 183,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "RPC",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "reqps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 75
+ },
+ "id": 185,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(rpc_duration_seconds_count{instance=~\"$instance\",success=\"success\"}[1m])",
+ "interval": "",
+ "legendFormat": "success {{ method }} {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(rpc_duration_seconds_count{instance=~\"$instance\",success=\"failure\"}[1m])",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "failure {{ method }} {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "RPS",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 75
+ },
+ "id": 186,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "db_begin_seconds{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": "db_begin_seconds: {{ method }} {{ instance }}",
+ "refId": "A"
+ }
+ ],
+ "title": "DB begin",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 83
+ },
+ "id": 187,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rpc_duration_seconds{quantile=\"$quantile\",instance=~\"$instance\"}",
+ "interval": "",
+ "legendFormat": " {{ method }} {{ instance }} {{ success }}",
+ "refId": "A"
+ }
+ ],
+ "title": "Timings",
+ "transformations": [],
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 83
+ },
+ "id": 188,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "go_goroutines{instance=~\"$instance\"}",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "go/goroutines: {{instance}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "go_threads{instance=~\"$instance\"}",
+ "instant": false,
+ "interval": "",
+ "legendFormat": "go/threads: {{instance}}",
+ "refId": "B"
+ }
+ ],
+ "title": "GO Goroutines and Threads",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 8,
+ "y": 91
+ },
+ "id": 189,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_keys_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "keys: {{ instance }} ",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_list_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "list: {{ instance }} ",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_code_keys_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code_keys: {{ instance }} ",
+ "refId": "C"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "cache_code_list_total{name=\"rpc\",instance=~\"$instance\"}",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code_list: {{ instance }} ",
+ "refId": "D"
+ }
+ ],
+ "title": "Cache keys",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 91
+ },
+ "id": 184,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "last"
+ ],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "editorMode": "code",
+ "exemplar": true,
+ "expr": "sum(delta(cache_total{result=\"hit\",name=\"rpc\",instance=~\"$instance\"}[1m]))/sum(delta(cache_total{name=\"rpc\",instance=~\"$instance\"}[1m])) ",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "hit rate: {{ instance }} ",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "sum(delta(cache_code_total{result=\"hit\",name=\"rpc\",instance=~\"$instance\"}[1m]))/sum(delta(cache_code_total{name=\"rpc\",instance=~\"$instance\"}[1m])) ",
+ "hide": false,
+ "interval": "",
+ "legendFormat": "code hit rate: {{ instance }} ",
+ "refId": "B"
+ }
+ ],
+ "title": "Cache hit-rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "datasource": {
+ "type": "prometheus"
+ },
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 97
+ },
+ "id": 75,
+ "panels": [],
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "refId": "A"
+ }
+ ],
+ "title": "Network",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "Bps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 12,
+ "x": 0,
+ "y": 98
+ },
+ "id": 96,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull",
+ "max",
+ "min"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(p2p_ingress{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "ingress: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "exemplar": true,
+ "expr": "rate(p2p_egress{instance=~\"$instance\"}[$rate_interval])",
+ "format": "time_series",
+ "hide": true,
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "egress: {{instance}}",
+ "refId": "C"
+ }
+ ],
+ "title": "Traffic",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 12,
+ "x": 12,
+ "y": 98
+ },
+ "id": 77,
+ "links": [],
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "lastNotNull",
+ "max",
+ "min"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "8.0.6",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "p2p_peer_count{instance=~\"$instance\"}",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "peers: {{instance}}-{{state}}",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "rate(p2p_dials{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "dials: {{instance}}",
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus"
+ },
+ "expr": "rate(p2p_serves{instance=~\"$instance\"}[1m])",
+ "format": "time_series",
+ "interval": "",
+ "intervalFactor": 1,
+ "legendFormat": "serves: {{instance}}",
+ "refId": "C"
+ }
+ ],
+ "title": "Peers",
+ "type": "timeseries"
+ }
+ ],
+ "refresh": "30s",
+ "revision": 1,
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": false,
+ "text": "0.97",
+ "value": "0.97"
+ },
+ "hide": 0,
+ "includeAll": false,
+ "multi": false,
+ "name": "quantile",
+ "options": [
+ {
+ "selected": false,
+ "text": "0.0",
+ "value": "0.0"
+ },
+ {
+ "selected": false,
+ "text": "0.25",
+ "value": "0.25"
+ },
+ {
+ "selected": false,
+ "text": "0.5",
+ "value": "0.5"
+ },
+ {
+ "selected": false,
+ "text": "0.9",
+ "value": "0.9"
+ },
+ {
+ "selected": true,
+ "text": "0.97",
+ "value": "0.97"
+ },
+ {
+ "selected": false,
+ "text": "0.99",
+ "value": "0.99"
+ },
+ {
+ "selected": false,
+ "text": "1",
+ "value": "1"
+ }
+ ],
+ "query": "0.0,0.25,0.5, 0.9, 0.97, 0.99, 1",
+ "queryValue": "",
+ "skipUrlSync": false,
+ "type": "custom"
+ },
+ {
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "PBFA97CFB590B2093"
+ },
+ "definition": "go_goroutines",
+ "hide": 0,
+ "includeAll": true,
+ "label": "instance",
+ "multi": true,
+ "name": "instance",
+ "options": [],
+ "query": {
+ "query": "go_goroutines",
+ "refId": "StandardVariableQuery"
+ },
+ "refresh": 1,
+ "regex": "/.*instance=\"([^\"]*).*/",
+ "skipUrlSync": false,
+ "sort": 0,
+ "tagValuesQuery": "",
+ "tagsQuery": "",
+ "type": "query",
+ "useTags": false
+ },
+ {
+ "auto": false,
+ "auto_count": 30,
+ "auto_min": "10s",
+ "current": {
+ "selected": false,
+ "text": "10m",
+ "value": "10m"
+ },
+ "hide": 0,
+ "label": "Rate Interval",
+ "name": "rate_interval",
+ "options": [
+ {
+ "selected": false,
+ "text": "1m",
+ "value": "1m"
+ },
+ {
+ "selected": true,
+ "text": "10m",
+ "value": "10m"
+ },
+ {
+ "selected": false,
+ "text": "30m",
+ "value": "30m"
+ },
+ {
+ "selected": false,
+ "text": "1h",
+ "value": "1h"
+ },
+ {
+ "selected": false,
+ "text": "3h",
+ "value": "3h"
+ },
+ {
+ "selected": false,
+ "text": "6h",
+ "value": "6h"
+ },
+ {
+ "selected": false,
+ "text": "12h",
+ "value": "12h"
+ },
+ {
+ "selected": false,
+ "text": "1d",
+ "value": "1d"
+ },
+ {
+ "selected": false,
+ "text": "7d",
+ "value": "7d"
+ },
+ {
+ "selected": false,
+ "text": "14d",
+ "value": "14d"
+ },
+ {
+ "selected": false,
+ "text": "30d",
+ "value": "30d"
+ }
+ ],
+ "query": "1m,10m,30m,1h,3h,6h,12h,1d,7d,14d,30d",
+ "queryValue": "",
+ "refresh": 2,
+ "skipUrlSync": false,
+ "type": "interval"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "astranet Internals",
+ "uid": "b42a61d7-02b1-416c-8ab4-b9c864356174",
+ "version": 19,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/deployments/prometheus/dashboards/dashboard.yml b/deployments/prometheus/dashboards/dashboard.yml
new file mode 100644
index 0000000..d8eb2f6
--- /dev/null
+++ b/deployments/prometheus/dashboards/dashboard.yml
@@ -0,0 +1,24 @@
+apiVersion: 1
+
+providers:
+ # an unique provider name
+ - name: 'ast'
+ # org id. will default to orgId 1 if not specified
+ orgId: 1
+ # name of the dashboard folder. Required
+ folder: ''
+ # folder UID. will be automatically generated if not specified
+ folderUid: ''
+ # provider type. Required
+ type: file
+ # disable dashboard deletion
+ disableDeletion: false
+ # enable dashboard editing
+ editable: true
+ # how often Grafana will scan for changed dashboards
+ updateIntervalSeconds: 10
+ # allow updating provisioned dashboards from the UI
+ allowUiUpdates: true
+ options:
+ # path to dashboard files on disk. Required
+ path: /etc/grafana/provisioning/dashboards
\ No newline at end of file
diff --git a/deployments/prometheus/datasources/prometheus.yml b/deployments/prometheus/datasources/prometheus.yml
new file mode 100644
index 0000000..d5dba38
--- /dev/null
+++ b/deployments/prometheus/datasources/prometheus.yml
@@ -0,0 +1,50 @@
+# config file version
+apiVersion: 1
+
+# list of datasources that should be deleted from the database
+deleteDatasources:
+ - name: Prometheus
+ orgId: 1
+
+# list of datasources to insert/update depending
+# whats available in the database
+datasources:
+ # name of the datasource. Required
+ - name: Prometheus
+ # datasource type. Required
+ type: prometheus
+ # access mode. direct or proxy. Required
+ access: proxy
+ # org id. will default to orgId 1 if not specified
+ orgId: 1
+ # url
+ url: http://prometheus:9090
+ # database password, if used
+ password:
+ # database user, if used
+ user:
+ # database name, if used
+ database:
+ # enable/disable basic auth
+ basicAuth: false
+ # basic auth username, if used
+ basicAuthUser:
+ # basic auth password, if used
+ basicAuthPassword:
+ # enable/disable with credentials headers
+ withCredentials:
+ # mark as default datasource. Max one per org
+ isDefault: true
+ #