diff --git a/.github/workflows/artifacts.yml b/.github/workflows/artifacts.yml new file mode 100644 index 000000000000..527a85885a3e --- /dev/null +++ b/.github/workflows/artifacts.yml @@ -0,0 +1,36 @@ +name: artifacts + +on: + push: + branches: + - devnet + - master + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.19' + + - name: Build Go-WEMIX tarball + run: USE_ROCKSDB=YES make gwemix.tar.gz + + - name: Stat Go-WEMIX tarball + run: | + ls -l build/gwemix.tar.gz + tar tf build/gwemix.tar.gz + + - name: Move results to artifact + run: mv build/gwemix.tar.gz gwemix-${{ github.ref_name }}-${{ github.sha }}-linux-amd64-rocksdb.tar.gz + + - name: Upload Go-WEMIX + uses: actions/upload-artifact@v4 + with: + name: artifact-${{ github.ref_name }}-${{ github.sha }} + path: gwemix-${{ github.ref_name }}-${{ github.sha }}-linux-amd64-rocksdb.tar.gz diff --git a/Dockerfile.wemix b/Dockerfile.wemix index 632e6ce193e0..3001685fc0f5 100644 --- a/Dockerfile.wemix +++ b/Dockerfile.wemix @@ -1,24 +1,94 @@ -# builder image +# Stage 1: Build stage +FROM golang:1.19 as builder -FROM ubuntu:focal as base +# Set environment variables +ENV PATH=/usr/local/go/bin:$PATH -SHELL ["/bin/bash", "-c"] +# Update and upgrade the package list +RUN apt-get update && \ + apt-get upgrade -q -y -RUN apt-get update -q -y && apt-get upgrade -q -y -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential ca-certificates curl libjemalloc-dev liblz4-dev libsnappy-dev libzstd-dev libudev-dev git +# Install required packages +RUN apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + openssl \ + make && \ + rm -rf /var/lib/apt/lists/* -# golang -RUN curl -sL -o /tmp/go.tar.gz https://dl.google.com/go/$(curl -sL https://golang.org/VERSION?m=text | head -1).linux-amd64.tar.gz && \ - pushd /usr/local/ && \ - tar xfz /tmp/go.tar.gz && \ - cd /usr/local/bin/ && \ - ln -sf ../go/bin/* . && \ - popd && \ - rm /tmp/go.tar.gz +# Define the location for custom certificates +ARG cert_location=/usr/local/share/ca-certificates -RUN apt autoremove && apt autoclean +# Fetch and install certificates for github.com and proxy.golang.org +RUN openssl s_client -showcerts -connect github.com:443 /dev/null | \ + openssl x509 -outform PEM > ${cert_location}/github.crt && \ + openssl s_client -showcerts -connect proxy.golang.org:443 /dev/null | \ + openssl x509 -outform PEM > ${cert_location}/proxy.golang.crt && \ + update-ca-certificates -ENTRYPOINT ["/bin/bash", "-c"] +# Clone the repository, install dependencies, and build the project +RUN git clone https://github.com/wemixarchive/go-wemix.git /go-wemix && \ + cd /go-wemix && \ + go mod download && \ + make -# EOF +# Clean up unnecessary packages and files after building +RUN apt-get remove -y \ + git \ + ca-certificates \ + openssl \ + make && \ + apt autoremove -y && \ + apt-get clean + +# Stage 2: Runtime stage +FROM ubuntu:22.04 + +# Set environment variables +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +# Update and upgrade the package list +RUN apt-get update && \ + apt-get upgrade -q -y + +# Install required runtime packages +RUN apt-get install -y --no-install-recommends \ + g++ \ + libc-dev \ + ca-certificates \ + bash \ + wget && \ + update-ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Create directories for wemix +RUN mkdir -p /usr/local/wemix /usr/local/wemix/keystore + +# Set environment variables +ENV PATH=/usr/local/wemix/bin:$PATH + +# Copy the built binaries and configuration files from the builder stage +COPY --from=builder /go-wemix/build /usr/local/wemix/ + +# Download and install solc +RUN wget -nv -O /usr/local/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux && \ + chmod a+x /usr/local/bin/solc + +# Create new accounts for wemix +RUN bash -c 'for i in 1 2 3 4; do \ + /usr/local/wemix/bin/gwemix wemix new-account --password <(echo demo) --out /usr/local/wemix/keystore/account-$i; \ +done' + +# Clean up unnecessary packages +RUN apt-get remove -y \ + g++ \ + libc-dev \ + wget && \ + apt autoremove -y && \ + apt-get clean + +# Expose necessary ports +EXPOSE 8588 8589 8598 + +# Set the entrypoint +ENTRYPOINT ["/usr/local/wemix/bin/gwemix"] \ No newline at end of file diff --git a/README.md b/README.md index 3d4dd711fd35..d844c72fb4a5 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,10 @@ After getting enodes of mining nodes, run gwemix as follows. bin/gwemix --syncmode full --datadir --bootnodes --http --http.addr 0.0.0.0 +### Starting Nodes With Docker + +See documentation [here](docs/container.md) + ### The original go-ethereum README follows... ## Go Ethereum diff --git a/build/checksums.txt b/build/checksums.txt index 4b7056e119dd..64c7fa70e77c 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -33,4 +33,52 @@ e9cb6f691e62a4d8b28dd52d2eab57cca72acfd5083b3c5417a72d2eb64def09 golangci-lint- d058dfb0c7fbd73be70f285d3f8d4d424192fe9b19760ddbb0b2c4b743b8656c golangci-lint-1.49.0-windows-amd64.zip c049d80297228db7065eabeac5114f77f04415dcd9b944e8d7c6426d9dd6e9dd golangci-lint-1.49.0-windows-arm64.zip ec9164bab7134ddb94f51c17fd37c109b0801ecd5494b6c0e27ca7898fbd7469 golangci-lint-1.49.0-windows-armv6.zip -68fd9e880d98073f436c58b6f6d2c141881ef49b06ca31137bc19da4e4e3b996 golangci-lint-1.49.0-windows-armv7.zip \ No newline at end of file +68fd9e880d98073f436c58b6f6d2c141881ef49b06ca31137bc19da4e4e3b996 golangci-lint-1.49.0-windows-armv7.zip + +01301af199161c5ea33a0350581e1bed43289a56b2fbed7539f99fca2f898278 golangci-lint-1.54.2-freebsd-amd64.tar.gz +03f1962e95066105476b8eb88b5685a95aba8ee0b41f5e986f2a61a4e1834738 golangci-lint-1.54.2-linux-mips64le.tar.gz +0a57b225c621b465bd67b7cab87e9e13063704468eb382653b5c163ec23abaa4 golangci-lint-1.54.2-windows-armv6.zip +1762e02abd5d2d7997c04682d8efe9faaa23760fa73b8a50cffc57e68f1b1173 golangci-lint-1.54.2-linux-386.deb +17c9ca05253efe833d47f38caf670aad2202b5e6515879a99873fabd4c7452b3 golangci-lint-1.54.2-linux-amd64.tar.gz +18172629827a2d595fff6a9933d9d20dc8e8ec140ad00a38e4d5723dcb7ed305 golangci-lint-1.54.2-linux-armv6.deb +19b389bc965b1fc687087dc34f09e7188f582d106c88c6c506b384be1e6656f6 golangci-lint-1.54.2-linux-amd64.rpm +2268895575ac5921633c8dd94844dea5765f1011c7814bcb73429120849a8f67 golangci-lint-1.54.2-linux-mips64le.deb +2e1a11787b08137ba16c3be6ab87de3ef5de470758b7a42441497f259e8e5a1d golangci-lint-1.54.2-windows-386.zip +30e7440fa14e2a9f4a9b9b825c61c1d6858073c74ea0aba3b2d42f08a81f4c4b golangci-lint417421bc38ff51dc18a75aa132df94a67ea078b2af2d973cdf3b1badca4b58bc golangci-lint-1.54.2-linux-ppc64le.tar.gz +42eb5bf1c935eb98f6c9ee1ce282ca0abe48475ea6bc8f682967855bd0bc5bdc golangci-lint-1.54.2-linux-mips64.rpm +50aaf3eb9b51efd2cb8ebab91725fbb1fae55d3399b66b3733118facb5087828 golangci-lint-1.54.2-source.tar.gz +522d26f9f2fefa28cc229d4acdef47e436637b7c5eaec4426217afa9061825c0 golangci-lint-1.54.2-linux-ppc64le.deb +58e0cd3b77cf0d30f59be804796a7fcae96b9fbd80c0ca7c824ec4b09de804c0 golangci-lint-1.54.2-linux-386.rpm +6a324882e39b47939b8bec23f04449c730123c9d2ff771c3f40942ab37a26d8d golangci-lint-1.54.2-windows-arm64.zip +7038b5c5cd477efbb1c828fbdf075704033f96f33598d72765f23aa87c286583 golangci-lint-1.54.2-linux-arm64.rpm +78c9f1acc357b8f9ba2a57dac40d61a091299877b1eeef6d1599f8a8715c023e golangci-lint-1.54.2-linux-loong64.tar.gz +7b11ea9de9aad90e55c66434a8a4cd30da0792b45e12a4bb5fbfd684042176fe golangci-lint-1.54.2-freebsd-armv6.tar.gz +7b33fb1be2f26b7e3d1f3c10ce9b2b5ce6d13bb1d8468a4b2ba794f05b4445e1 golangci-lint-1.54.2-darwin-arm64.tar.gz +7ce3a9e62a8835907300830aa9ac3c2e8e424c2bf98a4dcfe7e5221bd0272a89 golangci-lint-1.54.2-linux-riscv64.rpm +7eda41281f5fd722f4b60c86259bd6ece5eeef08d94c73e55987b45763d5ac9f golangci-lint-1.54.2-freebsd-armv7.tar.gz +8125ce58581d795356a09c71f82ce4fb9368e06b7ce393676907661b62c1c55b golangci-lint-1.54.2-linux-riscv64.tar.gz +839b4b620f75d9d827ec4f7ed1cf592f730a3fc5c6e2bf01f64c03ddd8d4d76c golangci-lint-1.54.2-netbsd-386.tar.gz +85674f50113bb49ffae80733b4ad493c7b58a6031ee7ded3fda2a0ae1bf94c88 golangci-lint-1.54.2-linux-loong64.rpm +860e2ab2453e7fa81d28487b91b6e479d9414baa464abac8ce1bb8f203affeec golangci-lint-1.54.2-linux-s390x.deb +8b3e8c296a9714641961e87403e4865787f16bfdce6e1ea31d46978cfbdb64e7 golangci-lint-1.54.2-linux-arm64.deb +8e42c8204dc2441b5f6873032f1f674b6b6946042d759bc244194d14da8ee7c5 golangci-lint-1.54.2-linux-riscv64.deb +8e8754108de4d8accf6bf209780b8a31ffee0d04eff6393e5a3c84c84991cd39 golangci-lint-1.54.2-linux-s390x.tar.gz +8fc12d9ab18982968a568619290c02c6a9622e29eb475db8b6eac4c1ffc1121a golangci-lint-1.54.2-linux-mips64.deb +9124be2628a93e4d4a65bf48dd049f85c20d1236e66ac321169490745f5de20e golangci-lint-1.54.2-netbsd-armv6.tar.gz +925c4097eae9e035b0b052a66d0a149f861e2ab611a4e677c7ffd2d4e05b9b89 golangci-lint-1.54.2-darwin-amd64.tar.gz +9db1154c2bfdab52822afb7ea8d505ef9e3d519402258f93a9809f4b871b9446 golangci-lint-1.54.2-freebsd-386.tar.gz +9fadc9ed7fe9a2c3abcb9113a580004d3262dbdef0d44d4271d1b7ad89657e7b golangci-lint-1.54.2-linux-s390x.rpm +a0fb8740e9771ff0b96511b04017940ada4c3d1e07b2e3c246a5078514c1be9e golangci-lint-1.54.2-illumos-amd64.tar.gz +a47ed1a6473ff9ccd2cc29d85fc197b6718fad8afcec3ae804070002f5d5f8a8 golangci-lint-1.54.2-netbsd-amd64.tar.gz +a9f14b33473c65fcfbf411ec054b53a87dbb849f4e09ee438f1ee76dbf3f3d4e golangci-lint-1.54.2-linux-arm64.tar.gz +abf8e2164bca508ab68e520ca51e887ca26498ff4c57e904a27cce05b426c81d golangci-lint-1.54.2-lintae6c9e000cdac4f39b64d01b4e8091a1600d97ffccbd1883a342e4693aade68f golangci-lint-1.54.2-linux-mips64le.rpm +b6352e83cc781c5f7e7b74d1e0e6dc137a9a200c3e852bb14009c01b8b19a7b1 golangci-lint-1.54.2-linux-armv6.tar.gz +c3a693f657488a928fc0bdd76ea242b33e2b0e77748b1f31db8a85eb84cf7954 golangci-lint-1.54.2-linux-ppc64le.rpm +c5fd737f6e3c49e168ff764b48ad4c212131d933538aa5973a60d85b8d030926 golangci-lint-1.54.2-linux-armv7.tar.gz +ce17d122f3f93e0a9e52009d2c03cc1c1a1ae28338c2702a1f53eccd10a1afa3 golangci-lint-1.54.2-windows-amd64.zip +d00b58c3fea03e314b5f0f8680606781d3c3a3428e6a4c8645997d537acd1992 golangci-lint-1.54.2-linux-mips64.tar.gz +e3091e11f040a7177f6c227514b61301189fb1bf37f4fb9da2efa041456ab81d golangci-lint-1.54.2-linux-armv7.deb +e4cfe85c6e39b7dab9d8b7f668222d9066ee8f064cd3dbd4153d15b280d636f4 golangci-lint-1.54.2-netbsd-armv7.tar.gz +f0df36248c26e30f8dd516ffc0f2158d450df733f684ddd429e3aa2d5cc23bfb golangci-lint-1.54.2-linux-armv6.rpm +f2b801c93ccfbd3d17132360f632c0281f78e5780f02727115172a33afd0b08e golangci-lint-1.54.2-linux-loong64.deb +f67b8fa171605747da66f93892257337d7ae71be59ca7b07548555f025abe836 golangci-lint-1.54.2-linux-386.tar.gz diff --git a/build/ci.go b/build/ci.go index d6431ecd5fcc..8628af54cf37 100644 --- a/build/ci.go +++ b/build/ci.go @@ -343,7 +343,7 @@ func doLint(cmdline []string) { // downloadLinter downloads and unpacks golangci-lint. func downloadLinter(cachedir string) string { - const version = "1.49.0" + const version = "1.54.2" csdb := build.MustLoadChecksums("build/checksums.txt") arch := runtime.GOARCH diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index ace2849c9f06..871b8002fe18 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -25,7 +25,6 @@ import ( "sort" "strconv" "strings" - "syscall" "time" "github.com/ethereum/go-ethereum/cmd/utils" @@ -40,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" "github.com/olekukonko/tablewriter" + "golang.org/x/sys/unix" "gopkg.in/urfave/cli.v1" ) @@ -597,7 +597,7 @@ func importLDBdata(ctx *cli.Context) error { stop = make(chan struct{}) ) defer stack.Close() - signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(interrupt, unix.SIGINT, unix.SIGTERM) defer signal.Stop(interrupt) defer close(interrupt) go func() { @@ -693,7 +693,7 @@ func exportChaindata(ctx *cli.Context) error { stop = make(chan struct{}) ) defer stack.Close() - signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(interrupt, unix.SIGINT, unix.SIGTERM) defer signal.Stop(interrupt) defer close(interrupt) go func() { diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 1cdbfe866863..cdbff4d8ff1a 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -26,7 +26,6 @@ import ( "sort" "strconv" "strings" - "syscall" "time" "github.com/elastic/gosigar" @@ -45,6 +44,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/wemix" + "golang.org/x/sys/unix" // Force-load the tracer engines to trigger registration _ "github.com/ethereum/go-ethereum/eth/tracers/js" @@ -514,8 +514,8 @@ func limitMaxRss(max int64) { timer := time.NewTimer(interval) for { <-timer.C - rusage := syscall.Rusage{} - err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) + rusage := unix.Rusage{} + err := unix.Getrusage(unix.RUSAGE_SELF, &rusage) if err != nil { log.Error("Getrusage() failed:", "reason", err) } else { diff --git a/cmd/geth/wemixcmd.go b/cmd/geth/wemixcmd.go index 632ac93f51b3..d301dd9c6a9e 100644 --- a/cmd/geth/wemixcmd.go +++ b/cmd/geth/wemixcmd.go @@ -17,7 +17,6 @@ import ( "path/filepath" "strconv" "strings" - "syscall" "github.com/charlanxcc/logrot" "github.com/ethereum/go-ethereum/accounts/keystore" @@ -29,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/wemix/metclient" + "golang.org/x/sys/unix" "gopkg.in/urfave/cli.v1" ) @@ -667,10 +667,10 @@ func logrota(ctx *cli.Context) error { if err != nil { return err } - syscall.Close(syscall.Stdout) - syscall.Close(syscall.Stdout) - syscall.Dup2(int(w.Fd()), syscall.Stdout) - syscall.Dup2(int(w.Fd()), syscall.Stderr) + unix.Close(unix.Stdout) + unix.Close(unix.Stdout) + unix.Dup2(int(w.Fd()), unix.Stdout) + unix.Dup2(int(w.Fd()), unix.Stderr) go logrot.LogRotate(r, logFile, logSize, logCount) diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index 47ad3b22c8dd..d9d8e605b15f 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -27,7 +27,6 @@ import ( "os/signal" "runtime" "strings" - "syscall" "time" "github.com/ethereum/go-ethereum/common" @@ -41,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rlp" + "golang.org/x/sys/unix" "gopkg.in/urfave/cli.v1" ) @@ -74,7 +74,7 @@ func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) { } go func() { sigc := make(chan os.Signal, 1) - signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigc, unix.SIGINT, unix.SIGTERM) defer signal.Stop(sigc) minFreeDiskSpace := 2 * ethconfig.Defaults.TrieDirtyCache // Default 2 * 256Mb @@ -105,7 +105,7 @@ func StartNode(ctx *cli.Context, stack *node.Node, isConsole bool) { // However, SIGTERM still shuts down the node. for { sig := <-sigc - if sig == syscall.SIGTERM { + if sig == unix.SIGTERM { shutdown() return } @@ -126,7 +126,7 @@ func monitorFreeDiskSpace(sigc chan os.Signal, path string, freeDiskSpaceCritica } if freeSpace < freeDiskSpaceCritical { log.Error("Low disk space. Gracefully shutting down Geth to prevent database corruption.", "available", common.StorageSize(freeSpace)) - sigc <- syscall.SIGTERM + sigc <- unix.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", common.StorageSize(freeSpace), "critical_level", common.StorageSize(freeDiskSpaceCritical)) @@ -140,7 +140,7 @@ func ImportChain(chain *core.BlockChain, fn string) error { // If a signal is received, the import will stop at the next batch. interrupt := make(chan os.Signal, 1) stop := make(chan struct{}) - signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(interrupt, unix.SIGINT, unix.SIGTERM) defer signal.Stop(interrupt) defer close(interrupt) go func() { diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 96e8cd654c53..6786ac702d82 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1041,6 +1041,9 @@ func setRandomBootstrapNodes(ctx *cli.Context, bootnodes []string) []string { } // select random bootnodes selectcount := params.BootnodeCount + if selectcount > bootnodeslen { + selectcount = bootnodeslen + } urls := make([]string, selectcount) tempnode := make([]string, bootnodeslen) copy(tempnode, bootnodes) diff --git a/common/fdlimit/fdlimit_bsd.go b/common/fdlimit/fdlimit_bsd.go index a3a6902c0925..abe73b96197a 100644 --- a/common/fdlimit/fdlimit_bsd.go +++ b/common/fdlimit/fdlimit_bsd.go @@ -19,7 +19,7 @@ package fdlimit -import "syscall" +import "golang.org/x/sys/unix" // This file is largely identical to fdlimit_unix.go, // but Rlimit fields have type int64 on *BSD so it needs @@ -29,8 +29,8 @@ import "syscall" // to the maximum hard-limit allowed by the OS. func Raise(max uint64) (uint64, error) { // Get the current limit - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // Try to update the limit to the max allowance @@ -38,10 +38,10 @@ func Raise(max uint64) (uint64, error) { if limit.Cur > int64(max) { limit.Cur = int64(max) } - if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return uint64(limit.Cur), nil @@ -50,8 +50,8 @@ func Raise(max uint64) (uint64, error) { // Current retrieves the number of file descriptors allowed to be opened by this // process. func Current() (int, error) { - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return int(limit.Cur), nil @@ -60,8 +60,8 @@ func Current() (int, error) { // Maximum retrieves the maximum number of file descriptors this process is // allowed to request for itself. func Maximum() (int, error) { - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return int(limit.Max), nil diff --git a/common/fdlimit/fdlimit_darwin.go b/common/fdlimit/fdlimit_darwin.go index 6b26fa00f12c..dc932238eae7 100644 --- a/common/fdlimit/fdlimit_darwin.go +++ b/common/fdlimit/fdlimit_darwin.go @@ -16,7 +16,7 @@ package fdlimit -import "syscall" +import "golang.org/x/sys/unix" // hardlimit is the number of file descriptors allowed at max by the kernel. const hardlimit = 10240 @@ -26,8 +26,8 @@ const hardlimit = 10240 // Returns the size it was set to (may differ from the desired 'max') func Raise(max uint64) (uint64, error) { // Get the current limit - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // Try to update the limit to the max allowance @@ -35,11 +35,11 @@ func Raise(max uint64) (uint64, error) { if limit.Cur > max { limit.Cur = max } - if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // MacOS can silently apply further caps, so retrieve the actually set limit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return limit.Cur, nil @@ -48,8 +48,8 @@ func Raise(max uint64) (uint64, error) { // Current retrieves the number of file descriptors allowed to be opened by this // process. func Current() (int, error) { - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return int(limit.Cur), nil @@ -59,8 +59,8 @@ func Current() (int, error) { // allowed to request for itself. func Maximum() (int, error) { // Retrieve the maximum allowed by dynamic OS limits - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // Cap it to OPEN_MAX (10240) because macos is a special snowflake diff --git a/common/fdlimit/fdlimit_unix.go b/common/fdlimit/fdlimit_unix.go index a1f388ebb78d..ce6bd41bceb2 100644 --- a/common/fdlimit/fdlimit_unix.go +++ b/common/fdlimit/fdlimit_unix.go @@ -19,15 +19,15 @@ package fdlimit -import "syscall" +import "golang.org/x/sys/unix" // Raise tries to maximize the file descriptor allowance of this process // to the maximum hard-limit allowed by the OS. // Returns the size it was set to (may differ from the desired 'max') func Raise(max uint64) (uint64, error) { // Get the current limit - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // Try to update the limit to the max allowance @@ -35,11 +35,11 @@ func Raise(max uint64) (uint64, error) { if limit.Cur > max { limit.Cur = max } - if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } // MacOS can silently apply further caps, so retrieve the actually set limit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return limit.Cur, nil @@ -48,8 +48,8 @@ func Raise(max uint64) (uint64, error) { // Current retrieves the number of file descriptors allowed to be opened by this // process. func Current() (int, error) { - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return int(limit.Cur), nil @@ -58,8 +58,8 @@ func Current() (int, error) { // Maximum retrieves the maximum number of file descriptors this process is // allowed to request for itself. func Maximum() (int, error) { - var limit syscall.Rlimit - if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil { + var limit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &limit); err != nil { return 0, err } return int(limit.Max), nil diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 506bd8cb8114..535d5b6c344e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -42,7 +42,6 @@ import ( var ( FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium - WemixBlockReward = big.NewInt(0) // Block reward in wei for Wemix ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople maxUncles = 2 // Maximum number of uncles allowed in a single block allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks @@ -609,6 +608,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. if err := accumulateRewards(chain.Config(), state, header, uncles); err != nil { return err } + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) return nil } @@ -698,22 +698,18 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) } else { - blockReward = WemixBlockReward - coinbase, rewards, err := wemixminer.CalculateRewards( - header.Number, blockReward, header.Fees, + rewards, err := wemixminer.CalculateRewards( + config, header.Number, header.Fees, func(addr common.Address, amt *big.Int) { state.AddBalance(addr, amt) }) if err == nil { header.Rewards = rewards - if coinbase != nil { - header.Coinbase = *coinbase - } } else { if err == wemixminer.ErrNotInitialized { reward := new(big.Int) if header.Fees != nil { - reward.Add(blockReward, header.Fees) + reward.Add(reward, header.Fees) } state.AddBalance(header.Coinbase, reward) return nil diff --git a/console/console.go b/console/console.go index 78706860e0cb..0b1b21165183 100644 --- a/console/console.go +++ b/console/console.go @@ -27,7 +27,6 @@ import ( "sort" "strings" "sync" - "syscall" "github.com/dop251/goja" "github.com/ethereum/go-ethereum/console/prompt" @@ -37,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/mattn/go-colorable" "github.com/peterh/liner" + "golang.org/x/sys/unix" ) var ( @@ -371,7 +371,7 @@ func (c *Console) interruptHandler() { // Unfortunately, it is not possible to abort the prompt in this case and // the c.readLines goroutine leaks. sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT) + signal.Notify(sig, unix.SIGINT) defer signal.Stop(sig) for { diff --git a/containers/docker/wemix/Dockerfile b/containers/docker/wemix/Dockerfile index 662ec9c997ab..3001685fc0f5 100644 --- a/containers/docker/wemix/Dockerfile +++ b/containers/docker/wemix/Dockerfile @@ -1,23 +1,94 @@ -FROM ubuntu:xenial - -ENV PATH=/usr/lib/go-1.9/bin:$PATH - -RUN /bin/bash -c '\ - apt-get update && apt-get upgrade -q -y && \ - apt-get install -y --no-install-recommends golang-1.9 git make gcc libc-dev ca-certificates wget && \ - git clone https://github.com/wemix3/go-wemix && \ - (cd go-wemix && make) && \ - mkdir -p /usr/local/wemix /usr/local/wemix/keystore && \ - cp -r go-wemix/build/bin go-wemix/build/conf /usr/local/wemix/ && \ - /usr/bin/wget -nv -O /usr/local/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux && \ - chmod a+x /usr/local/bin/solc && \ - for i in 1 2 3 4; do \ +# Stage 1: Build stage +FROM golang:1.19 as builder + +# Set environment variables +ENV PATH=/usr/local/go/bin:$PATH + +# Update and upgrade the package list +RUN apt-get update && \ + apt-get upgrade -q -y + +# Install required packages +RUN apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + openssl \ + make && \ + rm -rf /var/lib/apt/lists/* + +# Define the location for custom certificates +ARG cert_location=/usr/local/share/ca-certificates + +# Fetch and install certificates for github.com and proxy.golang.org +RUN openssl s_client -showcerts -connect github.com:443 /dev/null | \ + openssl x509 -outform PEM > ${cert_location}/github.crt && \ + openssl s_client -showcerts -connect proxy.golang.org:443 /dev/null | \ + openssl x509 -outform PEM > ${cert_location}/proxy.golang.crt && \ + update-ca-certificates + +# Clone the repository, install dependencies, and build the project +RUN git clone https://github.com/wemixarchive/go-wemix.git /go-wemix && \ + cd /go-wemix && \ + go mod download && \ + make + +# Clean up unnecessary packages and files after building +RUN apt-get remove -y \ + git \ + ca-certificates \ + openssl \ + make && \ + apt autoremove -y && \ + apt-get clean + +# Stage 2: Runtime stage +FROM ubuntu:22.04 + +# Set environment variables +ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt + +# Update and upgrade the package list +RUN apt-get update && \ + apt-get upgrade -q -y + +# Install required runtime packages +RUN apt-get install -y --no-install-recommends \ + g++ \ + libc-dev \ + ca-certificates \ + bash \ + wget && \ + update-ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Create directories for wemix +RUN mkdir -p /usr/local/wemix /usr/local/wemix/keystore + +# Set environment variables +ENV PATH=/usr/local/wemix/bin:$PATH + +# Copy the built binaries and configuration files from the builder stage +COPY --from=builder /go-wemix/build /usr/local/wemix/ + +# Download and install solc +RUN wget -nv -O /usr/local/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux && \ + chmod a+x /usr/local/bin/solc + +# Create new accounts for wemix +RUN bash -c 'for i in 1 2 3 4; do \ /usr/local/wemix/bin/gwemix wemix new-account --password <(echo demo) --out /usr/local/wemix/keystore/account-$i; \ - done && \ - apt-get remove -y golang-1.9 git make gcc libc-dev wget && apt autoremove -y && apt-get clean && \ - rm -rf /go-wemix' +done' + +# Clean up unnecessary packages +RUN apt-get remove -y \ + g++ \ + libc-dev \ + wget && \ + apt autoremove -y && \ + apt-get clean -EXPOSE 10009 -EXPOSE 10010 +# Expose necessary ports +EXPOSE 8588 8589 8598 -ENTRYPOINT ["/bin/bash"] +# Set the entrypoint +ENTRYPOINT ["/usr/local/wemix/bin/gwemix"] \ No newline at end of file diff --git a/containers/docker/wemix/docker-compose.yml b/containers/docker/wemix/docker-compose.yml index 1773a3e8b5e7..b180cc458fc3 100644 --- a/containers/docker/wemix/docker-compose.yml +++ b/containers/docker/wemix/docker-compose.yml @@ -1,34 +1,77 @@ -version: "3" services: wemix1: - container_name: wemix1 + build: + context: . + dockerfile: Dockerfile + image: wemix/node-demo:latest hostname: wemix1 - stdin_open: true + networks: + wemix_bridge: + ipv4_address: 172.16.237.11 + restart: unless-stopped tty: true - build: . - image: wemix/multinode-demo:latest volumes: - - ".:/opt" - entrypoint: ["/usr/local/wemix/bin/gwemix-demo.sh", "start-leader", "wemix1", "wemix2", "wemix3"] + - /etc/localtime:/etc/localtime:ro + - ./testdata/bootnode.sh:/usr/local/wemix/bin/bootnode.sh:ro + - ./testdata/gwemix.sh:/usr/local/wemix/bin/gwemix.sh:ro + - ./testdata/config.json:/usr/local/wemix/conf/config.json:ro + - ./testdata/deploy-governance.js:/usr/local/wemix/conf/deploy-governance.js:ro + - ./testdata/genesis.json:/usr/local/wemix/genesis.json:ro + - ./testdata/.rc:/usr/local/wemix/.rc:ro + - ./testdata/keystore:/usr/local/wemix/keystore + - ./testdata/nodekey/nodekey1:/usr/local/wemix/geth/nodekey:ro + ports: + - 8588:8588 + - 8589:8589 + - 8598:8598 + container_name: wemix1 + entrypoint: ["sh", "-c", "bootnode.sh && sleep 5 && tail -f /usr/local/wemix/logs/log"] wemix2: - container_name: wemix2 + image: wemix/node-demo:latest hostname: wemix2 - stdin_open: true + networks: + wemix_bridge: + ipv4_address: 172.16.237.12 + restart: unless-stopped tty: true - image: wemix/multinode-demo:latest depends_on: - - wemix1 + wemix1: + condition: service_started volumes: - - ".:/opt" - entrypoint: ["/usr/local/wemix/bin/gwemix-demo.sh", "start"] + - /etc/localtime:/etc/localtime:ro + - ./testdata/gwemix.sh:/usr/local/wemix/bin/gwemix.sh:ro + - ./testdata/genesis.json:/usr/local/wemix/genesis.json:ro + - ./testdata/.rc:/usr/local/wemix/.rc:ro + - ./testdata/keystore/account2:/usr/local/wemix/keystore/account2 + - ./testdata/nodekey/nodekey2:/usr/local/wemix/geth/nodekey:ro + container_name: wemix2 + entrypoint: ["sh", "-c", "gwemix.sh start && sleep 5 && tail -f /usr/local/wemix/logs/log"] wemix3: - container_name: wemix3 + image: wemix/node-demo:latest hostname: wemix3 - stdin_open: true + networks: + wemix_bridge: + ipv4_address: 172.16.237.13 + restart: unless-stopped tty: true - image: wemix/multinode-demo:latest depends_on: - - wemix1 + wemix1: + condition: service_started volumes: - - ".:/opt" - entrypoint: ["/usr/local/wemix/bin/gwemix-demo.sh", "start"] + - /etc/localtime:/etc/localtime:ro + - ./testdata/gwemix.sh:/usr/local/wemix/bin/gwemix.sh:ro + - ./testdata/genesis.json:/usr/local/wemix/genesis.json:ro + - ./testdata/.rc:/usr/local/wemix/.rc:ro + - ./testdata/keystore/account3:/usr/local/wemix/keystore/account3 + - ./testdata/nodekey/nodekey3:/usr/local/wemix/geth/nodekey:ro + container_name: wemix3 + entrypoint: ["sh", "-c", "gwemix.sh start && sleep 5 && tail -f /usr/local/wemix/logs/log"] + +networks: + wemix_bridge: + name: wemix_bridge + driver: bridge + ipam: + driver: default + config: + - subnet: 172.16.237.0/24 diff --git a/containers/docker/wemix/testdata/.rc b/containers/docker/wemix/testdata/.rc new file mode 100644 index 000000000000..792df1fb93df --- /dev/null +++ b/containers/docker/wemix/testdata/.rc @@ -0,0 +1,2 @@ +PORT=8588 +DISCOVER=0 \ No newline at end of file diff --git a/containers/docker/wemix/testdata/bootnode.sh b/containers/docker/wemix/testdata/bootnode.sh new file mode 100755 index 000000000000..cba2bcf17303 --- /dev/null +++ b/containers/docker/wemix/testdata/bootnode.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +cd $(dirname ${BASH_SOURCE[0]})/.. + +gwemix.sh start +sleep 5 + +RESULT=$(gwemix --exec "admin.wemixInfo.etcd" attach gwemix.ipc) + +if [[ "$RESULT" == "Fatal:"* ]]; then + echo "Failed to start gwemix!" + exit 1 +elif [[ "$RESULT" == "{}" ]]; then + echo "Initailize governance" + + gwemix.sh init-gov wemix conf/config.json + sleep 5 + + gwemix --exec "admin.wemixInfo" attach gwemix.ipc + gwemix --exec "admin.etcdInit()" attach gwemix.ipc # Initialize etcd + sleep 5 + gwemix --exec "admin.wemixInfo.etcd" attach gwemix.ipc +fi + +echo "Started gwemix!" \ No newline at end of file diff --git a/containers/docker/wemix/testdata/config.json b/containers/docker/wemix/testdata/config.json new file mode 100644 index 000000000000..74b5e54acb81 --- /dev/null +++ b/containers/docker/wemix/testdata/config.json @@ -0,0 +1,69 @@ +{ + "extraData": "The beginning of Wemix3.0 localnet", + "staker": "0xf00d9928ed1dada205aec56ab85e0e2ab5670ad5", + "ecosystem": "0x92875d5ba8b104c375a471d58a6de4f75ccb431f", + "maintenance": "0x2c77f2c8997182e31a19ec48e321f4d09f36819e", + "feecollector": "0x2c77f2c8997182e31a19ec48e321f4d09f36819e", + "env": { + "ballotDurationMin": 86400, + "ballotDurationMax": 604800, + "stakingMin": 1500000000000000000000000, + "stakingMax": 1500000000000000000000000, + "MaxIdleBlockInterval": 100, + "blockCreationTime": 5000, + "blockRewardAmount": 1000000000000000000, + "maxPriorityFeePerGas": 100000000000, + "rewardDistributionMethod": [ 4000, 1000, 2500, 2500 ], + "maxBaseFee": 50000000000000, + "blockGasLimit": 105000000, + "baseFeeMaxChangeRate": 55, + "gasTargetPercentage": 30 + }, + "members": [ + { + "addr": "0x92875d5ba8b104c375a471d58a6de4f75ccb431f", + "stake": 1500000000000000000000000, + "name": "node1", + "id": "0xe4dfe854223c1b85ea7962cd724d98eec1863a49847eb749bc5caa519104713c78a972ebf263b13329ceb8a7b5959125ab6e9a3c73c15f659cc1291d0a7aae1e", + "ip": "172.16.237.11", + "port": 8589, + "bootnode": true + }, + { + "addr": "0xb61544371ef73161496009a77989da30f8f91867", + "stake": 1500000000000000000000000, + "name": "node2", + "id": "0x4b8b229f97bbe68fa7cfc43e357fe74503733d0cc236b8d176d896f33547d8fcfd59daa0b4f9e1a4e83157adf1a386ea314dfb9a3e6efe747948fa5e10d5fff5", + "ip": "172.16.237.12", + "port": 8589, + "bootnode": false + }, + { + "addr": "0x708ebe75a5dbc5f56176db6e84ce5740897e8a17", + "stake": 1500000000000000000000000, + "name": "node3", + "id": "0xa6f08d187a8da5d76f072b0f0d114e1469e77a53713b710177bd6206b16331b8ead7d41bdeec1df3bb0bb56de8f102e86ecaf44bb6affb4383151c603ff5ac10", + "ip": "172.16.237.13", + "port": 8589, + "bootnode": false + } + ], + "accounts": [ + { + "addr": "0x92875d5ba8b104c375a471d58a6de4f75ccb431f", + "balance": 2000000000000000000000000 + }, + { + "addr": "0xb61544371ef73161496009a77989da30f8f91867", + "balance": 2000000000000000000000000 + }, + { + "addr": "0x708ebe75a5dbc5f56176db6e84ce5740897e8a17", + "balance": 2000000000000000000000000 + }, + { + "addr": "0x2c77f2c8997182e31a19ec48e321f4d09f36819e", + "balance": 2000000000000000000000000 + } + ] +} \ No newline at end of file diff --git a/containers/docker/wemix/testdata/deploy-governance.js b/containers/docker/wemix/testdata/deploy-governance.js new file mode 100644 index 000000000000..5add63dd15b2 --- /dev/null +++ b/containers/docker/wemix/testdata/deploy-governance.js @@ -0,0 +1,420 @@ +// deploy-governance.js + +// uses offline wallet +var GovernanceDeployer = new function() { + this.wallet = ["keystore/account1", "keystore/account2", "keystore/account3"] + this.account = [] + this.from = null + this.gas = 21000 * 1400 + this.gasPrice = eth.gasPrice + this._nonce = [] + this.receiptCheckParams = { "interval": 100, "count": 300 } + + // bool log(var args...) + this.log = function() { + var msg = "" + for (var i in arguments) { + if (msg.length > 0) + msg += " " + msg += arguments[i] + } + console.log(msg) + return true + } + + // void verifyCfg(json data) + // verifies config data, and normalize addresses + // throws exception on failure + this.verifyCfg = function(data) { + if (data.accounts.length == 0 || data.members.length == 0) + throw "At least one account and node are required" + var bootnodeExists = false + for (var i in data.members) { + var m = data.members[i] + if (!web3.isAddress(m.addr)) + throw "Invalid address 1 " + m.addr + data.members[i].addr = web3.toChecksumAddress(m.addr) + if (m.bootnode) + bootnodeExists = true + } + if (!bootnodeExists) + throw "Bootnode is not designated" + for (var i in data.accounts) { + var a = data.accounts[i] + if (!web3.isAddress(a.addr)) + throw "Invalid address " + a.addr + data.accounts[i].addr = web3.toChecksumAddress(a.addr) + } + if (data.staker) { + if (!web3.isAddress(data.staker)) + throw "Invalid staker address " + data.staker + data.staker = web3.toChecksumAddress(data.staker) + } + if (data.ecosystem) { + if (!web3.isAddress(data.ecosystem)) + throw "Invalid ecosystem address " + data.ecosystem + data.ecosystem = web3.toChecksumAddress(data.ecosystem) + } + if (data.maintenance) { + if (!web3.isAddress(data.maintenance)) + throw "Invalid maintenance address " + data.maintenance + data.maintenance = web3.toChecksumAddress(data.maintenance) + } + if (data.feecollector) { + if (!web3.isAddress(data.feecollector)) + throw "Invalid feecollector address " + data.feecollector + data.feecollector = web3.toChecksumAddress(data.feecollector) + } + } + + // bytes packNum(int num) + // pack a number into 256 bit bytes + this.packNum = function(num) { + return web3.padLeft(web3.toHex(num).substr(2), 64, "0") + } + + // { "nodes": string, "stakes": string, "staker": address, + // "ecosystem": address, "maintenance": address, "feecollector": address, + // "env": { env variables } } + // getInitialGovernanceMembersAndNodes(json data) + this.getInitialGovernanceMembersAndNodes = function(data) { + var nodes = "0x", stakes = "0x" + + for (var i = 0, l = data.members.length; i < l; i++) { + var m = data.members[i], id + if (m.id.length != 128 && m.id.length != 130) + throw "Invalid enode id " + m.id + id = m.id.length == 128 ? m.id : m.id.substr(2) + if (m.addr) { + if (m.addr.indexOf("0x") == 0) + m.addr = m.addr.substr(2) + if (!m.staker) + m.staker = m.addr + if (!m.voter) + m.voter = m.addr + if (!m.reward) + m.reward = m.addr + } + if (m.staker) { + if (m.staker.indexOf("0x") == 0) + m.staker = m.staker.substr(2) + if (!m.addr) + m.addr = m.staker + if (!m.voter) + m.voter = m.staker + if (!m.reward) + m.reward = m.staker + } + if (!m.addr && !m.staker) + throw "Address & staker are missing" + nodes += web3.padLeft(m.staker, 64, "0") + + web3.padLeft(m.voter, 64, "0") + + web3.padLeft(m.reward, 64, "0") + + this.packNum(m.name.length) + web3.fromAscii(m.name).substr(2) + + this.packNum(id.length/2) + id + + this.packNum(m.ip.length) + web3.fromAscii(m.ip).substr(2) + + this.packNum(m.port) + + stakes += web3.padLeft(m.staker, 64, "0") + + this.packNum(m.stake) + } + return { + "nodes": nodes, + "stakes": stakes, + "staker": data.staker, + "ecosystem": data.ecosystem, + "maintenance": data.maintenance, + "feecollector": data.feecollector, + "env": data.env + } + } + + this.nonce = function(index) { + return this._nonce[index]++ + } + + // returns transaction hash, or throws error + this.deployContract = function(data) { + var tx = { + from: this.from, + data: data, + gas: this.gas, + gasPrice: this.gasPrice, + nonce: this.nonce(0) + } + var stx = offlineWalletSignTx(this.account[0].id, tx, eth.chainId()) + return eth.sendRawTransaction(stx) + } + + // wait for transaction receipt for contract address, then + // load a contract + // Contract resolveContract(ABI abi, hash txh) + this.resolveContract = function(abi, txh) { + for (var i = 0; i < this.receiptCheckParams.count; i++ ) { + var r = eth.getTransactionReceipt(txh) + if (r != null && r.contractAddress != null) { + var ctr = web3.eth.contract(abi).at(r.contractAddress) + ctr.transactionHash = txh + return ctr + } + msleep(this.receiptCheckParams.interval) + } + throw "Cannot get contract address for " + txh + } + + // sends a simple or method transaction, returns transaction hash + this.sendTx = function(to, value, data) { + var tx = {from:this.from, to:to, gas:this.gas, + gasPrice:this.gasPrice, nonce:this.nonce(0)} + if (value) + tx.value = value + if (data) + tx.data = data + var stx = offlineWalletSignTx(this.account[0].id, tx, eth.chainId()) + return eth.sendRawTransaction(stx) + } + + this.checkReceipt = function(tx) { + for (var i = 0; i < this.receiptCheckParams.count; i++ ) { + var r = eth.getTransactionReceipt(tx) + if (r != null) + return web3.toBigNumber(r.status) == 1 + msleep(this.receiptCheckParams.interval) + } + return false + //throw "Cannot get a transaction receipt for " + tx + } + + this.sendStakingDeposit = function (from, nonce, to, data, stake) { + this.log("staker addr = " + from.address) + var tx = { from: from.address, to: to, gas: this.gas, gasPrice: this.gasPrice, nonce: nonce, value: "0" } + tx.value = stake + if (data) tx.data = data + var stx = offlineWalletSignTx(from.id, tx, eth.chainId()) + + return eth.sendRawTransaction(stx) + } + + // bool deploy(string walletUrl, string password, string cfg) + this.deploy = function(cfg, doInitOnce) { + for (var i = 0; i < this.wallet.length; i++) { + w = offlineWalletOpen(this.wallet[i], "1") + if (!w || !w.id || !w.address) { + throw "Offline wallet is not loaded" + } + this.account[i] = w + this._nonce[i] = eth.getTransactionCount(this.account[i].address, 'pending') + } + this.from = this.account[0].address + + var data + if (!(data = loadFile(cfg))) + throw "cannot load governance contract .js or config .json file" + + // check if contracts exist + var contractNames = [ "Registry", "EnvStorageImp", "Staking", "StakingImp", + "BallotStorage", "EnvStorage", "GovImp", "Gov" ] + for (var i in contractNames) { + var cn = contractNames[i] + if (eval("typeof " + cn + "_data") == "undefined" || + eval("typeof " + cn + "_contract") == "undefined") + throw cn + " not found" + } + + // check config.js + eval("var data = " + data) + this.verifyCfg(data) + + // initial members and nodes data + var initData = this.getInitialGovernanceMembersAndNodes(data) + + // bootnode + var bootNode = { + "name": web3.fromAscii(data.members[0].name), + "id": data.members[0].id, + "ip": web3.fromAscii(data.members[0].ip), + "port": data.members[0].port, + "stake": data.members[0].stake + } + + // contacts, transactions to be deployed + var registry, envStorageImp, staking, stakingImp, ballotStorage, envStorage, govImp, gov + var txs = new Array() + + // 1. deploy Registry and EnvStorageImp contracts + this.log("Deploying Registry and EnvStorageImp...") + registry = this.deployContract(Registry_data) + envStorageImp = this.deployContract(EnvStorageImp_data) + stakingImp = this.deployContract(StakingImp_data) + + this.log("Waiting for receipts...") + envStorageImp = this.resolveContract(EnvStorageImp_contract.abi, envStorageImp) + registry = this.resolveContract(Registry_contract.abi, registry) + stakingImp = this.resolveContract(StakingImp_contract.abi, stakingImp); + + // 2. deploy Staking, BallotStorage, EnvStorage, GovImp, Gov + this.log("Deploying Staking, BallotStorage, EnvStorage, GovImp & Gov...") + var code = Staking_contract.getData(stakingImp.address, {data: Staking_data}) + staking = this.deployContract(code) + var code = BallotStorage_contract.getData(registry.address, {data: BallotStorage_data}) + ballotStorage = this.deployContract(code) + code = EnvStorage_contract.getData(envStorageImp.address, {data: EnvStorage_data}) + envStorage = this.deployContract(code) + govImp = this.deployContract(GovImp_data) + + this.log("Waiting for receipts...") + govImp = this.resolveContract(GovImp_contract.abi, govImp) + code = Gov_contract.getData(govImp.address, { data: Gov_data }) + gov = this.deployContract(code) + + this.log("Waiting for gov contract...") + gov = this.resolveContract(Gov_contract.abi, gov) + envStorage = this.resolveContract(EnvStorage_contract.abi, envStorage) + ballotStorage = this.resolveContract(BallotStorage_contract.abi, ballotStorage) + staking = this.resolveContract(Staking_contract.abi, staking) + + // 3. setup registry + this.log("Setting registry...") + txs.length = 0 + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "Staking", staking.address)) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "BallotStorage", ballotStorage.address)) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "EnvStorage", envStorage.address)) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "GovernanceContract", gov.address)) + if (initData.staker) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "StakingReward", initData.staker)) + if (initData.ecosystem) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "Ecosystem", initData.ecosystem)) + if (initData.maintenance) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "Maintenance", initData.maintenance)) + if (initData.feecollector) + txs[txs.length] = this.sendTx(registry.address, null, + registry.setContractDomain.getData( + "FeeCollector", initData.feecollector)) + + // no need to wait for the receipts for the above + + // 4. initialize environment storage data: + this.log("Initializing environment storage...") + data.env = data.env || {} + // Just changing address doesn't work here. Address is embedded in + // the methods. Have to re-construct temporary EnvStorageImp here. + var tmpEnvStorageImp = web3.eth.contract(envStorageImp.abi).at(envStorage.address) + var envNames = [ + web3.sha3("blocksPer"), + web3.sha3("ballotDurationMin"), web3.sha3("ballotDurationMax"), + web3.sha3("stakingMin"), web3.sha3("stakingMax"), + web3.sha3("MaxIdleBlockInterval"), + web3.sha3("blockCreationTime"), + web3.sha3("blockRewardAmount"), + web3.sha3("maxPriorityFeePerGas"), + web3.sha3("blockRewardDistributionBlockProducer"), + web3.sha3("blockRewardDistributionStakingReward"), + web3.sha3("blockRewardDistributionEcosystem"), + web3.sha3("blockRewardDistributionMaintenance"), + web3.sha3("maxBaseFee"), + web3.sha3("blockGasLimit"), + web3.sha3("baseFeeMaxChangeRate"), + web3.sha3("gasTargetPercentage") ] + var rewardDistributionMethod = data.env.rewardDistributionMethod || [ 4000, 1000, 2500, 2500 ] + var envValues = [ + 1, + data.env.ballotDurationMin || 86400, + data.env.ballotDurationMax || 604800, + data.env.stakingMin || 1500000000000000000000000, + data.env.stakingMax || 1500000000000000000000000, + data.env.MaxIdleBlockInterval || 5, + data.env.blockCreationTime || 1000, + // mint amount: 1 wemix + data.env.blockRewardAmount || web3.toWei(1, 'ether'), + // tip: 100 gwei + data.env.maxPriorityFeePerGas || web3.toWei(100, 'gwei'), + // NCPs, WEMIX Staker, Eco System, Maintenance + rewardDistributionMethod[0], + rewardDistributionMethod[1], + rewardDistributionMethod[2], + rewardDistributionMethod[3], + // maxBaseFee * 21000 -> 1.05 wemix + data.env.maxBaseFee || web3.toWei(50000, 'gwei'), + data.env.blockGasLimit || 5000 * 21000, + data.env.baseFeeMaxChangeRate || 55, + data.env.gasTargetPercentage || 30 ] + txs[txs.length] = this.sendTx(envStorage.address, null, + tmpEnvStorageImp.initialize.getData(registry.address, envNames, envValues)) + + // 5. deposit staking + var tmpStakingImp = web3.eth.contract(stakingImp.abi).at(staking.address) + code = tmpStakingImp.init.getData(registry.address, + doInitOnce ? initData.stakes : "", {data: Staking_data}) + //"", {data: Staking_data}) + txs[txs.length] = this.sendTx(staking.address, null, code); + + for (var i = 1; i < this.account.length; i++) { + var d = tmpStakingImp.balanceOf.getData(this.account[i].address) + txs[txs.length] = this.sendStakingDeposit(this.account[i], this.nonce(i), staking.address, tmpStakingImp.deposit.getData(), web3.toBigNumber(bootNode.stake).toString(10)); + } + + this.log("txs.length = " + txs.length) + for(i=0;i /dev/null && pwd) + cd "$OPWD" +} + +function get_data_dir () +{ + if [ ! "$1" = "" ]; then + if [ -x "$1/bin/gwemix" ]; then + echo $1 + else + d=${WEMIX_DIR}/$1 + if [ -x "$d/bin/gwemix" ]; then + echo $d + fi + fi + else + echo $(dirname $(get_script_dir)) + fi +} + +# void init(String node, String config_json) +function init () +{ + NODE="$1" + CONFIG="$2" + + if [ ! -f "$CONFIG" ]; then + echo "Cannot find config file: $2" + return 1 + fi + + d=$(get_data_dir "${NODE}") + if [ -x "$d/bin/gwemix" ]; then + GWEMIX="$d/bin/gwemix" + else + echo "Cannot find gwemix" + return 1 + fi + + if [ ! -f "${d}/conf/genesis-template.json" ]; then + echo "Cannot find template files." + return 1 + fi + + echo "wiping out data..." + wipe $NODE + + [ -d "$d/geth" ] || mkdir -p "$d/geth" + [ -d "$d/logs" ] || mkdir -p "$d/logs" + + ${GWEMIX} wemix genesis --data "$CONFIG" --genesis "$d/conf/genesis-template.json" --out "$d/genesis.json" + [ $? = 0 ] || return $? + + echo "PORT=8588 +DISCOVER=0" > $d/.rc + ${GWEMIX} --datadir $d init $d/genesis.json + # echo "Generating dags for epoch 0 and 1..." + # ${GWEMIX} makedag 0 $d/.ethash & + # ${GWEMIX} makedag 30000 $d/.ethash & + wait +} + +# void init_gov(String node, String config_json, String account_file, bool doInitOnce) +# account_file can be +# 1. keystore file: "" +# 2. nano ledger: "ledger:" +# 3. trezor: "trezor:" +function init_gov () +{ + NODE="$1" + CONFIG="$2" + INIT_ONCE=true + + if [ ! -f "$CONFIG" ]; then + echo "Cannot find config file: $2" + return 1 + fi + + d=$(get_data_dir "${NODE}") + if [ -x "$d/bin/gwemix" ]; then + GWEMIX="$d/bin/gwemix" + else + echo "Cannot find gwemix" + return 1 + fi + + if [ ! -f "${d}/conf/WemixGovernance.js" ]; then + echo "Cannot find ${d}/conf/WemixGovernance.js" + return 1 + fi + + PORT=$(grep PORT ${d}/.rc | sed -e 's/PORT=//') + [ "$PORT" = "" ] && PORT=8588 + + exec ${GWEMIX} attach http://localhost:${PORT} --preload "$d/conf/WemixGovernance.js,$d/conf/deploy-governance.js" --exec 'GovernanceDeployer.deploy("'${CONFIG}'", '${INIT_ONCE}')' +} + +function wipe () +{ + d=$(get_data_dir "$1") + if [ ! -x "$d/bin/gwemix" ]; then + echo "Is '$1' wemix data directory?" + return + fi + + cd $d + /bin/rm -rf geth/LOCK geth/chaindata geth/ethash geth/lightchaindata \ + geth/transactions.rlp geth/nodes geth/triecache gwemix.ipc logs/* etcd +} + +function clean () +{ + d=$(get_data_dir "$1") + if [ -x "$d/bin/gwemix" ]; then + GWEMIX="$d/bin/gwemix" + else + echo "Cannot find gwemix" + return + fi + + cd $d + $GWEMIX --datadir ${PWD} removedb +} + +function start () +{ + d=$(get_data_dir "$1") + if [ -x "$d/bin/gwemix" ]; then + GWEMIX="$d/bin/gwemix" + else + echo "Cannot find gwemix" + return + fi + + [ -f "$d/.rc" ] && source "$d/.rc" + [ "$COINBASE" = "" ] && COINBASE="" || COINBASE="--miner.etherbase $COINBASE" + + RPCOPT="--http --http.addr 0.0.0.0 --mine" + [ "$PORT" = "" ] || RPCOPT="${RPCOPT} --http.port ${PORT}" + RPCOPT="${RPCOPT} --ws --ws.addr 0.0.0.0" + [ "$PORT" = "" ] || RPCOPT="${RPCOPT} --ws.port $((${PORT}+10))" + [ "$NONCE_LIMIT" = "" ] || NONCE_LIMIT="--noncelimit $NONCE_LIMIT" + [ "$BOOT_NODES" = "" ] || BOOT_NODES="--bootnodes $BOOT_NODES" + [ "$TESTNET" = "1" ] && TESTNET=--wemix-testnet + if [ "$DISCOVER" = "0" ]; then + DISCOVER=--nodiscover + else + DISCOVER= + fi + case $SYNC_MODE in + "full") + SYNC_MODE="--syncmode full";; + "fast") + SYNC_MODE="--syncmode fast";; + "snap") + SYNC_MODE="--syncmode snap";; + *) + SYNC_MODE="--syncmode full --gcmode archive";; + esac + + OPTS="$COINBASE $DISCOVER $RPCOPT $BOOT_NODES $NONCE_LIMIT $TESTNET $SYNC_MODE ${GWEMIX_OPTS}" + [ "$PORT" = "" ] || OPTS="${OPTS} --port $(($PORT + 1))" + [ "$HUB" = "" ] || OPTS="${OPTS} --hub ${HUB}" + [ "$MAX_TXS_PER_BLOCK" = "" ] || OPTS="${OPTS} --maxtxsperblock ${MAX_TXS_PER_BLOCK}" + + [ -d "$d/logs" ] || mkdir -p $d/logs + [ "$LOG_FILESIZE" = "" ] && LOG_FILESIZE="10M" + [ "$LOG_FILECOUNT" = "" ] && LOG_FILECOUNT="5" + + cd $d + if [ ! "$2" = "inner" ]; then + $GWEMIX --datadir ${PWD} --metrics $OPTS 2>&1 | \ + ${d}/bin/logrot ${d}/logs/log ${LOG_FILESIZE} ${LOG_FILECOUNT} & + else + if [ -x "$d/bin/logrot" ]; then + exec > >($d/bin/logrot $d/logs/log ${LOG_FILESIZE} ${LOG_FILECOUNT}) + exec 2>&1 + fi + exec $GWEMIX --datadir ${PWD} --metrics $OPTS + fi +} + +function get_gwemix_pids () +{ + ps axww | grep -v grep | grep "gwemix.*datadir.*${1}" | awk '{print $1}' +} + +function do_nodes () +{ + LHN=$(hostname) + CMD=${1/-nodes/} + shift + while [ ! "$1" = "" -a ! "$2" = "" ]; do + if [ "$1" = "$LHN" -o "$1" = "${LHN/.*/}" ]; then + $0 ${CMD} $2 + else + ssh -f $1 ${WEMIX_DIR}/$2/bin/gwemix.sh ${CMD} $2 + fi + shift + shift + done +} + +function usage () +{ + echo "Usage: `basename $0` [init | + init-gov | + clean [] | wipe [] | console [] | + [re]start [] | stop [] | [re]start-nodes | stop-nodes] + +*-nodes uses NODES environment variable: [ ]+ +" +} + +case "$1" in +"init") + if [ $# -lt 3 ]; then + usage; + else + init "$2" "$3" + fi + ;; + +"init-gov") + if [ $# -lt 3 ]; then + usage; + else + init_gov "$2" "$3" "$4" + fi + ;; + +"wipe") + wipe $2 + ;; + +"clean") + clean $2 + ;; + +"stop") + echo -n "stopping..." + dir=$(get_data_dir $2) + PIDS=$(get_gwemix_pids ${dir}) + if [ ! "$PIDS" = "" ]; then + # check if we're the miner or leader + CMD=' +function check_if_mining() { + for (var i = 0; i < 15; i++) { + try { + var token = debug.etcdGet("token") + eval("token = " + token) + // console.log("miner -> " + token.miner) + if (token.miner != admin.wemixInfo.self.name) { + break + } else { + console.log("we are the miner, sleeping...") + admin.sleep(0.25) + } + } catch { + admin.sleep(0.25) + } + } +} +if (admin.wemixInfo != null && admin.wemixInfo.self != null) { + check_if_mining() + if (admin.wemixInfo.etcd.leader.name == admin.wemixInfo.self.name) { + var nodes = admin.wemixNodes("", 0) + for (var n of nodes) { + if (admin.wemixInfo.etcd.leader.name != admin.wemixInfo.self.name) { + break + } + if (n.status == "up" && n.name != admin.wemixInfo.self.name) { + console.log("moving leader to " + n.name) + admin.etcdMoveLeader(n.name) + } + } + } + check_if_mining() +}' + ${dir}/bin/gwemix attach ipc:${dir}/gwemix.ipc --exec "$CMD" | grep -v "undefined" + echo $PIDS | xargs -L1 kill + fi + for i in {1..200}; do + PIDS=$(get_gwemix_pids ${dir}) + [ "$PIDS" = "" ] && break + echo -n "." + sleep 1 + done + PIDS=$(get_gwemix_pids ${dir}) + if [ ! "$PIDS" = "" ]; then + echo $PIDS | xargs -L1 kill -9 + fi + # wait until geth/chaindata is free + for i in {1..200}; do + lsof ${dir}/geth/chaindata/LOG 2>&1 | grep -q gwemix > /dev/null 2>&1 || break + sleep 1 + done + echo "done." + ;; + +"start") + start $2 + ;; + +"start-inner") + if [ "$2" = "" ]; then + usage; + else + start $2 inner + fi + ;; + +"restart") + $0 stop $2 + start $2 + ;; + +"start-nodes"|"restart-nodes"|"stop-nodes") + if [ "${NODES}" = "" ]; then + echo "NODES is not defined" + fi + do_nodes $1 ${NODES} + ;; + +"console") + d=$(get_data_dir "$2") + if [ ! -d $d ]; then + usage; exit; + fi + RCJS= + if [ -f "$d/rc.js" ]; then + RCJS="--preload $d/rc.js" + fi + exec ${d}/bin/gwemix ${RCJS} attach ipc:${d}/gwemix.ipc + ;; + +*) + usage; + ;; +esac + +# EOF diff --git a/containers/docker/wemix/testdata/keystore/account1 b/containers/docker/wemix/testdata/keystore/account1 new file mode 100644 index 000000000000..239d507bec25 --- /dev/null +++ b/containers/docker/wemix/testdata/keystore/account1 @@ -0,0 +1 @@ +{"address":"92875d5ba8b104c375a471d58a6de4f75ccb431f","crypto":{"cipher":"aes-128-ctr","ciphertext":"a07c46d69f5e35b76214af74004686a5c0abf54687dce4e3358318a6d7e3cf23","cipherparams":{"iv":"68f1ee7bd8e3c64ca00e5696b60a7846"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4166f64bc5dfa526cf1a1da89b26cf1ffb60784827dda85a1ccca6193f818deb"},"mac":"6a2e60f7b74f42802d741503dececef073eaff56374bcfdf2a009c6424d96176"},"id":"caa93028-d6fc-4daf-8c48-152d2bd802bd","version":3} \ No newline at end of file diff --git a/containers/docker/wemix/testdata/keystore/account2 b/containers/docker/wemix/testdata/keystore/account2 new file mode 100644 index 000000000000..55e020da3d94 --- /dev/null +++ b/containers/docker/wemix/testdata/keystore/account2 @@ -0,0 +1 @@ +{"address":"b61544371ef73161496009a77989da30f8f91867","crypto":{"cipher":"aes-128-ctr","ciphertext":"cfc35242ce14823f04188ec46b3591cd17dd112d0cd8b90b4e9ef5600fd9c488","cipherparams":{"iv":"58dbb26dc76dddf7fcfc4ad9bdb18f58"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"17c027b25228ac748c2999dabc4047ac72af698c3260f5deadd025c97027d436"},"mac":"469b428c69e9ed23c509c63211205b354632a018c5798bb02a0456a890018a4b"},"id":"572324e3-fd42-4ef9-a877-843a9d6ea231","version":3} \ No newline at end of file diff --git a/containers/docker/wemix/testdata/keystore/account3 b/containers/docker/wemix/testdata/keystore/account3 new file mode 100644 index 000000000000..f4e8cd39989d --- /dev/null +++ b/containers/docker/wemix/testdata/keystore/account3 @@ -0,0 +1 @@ +{"address":"708ebe75a5dbc5f56176db6e84ce5740897e8a17","crypto":{"cipher":"aes-128-ctr","ciphertext":"5cc062f0bd18808a0708877b79fe85f608ba05a43256f17426c1991e97c7b75b","cipherparams":{"iv":"a84efff4ec48f5ad9ca269a06e893f32"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b994ab1c05a7da29b2f29b5dfc3dcd39acd1fc07bd19b5c6184ac4c4bc2a3f89"},"mac":"13536b9717e35d2665d6f86b96d3f4c80d8921853cc5ca9be0c13ca9f83a7cbd"},"id":"4aed4e3a-9545-4c6f-a832-db9061962ace","version":3} \ No newline at end of file diff --git a/containers/docker/wemix/testdata/keystore/maintenance b/containers/docker/wemix/testdata/keystore/maintenance new file mode 100644 index 000000000000..853b48f908aa --- /dev/null +++ b/containers/docker/wemix/testdata/keystore/maintenance @@ -0,0 +1 @@ +{"address":"2c77f2c8997182e31a19ec48e321f4d09f36819e","crypto":{"cipher":"aes-128-ctr","ciphertext":"92fdc92f09b59c20ff2a39d5cacc8953006679f797a446371d20caaa9b44201b","cipherparams":{"iv":"560b9a6fe1bba58cfaf4a5a4f95cb7d3"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"cfd2974920c10638af987464ed23ccfad5a787b067bffb346c6f6adbc44e0141"},"mac":"a3fbe185cfad466d73addcc814d0c91771707e95d0cf04431e2276dee04f1bdb"},"id":"aa594e16-1669-4d7c-bd5f-ada55400427c","version":3} \ No newline at end of file diff --git a/containers/docker/wemix/testdata/nodekey/nodekey1 b/containers/docker/wemix/testdata/nodekey/nodekey1 new file mode 100644 index 000000000000..54bde66ceb64 --- /dev/null +++ b/containers/docker/wemix/testdata/nodekey/nodekey1 @@ -0,0 +1 @@ +6af367dc76aaf0dd08464c2af10df669343601e458172c8822d7baebf72dd58b \ No newline at end of file diff --git a/containers/docker/wemix/testdata/nodekey/nodekey2 b/containers/docker/wemix/testdata/nodekey/nodekey2 new file mode 100644 index 000000000000..3c2f344d5931 --- /dev/null +++ b/containers/docker/wemix/testdata/nodekey/nodekey2 @@ -0,0 +1 @@ +9d5e6380dbcd2e202f699867f31302059918c5b90fcbd02207214f8ca89fa62d \ No newline at end of file diff --git a/containers/docker/wemix/testdata/nodekey/nodekey3 b/containers/docker/wemix/testdata/nodekey/nodekey3 new file mode 100644 index 000000000000..582ffe51f88c --- /dev/null +++ b/containers/docker/wemix/testdata/nodekey/nodekey3 @@ -0,0 +1 @@ +9f011066ffb5ec558d1bc6efaf1efac620e79fd44a3a3fcf30fbb1015f0a5b1d \ No newline at end of file diff --git a/core/block_validator.go b/core/block_validator.go index 028beadc491a..1bc13d8a2694 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -18,7 +18,6 @@ package core import ( "fmt" - "math/big" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" @@ -80,14 +79,11 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) } - if !wemixminer.IsPoW() && block.Fees().Cmp(fees) != 0 { - return fmt.Errorf("invalid fees collected (remote: %v local: %v)", block.Fees(), fees) - } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. @@ -100,11 +96,13 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } + // Validate the state root against the received state root and throw // an error if they don't match. if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } + return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 41d4bbeb84e2..14e9ba61a6f8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -43,7 +43,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" - wemixminer "github.com/ethereum/go-ethereum/wemix/miner" lru "github.com/hashicorp/golang-lru" ) @@ -1608,11 +1607,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) } - // wemix: reward calculation uses governance contract, meaning - // the previous block is required. For fast sync, we need to wait for - // governance is initialized and try again. - retryCount := 2 - retry: statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps) if err != nil { return it.index, err @@ -1642,13 +1636,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } - // Update the metrics touched during block processing accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them @@ -1664,18 +1657,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Validate the state using the default validator substart = time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, fees); err != nil { - if retryCount--; !wemixminer.IsPoW() && retryCount > 0 { - // make sure the previous block exists in order to calculate rewards distribution - for try := 100; try > 0; try-- { - if _, _, err := wemixminer.CalculateRewards(block.Number(), big.NewInt(0), big.NewInt(100000000), nil); err == nil { - break - } - time.Sleep(100 * time.Millisecond) - } - goto retry - } - + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index cda30f0f5860..6842049880ab 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -159,12 +159,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) + receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/chain_makers.go b/core/chain_makers.go index db059c6e5071..4db017f3d621 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -104,7 +104,12 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), len(b.txs)) - fees := new(big.Int) + var fees *big.Int + if b.header.Fees != nil { + fees = b.header.Fees // wemix block has `Fees` field + } else { + fees = new(big.Int) + } receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, fees, vm.Config{}) if err != nil { panic(err) @@ -306,6 +311,9 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S Number: new(big.Int).Add(parent.Number(), common.Big1), Time: time, } + if !wemixminer.IsPoW() { + header.Fees = new(big.Int) + } if chain.Config().IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) if !chain.Config().IsLondon(parent.Number()) { diff --git a/core/state_processor.go b/core/state_processor.go index e2b2c1fc5efc..6edd6592de9f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -56,7 +56,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -77,23 +77,31 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) + receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + if err != nil { + return receipts, nil, 0, err + } + + header.Fees = fees + if block.Hash() != header.Hash() { + return receipts, nil, 0, fmt.Errorf("remote block hash is different from being processed one locally (remote=%v, local=%v)", block.Header().Hash(), header.Hash()) + } - return receipts, allLogs, *usedGas, fees, nil + return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -151,5 +159,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) - return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, fees, vmenv) + return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, fees, vmenv) } diff --git a/core/types.go b/core/types.go index ae909a48fb1f..4c5b74a49865 100644 --- a/core/types.go +++ b/core/types.go @@ -17,8 +17,6 @@ package core import ( - "math/big" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -33,7 +31,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -49,5 +47,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) } diff --git a/core/types/block.go b/core/types/block.go index f55a0ad127ff..cb61514a931d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -324,6 +324,9 @@ func CopyHeader(h *Header) *Header { cpy.Rewards = make([]byte, len(h.Rewards)) copy(cpy.Rewards, h.Rewards) } + if h.Fees != nil { + cpy.Fees = new(big.Int).Set(h.Fees) + } return &cpy } diff --git a/FEEDELEGATION.md b/docs/FEEDELEGATION.md similarity index 100% rename from FEEDELEGATION.md rename to docs/FEEDELEGATION.md diff --git a/docs/container.md b/docs/container.md new file mode 100644 index 000000000000..4464b14da648 --- /dev/null +++ b/docs/container.md @@ -0,0 +1,9 @@ +### Starting Nodes With Docker + +Before starting node, an image build is required. Runs from the `containers/docker/wemix` directory. + + docker compose build + +After, run gwemix as follows. + + docker compose up -d \ No newline at end of file diff --git a/eth/api.go b/eth/api.go index 4ebd7231e162..23e27d61702d 100644 --- a/eth/api.go +++ b/eth/api.go @@ -678,3 +678,104 @@ func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64 } return 0, fmt.Errorf("No state found") } + +type PublicWemixAPI struct { + e *Ethereum +} + +type BriocheConfigResult struct { + BlockReward *hexutil.Big `json:"blockReward,omitempty"` + FirstHalvingBlock *hexutil.Big `json:"firstHalvingBlock,omitempty"` + HalvingPeriod *hexutil.Big `json:"halvingPeriod,omitempty"` + FinishRewardBlock *hexutil.Big `json:"finishRewardBlock,omitempty"` + HalvingTimes hexutil.Uint64 `json:"halvingTimes,omitempty"` + HalvingRate hexutil.Uint64 `json:"halvingRate,omitempty"` +} + +type HalvingInfo struct { + HalvingTimes hexutil.Uint64 `json:"halvingTimes"` + StartBlock *hexutil.Big `json:"startBlock"` + EndBlock *hexutil.Big `json:"endBlock"` + BlockReward *hexutil.Big `json:"blockReward"` +} + +func NewPublicWemixAPI(e *Ethereum) *PublicWemixAPI { + return &PublicWemixAPI{e} +} + +func (api *PublicWemixAPI) BriocheConfig() BriocheConfigResult { + bc := api.e.BlockChain().Config().Brioche + return BriocheConfigResult{ + BlockReward: (*hexutil.Big)(bc.BlockReward), + FirstHalvingBlock: (*hexutil.Big)(bc.FirstHalvingBlock), + HalvingPeriod: (*hexutil.Big)(bc.HalvingPeriod), + FinishRewardBlock: (*hexutil.Big)(bc.FinishRewardBlock), + HalvingTimes: hexutil.Uint64(bc.HalvingTimes), + HalvingRate: hexutil.Uint64(bc.HalvingRate), + } +} + +func (api *PublicWemixAPI) HalvingSchedule() []*HalvingInfo { + bc := api.e.BlockChain().Config().Brioche + if bc.FirstHalvingBlock == nil || bc.HalvingPeriod == nil || bc.HalvingTimes == 0 { + return nil + } + + var lastRewardBlock *big.Int + if bc.FinishRewardBlock != nil { + lastRewardBlock = new(big.Int).Sub(bc.FinishRewardBlock, common.Big1) + } + + result := make([]*HalvingInfo, 0) + for i := uint64(0); i < bc.HalvingTimes; i++ { + startBlock := new(big.Int).Add(bc.FirstHalvingBlock, new(big.Int).Mul(bc.HalvingPeriod, new(big.Int).SetUint64(i))) + if lastRewardBlock != nil && startBlock.Cmp(lastRewardBlock) > 0 { + break + } + result = append(result, &HalvingInfo{ + HalvingTimes: hexutil.Uint64(i + 1), + StartBlock: (*hexutil.Big)(startBlock), + EndBlock: (*hexutil.Big)(new(big.Int).Sub(new(big.Int).Add(startBlock, bc.HalvingPeriod), common.Big1)), + BlockReward: (*hexutil.Big)(api.getBriocheBlockReward(startBlock)), + }) + } + + result[len(result)-1].EndBlock = (*hexutil.Big)(lastRewardBlock) + + return result +} + +func (api *PublicWemixAPI) GetBriocheBlockReward(blockNumber rpc.BlockNumber) *hexutil.Big { + height := new(big.Int) + switch blockNumber { + case rpc.LatestBlockNumber: + height.Set(api.e.BlockChain().CurrentHeader().Number) + case rpc.FinalizedBlockNumber: + height.Set(api.e.BlockChain().CurrentHeader().Number) + case rpc.PendingBlockNumber: + height.Set(api.e.miner.PendingBlock().Header().Number) + default: + height.SetInt64(blockNumber.Int64()) + } + + return (*hexutil.Big)(api.getBriocheBlockReward(height)) +} + +func (api *PublicWemixAPI) getBriocheBlockReward(blockNumber *big.Int) *big.Int { + if wemixapi.Info == nil { + return nil + } + wemixInfoPtr, ok := wemixapi.Info().(*map[string]interface{}) + if !ok { + return nil + } + wemixInfo := *wemixInfoPtr + config := api.e.BlockChain().Config() + height := new(big.Int).Set(blockNumber) + + if config.IsBrioche(height) { + return config.Brioche.GetBriocheBlockReward(wemixInfo["defaultBriocheBlockReward"].(*big.Int), height) + } else { + return wemixInfo["blockReward"].(*big.Int) + } +} diff --git a/eth/backend.go b/eth/backend.go index 30809545e97b..08bfc4967e95 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -292,6 +292,15 @@ func (s *Ethereum) APIs() []rpc.API { // Append any APIs exposed explicitly by the consensus engine apis = append(apis, s.engine.APIs(s.BlockChain())...) + if brioche := s.blockchain.Config().Brioche; brioche != nil { + apis = append(apis, rpc.API{ + Namespace: "wemix", + Version: "1.0", + Service: NewPublicWemixAPI(s), + Public: true, + }) + } + // Append all the local APIs and return return append(apis, []rpc.API{ { diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9eef5dfd8d70..9537a5a3e7fa 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -384,6 +384,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int, // Post a user notification of the sync (only once per session) if atomic.CompareAndSwapInt32(&d.notified, 0, 1) { log.Info("Block synchronisation started") + defer log.Info("Block synchronisation finished") } if mode == SnapSync { // Snap sync uses the snapshot namespace to store potentially flakey data until diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index 9a2769fa0d12..9db02a5b3ac2 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/forkid" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" ) @@ -51,7 +52,7 @@ func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis }) }() go func() { - errc <- p.readStatus(network, &status, genesis, forkFilter) + errc <- p.readStatus(network, &status, genesis, forkFilter, forkID) }() timeout := time.NewTimer(handshakeTimeout) defer timeout.Stop() @@ -76,7 +77,7 @@ func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis } // readStatus reads the remote handshake message. -func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error { +func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter, myForkID forkid.ID) error { msg, err := p.rw.ReadMsg() if err != nil { return err @@ -100,7 +101,12 @@ func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.H if status.Genesis != genesis { return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) } - if err := forkFilter(status.ForkID); err != nil { + + // Wemix cannot tolerate a fork among etcd members, so we need to check fork ID manually before the hard fork date + if status.ForkID.Next != myForkID.Next { + defer log.Warn("Checking ForkID: different next fork", "peer", p.ID(), "peerForkID", status.ForkID, "myForkID", myForkID, "error", err) + } + if err = forkFilter(status.ForkID); err != nil { return fmt.Errorf("%w: %v", errForkIDRejected, err) } return nil diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 98be012f3326..70969c3e1424 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index dd01c08f539e..b475feba2c3f 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -401,7 +401,7 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { t.Fatalf("unexpected error: %v", err) } - // Test tx in block interupted. + // Test tx in block interrupted. ctx, cancel := context.WithCancel(context.Background()) cancel() tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) diff --git a/go.mod b/go.mod index 1956e64450e9..0e3cb402128d 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,8 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef + github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 + github.com/yoseplee/vrf v0.0.0-20210814110709-d1caf509310b go.etcd.io/etcd/api/v3 v3.5.2 go.etcd.io/etcd/client/v3 v3.5.2 go.etcd.io/etcd/server/v3 v3.5.2 @@ -72,8 +74,6 @@ require ( golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/urfave/cli.v1 v1.20.0 - github.com/yoseplee/vrf v0.0.0-20210814110709-d1caf509310b - github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 ) require ( diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go index fd7a4a8b7f4c..b9942e2e5a26 100644 --- a/internal/cmdtest/test_cmd.go +++ b/internal/cmdtest/test_cmd.go @@ -27,7 +27,6 @@ import ( "strings" "sync" "sync/atomic" - "syscall" "testing" "text/template" "time" @@ -208,9 +207,7 @@ func (tt *TestCmd) ExitStatus() int { if tt.Err != nil { exitErr := tt.Err.(*exec.ExitError) if exitErr != nil { - if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus() - } + return exitErr.ProcessState.ExitCode() } } return 0 diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index a50eb615ead0..64d871e5139f 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -30,6 +30,7 @@ var Modules = map[string]string{ "txpool": TxpoolJs, "les": LESJs, "vflux": VfluxJs, + "wemix": WemixJs, } const CliqueJs = ` @@ -1004,3 +1005,27 @@ web3._extend({ ] }); ` + +const WemixJs = ` +web3._extend({ + property: 'wemix', + methods: [ + new web3._extend.Method({ + name: 'getBriocheBlockReward', + call: 'wemix_getBriocheBlockReward', + params: 1 + }) + ], + properties: + [ + new web3._extend.Property({ + name: 'briocheConfig', + getter: 'wemix_briocheConfig' + }), + new web3._extend.Property({ + name: 'halvingSchedule', + getter: 'wemix_halvingSchedule' + }) + ] +}); +` diff --git a/metrics/cputime_unix.go b/metrics/cputime_unix.go index 3c56a75d0077..3948ef6f8882 100644 --- a/metrics/cputime_unix.go +++ b/metrics/cputime_unix.go @@ -20,15 +20,14 @@ package metrics import ( - syscall "golang.org/x/sys/unix" - "github.com/ethereum/go-ethereum/log" + "golang.org/x/sys/unix" ) // getProcessCPUTime retrieves the process' CPU time since program startup. func getProcessCPUTime() int64 { - var usage syscall.Rusage - if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { + var usage unix.Rusage + if err := unix.Getrusage(unix.RUSAGE_SELF, &usage); err != nil { log.Warn("Failed to retrieve CPU time", "err", err) return 0 } diff --git a/node/errors.go b/node/errors.go index 67547bf691f1..6855c8ad6103 100644 --- a/node/errors.go +++ b/node/errors.go @@ -20,7 +20,8 @@ import ( "errors" "fmt" "reflect" - "syscall" + + "golang.org/x/sys/unix" ) var ( @@ -33,7 +34,7 @@ var ( ) func convertFileLockError(err error) error { - if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { + if errno, ok := err.(unix.Errno); ok && datadirInUseErrnos[uint(errno)] { return ErrDatadirUsed } return err diff --git a/p2p/netutil/toobig_windows.go b/p2p/netutil/toobig_windows.go index 652903e83c30..128150f7bd1f 100644 --- a/p2p/netutil/toobig_windows.go +++ b/p2p/netutil/toobig_windows.go @@ -22,10 +22,11 @@ package netutil import ( "net" "os" - "syscall" + + "golang.org/x/sys/unix" ) -const _WSAEMSGSIZE = syscall.Errno(10040) +const _WSAEMSGSIZE = unix.Errno(10040) // isPacketTooBig reports whether err indicates that a UDP packet didn't // fit the receive buffer. On Windows, WSARecvFrom returns diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go index 35ccdfb06882..d6d8de89fc69 100644 --- a/p2p/simulations/adapters/exec.go +++ b/p2p/simulations/adapters/exec.go @@ -31,7 +31,6 @@ import ( "path/filepath" "strings" "sync" - "syscall" "time" "github.com/docker/docker/pkg/reexec" @@ -41,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rpc" "github.com/gorilla/websocket" + "golang.org/x/sys/unix" ) func init() { @@ -295,7 +295,7 @@ func (n *ExecNode) Stop() error { n.Info = nil } - if err := n.Cmd.Process.Signal(syscall.SIGTERM); err != nil { + if err := n.Cmd.Process.Signal(unix.SIGTERM); err != nil { return n.Cmd.Process.Kill() } waitErr := make(chan error, 1) @@ -438,7 +438,7 @@ func execP2PNode() { // Stop the stack if we get a SIGTERM signal. go func() { sigc := make(chan os.Signal, 1) - signal.Notify(sigc, syscall.SIGTERM) + signal.Notify(sigc, unix.SIGTERM) defer signal.Stop(sigc) <-sigc log.Info("Received SIGTERM, shutting down...") diff --git a/params/config.go b/params/config.go index 39d494868726..c86356c663ad 100644 --- a/params/config.go +++ b/params/config.go @@ -159,7 +159,16 @@ var ( LondonBlock: big.NewInt(0), PangyoBlock: big.NewInt(0), ApplepieBlock: big.NewInt(20_476_911), + BriocheBlock: big.NewInt(53_525_500), // target date: 24-07-01 00:00:00 (GMT+09) Ethash: new(EthashConfig), + Brioche: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(53_525_500), + HalvingPeriod: big.NewInt(63_115_200), + FinishRewardBlock: big.NewInt(2_467_714_000), // target date: 2101-01-01 00:00:00 (GMT+09) + HalvingTimes: 16, + HalvingRate: 50, + }, } // WemixTestnetChainConfig contains the chain parameters to run a node on the Wemix test network. @@ -180,7 +189,16 @@ var ( LondonBlock: big.NewInt(0), PangyoBlock: big.NewInt(10_000_000), ApplepieBlock: big.NewInt(26_240_268), + BriocheBlock: big.NewInt(59_414_700), // target date: 24-06-04 11:00:41 (GMT+09) Ethash: new(EthashConfig), + Brioche: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(59_414_700), + HalvingPeriod: big.NewInt(63_115_200), + FinishRewardBlock: big.NewInt(2_473_258_000), // target date: 2100-12-01 11:02:21 (GMT+09) + HalvingTimes: 16, + HalvingRate: 50, + }, } // SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. @@ -303,16 +321,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, new(EthashConfig), nil, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int), false) ) @@ -395,19 +413,75 @@ type ChainConfig struct { MergeForkBlock *big.Int `json:"mergeForkBlock,omitempty"` // EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in merge proceedings) PangyoBlock *big.Int `json:"pangyoBlock,omitempty"` // Pangyo switch block (nil = no fork, 0 = already on pangyo) ApplepieBlock *big.Int `json:"applepieBlock,omitempty"` // Applepie switch block (nil = no fork, 0 = already on applepie) + BriocheBlock *big.Int `json:"briocheBlock,omitempty"` // Brioche switch block (nil = no fork, 0 = already on brioche) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` + Brioche *BriocheConfig `json:"brioche,omitempty"` // if this config is nil, brioche halving is not applied } // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} +// Brioche halving configuration +type BriocheConfig struct { + // if the chain is on brioche hard fork, `RewardAmount` of gov contract is not used rather this BlockReward is used + BlockReward *big.Int `json:"blockReward,omitempty"` // nil - use default block reward(1e18) + FirstHalvingBlock *big.Int `json:"firstHalvingBlock,omitempty"` // nil - halving is not work. including this block + HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // nil - halving is not work + FinishRewardBlock *big.Int `json:"finishRewardBlock,omitempty"` // nil - block reward goes on endlessly + HalvingTimes uint64 `json:"halvingTimes,omitempty"` // 0 - no halving + HalvingRate uint32 `json:"halvingRate,omitempty"` // 0 - no reward on halving; 100 - no halving; >100 - increasing reward +} + +func (bc *BriocheConfig) GetBriocheBlockReward(defaultReward *big.Int, num *big.Int) *big.Int { + blockReward := new(big.Int).Set(defaultReward) // default brioche block reward + if bc != nil { + if bc.BlockReward != nil { + blockReward = new(big.Int).Set(bc.BlockReward) + } + if bc.FinishRewardBlock != nil && + bc.FinishRewardBlock.Cmp(num) <= 0 { + blockReward = big.NewInt(0) + } else if bc.FirstHalvingBlock != nil && + bc.HalvingPeriod != nil && + bc.HalvingTimes > 0 && + num.Cmp(bc.FirstHalvingBlock) >= 0 { + blockReward = bc.calcHalvedReward(blockReward, num) + } + } + return blockReward +} + +func (bc *BriocheConfig) calcHalvedReward(baseReward *big.Int, num *big.Int) *big.Int { + elapsed := new(big.Int).Sub(num, bc.FirstHalvingBlock) + times := new(big.Int).Add(common.Big1, new(big.Int).Div(elapsed, bc.HalvingPeriod)) + if times.Uint64() > bc.HalvingTimes { + times = big.NewInt(int64(bc.HalvingTimes)) + } + + reward := new(big.Int).Set(baseReward) + numerator := new(big.Int).Exp(big.NewInt(int64(bc.HalvingRate)), times, nil) + denominator := new(big.Int).Exp(big.NewInt(100), times, nil) + return reward.Div(reward.Mul(reward, numerator), denominator) +} + +func (bc *BriocheConfig) String() string { + return fmt.Sprintf("{BlockReward: %v FirstHalvingBlock: %v HalvingPeriod: %v FinishRewardBlock: %v HalvingTimes: %v HalvingRate: %v}", + bc.BlockReward, + bc.FirstHalvingBlock, + bc.HalvingPeriod, + bc.FinishRewardBlock, + bc.HalvingTimes, + bc.HalvingRate, + ) +} + // String implements the stringer interface, returning the consensus engine details. func (c *EthashConfig) String() string { return "ethash" @@ -435,7 +509,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, Terminal TD: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, BriocheConfig: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -454,7 +528,9 @@ func (c *ChainConfig) String() string { c.MergeForkBlock, c.PangyoBlock, c.ApplepieBlock, + c.BriocheBlock, c.TerminalTotalDifficulty, + c.Brioche, engine, ) } @@ -537,6 +613,10 @@ func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool { return isForked(c.ArrowGlacierBlock, num) } +func (c *ChainConfig) IsBrioche(num *big.Int) bool { + return isForked(c.BriocheBlock, num) +} + // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { if c.TerminalTotalDifficulty == nil { @@ -592,6 +672,9 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "londonBlock", block: c.LondonBlock}, {name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true}, {name: "mergeStartBlock", block: c.MergeForkBlock, optional: true}, + {name: "pangyoBlock", block: c.PangyoBlock, optional: true}, + {name: "applepieBlock", block: c.ApplepieBlock, optional: true}, + {name: "briocheBlock", block: c.BriocheBlock, optional: true}, } { if lastFork.name != "" { // Next one must be higher number @@ -667,6 +750,15 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.MergeForkBlock, newcfg.MergeForkBlock, head) { return newCompatError("Merge Start fork block", c.MergeForkBlock, newcfg.MergeForkBlock) } + if isForkIncompatible(c.PangyoBlock, newcfg.PangyoBlock, head) { + return newCompatError("Pangyo fork block", c.PangyoBlock, newcfg.PangyoBlock) + } + if isForkIncompatible(c.ApplepieBlock, newcfg.ApplepieBlock, head) { + return newCompatError("Applepie fork block", c.ApplepieBlock, newcfg.ApplepieBlock) + } + if isForkIncompatible(c.BriocheBlock, newcfg.BriocheBlock, head) { + return newCompatError("Brioche fork block", c.BriocheBlock, newcfg.BriocheBlock) + } return nil } @@ -736,7 +828,7 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge bool - IsPangyo, IsApplepie bool + IsPangyo, IsApplepie, IsBrioche bool } // Rules ensures c's ChainID is not nil. @@ -760,5 +852,6 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules { IsMerge: isMerge, IsPangyo: c.IsPangyo(num), IsApplepie: c.IsApplepie(num), + IsBrioche: c.IsBrioche(num), } } diff --git a/params/config_test.go b/params/config_test.go index 3c8ebaf4a511..17b49b3d18ff 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -96,3 +96,339 @@ func TestCheckCompatible(t *testing.T) { } } } + +func TestHalveRewards(t *testing.T) { + testcases := []struct { + reward *big.Int + period *big.Int + past *big.Int + times uint64 + rate uint32 + expected *big.Int + }{ + // sample test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, 50, big.NewInt(25e16)}, + + // times test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, 50, big.NewInt(25e16)}, + + // rate test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 10, big.NewInt(1e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(200), 3, 10, big.NewInt(1e15)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 20, big.NewInt(4e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 30, big.NewInt(9e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 90, big.NewInt(81e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 200, big.NewInt(4e18)}, + + // brioche halving test + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, 50, big.NewInt(125e15)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, 50, big.NewInt(625e14)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, 50, big.NewInt(3125e13)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, 50, big.NewInt(15625e12)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, 50, big.NewInt(78125e11)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, 50, big.NewInt(390625e10)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, 50, big.NewInt(1953125e9)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, 50, big.NewInt(9765625e8)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, 50, big.NewInt(48828125e7)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, 50, big.NewInt(244140625e6)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, 50, big.NewInt(1220703125e5)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, 50, big.NewInt(6103515625e4)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, 50, big.NewInt(30517578125e3)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, 50, big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, 50, big.NewInt(152587890625e2)}, + } + + for _, tc := range testcases { + brioche := &BriocheConfig{ + BlockReward: tc.reward, + FirstHalvingBlock: big.NewInt(0), + HalvingPeriod: tc.period, + HalvingTimes: tc.times, + HalvingRate: tc.rate, + } + halved := brioche.calcHalvedReward(tc.reward, tc.past) + if tc.expected.Cmp(halved) != 0 { + t.Errorf("halved reward mismatched (expected=%v, actual=%v)", tc.expected, halved) + } + } +} + +func TestGetBriocheBlockReward(t *testing.T) { + defaultBlockReward := big.NewInt(1234e14) + testcases := []struct { + id int32 + briocheConfig *BriocheConfig + blockNum *big.Int + expected *big.Int + }{ + // nil case + { + id: 1, + briocheConfig: nil, + blockNum: big.NewInt(100), + expected: defaultBlockReward, + }, + + // normal case + { + id: 2, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(5e17), + }, + { + id: 3, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(201), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(25e16), + }, + + // base block reward variations + { + id: 4, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(7e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(35e17), + }, + { + id: 5, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(3), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1), + }, + { + id: 6, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + id: 7, + briocheConfig: &BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: new(big.Int).Div(defaultBlockReward, big.NewInt(2)), + }, + + // no halving + { + id: 8, + briocheConfig: &BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: nil, + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: defaultBlockReward, + }, + { + id: 9, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: nil, + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + { + id: 10, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(101), + HalvingTimes: 0, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + { + id: 11, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(99), // not yet halving time + expected: big.NewInt(10), + }, + { + id: 12, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 100, // no halving rate + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + + // no reward case + { + id: 13, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 0, // no reward + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + id: 14, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(199), + expected: big.NewInt(5), + }, + { + id: 15, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(0), + }, + + // halving rate variations + { + id: 16, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1e17), + }, + { + id: 17, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(1e16), + }, + { + id: 18, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 1, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(1e12), + }, + { + id: 19, + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + FinishRewardBlock: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 99, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(970299e12), + }, + } + + for _, tc := range testcases { + actual := tc.briocheConfig.GetBriocheBlockReward(defaultBlockReward, tc.blockNum) + if tc.expected.Cmp(actual) != 0 { + t.Errorf("getBriocheReward mismatched (id=%d, expected=%v, actual=%v)", tc.id, tc.expected, actual) + } + } +} diff --git a/params/version.go b/params/version.go index 65bce5efb03b..daf0ba2ca895 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 0 // Major version component of the current release VersionMinor = 10 // Minor version component of the current release - VersionPatch = 7 // Patch version component of the current release + VersionPatch = 8 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string ) diff --git a/params/wemix_config.go b/params/wemix_config.go index 772976c16345..455782306915 100644 --- a/params/wemix_config.go +++ b/params/wemix_config.go @@ -16,13 +16,7 @@ var ( WemixTestnetBootnodes = []string{ "enode://6137facc7a938d245d3a9b8a8ab2bed33b4d4dbc6f75058e176d54f3f9689ac5b7fad00efbda3eec8a292412d3616e84507adecc12c175eaa5dd7c1374a46fb2@20.41.113.133:8589", - "enode://a7274dcc5653edf72db753208ce21fce4ed23f967f94eaa720d1296ef4ceb99ed429aec2daedf01864e07c38d48407e8c02b768698b4f34aac845829045b6705@20.41.113.165:8589", "enode://7b53a01356fd4b227dc354f1f725705257c96b504f2da4bb6e0f1beb6bbd4e9203608711e13035f278dbfd144a904bddb3fe526719d878e15b3e9f759afbef85@20.89.67.116:8589", - "enode://f8b4979b8e8afd5031ced4ebb9c8e7ce0ad8d914be3186bc8a67346fbd1c9cae1447d2c006d986e910820956b755ce32e40f5af953e3e68812bb5f3f62ce8cd3@20.222.40.143:8589", - "enode://64e4cb4b0702f1fb33ca8f5beb62f19c928ecc0acf925c53312e44ea53ccf381616178bcc77f70eec68770158b8d8be994b4bde317bea33c13185bdc689a018e@20.24.30.174:8589", - "enode://0778431754005b5a0332ffbb61d54983668cbfdbfcd5d830364c0b7fb0355f81306ef761762eb38f0800f37b1aebbd4f680fde33cfc9c683bb3c8283d12dc64a@20.212.107.1:8589", - "enode://0b8f4bc0ada2695f5d064953cf9f2486c4fc3560dc662363d01a1908df9d273118009755413a3eb6e651f1cdc62ec5425e03ecf6e511c3a2fddae690e91f2b5c@20.245.249.106:8589", - "enode://1a66550c2e7ec3e07aa39efc615331539aadca109377575a4c86bf84d24e66317dd1939f99016699f217285c2a82e4f1e225796666f6af59ab2b29b3c8243fc8@20.245.249.99:8589", } WemixGenesisFile string diff --git a/wemix/admin.go b/wemix/admin.go index 5441dfa14f65..e26618d61749 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -123,11 +123,12 @@ type rewardParameters struct { var ( // "Wemix Registry" - magic, _ = big.NewInt(0).SetString("0x57656d6978205265676973747279", 0) - etcdClusterName = "Wemix" - big0 = big.NewInt(0) - nilAddress = common.Address{} - admin *wemixAdmin + magic, _ = new(big.Int).SetString("0x57656d6978205265676973747279", 0) + etcdClusterName = "Wemix" + big0 = big.NewInt(0) + nilAddress = common.Address{} + defaultBriocheBlockReward = big.NewInt(1e18) + admin *wemixAdmin ErrAlreadyRunning = errors.New("already running") ErrExists = errors.New("already exists") @@ -1013,31 +1014,6 @@ type reward struct { Reward *big.Int `json:"reward"` } -func (ma *wemixAdmin) verifyRewards(r1, r2 []byte) error { - var err error - var a, b []reward - - if err = json.Unmarshal(r1, &a); err != nil { - return err - } - if err = json.Unmarshal(r2, &b); err != nil { - return err - } - - err = fmt.Errorf("Incorrect Rewards") - if len(a) != len(b) { - return err - } - for i := 0; i < len(a); i++ { - if !bytes.Equal(a[i].Addr.Bytes(), b[i].Addr.Bytes()) || - a[i].Reward != b[i].Reward { - return err - } - } - - return nil -} - // handles rewards in testnet block 94 func handleBlock94Rewards(height *big.Int, rp *rewardParameters, fees *big.Int) []reward { if height.Int64() != 94 || len(rp.members) != 0 || @@ -1051,7 +1027,7 @@ func handleBlock94Rewards(height *big.Int, rp *rewardParameters, fees *big.Int) // distributeRewards divides the rewardAmount among members according to their // stakes, and allocates rewards to staker, ecoSystem, and maintenance accounts. -func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([]reward, error) { +func distributeRewards(height *big.Int, rp *rewardParameters, blockReward *big.Int, fees *big.Int) ([]reward, error) { dm := new(big.Int) for i := 0; i < len(rp.distributionMethod); i++ { dm.Add(dm, rp.distributionMethod[i]) @@ -1061,14 +1037,14 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] } v10000 := big.NewInt(10000) - minerAmount := new(big.Int).Set(rp.rewardAmount) + minerAmount := new(big.Int).Set(blockReward) minerAmount.Div(minerAmount.Mul(minerAmount, rp.distributionMethod[0]), v10000) - stakerAmount := new(big.Int).Set(rp.rewardAmount) + stakerAmount := new(big.Int).Set(blockReward) stakerAmount.Div(stakerAmount.Mul(stakerAmount, rp.distributionMethod[1]), v10000) - ecoSystemAmount := new(big.Int).Set(rp.rewardAmount) + ecoSystemAmount := new(big.Int).Set(blockReward) ecoSystemAmount.Div(ecoSystemAmount.Mul(ecoSystemAmount, rp.distributionMethod[2]), v10000) // the rest goes to maintenance - maintenanceAmount := new(big.Int).Set(rp.rewardAmount) + maintenanceAmount := new(big.Int).Set(blockReward) maintenanceAmount.Sub(maintenanceAmount, minerAmount) maintenanceAmount.Sub(maintenanceAmount, stakerAmount) maintenanceAmount.Sub(maintenanceAmount, ecoSystemAmount) @@ -1102,7 +1078,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] } d.Mul(d, vn) b.Sub(b, d) - for i, ix := 0, height.Int64()%int64(n); b.Cmp(v0) > 0; i, ix = i+1, (ix+1)%int64(n) { + for ix := height.Int64() % int64(n); b.Cmp(v0) > 0; ix = (ix + 1) % int64(n) { rewards[ix].Reward.Add(rewards[ix].Reward, v1) b.Sub(b, v1) } @@ -1146,17 +1122,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] return rewards, nil } -func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - rp, err := ma.getRewardParams(ctx, big.NewInt(num.Int64()-1)) - if err != nil { - // all goes to the coinbase - err = wemixminer.ErrNotInitialized - return - } - +func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (rewards []byte, err error) { if (rp.staker == nil && rp.ecoSystem == nil && rp.maintenance == nil) || len(rp.members) == 0 { // handle testnet block 94 rewards if rewards94 := handleBlock94Rewards(num, rp, fees); rewards94 != nil { @@ -1172,14 +1138,21 @@ func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalan return } - // determine coinbase - if len(rp.members) > 0 { - mix := int(num.Int64()/ma.blocksPer) % len(rp.members) - coinbase = &common.Address{} - coinbase.SetBytes(rp.members[mix].Reward.Bytes()) + var blockReward *big.Int + if config.IsBrioche(num) { + blockReward = config.Brioche.GetBriocheBlockReward(defaultBriocheBlockReward, num) + } else { + // if the wemix chain is not on brioche hard fork, use the `rewardAmount` from gov contract + blockReward = new(big.Int).Set(rp.rewardAmount) } - rr, errr := distributeRewards(num, rp, fees) + // block reward + // - not brioche chain: use `EnvStorageImp.getBlockRewardAmount()` + // - brioche chain + // - config.Brioche.BlockReward != nil: config.Brioche.BlockReward + // - config.Brioche.BlockReward == nil: 1e18 + // - apply halving for BlockReward + rr, errr := distributeRewards(num, rp, blockReward, fees) if errr != nil { err = errr return @@ -1195,13 +1168,17 @@ func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalan return } -func calculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { - return admin.calculateRewards(num, blockReward, fees, addBalance) -} +func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() -func verifyRewards(num *big.Int, rewards string) error { - return nil - //return admin.verifyRewards(num, rewards) + rp, err := admin.getRewardParams(ctx, big.NewInt(num.Int64()-1)) + if err != nil { + // all goes to the coinbase + return nil, wemixminer.ErrNotInitialized + } + + return calculateRewardsWithParams(config, rp, num, fees, addBalance) } func getCoinbase(height *big.Int) (coinbase common.Address, err error) { @@ -1546,24 +1523,25 @@ func Info() interface{} { }) info := &map[string]interface{}{ - "consensus": params.ConsensusMethod, - "registry": admin.registry.To, - "governance": admin.gov.To, - "staking": admin.staking.To, - "modifiedblock": admin.modifiedBlock, - "blocksPer": admin.blocksPer, - "blockInterval": admin.blockInterval, - "blockReward": admin.blockReward, - "maxPriorityFeePerGas": admin.maxPriorityFeePerGas, - "blockGasLimit": admin.gasLimit, - "maxBaseFee": admin.maxBaseFee, - "baseFeeMaxChangeRate": admin.baseFeeMaxChangeRate, - "gasTargetPercentage": admin.gasTargetPercentage, - "self": self, - "nodes": nodes, - "miners": admin.miners(), - "etcd": admin.etcdInfo(), - "maxIdle": params.MaxIdleBlockInterval, + "consensus": params.ConsensusMethod, + "registry": admin.registry.To, + "governance": admin.gov.To, + "staking": admin.staking.To, + "modifiedblock": admin.modifiedBlock, + "blocksPer": admin.blocksPer, + "blockInterval": admin.blockInterval, + "blockReward": admin.blockReward, + "maxPriorityFeePerGas": admin.maxPriorityFeePerGas, + "blockGasLimit": admin.gasLimit, + "maxBaseFee": admin.maxBaseFee, + "baseFeeMaxChangeRate": admin.baseFeeMaxChangeRate, + "gasTargetPercentage": admin.gasTargetPercentage, + "self": self, + "nodes": nodes, + "miners": admin.miners(), + "etcd": admin.etcdInfo(), + "maxIdle": params.MaxIdleBlockInterval, + "defaultBriocheBlockReward": defaultBriocheBlockReward, } return info } @@ -1816,7 +1794,6 @@ func init() { wemixminer.AmHubFunc = AmHub wemixminer.SuggestGasPriceFunc = suggestGasPrice wemixminer.CalculateRewardsFunc = calculateRewards - wemixminer.VerifyRewardsFunc = verifyRewards wemixminer.GetCoinbaseFunc = getCoinbase wemixminer.SignBlockFunc = signBlock wemixminer.VerifyBlockSigFunc = verifyBlockSig diff --git a/wemix/miner/miner.go b/wemix/miner/miner.go index 45abda5a5c59..f0ba1bd35db8 100644 --- a/wemix/miner/miner.go +++ b/wemix/miner/miner.go @@ -16,8 +16,7 @@ var ( AmPartnerFunc func() bool IsPartnerFunc func(string) bool AmHubFunc func(string) int - CalculateRewardsFunc func(*big.Int, *big.Int, *big.Int, func(common.Address, *big.Int)) (*common.Address, []byte, error) - VerifyRewardsFunc func(*big.Int, string) error + CalculateRewardsFunc func(*params.ChainConfig, *big.Int, *big.Int, func(common.Address, *big.Int)) ([]byte, error) GetCoinbaseFunc func(height *big.Int) (coinbase common.Address, err error) SignBlockFunc func(height *big.Int, hash common.Hash) (coinbase common.Address, sig []byte, err error) VerifyBlockSigFunc func(height *big.Int, coinbase common.Address, nodeId []byte, hash common.Hash, sig []byte, checkMinerLimit bool) bool @@ -79,19 +78,11 @@ func IsPoW() bool { return params.ConsensusMethod == params.ConsensusPoW } -func CalculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { +func CalculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { if CalculateRewardsFunc == nil { - return nil, nil, ErrNotInitialized + return nil, ErrNotInitialized } else { - return CalculateRewardsFunc(num, blockReward, fees, addBalance) - } -} - -func VerifyRewards(num *big.Int, rewards string) error { - if VerifyRewardsFunc == nil { - return ErrNotInitialized - } else { - return VerifyRewardsFunc(num, rewards) + return CalculateRewardsFunc(config, num, fees, addBalance) } } diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 0737abd492c1..dcafac7732e8 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -3,13 +3,32 @@ package wemix import ( + "bytes" + "crypto/ecdsa" "encoding/json" "math/big" + "strings" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + wemixminer "github.com/ethereum/go-ethereum/wemix/miner" ) +func hexToBigInt(hexNum string) *big.Int { + if num, ok := new(big.Int).SetString(hexNum[2:], 16); ok { + return num + } else { + return nil + } +} + // TestDistributeRewards tests the DistributeRewards function func TestDistributeRewards(t *testing.T) { @@ -18,14 +37,6 @@ func TestDistributeRewards(t *testing.T) { return &address } - hexToBigInt := func(hexNum string) *big.Int { - if num, ok := new(big.Int).SetString(hexNum[2:], 16); ok { - return num - } else { - return nil - } - } - // Test cases tests := []struct { name string @@ -135,13 +146,279 @@ func TestDistributeRewards(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the distributeRewards function - rewards, err := distributeRewards(tt.height, tt.rp, tt.fees) + rewards, err := distributeRewards(tt.height, tt.rp, tt.rp.rewardAmount, tt.fees) rewardsString, _ := json.Marshal(rewards) if string(rewardsString) != tt.want { t.Errorf("distributeRewards() failed: %v, %v <-> %v", err, tt.want, string(rewardsString)) } + + distTotal := new(big.Int) + for _, dist := range tt.rp.distributionMethod { + distTotal.Add(distTotal, dist) + } + totalRewards := new(big.Int) + memberRewards := new(big.Int) + for i, reward := range rewards { + totalRewards.Add(totalRewards, reward.Reward) + if i < len(tt.rp.members) { + memberRewards.Add(memberRewards, reward.Reward) + } + } + totalAmount := new(big.Int).Set(tt.rp.rewardAmount) + totalAmount.Add(totalAmount, tt.fees) + memberAmount := new(big.Int).Set(tt.rp.rewardAmount) + memberAmount = memberAmount.Mul(memberAmount, tt.rp.distributionMethod[0]) + memberAmount = memberAmount.Div(memberAmount, distTotal) + if memberRewards.Cmp(memberAmount) != 0 { + t.Errorf("members reward amount mismatched! sum=%d, rewards=%d", memberRewards, memberAmount) + } + if totalRewards.Cmp(totalAmount) != 0 { + t.Errorf("total reward amount mismatched! sum=%d, rewards=%d", totalRewards, totalAmount) + } }) } } +func makeCalculateRewardFunc(rp *rewardParameters) func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { + return func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { + return calculateRewardsWithParams(config, rp, num, fees, addBalance) + } +} + +func makeSignBlockFunc(privateKey *ecdsa.PrivateKey) func(height *big.Int, hash common.Hash) (common.Address, []byte, error) { + return func(height *big.Int, hash common.Hash) (coinbase common.Address, sig []byte, err error) { + data := append(height.Bytes(), hash.Bytes()...) + data = crypto.Keccak256(data) + sig, _ = crypto.Sign(data, privateKey) + return crypto.PubkeyToAddress(privateKey.PublicKey), sig, nil + } +} + +func verifyBlockSigForTest(height *big.Int, coinbase common.Address, nodeId []byte, hash common.Hash, sig []byte, checkMinerLimit bool) bool { + var data []byte + data = append(height.Bytes(), hash.Bytes()...) + data = crypto.Keccak256(data) + pubKey, err := crypto.SigToPub(data, sig) + if err != nil { + return false + } + signer := crypto.PubkeyToAddress(*pubKey) + if err != nil || !bytes.Equal(coinbase.Bytes(), signer.Bytes()) { + return false + } + return true +} + +func TestRewardValidation(t *testing.T) { + // use wemix consensus + params.ConsensusMethod = params.ConsensusPoA + + var ( + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(100000000000000) + deleteAddr = common.Address{1} + gspec = &core.Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: common.Big0, + BriocheBlock: common.Big0, + Brioche: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(100), + FirstHalvingBlock: big.NewInt(0), + HalvingPeriod: big.NewInt(10), + FinishRewardBlock: big.NewInt(30), + HalvingTimes: 3, + HalvingRate: 50, + }}, + Alloc: core.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: big.NewInt(0)}}, + } + genesis = gspec.MustCommit(db) + signer = types.LatestSigner(gspec.Config) + ) + + rp := &rewardParameters{ + rewardAmount: big.NewInt(1e18), + staker: &common.Address{0x11}, + ecoSystem: &common.Address{0x22}, + maintenance: &common.Address{0x33}, + feeCollector: &common.Address{0x44}, + members: []*wemixMember{ + { + Staker: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Reward: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Stake: hexToBigInt("0x1a784379d99db42000000"), + }, + { + Staker: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Reward: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Stake: hexToBigInt("0xe8ef1e96ae3897800000"), + }, + { + Staker: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Reward: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Stake: hexToBigInt("0xc92b9a6adc4825c00000"), + }, + }, + blocksPer: 1, + distributionMethod: []*big.Int{big.NewInt(4000), big.NewInt(1000), big.NewInt(2500), big.NewInt(2500)}, + } + + wemixminer.CalculateRewardsFunc = makeCalculateRewardFunc(rp) + wemixminer.SignBlockFunc = makeSignBlockFunc(key) + wemixminer.VerifyBlockSigFunc = verifyBlockSigForTest + + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + byzantineConfig := ¶ms.ChainConfig{ + ChainID: gspec.Config.ChainID, + LondonBlock: gspec.Config.LondonBlock, + BriocheBlock: gspec.Config.BriocheBlock, + Brioche: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(200), // different reward!! + FirstHalvingBlock: gspec.Config.Brioche.FirstHalvingBlock, + HalvingPeriod: gspec.Config.Brioche.HalvingPeriod, + FinishRewardBlock: gspec.Config.Brioche.FinishRewardBlock, + HalvingTimes: gspec.Config.Brioche.HalvingTimes, + HalvingRate: gspec.Config.Brioche.HalvingRate, + }} + blocks, _ := core.GenerateChain(byzantineConfig, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) + + if _, err := blockchain.InsertChain(blocks); err != nil { + if !strings.HasPrefix(err.Error(), "remote block hash is different") { + t.Fatal(err) + } + } else { + t.Fatal("reward validation failed") + } +} + +func TestBriocheHardFork(t *testing.T) { + // use wemix consensus + params.ConsensusMethod = params.ConsensusPoA + + var ( + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(100000000000000000) + deleteAddr = common.Address{1} + gspec = &core.Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: common.Big0, + BriocheBlock: big.NewInt(2), + Brioche: ¶ms.BriocheConfig{ + // 1 block reward: 1e18 + // 2 block reward: 4e17 (brioche start) + // 3 block reward: 4e17 + // 4 block reward: 2e17 (first halving) + // 5 block reward: 2e17 + // 6 block reward: 1e17 (second halving) + // 7~ block reward: 0 + BlockReward: big.NewInt(4e17), + FirstHalvingBlock: big.NewInt(4), + HalvingPeriod: big.NewInt(2), + FinishRewardBlock: big.NewInt(7), + HalvingTimes: 2, + HalvingRate: 50, + }}, + Alloc: core.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: big.NewInt(0)}}, + } + genesis = gspec.MustCommit(db) + signer = types.LatestSigner(gspec.Config) + ) + + expectedBlockReward := []*big.Int{ + big.NewInt(0), // zero block reward; not used + big.NewInt(1e18), + big.NewInt(4e17), + big.NewInt(4e17), + big.NewInt(2e17), + big.NewInt(2e17), + big.NewInt(1e17), + big.NewInt(0), + } + + miners := []common.Address{ + common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + } + rp := &rewardParameters{ + rewardAmount: big.NewInt(1e18), + staker: &common.Address{0x11}, + ecoSystem: &common.Address{0x22}, + maintenance: &common.Address{0x33}, + feeCollector: &common.Address{0x44}, + members: []*wemixMember{ + { + Staker: miners[0], + Reward: miners[0], + Stake: hexToBigInt("0xFE1C215E8F838E00000"), // 75e21 (75%) + }, + { + Staker: miners[1], + Reward: miners[1], + Stake: hexToBigInt("0x54B40B1F852BDA00000"), // 25e21 (25%) + }, + }, + blocksPer: 1, + distributionMethod: []*big.Int{big.NewInt(5000), big.NewInt(0), big.NewInt(2500), big.NewInt(2500)}, // miner, staker, eco, maintenance + } + + wemixminer.CalculateRewardsFunc = makeCalculateRewardFunc(rp) + wemixminer.SignBlockFunc = makeSignBlockFunc(key) + wemixminer.VerifyBlockSigFunc = verifyBlockSigForTest + + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + parent := genesis + for i := 1; i <= 7; i++ { + statedb, _ := blockchain.State() + miner0Bal := statedb.GetBalance(miners[0]) + miner1Bal := statedb.GetBalance(miners[1]) + + blocks, _ := core.GenerateChain(gspec.Config, parent, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } + statedb, _ = blockchain.State() + + miner0Reward := new(big.Int).Div(expectedBlockReward[i], big.NewInt(2)) + miner0Reward = miner0Reward.Mul(miner0Reward, big.NewInt(3)) + miner0Reward = miner0Reward.Div(miner0Reward, big.NewInt(4)) + + miner1Reward := new(big.Int).Div(expectedBlockReward[i], big.NewInt(2)) + miner1Reward = miner1Reward.Div(miner1Reward, big.NewInt(4)) + + miner0Bal = new(big.Int).Add(miner0Bal, miner0Reward) + miner1Bal = new(big.Int).Add(miner1Bal, miner1Reward) + if statedb.GetBalance(miners[0]).Cmp(miner0Bal) != 0 { + t.Logf("miner bal = %v, expected = %v", statedb.GetBalance(miners[0]), miner0Bal) + t.Fatal("block reward mismatched for miner0") + } + if statedb.GetBalance(miners[1]).Cmp(miner1Bal) != 0 { + t.Fatal("block reward mismatched for miner1") + } + + parent = blocks[0] + } +} + // EOF