diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 79c7a53014..1a954e8ab5 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -7,6 +7,4 @@ core/ @karalabe @holiman
eth/ @karalabe
les/ @zsfelfoldi
light/ @zsfelfoldi
-mobile/ @karalabe
p2p/ @fjl @zsfelfoldi
-whisper/ @gballet @gluk256
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 64dfde9142..3306363dd0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -55,7 +55,7 @@ jobs:
uses: actions/setup-go@v5
with:
cache: false
- go-version: '1.21.x'
+ go-version: '1.22.x'
- name: Run tests
run: ${{ matrix.script }}
env:
diff --git a/.travis.yml.bak b/.travis.yml.bak
deleted file mode 100644
index b4e3db53b3..0000000000
--- a/.travis.yml.bak
+++ /dev/null
@@ -1,180 +0,0 @@
-sudo: required
-language: go
-go_import_path: github.com/XinFinOrg/XDPoSChain
-
-on:
- branches:
- - master
- - dev-upgrade
- tags: true
-
-env:
- global:
- - GOPROXY=https://proxy.golang.org
- - GO111MODULE=on
- # Terraform env
- - tf_version=1.3.0
- # Setting terraform init CLI options - https://www.terraform.io/docs/commands/init.html
- - tf_init_cli_options=" -input=false"
- # Set terraform validation CLI options - https://www.terraform.io/docs/commands/validate.html
- - tf_validation_cli_options=""
- # Set terraform plan CLI options - https://www.terraform.io/docs/commands/plan.html
- - tf_plan_cli_options=" -lock=false -input=false"
- # Set terraform apply CLI options - https://www.terraform.io/docs/commands/apply.html
- - tf_apply_cli_options=" -auto-approve -input=false"
-
-
-jobs:
- include:
- - stage: Tests
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: A-B tests
- script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[a-b].*")
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[a-m].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: C-[a-m] tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[n-o].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: C-[n-o] tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/c[p-z].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: C-[p-z] tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[d-i].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: D-I tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[j-n].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: J-N tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[o-r].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: O-R tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/s.*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: S tests
- - script: travis_retry go run build/ci.go test -coverage $(go list ./... | grep "github.com\/XinFinOrg\/XDPoSChain\/[t-z].*")
- os: linux
- dist: bionic
- go: 1.21.x
- env:
- - GO111MODULE=auto
- name: T-Z tests
-
- - stage: TAG Build
- if: tag IS present
- services:
- - docker
- install: skip
- before_script:
- - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- - docker --version # document the version travis is using
- - docker build -t xinfinorg/xdposchain:$TRAVIS_TAG -f cicd/Dockerfile .
- script:
- - docker push xinfinorg/xdposchain:$TRAVIS_TAG
-
- - stage: (Devnet) Build, and push images
- if: branch = dev-upgrade AND type = push AND tag IS blank
- services:
- - docker
- install: skip
- before_script:
- - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- - docker --version # document the version travis is using
- - docker pull xinfinorg/devnet:latest # build the "previous" tag so that our devnet environment can run with both old and new code at the same time.
- - docker tag xinfinorg/devnet:latest xinfinorg/devnet:previous
- - docker rmi xinfinorg/devnet:latest
- - docker build -t xinfinorg/devnet:latest -f cicd/Dockerfile .
- script:
- - docker push xinfinorg/devnet:latest
- - docker push xinfinorg/devnet:previous
-
- - stage: (Devnet)Terraform plan
- if: branch = dev-upgrade AND type = push AND tag IS blank
- dist: xenial
- language: bash
- install:
- - wget https://releases.hashicorp.com/terraform/"$tf_version"/terraform_"$tf_version"_linux_amd64.zip
- - unzip terraform_"$tf_version"_linux_amd64.zip
- - sudo mv terraform /usr/local/bin/
- - rm terraform_"$tf_version"_linux_amd64.zip
- script:
- - echo "Pull request detected, creating change plan(Devnet)"
- - cd cicd/devnet/terraform
- # Terraform init, validate, then create change plan. If any fail, fail validation
- - terraform init $tf_init_cli_options
- - terraform validate $tf_validation_cli_options
- - terraform plan $tf_plan_cli_options
-
- - stage: (Devnet) Terraform apply
- if: branch = dev-upgrade AND type = push AND tag IS blank
- dist: xenial
- language: bash
- install:
- # Download and install terraform before each run
- - wget https://releases.hashicorp.com/terraform/"$tf_version"/terraform_"$tf_version"_linux_amd64.zip
- - unzip terraform_"$tf_version"_linux_amd64.zip
- - sudo mv terraform /usr/local/bin/
- - rm terraform_"$tf_version"_linux_amd64.zip
- - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- - unzip awscliv2.zip
- - sudo ./aws/install
- - export PATH=$PATH:$HOME/.local/bin # put aws in the path
- script:
- - echo "Merge detected, executing changes(Devnet)"
- - cd cicd/devnet/terraform
- # Terraform init and then apply changes to environment
- - terraform init $tf_init_cli_options
- - terraform apply $tf_apply_cli_options
- - sleep 5
- - |
- source .env
- for ((i=$us_east_2_start;i<$us_east_2_end;i++)); do
- echo "Force deploy xdc-$i"
- aws ecs update-service --region us-east-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager;
- done
- for ((i=$eu_west_1_start;i<$eu_west_1_end;i++)); do
- echo "Force deploy xdc-$i"
- aws ecs update-service --region eu-west-1 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager;
- done
- for ((i=$ap_southeast_2_start;i<$ap_southeast_2_end;i++)); do
- echo "Force deploy xdc-$i"
- aws ecs update-service --region ap-southeast-2 --cluster devnet-xdcnode-cluster --service ecs-service-xdc$i --force-new-deployment --no-cli-pager;
- done
- aws ecs update-service --region ap-southeast-1 --cluster devnet-xdcnode-cluster --service ecs-service-rpc1 --force-new-deployment --no-cli-pager;
-
- - stage: (Devnet) Send Deployment Notification
- if: branch = dev-upgrade AND type = push AND tag IS blank
- language: bash
- script:
- - curl --location --request POST "66.94.98.186:8080/deploy?environment=devnet&service=xdc&version=$TRAVIS_COMMIT"
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 1207545aa7..5aba541a49 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.21-alpine as builder
+FROM golang:1.22-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git
diff --git a/Dockerfile.bootnode b/Dockerfile.bootnode
index bb906b581f..374bea3d3c 100644
--- a/Dockerfile.bootnode
+++ b/Dockerfile.bootnode
@@ -1,4 +1,4 @@
-FROM golang:1.21-alpine as builder
+FROM golang:1.22-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers
diff --git a/Dockerfile.node b/Dockerfile.node
index 3d6f58495b..b3e0f299d9 100644
--- a/Dockerfile.node
+++ b/Dockerfile.node
@@ -1,4 +1,4 @@
-FROM golang:1.21-alpine as builder
+FROM golang:1.22-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git
diff --git a/Makefile b/Makefile
index 1ab433b070..3bd726906c 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
GOBIN = $(shell pwd)/build/bin
GOFMT = gofmt
-GO ?= 1.21.3
+GO ?= 1.22.10
GO_PACKAGES = .
GO_FILES := $(shell find $(shell go list -f '{{.Dir}}' $(GO_PACKAGES)) -name \*.go)
diff --git a/XDCx/XDCx.go b/XDCx/XDCx.go
index 3dc3386d92..3fbaf0c783 100644
--- a/XDCx/XDCx.go
+++ b/XDCx/XDCx.go
@@ -10,6 +10,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxDAO"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/state"
@@ -17,7 +18,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/rpc"
- lru "github.com/hashicorp/golang-lru"
"golang.org/x/sync/syncmap"
)
@@ -59,8 +59,8 @@ type XDCX struct {
sdkNode bool
settings syncmap.Map // holds configuration settings that can be dynamically changed
- tokenDecimalCache *lru.Cache
- orderCache *lru.Cache
+ tokenDecimalCache *lru.Cache[common.Address, *big.Int]
+ orderCache *lru.Cache[common.Hash, map[common.Hash]tradingstate.OrderHistoryItem]
}
func (XDCx *XDCX) Protocols() []p2p.Protocol {
@@ -94,19 +94,11 @@ func NewMongoDBEngine(cfg *Config) *XDCxDAO.MongoDatabase {
}
func New(cfg *Config) *XDCX {
- tokenDecimalCache, err := lru.New(defaultCacheLimit)
- if err != nil {
- log.Warn("[XDCx-New] fail to create new lru for token decimal", "error", err)
- }
- orderCache, err := lru.New(tradingstate.OrderCacheLimit)
- if err != nil {
- log.Warn("[XDCx-New] fail to create new lru for order", "error", err)
- }
XDCX := &XDCX{
orderNonce: make(map[common.Address]*big.Int),
Triegc: prque.New(nil),
- tokenDecimalCache: tokenDecimalCache,
- orderCache: orderCache,
+ tokenDecimalCache: lru.NewCache[common.Address, *big.Int](defaultCacheLimit),
+ orderCache: lru.NewCache[common.Hash, map[common.Hash]tradingstate.OrderHistoryItem](tradingstate.OrderCacheLimit),
}
// default DBEngine: levelDB
@@ -275,11 +267,11 @@ func (XDCx *XDCX) GetAveragePriceLastEpoch(chain consensus.ChainContext, statedb
if inversePrice != nil && inversePrice.Sign() > 0 {
quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, quoteToken)
if err != nil || quoteTokenDecimal.Sign() == 0 {
- return nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", quoteToken.String(), err)
+ return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", quoteToken.String(), err)
}
baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, baseToken)
if err != nil || baseTokenDecimal.Sign() == 0 {
- return nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", baseToken.String(), err)
+ return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", baseToken.String(), err)
}
price = new(big.Int).Mul(baseTokenDecimal, quoteTokenDecimal)
price = new(big.Int).Div(price, inversePrice)
@@ -302,7 +294,7 @@ func (XDCx *XDCX) ConvertXDCToToken(chain consensus.ChainContext, statedb *state
tokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, token)
if err != nil || tokenDecimal.Sign() == 0 {
- return common.Big0, common.Big0, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", token.String(), err)
+ return common.Big0, common.Big0, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", token.String(), err)
}
tokenQuantity := new(big.Int).Mul(quantity, tokenDecimal)
tokenQuantity = new(big.Int).Div(tokenQuantity, tokenPriceInXDC)
@@ -568,7 +560,7 @@ func (XDCx *XDCX) GetTradingState(block *types.Block, author common.Address) (*t
return nil, err
}
if XDCx.StateCache == nil {
- return nil, errors.New("Not initialized XDCx")
+ return nil, errors.New("not initialized XDCx")
}
return tradingstate.New(root, XDCx.StateCache)
}
@@ -607,12 +599,9 @@ func (XDCx *XDCX) GetTradingStateRoot(block *types.Block, author common.Address)
}
func (XDCx *XDCX) UpdateOrderCache(baseToken, quoteToken common.Address, orderHash common.Hash, txhash common.Hash, lastState tradingstate.OrderHistoryItem) {
- var orderCacheAtTxHash map[common.Hash]tradingstate.OrderHistoryItem
- c, ok := XDCx.orderCache.Get(txhash)
- if !ok || c == nil {
+ orderCacheAtTxHash, ok := XDCx.orderCache.Get(txhash)
+ if !ok || orderCacheAtTxHash == nil {
orderCacheAtTxHash = make(map[common.Hash]tradingstate.OrderHistoryItem)
- } else {
- orderCacheAtTxHash = c.(map[common.Hash]tradingstate.OrderHistoryItem)
}
orderKey := tradingstate.GetOrderHistoryKey(baseToken, quoteToken, orderHash)
_, ok = orderCacheAtTxHash[orderKey]
@@ -629,16 +618,15 @@ func (XDCx *XDCX) RollbackReorgTxMatch(txhash common.Hash) error {
items := db.GetListItemByTxHash(txhash, &tradingstate.OrderItem{})
if items != nil {
for _, order := range items.([]*tradingstate.OrderItem) {
- c, ok := XDCx.orderCache.Get(txhash)
- log.Debug("XDCx reorg: rollback order", "txhash", txhash.Hex(), "order", tradingstate.ToJSON(order), "orderHistoryItem", c)
- if !ok {
+ orderCacheAtTxHash, ok := XDCx.orderCache.Get(txhash)
+ log.Debug("XDCx reorg: rollback order", "txhash", txhash.Hex(), "order", tradingstate.ToJSON(order), "orderHistoryItem", orderCacheAtTxHash)
+ if !ok || orderCacheAtTxHash == nil {
log.Debug("XDCx reorg: remove order due to no orderCache", "order", tradingstate.ToJSON(order))
if err := db.DeleteObject(order.Hash, &tradingstate.OrderItem{}); err != nil {
log.Crit("SDKNode: failed to remove reorg order", "err", err.Error(), "order", tradingstate.ToJSON(order))
}
continue
}
- orderCacheAtTxHash := c.(map[common.Hash]tradingstate.OrderHistoryItem)
orderHistoryItem := orderCacheAtTxHash[tradingstate.GetOrderHistoryKey(order.BaseToken, order.QuoteToken, order.Hash)]
if (orderHistoryItem == tradingstate.OrderHistoryItem{}) {
log.Debug("XDCx reorg: remove order due to empty orderHistory", "order", tradingstate.ToJSON(order))
diff --git a/XDCx/order_processor.go b/XDCx/order_processor.go
index 86d156fec8..a950ca43b7 100644
--- a/XDCx/order_processor.go
+++ b/XDCx/order_processor.go
@@ -243,7 +243,7 @@ func (XDCx *XDCX) processOrderList(coinbase common.Address, chain consensus.Chai
inversePrice := tradingStateDB.GetLastPrice(tradingstate.GetTradingOrderBookHash(common.XDCNativeAddressBinary, oldestOrder.QuoteToken))
quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, oldestOrder.QuoteToken)
if err != nil || quoteTokenDecimal.Sign() == 0 {
- return nil, nil, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", oldestOrder.QuoteToken.String(), err)
+ return nil, nil, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", oldestOrder.QuoteToken.String(), err)
}
log.Debug("TryGet inversePrice XDC/QuoteToken", "inversePrice", inversePrice)
if inversePrice != nil && inversePrice.Sign() > 0 {
@@ -368,11 +368,11 @@ func (XDCx *XDCX) processOrderList(coinbase common.Address, chain consensus.Chai
func (XDCx *XDCX) getTradeQuantity(quotePrice *big.Int, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, takerOrder *tradingstate.OrderItem, makerOrder *tradingstate.OrderItem, quantityToTrade *big.Int) (*big.Int, bool, *tradingstate.SettleBalance, error) {
baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.BaseToken)
if err != nil || baseTokenDecimal.Sign() == 0 {
- return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.BaseToken.String(), err)
+ return tradingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.BaseToken.String(), err)
}
quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.QuoteToken)
if err != nil || quoteTokenDecimal.Sign() == 0 {
- return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.QuoteToken.String(), err)
+ return tradingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.QuoteToken.String(), err)
}
if makerOrder.QuoteToken == common.XDCNativeAddressBinary {
quotePrice = quoteTokenDecimal
@@ -526,7 +526,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tradingsta
matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee)
if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) {
- return fmt.Errorf("Echange owner empty , Taker: %v , maker : %v ", takerExOwner, makerExOwner)
+ return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner)
}
mapBalances := map[common.Address]map[common.Address]*big.Int{}
//Checking balance
@@ -656,9 +656,10 @@ func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradi
}
log.Debug("ProcessCancelOrder", "baseToken", originOrder.BaseToken, "quoteToken", originOrder.QuoteToken)
feeRate := tradingstate.GetExRelayerFee(originOrder.ExchangeAddress, statedb)
- tokenCancelFee, tokenPriceInXDC := common.Big0, common.Big0
+ var tokenCancelFee, tokenPriceInXDC *big.Int
if !chain.Config().IsTIPXDCXCancellationFee(header.Number) {
tokenCancelFee = getCancelFeeV1(baseTokenDecimal, feeRate, &originOrder)
+ tokenPriceInXDC = common.Big0
} else {
tokenCancelFee, tokenPriceInXDC = XDCx.getCancelFee(chain, statedb, tradingStateDB, &originOrder, feeRate)
}
@@ -721,7 +722,7 @@ func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradi
// cancellation fee = 1/10 trading fee
// deprecated after hardfork at TIPXDCXCancellationFee
func getCancelFeeV1(baseTokenDecimal *big.Int, feeRate *big.Int, order *tradingstate.OrderItem) *big.Int {
- cancelFee := big.NewInt(0)
+ var cancelFee *big.Int
if order.Side == tradingstate.Ask {
// SELL 1 BTC => XDC ,,
// order.Quantity =1 && fee rate =2
@@ -748,8 +749,7 @@ func (XDCx *XDCX) getCancelFee(chain consensus.ChainContext, statedb *state.Stat
if feeRate == nil || feeRate.Sign() == 0 {
return common.Big0, common.Big0
}
- cancelFee := big.NewInt(0)
- tokenPriceInXDC := big.NewInt(0)
+ var cancelFee, tokenPriceInXDC *big.Int
var err error
if order.Side == tradingstate.Ask {
cancelFee, tokenPriceInXDC, err = XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.BaseToken, common.RelayerCancelFee)
diff --git a/XDCx/token.go b/XDCx/token.go
index 5aa8554cf9..c44dbe7e29 100644
--- a/XDCx/token.go
+++ b/XDCx/token.go
@@ -4,15 +4,14 @@ import (
"math/big"
"strings"
- "github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/log"
-
"github.com/XinFinOrg/XDPoSChain"
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/log"
)
// GetTokenAbi return token abi
@@ -45,8 +44,8 @@ func RunContract(chain consensus.ChainContext, statedb *state.StateDB, contractA
}
func (XDCx *XDCX) GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, tokenAddr common.Address) (*big.Int, error) {
- if tokenDecimal, ok := XDCx.tokenDecimalCache.Get(tokenAddr); ok {
- return tokenDecimal.(*big.Int), nil
+ if tokenDecimal, ok := XDCx.tokenDecimalCache.Get(tokenAddr); ok && tokenDecimal != nil {
+ return tokenDecimal, nil
}
if tokenAddr == common.XDCNativeAddressBinary {
XDCx.tokenDecimalCache.Add(tokenAddr, common.BasePrice)
diff --git a/XDCx/tradingstate/database.go b/XDCx/tradingstate/database.go
index b40f916a0f..2fe2193243 100644
--- a/XDCx/tradingstate/database.go
+++ b/XDCx/tradingstate/database.go
@@ -23,7 +23,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/trie"
- lru "github.com/hashicorp/golang-lru"
)
// Trie cache generation limit after which to evic trie nodes from memory.
@@ -33,9 +32,6 @@ const (
// Number of past tries to keep. This value is chosen such that
// reasonable chain reorg depths will hit an existing trie.
maxPastTries = 12
-
- // Number of codehash->size associations to keep.
- codeSizeCacheSize = 100000
)
// Database wraps access to tries and contract code.
@@ -79,18 +75,15 @@ type Trie interface {
// intermediate trie-node memory pool between the low level storage layer and the
// high level trie abstraction.
func NewDatabase(db ethdb.Database) Database {
- csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{
- db: trie.NewDatabase(db),
- codeSizeCache: csc,
+ db: trie.NewDatabase(db),
}
}
type cachingDB struct {
- db *trie.Database
- mu sync.Mutex
- pastTries []*XDCXTrie
- codeSizeCache *lru.Cache
+ db *trie.Database
+ mu sync.Mutex
+ pastTries []*XDCXTrie
}
// OpenTrie opens the main account trie.
diff --git a/XDCx/tradingstate/dump.go b/XDCx/tradingstate/dump.go
index 41281dd459..58026ed2a1 100644
--- a/XDCx/tradingstate/dump.go
+++ b/XDCx/tradingstate/dump.go
@@ -47,13 +47,13 @@ type DumpOrderBookInfo struct {
LowestLiquidationPrice *big.Int
}
-func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpOrderList{}
- it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil))
for it.Next() {
priceHash := common.BytesToHash(it.Key)
if common.EmptyHash(priceHash) {
@@ -65,15 +65,15 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price)
}
- stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil)
- mapResult[price] = stateOrderList.DumpOrderList(self.db)
+ stateOrderList := newStateOrderList(t, Ask, orderBook, priceHash, data, nil)
+ mapResult[price] = stateOrderList.DumpOrderList(t.db)
}
}
for priceHash, stateOrderList := range exhangeObject.stateAskObjects {
if stateOrderList.Volume().Sign() > 0 {
- mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(self.db)
+ mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(t.db)
}
}
listPrice := []*big.Int{}
@@ -90,13 +90,13 @@ func (self *TradingStateDB) DumpAskTrie(orderBook common.Hash) (map[*big.Int]Dum
return result, nil
}
-func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpOrderList{}
- it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil))
for it.Next() {
priceHash := common.BytesToHash(it.Key)
if common.EmptyHash(priceHash) {
@@ -108,15 +108,15 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price)
}
- stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil)
- mapResult[price] = stateOrderList.DumpOrderList(self.db)
+ stateOrderList := newStateOrderList(t, Bid, orderBook, priceHash, data, nil)
+ mapResult[price] = stateOrderList.DumpOrderList(t.db)
}
}
for priceHash, stateOrderList := range exhangeObject.stateBidObjects {
if stateOrderList.Volume().Sign() > 0 {
- mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(self.db)
+ mapResult[new(big.Int).SetBytes(priceHash.Bytes())] = stateOrderList.DumpOrderList(t.db)
}
}
listPrice := []*big.Int{}
@@ -133,13 +133,13 @@ func (self *TradingStateDB) DumpBidTrie(orderBook common.Hash) (map[*big.Int]Dum
return mapResult, nil
}
-func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]*big.Int{}
- it := trie.NewIterator(exhangeObject.getBidsTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getBidsTrie(t.db).NodeIterator(nil))
for it.Next() {
priceHash := common.BytesToHash(it.Key)
if common.EmptyHash(priceHash) {
@@ -151,9 +151,9 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price :%v", orderBook.Hex(), price)
}
- stateOrderList := newStateOrderList(self, Bid, orderBook, priceHash, data, nil)
+ stateOrderList := newStateOrderList(t, Bid, orderBook, priceHash, data, nil)
mapResult[price] = stateOrderList.data.Volume
}
}
@@ -176,13 +176,13 @@ func (self *TradingStateDB) GetBids(orderBook common.Hash) (map[*big.Int]*big.In
return mapResult, nil
}
-func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]*big.Int{}
- it := trie.NewIterator(exhangeObject.getAsksTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getAsksTrie(t.db).NodeIterator(nil))
for it.Next() {
priceHash := common.BytesToHash(it.Key)
if common.EmptyHash(priceHash) {
@@ -194,9 +194,9 @@ func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.In
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price)
}
- stateOrderList := newStateOrderList(self, Ask, orderBook, priceHash, data, nil)
+ stateOrderList := newStateOrderList(t, Ask, orderBook, priceHash, data, nil)
mapResult[price] = stateOrderList.data.Volume
}
}
@@ -218,22 +218,23 @@ func (self *TradingStateDB) GetAsks(orderBook common.Hash) (map[*big.Int]*big.In
}
return result, nil
}
-func (self *stateOrderList) DumpOrderList(db Database) DumpOrderList {
- mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
- orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil))
+
+func (s *stateOrderList) DumpOrderList(db Database) DumpOrderList {
+ mapResult := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}}
+ orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil))
for orderListIt.Next() {
keyHash := common.BytesToHash(orderListIt.Key)
if common.EmptyHash(keyHash) {
continue
}
- if _, exist := self.cachedStorage[keyHash]; exist {
+ if _, exist := s.cachedStorage[keyHash]; exist {
continue
} else {
_, content, _, _ := rlp.Split(orderListIt.Value)
mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content)
}
}
- for key, value := range self.cachedStorage {
+ for key, value := range s.cachedStorage {
if !common.EmptyHash(value) {
mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes())
}
@@ -245,17 +246,17 @@ func (self *stateOrderList) DumpOrderList(db Database) DumpOrderList {
sort.Slice(listIds, func(i, j int) bool {
return listIds[i].Cmp(listIds[j]) < 0
})
- result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
+ result := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}}
for _, id := range listIds {
result.Orders[id] = mapResult.Orders[id]
}
return mapResult
}
-func (self *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
result := &DumpOrderBookInfo{}
result.LastPrice = exhangeObject.data.LastPrice
@@ -264,29 +265,29 @@ func (self *TradingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrder
result.MediumPriceBeforeEpoch = exhangeObject.data.MediumPriceBeforeEpoch
result.Nonce = exhangeObject.data.Nonce
result.TotalQuantity = exhangeObject.data.TotalQuantity
- result.BestAsk = new(big.Int).SetBytes(exhangeObject.getBestPriceAsksTrie(self.db).Bytes())
- result.BestBid = new(big.Int).SetBytes(exhangeObject.getBestBidsTrie(self.db).Bytes())
- lowestPrice, _ := exhangeObject.getLowestLiquidationPrice(self.db)
+ result.BestAsk = new(big.Int).SetBytes(exhangeObject.getBestPriceAsksTrie(t.db).Bytes())
+ result.BestBid = new(big.Int).SetBytes(exhangeObject.getBestBidsTrie(t.db).Bytes())
+ lowestPrice, _ := exhangeObject.getLowestLiquidationPrice(t.db)
result.LowestLiquidationPrice = new(big.Int).SetBytes(lowestPrice.Bytes())
return result, nil
}
-func (self *stateLendingBook) DumpOrderList(db Database) DumpOrderList {
- mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
- orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil))
+func (s *stateLendingBook) DumpOrderList(db Database) DumpOrderList {
+ mapResult := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}}
+ orderListIt := trie.NewIterator(s.getTrie(db).NodeIterator(nil))
for orderListIt.Next() {
keyHash := common.BytesToHash(orderListIt.Key)
if common.EmptyHash(keyHash) {
continue
}
- if _, exist := self.cachedStorage[keyHash]; exist {
+ if _, exist := s.cachedStorage[keyHash]; exist {
continue
} else {
_, content, _, _ := rlp.Split(orderListIt.Value)
mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content)
}
}
- for key, value := range self.cachedStorage {
+ for key, value := range s.cachedStorage {
if !common.EmptyHash(value) {
mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes())
}
@@ -298,33 +299,33 @@ func (self *stateLendingBook) DumpOrderList(db Database) DumpOrderList {
sort.Slice(listIds, func(i, j int) bool {
return listIds[i].Cmp(listIds[j]) < 0
})
- result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
+ result := DumpOrderList{Volume: s.Volume(), Orders: map[*big.Int]*big.Int{}}
for _, id := range listIds {
result.Orders[id] = mapResult.Orders[id]
}
return mapResult
}
-func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, error) {
- result := DumpLendingBook{Volume: self.Volume(), LendingBooks: map[common.Hash]DumpOrderList{}}
- it := trie.NewIterator(self.getTrie(db).NodeIterator(nil))
+func (l *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook, error) {
+ result := DumpLendingBook{Volume: l.Volume(), LendingBooks: map[common.Hash]DumpOrderList{}}
+ it := trie.NewIterator(l.getTrie(db).NodeIterator(nil))
for it.Next() {
lendingBook := common.BytesToHash(it.Key)
if common.EmptyHash(lendingBook) {
continue
}
- if _, exist := self.stateLendingBooks[lendingBook]; exist {
+ if _, exist := l.stateLendingBooks[lendingBook]; exist {
continue
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return result, fmt.Errorf("Failed to decode state lending book orderbook : %s ,liquidation price :%s , lendingBook : %s ,err : %v", self.orderBook, self.liquidationPrice, lendingBook, err)
+ return result, fmt.Errorf("failed to decode state lending book orderbook: %s, liquidation price: %s , lendingBook: %s , err: %v", l.orderBook, l.liquidationPrice, lendingBook, err)
}
- stateLendingBook := newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, data, nil)
+ stateLendingBook := newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, data, nil)
result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db)
}
}
- for lendingBook, stateLendingBook := range self.stateLendingBooks {
+ for lendingBook, stateLendingBook := range l.stateLendingBooks {
if !common.EmptyHash(lendingBook) {
result.LendingBooks[lendingBook] = stateLendingBook.DumpOrderList(db)
}
@@ -332,13 +333,13 @@ func (self *liquidationPriceState) DumpLendingBook(db Database) (DumpLendingBook
return result, nil
}
-func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*big.Int]DumpLendingBook, error) {
- exhangeObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map[*big.Int]DumpLendingBook, error) {
+ exhangeObject := t.getStateExchangeObject(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpLendingBook{}
- it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getLiquidationPriceTrie(t.db).NodeIterator(nil))
for it.Next() {
priceHash := common.BytesToHash(it.Key)
if common.EmptyHash(priceHash) {
@@ -350,10 +351,10 @@ func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map
} else {
var data orderList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,price :%v ", orderBook.Hex(), price)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , price : %v", orderBook.Hex(), price)
}
- liquidationPriceState := newLiquidationPriceState(self, orderBook, priceHash, data, nil)
- dumpLendingBook, err := liquidationPriceState.DumpLendingBook(self.db)
+ liquidationPriceState := newLiquidationPriceState(t, orderBook, priceHash, data, nil)
+ dumpLendingBook, err := liquidationPriceState.DumpLendingBook(t.db)
if err != nil {
return nil, err
}
@@ -362,7 +363,7 @@ func (self *TradingStateDB) DumpLiquidationPriceTrie(orderBook common.Hash) (map
}
for priceHash, liquidationPriceState := range exhangeObject.liquidationPriceStates {
if liquidationPriceState.Volume().Sign() > 0 {
- dumpLendingBook, err := liquidationPriceState.DumpLendingBook(self.db)
+ dumpLendingBook, err := liquidationPriceState.DumpLendingBook(t.db)
if err != nil {
return nil, err
}
diff --git a/XDCx/tradingstate/relayer_state.go b/XDCx/tradingstate/relayer_state.go
index 348f213041..d5b79bf0c0 100644
--- a/XDCx/tradingstate/relayer_state.go
+++ b/XDCx/tradingstate/relayer_state.go
@@ -106,7 +106,7 @@ func GetAllTradingPairs(statedb *state.StateDB) (map[common.Hash]bool, error) {
toTokenSlot := new(big.Int).Add(locBig, RelayerStructMappingSlot["_toTokens"])
toTokenLength := statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), common.BigToHash(toTokenSlot)).Big().Uint64()
if toTokenLength != fromTokenLength {
- return map[common.Hash]bool{}, fmt.Errorf("Invalid length from token & to toke : from :%d , to :%d ", fromTokenLength, toTokenLength)
+ return map[common.Hash]bool{}, fmt.Errorf("invalid length from token & to token: from :%d , to :%d ", fromTokenLength, toTokenLength)
}
fromTokens := []common.Address{}
fromTokenSlotHash := common.BytesToHash(fromTokenSlot.Bytes())
@@ -279,7 +279,7 @@ func CheckAddTokenBalance(addr common.Address, value *big.Int, token common.Addr
newBalance := new(big.Int).Add(balance, value)
log.Debug("CheckAddTokenBalance settle balance: ADD TOKEN BALANCE ", "token", token.String(), "address", addr.String(), "balance", balance, "value", value, "newBalance", newBalance)
if common.BigToHash(newBalance).Big().Cmp(newBalance) != 0 {
- return nil, fmt.Errorf("Overflow when try add token balance , max is 2^256 , balance : %v , value:%v ", balance, value)
+ return nil, fmt.Errorf("overflow when try add token balance , max is 2^256 , balance : %v , value : %v", balance, value)
} else {
return newBalance, nil
}
diff --git a/XDCx/tradingstate/state_lendingbook.go b/XDCx/tradingstate/state_lendingbook.go
index b6af4aded1..6f8b77695b 100644
--- a/XDCx/tradingstate/state_lendingbook.go
+++ b/XDCx/tradingstate/state_lendingbook.go
@@ -65,59 +65,59 @@ func newStateLendingBook(orderBook common.Hash, price common.Hash, lendingBook c
}
}
-func (self *stateLendingBook) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, self.data)
+func (s *stateLendingBook) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, s.data)
}
-func (self *stateLendingBook) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (s *stateLendingBook) setError(err error) {
+ if s.dbErr == nil {
+ s.dbErr = err
}
}
-func (self *stateLendingBook) getTrie(db Database) Trie {
- if self.trie == nil {
+func (s *stateLendingBook) getTrie(db Database) Trie {
+ if s.trie == nil {
var err error
- self.trie, err = db.OpenStorageTrie(self.lendingBook, self.data.Root)
+ s.trie, err = db.OpenStorageTrie(s.lendingBook, s.data.Root)
if err != nil {
- self.trie, _ = db.OpenStorageTrie(self.price, EmptyHash)
- self.setError(fmt.Errorf("can't create storage trie: %v", err))
+ s.trie, _ = db.OpenStorageTrie(s.price, EmptyHash)
+ s.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
- return self.trie
+ return s.trie
}
-func (self *stateLendingBook) Exist(db Database, lendingId common.Hash) bool {
- amount, exists := self.cachedStorage[lendingId]
+func (s *stateLendingBook) Exist(db Database, lendingId common.Hash) bool {
+ amount, exists := s.cachedStorage[lendingId]
if exists {
return true
}
// Load from DB in case it is missing.
- enc, err := self.getTrie(db).TryGet(lendingId[:])
+ enc, err := s.getTrie(db).TryGet(lendingId[:])
if err != nil {
- self.setError(err)
+ s.setError(err)
return false
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
- self.setError(err)
+ s.setError(err)
}
amount.SetBytes(content)
}
if (amount != common.Hash{}) {
- self.cachedStorage[lendingId] = amount
+ s.cachedStorage[lendingId] = amount
}
return true
}
-func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash {
+func (s *stateLendingBook) getAllTradeIds(db Database) []common.Hash {
tradeIds := []common.Hash{}
- lendingBookTrie := self.getTrie(db)
+ lendingBookTrie := s.getTrie(db)
if lendingBookTrie == nil {
return tradeIds
}
- for id, value := range self.cachedStorage {
+ for id, value := range s.cachedStorage {
if !common.EmptyHash(value) {
tradeIds = append(tradeIds, id)
}
@@ -125,7 +125,7 @@ func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash {
orderListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil))
for orderListIt.Next() {
id := common.BytesToHash(orderListIt.Key)
- if _, exist := self.cachedStorage[id]; exist {
+ if _, exist := s.cachedStorage[id]; exist {
continue
}
tradeIds = append(tradeIds, id)
@@ -133,83 +133,83 @@ func (self *stateLendingBook) getAllTradeIds(db Database) []common.Hash {
return tradeIds
}
-func (self *stateLendingBook) insertTradingId(db Database, tradeId common.Hash) {
- self.setTradingId(tradeId, tradeId)
- self.setError(self.getTrie(db).TryUpdate(tradeId[:], tradeId[:]))
+func (s *stateLendingBook) insertTradingId(db Database, tradeId common.Hash) {
+ s.setTradingId(tradeId, tradeId)
+ s.setError(s.getTrie(db).TryUpdate(tradeId[:], tradeId[:]))
}
-func (self *stateLendingBook) removeTradingId(db Database, tradeId common.Hash) {
- tr := self.getTrie(db)
- self.setError(tr.TryDelete(tradeId[:]))
- self.setTradingId(tradeId, EmptyHash)
+func (s *stateLendingBook) removeTradingId(db Database, tradeId common.Hash) {
+ tr := s.getTrie(db)
+ s.setError(tr.TryDelete(tradeId[:]))
+ s.setTradingId(tradeId, EmptyHash)
}
-func (self *stateLendingBook) setTradingId(tradeId common.Hash, value common.Hash) {
- self.cachedStorage[tradeId] = value
- self.dirtyStorage[tradeId] = value
+func (s *stateLendingBook) setTradingId(tradeId common.Hash, value common.Hash) {
+ s.cachedStorage[tradeId] = value
+ s.dirtyStorage[tradeId] = value
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+ if s.onDirty != nil {
+ s.onDirty(s.lendingBook)
+ s.onDirty = nil
}
}
-func (self *stateLendingBook) updateTrie(db Database) Trie {
- tr := self.getTrie(db)
- for key, value := range self.dirtyStorage {
- delete(self.dirtyStorage, key)
+func (s *stateLendingBook) updateTrie(db Database) Trie {
+ tr := s.getTrie(db)
+ for key, value := range s.dirtyStorage {
+ delete(s.dirtyStorage, key)
if value == EmptyHash {
- self.setError(tr.TryDelete(key[:]))
+ s.setError(tr.TryDelete(key[:]))
continue
}
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
- self.setError(tr.TryUpdate(key[:], v))
+ s.setError(tr.TryUpdate(key[:], v))
}
return tr
}
-func (self *stateLendingBook) updateRoot(db Database) error {
- self.updateTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (s *stateLendingBook) updateRoot(db Database) error {
+ s.updateTrie(db)
+ if s.dbErr != nil {
+ return s.dbErr
}
- root, err := self.trie.Commit(nil)
+ root, err := s.trie.Commit(nil)
if err == nil {
- self.data.Root = root
+ s.data.Root = root
}
return err
}
-func (self *stateLendingBook) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateLendingBook {
- stateLendingBook := newStateLendingBook(self.lendingBook, self.orderBook, self.price, self.data, onDirty)
- if self.trie != nil {
- stateLendingBook.trie = db.db.CopyTrie(self.trie)
+func (s *stateLendingBook) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateLendingBook {
+ stateLendingBook := newStateLendingBook(s.lendingBook, s.orderBook, s.price, s.data, onDirty)
+ if s.trie != nil {
+ stateLendingBook.trie = db.db.CopyTrie(s.trie)
}
- for key, value := range self.dirtyStorage {
+ for key, value := range s.dirtyStorage {
stateLendingBook.dirtyStorage[key] = value
}
- for key, value := range self.cachedStorage {
+ for key, value := range s.cachedStorage {
stateLendingBook.cachedStorage[key] = value
}
return stateLendingBook
}
-func (c *stateLendingBook) AddVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Add(c.data.Volume, amount))
+func (s *stateLendingBook) AddVolume(amount *big.Int) {
+ s.setVolume(new(big.Int).Add(s.data.Volume, amount))
}
-func (c *stateLendingBook) subVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Sub(c.data.Volume, amount))
+func (s *stateLendingBook) subVolume(amount *big.Int) {
+ s.setVolume(new(big.Int).Sub(s.data.Volume, amount))
}
-func (self *stateLendingBook) setVolume(volume *big.Int) {
- self.data.Volume = volume
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+func (s *stateLendingBook) setVolume(volume *big.Int) {
+ s.data.Volume = volume
+ if s.onDirty != nil {
+ s.onDirty(s.lendingBook)
+ s.onDirty = nil
}
}
-func (self *stateLendingBook) Volume() *big.Int {
- return self.data.Volume
+func (s *stateLendingBook) Volume() *big.Int {
+ return s.data.Volume
}
diff --git a/XDCx/tradingstate/state_liquidationprice.go b/XDCx/tradingstate/state_liquidationprice.go
index 5a4e4aca63..d978512dc6 100644
--- a/XDCx/tradingstate/state_liquidationprice.go
+++ b/XDCx/tradingstate/state_liquidationprice.go
@@ -68,54 +68,54 @@ func newLiquidationPriceState(db *TradingStateDB, orderBook common.Hash, price c
}
// EncodeRLP implements rlp.Encoder.
-func (c *liquidationPriceState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (l *liquidationPriceState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, l.data)
}
// setError remembers the first non-nil error it is called with.
-func (self *liquidationPriceState) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (l *liquidationPriceState) setError(err error) {
+ if l.dbErr == nil {
+ l.dbErr = err
}
}
-func (self *liquidationPriceState) MarkStateLendingBookDirty(price common.Hash) {
- self.stateLendingBooksDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.liquidationPrice)
- self.onDirty = nil
+func (l *liquidationPriceState) MarkStateLendingBookDirty(price common.Hash) {
+ l.stateLendingBooksDirty[price] = struct{}{}
+ if l.onDirty != nil {
+ l.onDirty(l.liquidationPrice)
+ l.onDirty = nil
}
}
-func (self *liquidationPriceState) createLendingBook(db Database, lendingBook common.Hash) (newobj *stateLendingBook) {
- newobj = newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, orderList{Volume: Zero}, self.MarkStateLendingBookDirty)
- self.stateLendingBooks[lendingBook] = newobj
- self.stateLendingBooksDirty[lendingBook] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.liquidationPrice)
- self.onDirty = nil
+func (l *liquidationPriceState) createLendingBook(db Database, lendingBook common.Hash) (newobj *stateLendingBook) {
+ newobj = newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, orderList{Volume: Zero}, l.MarkStateLendingBookDirty)
+ l.stateLendingBooks[lendingBook] = newobj
+ l.stateLendingBooksDirty[lendingBook] = struct{}{}
+ if l.onDirty != nil {
+ l.onDirty(l.liquidationPrice)
+ l.onDirty = nil
}
return newobj
}
-func (self *liquidationPriceState) getTrie(db Database) Trie {
- if self.trie == nil {
+func (l *liquidationPriceState) getTrie(db Database) Trie {
+ if l.trie == nil {
var err error
- self.trie, err = db.OpenStorageTrie(self.liquidationPrice, self.data.Root)
+ l.trie, err = db.OpenStorageTrie(l.liquidationPrice, l.data.Root)
if err != nil {
- self.trie, _ = db.OpenStorageTrie(self.liquidationPrice, EmptyHash)
- self.setError(fmt.Errorf("can't create storage trie: %v", err))
+ l.trie, _ = db.OpenStorageTrie(l.liquidationPrice, EmptyHash)
+ l.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
- return self.trie
+ return l.trie
}
-func (self *liquidationPriceState) updateTrie(db Database) Trie {
- tr := self.getTrie(db)
- for lendingId, stateObject := range self.stateLendingBooks {
- delete(self.stateLendingBooksDirty, lendingId)
+func (l *liquidationPriceState) updateTrie(db Database) Trie {
+ tr := l.getTrie(db)
+ for lendingId, stateObject := range l.stateLendingBooks {
+ delete(l.stateLendingBooksDirty, lendingId)
if stateObject.empty() {
- self.setError(tr.TryDelete(lendingId[:]))
+ l.setError(tr.TryDelete(lendingId[:]))
continue
}
err := stateObject.updateRoot(db)
@@ -125,17 +125,17 @@ func (self *liquidationPriceState) updateTrie(db Database) Trie {
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(stateObject)
- self.setError(tr.TryUpdate(lendingId[:], v))
+ l.setError(tr.TryUpdate(lendingId[:], v))
}
return tr
}
-func (self *liquidationPriceState) updateRoot(db Database) error {
- self.updateTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (l *liquidationPriceState) updateRoot(db Database) error {
+ l.updateTrie(db)
+ if l.dbErr != nil {
+ return l.dbErr
}
- root, err := self.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := l.trie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -146,57 +146,57 @@ func (self *liquidationPriceState) updateRoot(db Database) error {
return nil
})
if err == nil {
- self.data.Root = root
+ l.data.Root = root
}
return err
}
-func (self *liquidationPriceState) deepCopy(db *TradingStateDB, onDirty func(liquidationPrice common.Hash)) *liquidationPriceState {
- stateOrderList := newLiquidationPriceState(db, self.orderBook, self.liquidationPrice, self.data, onDirty)
- if self.trie != nil {
- stateOrderList.trie = db.db.CopyTrie(self.trie)
+func (l *liquidationPriceState) deepCopy(db *TradingStateDB, onDirty func(liquidationPrice common.Hash)) *liquidationPriceState {
+ stateOrderList := newLiquidationPriceState(db, l.orderBook, l.liquidationPrice, l.data, onDirty)
+ if l.trie != nil {
+ stateOrderList.trie = db.db.CopyTrie(l.trie)
}
- for key, value := range self.stateLendingBooks {
- stateOrderList.stateLendingBooks[key] = value.deepCopy(db, self.MarkStateLendingBookDirty)
+ for key, value := range l.stateLendingBooks {
+ stateOrderList.stateLendingBooks[key] = value.deepCopy(db, l.MarkStateLendingBookDirty)
}
- for key, value := range self.stateLendingBooksDirty {
+ for key, value := range l.stateLendingBooksDirty {
stateOrderList.stateLendingBooksDirty[key] = value
}
return stateOrderList
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *liquidationPriceState) getStateLendingBook(db Database, lendingBook common.Hash) (stateObject *stateLendingBook) {
+func (l *liquidationPriceState) getStateLendingBook(db Database, lendingBook common.Hash) (stateObject *stateLendingBook) {
// Prefer 'live' objects.
- if obj := self.stateLendingBooks[lendingBook]; obj != nil {
+ if obj := l.stateLendingBooks[lendingBook]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getTrie(db).TryGet(lendingBook[:])
+ enc, err := l.getTrie(db).TryGet(lendingBook[:])
if len(enc) == 0 {
- self.setError(err)
+ l.setError(err)
return nil
}
var data orderList
if err := rlp.DecodeBytes(enc, &data); err != nil {
- log.Error("Failed to decode state lending book ", "orderbook", self.orderBook, "liquidation price", self.liquidationPrice, "lendingBook", lendingBook, "err", err)
+ log.Error("Failed to decode state lending book ", "orderbook", l.orderBook, "liquidation price", l.liquidationPrice, "lendingBook", lendingBook, "err", err)
return nil
}
// Insert into the live set.
- obj := newStateLendingBook(self.orderBook, self.liquidationPrice, lendingBook, data, self.MarkStateLendingBookDirty)
- self.stateLendingBooks[lendingBook] = obj
+ obj := newStateLendingBook(l.orderBook, l.liquidationPrice, lendingBook, data, l.MarkStateLendingBookDirty)
+ l.stateLendingBooks[lendingBook] = obj
return obj
}
-func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common.Hash][]common.Hash {
+func (l *liquidationPriceState) getAllLiquidationData(db Database) map[common.Hash][]common.Hash {
liquidationData := map[common.Hash][]common.Hash{}
- lendingBookTrie := self.getTrie(db)
+ lendingBookTrie := l.getTrie(db)
if lendingBookTrie == nil {
return liquidationData
}
lendingBooks := []common.Hash{}
- for id, stateLendingBook := range self.stateLendingBooks {
+ for id, stateLendingBook := range l.stateLendingBooks {
if !stateLendingBook.empty() {
lendingBooks = append(lendingBooks, id)
}
@@ -204,13 +204,13 @@ func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common
lendingBookListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil))
for lendingBookListIt.Next() {
id := common.BytesToHash(lendingBookListIt.Key)
- if _, exist := self.stateLendingBooks[id]; exist {
+ if _, exist := l.stateLendingBooks[id]; exist {
continue
}
lendingBooks = append(lendingBooks, id)
}
for _, lendingBook := range lendingBooks {
- stateLendingBook := self.getStateLendingBook(db, lendingBook)
+ stateLendingBook := l.getStateLendingBook(db, lendingBook)
if stateLendingBook != nil {
liquidationData[lendingBook] = stateLendingBook.getAllTradeIds(db)
}
@@ -218,22 +218,22 @@ func (self *liquidationPriceState) getAllLiquidationData(db Database) map[common
return liquidationData
}
-func (c *liquidationPriceState) AddVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Add(c.data.Volume, amount))
+func (l *liquidationPriceState) AddVolume(amount *big.Int) {
+ l.setVolume(new(big.Int).Add(l.data.Volume, amount))
}
func (c *liquidationPriceState) subVolume(amount *big.Int) {
c.setVolume(new(big.Int).Sub(c.data.Volume, amount))
}
-func (self *liquidationPriceState) setVolume(volume *big.Int) {
- self.data.Volume = volume
- if self.onDirty != nil {
- self.onDirty(self.liquidationPrice)
- self.onDirty = nil
+func (l *liquidationPriceState) setVolume(volume *big.Int) {
+ l.data.Volume = volume
+ if l.onDirty != nil {
+ l.onDirty(l.liquidationPrice)
+ l.onDirty = nil
}
}
-func (self *liquidationPriceState) Volume() *big.Int {
- return self.data.Volume
+func (l *liquidationPriceState) Volume() *big.Int {
+ return l.data.Volume
}
diff --git a/XDCx/tradingstate/state_orderItem.go b/XDCx/tradingstate/state_orderItem.go
index b1e390b505..758dba829f 100644
--- a/XDCx/tradingstate/state_orderItem.go
+++ b/XDCx/tradingstate/state_orderItem.go
@@ -53,23 +53,23 @@ func newStateOrderItem(orderBook common.Hash, orderId common.Hash, data OrderIte
}
// EncodeRLP implements rlp.Encoder.
-func (c *stateOrderItem) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (s *stateOrderItem) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, s.data)
}
-func (self *stateOrderItem) deepCopy(onDirty func(orderId common.Hash)) *stateOrderItem {
- stateOrderList := newStateOrderItem(self.orderBook, self.orderId, self.data, onDirty)
+func (s *stateOrderItem) deepCopy(onDirty func(orderId common.Hash)) *stateOrderItem {
+ stateOrderList := newStateOrderItem(s.orderBook, s.orderId, s.data, onDirty)
return stateOrderList
}
-func (self *stateOrderItem) setVolume(volume *big.Int) {
- self.data.Quantity = volume
- if self.onDirty != nil {
- self.onDirty(self.orderId)
- self.onDirty = nil
+func (s *stateOrderItem) setVolume(volume *big.Int) {
+ s.data.Quantity = volume
+ if s.onDirty != nil {
+ s.onDirty(s.orderId)
+ s.onDirty = nil
}
}
-func (self *stateOrderItem) Quantity() *big.Int {
- return self.data.Quantity
+func (s *stateOrderItem) Quantity() *big.Int {
+ return s.data.Quantity
}
diff --git a/XDCx/tradingstate/state_orderList.go b/XDCx/tradingstate/state_orderList.go
index 44de770e9d..7946ec538f 100644
--- a/XDCx/tradingstate/state_orderList.go
+++ b/XDCx/tradingstate/state_orderList.go
@@ -75,14 +75,14 @@ func newStateOrderList(db *TradingStateDB, orderType string, orderBook common.Ha
}
// EncodeRLP implements rlp.Encoder.
-func (c *stateOrderList) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (s *stateOrderList) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, s.data)
}
// setError remembers the first non-nil error it is called with.
-func (self *stateOrderList) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (s *stateOrderList) setError(err error) {
+ if s.dbErr == nil {
+ s.dbErr = err
}
}
@@ -99,90 +99,90 @@ func (c *stateOrderList) getTrie(db Database) Trie {
}
// GetState returns a value in orderId storage.
-func (self *stateOrderList) GetOrderAmount(db Database, orderId common.Hash) common.Hash {
- amount, exists := self.cachedStorage[orderId]
+func (s *stateOrderList) GetOrderAmount(db Database, orderId common.Hash) common.Hash {
+ amount, exists := s.cachedStorage[orderId]
if exists {
return amount
}
// Load from DB in case it is missing.
- enc, err := self.getTrie(db).TryGet(orderId[:])
+ enc, err := s.getTrie(db).TryGet(orderId[:])
if err != nil {
- self.setError(err)
+ s.setError(err)
return EmptyHash
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
- self.setError(err)
+ s.setError(err)
}
amount.SetBytes(content)
}
if (amount != common.Hash{}) {
- self.cachedStorage[orderId] = amount
+ s.cachedStorage[orderId] = amount
}
return amount
}
// SetState updates a value in orderId storage.
-func (self *stateOrderList) insertOrderItem(db Database, orderId common.Hash, amount common.Hash) {
- self.setOrderItem(orderId, amount)
- self.setError(self.getTrie(db).TryUpdate(orderId[:], amount[:]))
+func (s *stateOrderList) insertOrderItem(db Database, orderId common.Hash, amount common.Hash) {
+ s.setOrderItem(orderId, amount)
+ s.setError(s.getTrie(db).TryUpdate(orderId[:], amount[:]))
}
// SetState updates a value in orderId storage.
-func (self *stateOrderList) removeOrderItem(db Database, orderId common.Hash) {
- tr := self.getTrie(db)
- self.setError(tr.TryDelete(orderId[:]))
- self.setOrderItem(orderId, EmptyHash)
+func (s *stateOrderList) removeOrderItem(db Database, orderId common.Hash) {
+ tr := s.getTrie(db)
+ s.setError(tr.TryDelete(orderId[:]))
+ s.setOrderItem(orderId, EmptyHash)
}
-func (self *stateOrderList) setOrderItem(orderId common.Hash, amount common.Hash) {
- self.cachedStorage[orderId] = amount
- self.dirtyStorage[orderId] = amount
+func (s *stateOrderList) setOrderItem(orderId common.Hash, amount common.Hash) {
+ s.cachedStorage[orderId] = amount
+ s.dirtyStorage[orderId] = amount
- if self.onDirty != nil {
- self.onDirty(self.Price())
- self.onDirty = nil
+ if s.onDirty != nil {
+ s.onDirty(s.Price())
+ s.onDirty = nil
}
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
-func (self *stateOrderList) updateTrie(db Database) Trie {
- tr := self.getTrie(db)
- for orderId, amount := range self.dirtyStorage {
- delete(self.dirtyStorage, orderId)
+func (s *stateOrderList) updateTrie(db Database) Trie {
+ tr := s.getTrie(db)
+ for orderId, amount := range s.dirtyStorage {
+ delete(s.dirtyStorage, orderId)
if amount == EmptyHash {
- self.setError(tr.TryDelete(orderId[:]))
+ s.setError(tr.TryDelete(orderId[:]))
continue
}
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(amount[:], "\x00"))
- self.setError(tr.TryUpdate(orderId[:], v))
+ s.setError(tr.TryUpdate(orderId[:], v))
}
return tr
}
// UpdateRoot sets the trie root to the current root orderId of
-func (self *stateOrderList) updateRoot(db Database) error {
- self.updateTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (s *stateOrderList) updateRoot(db Database) error {
+ s.updateTrie(db)
+ if s.dbErr != nil {
+ return s.dbErr
}
- root, err := self.trie.Commit(nil)
+ root, err := s.trie.Commit(nil)
if err == nil {
- self.data.Root = root
+ s.data.Root = root
}
return err
}
-func (self *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateOrderList {
- stateOrderList := newStateOrderList(db, self.orderType, self.orderBook, self.price, self.data, onDirty)
- if self.trie != nil {
- stateOrderList.trie = db.db.CopyTrie(self.trie)
+func (s *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price common.Hash)) *stateOrderList {
+ stateOrderList := newStateOrderList(db, s.orderType, s.orderBook, s.price, s.data, onDirty)
+ if s.trie != nil {
+ stateOrderList.trie = db.db.CopyTrie(s.trie)
}
- for orderId, amount := range self.dirtyStorage {
+ for orderId, amount := range s.dirtyStorage {
stateOrderList.dirtyStorage[orderId] = amount
}
- for orderId, amount := range self.cachedStorage {
+ for orderId, amount := range s.cachedStorage {
stateOrderList.cachedStorage[orderId] = amount
}
return stateOrderList
@@ -190,29 +190,29 @@ func (self *stateOrderList) deepCopy(db *TradingStateDB, onDirty func(price comm
// AddVolume removes amount from c's balance.
// It is used to add funds to the destination exchanges of a transfer.
-func (c *stateOrderList) AddVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Add(c.data.Volume, amount))
+func (s *stateOrderList) AddVolume(amount *big.Int) {
+ s.setVolume(new(big.Int).Add(s.data.Volume, amount))
}
// AddVolume removes amount from c's balance.
// It is used to add funds to the destination exchanges of a transfer.
-func (c *stateOrderList) subVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Sub(c.data.Volume, amount))
+func (s *stateOrderList) subVolume(amount *big.Int) {
+ s.setVolume(new(big.Int).Sub(s.data.Volume, amount))
}
-func (self *stateOrderList) setVolume(volume *big.Int) {
- self.data.Volume = volume
- if self.onDirty != nil {
- self.onDirty(self.price)
- self.onDirty = nil
+func (s *stateOrderList) setVolume(volume *big.Int) {
+ s.data.Volume = volume
+ if s.onDirty != nil {
+ s.onDirty(s.price)
+ s.onDirty = nil
}
}
// Returns the address of the contract/orderId
-func (c *stateOrderList) Price() common.Hash {
- return c.price
+func (s *stateOrderList) Price() common.Hash {
+ return s.price
}
-func (self *stateOrderList) Volume() *big.Int {
- return self.data.Volume
+func (s *stateOrderList) Volume() *big.Int {
+ return s.data.Volume
}
diff --git a/XDCx/tradingstate/state_orderbook.go b/XDCx/tradingstate/state_orderbook.go
index 3bfdd33e0e..8d74d748e4 100644
--- a/XDCx/tradingstate/state_orderbook.go
+++ b/XDCx/tradingstate/state_orderbook.go
@@ -66,35 +66,35 @@ type tradingExchanges struct {
}
// empty returns whether the orderId is considered empty.
-func (s *tradingExchanges) empty() bool {
- if s.data.Nonce != 0 {
+func (te *tradingExchanges) empty() bool {
+ if te.data.Nonce != 0 {
return false
}
- if s.data.LendingCount != nil && s.data.LendingCount.Sign() > 0 {
+ if te.data.LendingCount != nil && te.data.LendingCount.Sign() > 0 {
return false
}
- if s.data.LastPrice != nil && s.data.LastPrice.Sign() > 0 {
+ if te.data.LastPrice != nil && te.data.LastPrice.Sign() > 0 {
return false
}
- if s.data.MediumPrice != nil && s.data.MediumPrice.Sign() > 0 {
+ if te.data.MediumPrice != nil && te.data.MediumPrice.Sign() > 0 {
return false
}
- if s.data.MediumPriceBeforeEpoch != nil && s.data.MediumPriceBeforeEpoch.Sign() > 0 {
+ if te.data.MediumPriceBeforeEpoch != nil && te.data.MediumPriceBeforeEpoch.Sign() > 0 {
return false
}
- if s.data.TotalQuantity != nil && s.data.TotalQuantity.Sign() > 0 {
+ if te.data.TotalQuantity != nil && te.data.TotalQuantity.Sign() > 0 {
return false
}
- if !common.EmptyHash(s.data.AskRoot) {
+ if !common.EmptyHash(te.data.AskRoot) {
return false
}
- if !common.EmptyHash(s.data.BidRoot) {
+ if !common.EmptyHash(te.data.BidRoot) {
return false
}
- if !common.EmptyHash(s.data.OrderRoot) {
+ if !common.EmptyHash(te.data.OrderRoot) {
return false
}
- if !common.EmptyHash(s.data.LiquidationPriceRoot) {
+ if !common.EmptyHash(te.data.LiquidationPriceRoot) {
return false
}
return true
@@ -119,46 +119,46 @@ func newStateExchanges(db *TradingStateDB, hash common.Hash, data tradingExchang
}
// EncodeRLP implements rlp.Encoder.
-func (c *tradingExchanges) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (te *tradingExchanges) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, te.data)
}
// setError remembers the first non-nil error it is called with.
-func (self *tradingExchanges) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (te *tradingExchanges) setError(err error) {
+ if te.dbErr == nil {
+ te.dbErr = err
}
}
-func (c *tradingExchanges) getAsksTrie(db Database) Trie {
- if c.asksTrie == nil {
+func (te *tradingExchanges) getAsksTrie(db Database) Trie {
+ if te.asksTrie == nil {
var err error
- c.asksTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.AskRoot)
+ te.asksTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.AskRoot)
if err != nil {
- c.asksTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash)
- c.setError(fmt.Errorf("can't create asks trie: %v", err))
+ te.asksTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash)
+ te.setError(fmt.Errorf("can't create asks trie: %v", err))
}
}
- return c.asksTrie
+ return te.asksTrie
}
-func (c *tradingExchanges) getOrdersTrie(db Database) Trie {
- if c.ordersTrie == nil {
+func (te *tradingExchanges) getOrdersTrie(db Database) Trie {
+ if te.ordersTrie == nil {
var err error
- c.ordersTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.OrderRoot)
+ te.ordersTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.OrderRoot)
if err != nil {
- c.ordersTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash)
- c.setError(fmt.Errorf("can't create asks trie: %v", err))
+ te.ordersTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash)
+ te.setError(fmt.Errorf("can't create asks trie: %v", err))
}
}
- return c.ordersTrie
+ return te.ordersTrie
}
-func (c *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash {
- trie := c.getAsksTrie(db)
+func (te *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash {
+ trie := te.getAsksTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
- log.Error("Failed find best price ask trie ", "orderbook", c.orderBookHash.Hex())
+ log.Error("Failed find best price ask trie ", "orderbook", te.orderBookHash.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -166,23 +166,23 @@ func (c *tradingExchanges) getBestPriceAsksTrie(db Database) common.Hash {
return EmptyHash
}
price := common.BytesToHash(encKey)
- if _, exit := c.stateAskObjects[price]; !exit {
+ if _, exit := te.stateAskObjects[price]; !exit {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash
}
- obj := newStateOrderList(c.db, Bid, c.orderBookHash, price, data, c.MarkStateAskObjectDirty)
- c.stateAskObjects[price] = obj
+ obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty)
+ te.stateAskObjects[price] = obj
}
return common.BytesToHash(encKey)
}
-func (c *tradingExchanges) getBestBidsTrie(db Database) common.Hash {
- trie := c.getBidsTrie(db)
+func (te *tradingExchanges) getBestBidsTrie(db Database) common.Hash {
+ trie := te.getBidsTrie(db)
encKey, encValue, err := trie.TryGetBestRightKeyAndValue()
if err != nil {
- log.Error("Failed find best price bid trie ", "orderbook", c.orderBookHash.Hex())
+ log.Error("Failed find best price bid trie ", "orderbook", te.orderBookHash.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -190,27 +190,27 @@ func (c *tradingExchanges) getBestBidsTrie(db Database) common.Hash {
return EmptyHash
}
price := common.BytesToHash(encKey)
- if _, exit := c.stateBidObjects[price]; !exit {
+ if _, exit := te.stateBidObjects[price]; !exit {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best bid trie", "err", err)
return EmptyHash
}
// Insert into the live set.
- obj := newStateOrderList(c.db, Bid, c.orderBookHash, price, data, c.MarkStateBidObjectDirty)
- c.stateBidObjects[price] = obj
+ obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty)
+ te.stateBidObjects[price] = obj
}
return common.BytesToHash(encKey)
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
-func (self *tradingExchanges) updateAsksTrie(db Database) Trie {
- tr := self.getAsksTrie(db)
- for price, orderList := range self.stateAskObjects {
- if _, isDirty := self.stateAskObjectsDirty[price]; isDirty {
- delete(self.stateAskObjectsDirty, price)
+func (te *tradingExchanges) updateAsksTrie(db Database) Trie {
+ tr := te.getAsksTrie(db)
+ for price, orderList := range te.stateAskObjects {
+ if _, isDirty := te.stateAskObjectsDirty[price]; isDirty {
+ delete(te.stateAskObjectsDirty, price)
if orderList.empty() {
- self.setError(tr.TryDelete(price[:]))
+ te.setError(tr.TryDelete(price[:]))
continue
}
err := orderList.updateRoot(db)
@@ -219,7 +219,7 @@ func (self *tradingExchanges) updateAsksTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
- self.setError(tr.TryUpdate(price[:], v))
+ te.setError(tr.TryUpdate(price[:], v))
}
}
@@ -228,23 +228,23 @@ func (self *tradingExchanges) updateAsksTrie(db Database) Trie {
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *tradingExchanges) updateAsksRoot(db Database) error {
- self.updateAsksTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (te *tradingExchanges) updateAsksRoot(db Database) error {
+ te.updateAsksTrie(db)
+ if te.dbErr != nil {
+ return te.dbErr
}
- self.data.AskRoot = self.asksTrie.Hash()
+ te.data.AskRoot = te.asksTrie.Hash()
return nil
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *tradingExchanges) CommitAsksTrie(db Database) error {
- self.updateAsksTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (te *tradingExchanges) CommitAsksTrie(db Database) error {
+ te.updateAsksTrie(db)
+ if te.dbErr != nil {
+ return te.dbErr
}
- root, err := self.asksTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := te.asksTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -255,31 +255,31 @@ func (self *tradingExchanges) CommitAsksTrie(db Database) error {
return nil
})
if err == nil {
- self.data.AskRoot = root
+ te.data.AskRoot = root
}
return err
}
-func (c *tradingExchanges) getBidsTrie(db Database) Trie {
- if c.bidsTrie == nil {
+func (te *tradingExchanges) getBidsTrie(db Database) Trie {
+ if te.bidsTrie == nil {
var err error
- c.bidsTrie, err = db.OpenStorageTrie(c.orderBookHash, c.data.BidRoot)
+ te.bidsTrie, err = db.OpenStorageTrie(te.orderBookHash, te.data.BidRoot)
if err != nil {
- c.bidsTrie, _ = db.OpenStorageTrie(c.orderBookHash, EmptyHash)
- c.setError(fmt.Errorf("can't create bids trie: %v", err))
+ te.bidsTrie, _ = db.OpenStorageTrie(te.orderBookHash, EmptyHash)
+ te.setError(fmt.Errorf("can't create bids trie: %v", err))
}
}
- return c.bidsTrie
+ return te.bidsTrie
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
-func (self *tradingExchanges) updateBidsTrie(db Database) Trie {
- tr := self.getBidsTrie(db)
- for price, orderList := range self.stateBidObjects {
- if _, isDirty := self.stateBidObjectsDirty[price]; isDirty {
- delete(self.stateBidObjectsDirty, price)
+func (te *tradingExchanges) updateBidsTrie(db Database) Trie {
+ tr := te.getBidsTrie(db)
+ for price, orderList := range te.stateBidObjects {
+ if _, isDirty := te.stateBidObjectsDirty[price]; isDirty {
+ delete(te.stateBidObjectsDirty, price)
if orderList.empty() {
- self.setError(tr.TryDelete(price[:]))
+ te.setError(tr.TryDelete(price[:]))
continue
}
err := orderList.updateRoot(db)
@@ -288,25 +288,25 @@ func (self *tradingExchanges) updateBidsTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
- self.setError(tr.TryUpdate(price[:], v))
+ te.setError(tr.TryUpdate(price[:], v))
}
}
return tr
}
-func (self *tradingExchanges) updateBidsRoot(db Database) {
- self.updateBidsTrie(db)
- self.data.BidRoot = self.bidsTrie.Hash()
+func (te *tradingExchanges) updateBidsRoot(db Database) {
+ te.updateBidsTrie(db)
+ te.data.BidRoot = te.bidsTrie.Hash()
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *tradingExchanges) CommitBidsTrie(db Database) error {
- self.updateBidsTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (te *tradingExchanges) CommitBidsTrie(db Database) error {
+ te.updateBidsTrie(db)
+ if te.dbErr != nil {
+ return te.dbErr
}
- root, err := self.bidsTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := te.bidsTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -317,116 +317,116 @@ func (self *tradingExchanges) CommitBidsTrie(db Database) error {
return nil
})
if err == nil {
- self.data.BidRoot = root
+ te.data.BidRoot = root
}
return err
}
-func (self *tradingExchanges) deepCopy(db *TradingStateDB, onDirty func(hash common.Hash)) *tradingExchanges {
- stateExchanges := newStateExchanges(db, self.orderBookHash, self.data, onDirty)
- if self.asksTrie != nil {
- stateExchanges.asksTrie = db.db.CopyTrie(self.asksTrie)
+func (te *tradingExchanges) deepCopy(db *TradingStateDB, onDirty func(hash common.Hash)) *tradingExchanges {
+ stateExchanges := newStateExchanges(db, te.orderBookHash, te.data, onDirty)
+ if te.asksTrie != nil {
+ stateExchanges.asksTrie = db.db.CopyTrie(te.asksTrie)
}
- if self.bidsTrie != nil {
- stateExchanges.bidsTrie = db.db.CopyTrie(self.bidsTrie)
+ if te.bidsTrie != nil {
+ stateExchanges.bidsTrie = db.db.CopyTrie(te.bidsTrie)
}
- if self.ordersTrie != nil {
- stateExchanges.ordersTrie = db.db.CopyTrie(self.ordersTrie)
+ if te.ordersTrie != nil {
+ stateExchanges.ordersTrie = db.db.CopyTrie(te.ordersTrie)
}
- for price, bidObject := range self.stateBidObjects {
- stateExchanges.stateBidObjects[price] = bidObject.deepCopy(db, self.MarkStateBidObjectDirty)
+ for price, bidObject := range te.stateBidObjects {
+ stateExchanges.stateBidObjects[price] = bidObject.deepCopy(db, te.MarkStateBidObjectDirty)
}
- for price := range self.stateBidObjectsDirty {
+ for price := range te.stateBidObjectsDirty {
stateExchanges.stateBidObjectsDirty[price] = struct{}{}
}
- for price, askObject := range self.stateAskObjects {
- stateExchanges.stateAskObjects[price] = askObject.deepCopy(db, self.MarkStateAskObjectDirty)
+ for price, askObject := range te.stateAskObjects {
+ stateExchanges.stateAskObjects[price] = askObject.deepCopy(db, te.MarkStateAskObjectDirty)
}
- for price := range self.stateAskObjectsDirty {
+ for price := range te.stateAskObjectsDirty {
stateExchanges.stateAskObjectsDirty[price] = struct{}{}
}
- for orderId, orderItem := range self.stateOrderObjects {
- stateExchanges.stateOrderObjects[orderId] = orderItem.deepCopy(self.MarkStateOrderObjectDirty)
+ for orderId, orderItem := range te.stateOrderObjects {
+ stateExchanges.stateOrderObjects[orderId] = orderItem.deepCopy(te.MarkStateOrderObjectDirty)
}
- for orderId := range self.stateOrderObjectsDirty {
+ for orderId := range te.stateOrderObjectsDirty {
stateExchanges.stateOrderObjectsDirty[orderId] = struct{}{}
}
- for price, liquidationPrice := range self.liquidationPriceStates {
- stateExchanges.liquidationPriceStates[price] = liquidationPrice.deepCopy(db, self.MarkStateLiquidationPriceDirty)
+ for price, liquidationPrice := range te.liquidationPriceStates {
+ stateExchanges.liquidationPriceStates[price] = liquidationPrice.deepCopy(db, te.MarkStateLiquidationPriceDirty)
}
- for price := range self.liquidationPriceStatesDirty {
+ for price := range te.liquidationPriceStatesDirty {
stateExchanges.liquidationPriceStatesDirty[price] = struct{}{}
}
return stateExchanges
}
// Returns the address of the contract/orderId
-func (c *tradingExchanges) Hash() common.Hash {
- return c.orderBookHash
+func (te *tradingExchanges) Hash() common.Hash {
+ return te.orderBookHash
}
-func (self *tradingExchanges) SetNonce(nonce uint64) {
- self.setNonce(nonce)
+func (te *tradingExchanges) SetNonce(nonce uint64) {
+ te.setNonce(nonce)
}
-func (self *tradingExchanges) setNonce(nonce uint64) {
- self.data.Nonce = nonce
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) setNonce(nonce uint64) {
+ te.data.Nonce = nonce
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
-func (self *tradingExchanges) Nonce() uint64 {
- return self.data.Nonce
+func (te *tradingExchanges) Nonce() uint64 {
+ return te.data.Nonce
}
-func (self *tradingExchanges) setLastPrice(price *big.Int) {
- self.data.LastPrice = price
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) setLastPrice(price *big.Int) {
+ te.data.LastPrice = price
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
-func (self *tradingExchanges) setMediumPriceBeforeEpoch(price *big.Int) {
- self.data.MediumPriceBeforeEpoch = price
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) setMediumPriceBeforeEpoch(price *big.Int) {
+ te.data.MediumPriceBeforeEpoch = price
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
-func (self *tradingExchanges) setMediumPrice(price *big.Int, quantity *big.Int) {
- self.data.MediumPrice = price
- self.data.TotalQuantity = quantity
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) setMediumPrice(price *big.Int, quantity *big.Int) {
+ te.data.MediumPrice = price
+ te.data.TotalQuantity = quantity
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
// updateStateExchangeObject writes the given object to the trie.
-func (self *tradingExchanges) removeStateOrderListAskObject(db Database, stateOrderList *stateOrderList) {
- self.setError(self.asksTrie.TryDelete(stateOrderList.price[:]))
+func (te *tradingExchanges) removeStateOrderListAskObject(db Database, stateOrderList *stateOrderList) {
+ te.setError(te.asksTrie.TryDelete(stateOrderList.price[:]))
}
// updateStateExchangeObject writes the given object to the trie.
-func (self *tradingExchanges) removeStateOrderListBidObject(db Database, stateOrderList *stateOrderList) {
- self.setError(self.bidsTrie.TryDelete(stateOrderList.price[:]))
+func (te *tradingExchanges) removeStateOrderListBidObject(db Database, stateOrderList *stateOrderList) {
+ te.setError(te.bidsTrie.TryDelete(stateOrderList.price[:]))
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *tradingExchanges) getStateOrderListAskObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
+func (te *tradingExchanges) getStateOrderListAskObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
// Prefer 'live' objects.
- if obj := self.stateAskObjects[price]; obj != nil {
+ if obj := te.stateAskObjects[price]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getAsksTrie(db).TryGet(price[:])
+ enc, err := te.getAsksTrie(db).TryGet(price[:])
if len(enc) == 0 {
- self.setError(err)
+ te.setError(err)
return nil
}
var data orderList
@@ -435,50 +435,50 @@ func (self *tradingExchanges) getStateOrderListAskObject(db Database, price comm
return nil
}
// Insert into the live set.
- obj := newStateOrderList(self.db, Bid, self.orderBookHash, price, data, self.MarkStateAskObjectDirty)
- self.stateAskObjects[price] = obj
+ obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateAskObjectDirty)
+ te.stateAskObjects[price] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *tradingExchanges) MarkStateAskObjectDirty(price common.Hash) {
- self.stateAskObjectsDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) MarkStateAskObjectDirty(price common.Hash) {
+ te.stateAskObjectsDirty[price] = struct{}{}
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
-func (self *tradingExchanges) createStateOrderListAskObject(db Database, price common.Hash) (newobj *stateOrderList) {
- newobj = newStateOrderList(self.db, Ask, self.orderBookHash, price, orderList{Volume: Zero}, self.MarkStateAskObjectDirty)
- self.stateAskObjects[price] = newobj
- self.stateAskObjectsDirty[price] = struct{}{}
+func (te *tradingExchanges) createStateOrderListAskObject(db Database, price common.Hash) (newobj *stateOrderList) {
+ newobj = newStateOrderList(te.db, Ask, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateAskObjectDirty)
+ te.stateAskObjects[price] = newobj
+ te.stateAskObjectsDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
- self.setError(self.asksTrie.TryUpdate(price[:], data))
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+ te.setError(te.asksTrie.TryUpdate(price[:], data))
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
return newobj
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *tradingExchanges) getStateBidOrderListObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
+func (te *tradingExchanges) getStateBidOrderListObject(db Database, price common.Hash) (stateOrderList *stateOrderList) {
// Prefer 'live' objects.
- if obj := self.stateBidObjects[price]; obj != nil {
+ if obj := te.stateBidObjects[price]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getBidsTrie(db).TryGet(price[:])
+ enc, err := te.getBidsTrie(db).TryGet(price[:])
if len(enc) == 0 {
- self.setError(err)
+ te.setError(err)
return nil
}
var data orderList
@@ -487,50 +487,50 @@ func (self *tradingExchanges) getStateBidOrderListObject(db Database, price comm
return nil
}
// Insert into the live set.
- obj := newStateOrderList(self.db, Bid, self.orderBookHash, price, data, self.MarkStateBidObjectDirty)
- self.stateBidObjects[price] = obj
+ obj := newStateOrderList(te.db, Bid, te.orderBookHash, price, data, te.MarkStateBidObjectDirty)
+ te.stateBidObjects[price] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *tradingExchanges) MarkStateBidObjectDirty(price common.Hash) {
- self.stateBidObjectsDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) MarkStateBidObjectDirty(price common.Hash) {
+ te.stateBidObjectsDirty[price] = struct{}{}
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
-func (self *tradingExchanges) createStateBidOrderListObject(db Database, price common.Hash) (newobj *stateOrderList) {
- newobj = newStateOrderList(self.db, Bid, self.orderBookHash, price, orderList{Volume: Zero}, self.MarkStateBidObjectDirty)
- self.stateBidObjects[price] = newobj
- self.stateBidObjectsDirty[price] = struct{}{}
+func (te *tradingExchanges) createStateBidOrderListObject(db Database, price common.Hash) (newobj *stateOrderList) {
+ newobj = newStateOrderList(te.db, Bid, te.orderBookHash, price, orderList{Volume: Zero}, te.MarkStateBidObjectDirty)
+ te.stateBidObjects[price] = newobj
+ te.stateBidObjectsDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
- self.setError(self.bidsTrie.TryUpdate(price[:], data))
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+ te.setError(te.bidsTrie.TryUpdate(price[:], data))
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
return newobj
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *tradingExchanges) getStateOrderObject(db Database, orderId common.Hash) (stateOrderItem *stateOrderItem) {
+func (te *tradingExchanges) getStateOrderObject(db Database, orderId common.Hash) (stateOrderItem *stateOrderItem) {
// Prefer 'live' objects.
- if obj := self.stateOrderObjects[orderId]; obj != nil {
+ if obj := te.stateOrderObjects[orderId]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getOrdersTrie(db).TryGet(orderId[:])
+ enc, err := te.getOrdersTrie(db).TryGet(orderId[:])
if len(enc) == 0 {
- self.setError(err)
+ te.setError(err)
return nil
}
var data OrderItem
@@ -539,48 +539,48 @@ func (self *tradingExchanges) getStateOrderObject(db Database, orderId common.Ha
return nil
}
// Insert into the live set.
- obj := newStateOrderItem(self.orderBookHash, orderId, data, self.MarkStateOrderObjectDirty)
- self.stateOrderObjects[orderId] = obj
+ obj := newStateOrderItem(te.orderBookHash, orderId, data, te.MarkStateOrderObjectDirty)
+ te.stateOrderObjects[orderId] = obj
return obj
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *tradingExchanges) MarkStateOrderObjectDirty(orderId common.Hash) {
- self.stateOrderObjectsDirty[orderId] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (te *tradingExchanges) MarkStateOrderObjectDirty(orderId common.Hash) {
+ te.stateOrderObjectsDirty[orderId] = struct{}{}
+ if te.onDirty != nil {
+ te.onDirty(te.Hash())
+ te.onDirty = nil
}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
-func (self *tradingExchanges) createStateOrderObject(db Database, orderId common.Hash, order OrderItem) (newobj *stateOrderItem) {
- newobj = newStateOrderItem(self.orderBookHash, orderId, order, self.MarkStateOrderObjectDirty)
+func (t *tradingExchanges) createStateOrderObject(db Database, orderId common.Hash, order OrderItem) (newobj *stateOrderItem) {
+ newobj = newStateOrderItem(t.orderBookHash, orderId, order, t.MarkStateOrderObjectDirty)
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID))
- self.stateOrderObjects[orderIdHash] = newobj
- self.stateOrderObjectsDirty[orderIdHash] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.orderBookHash)
- self.onDirty = nil
+ t.stateOrderObjects[orderIdHash] = newobj
+ t.stateOrderObjectsDirty[orderIdHash] = struct{}{}
+ if t.onDirty != nil {
+ t.onDirty(t.orderBookHash)
+ t.onDirty = nil
}
return newobj
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
-func (self *tradingExchanges) updateOrdersTrie(db Database) Trie {
- tr := self.getOrdersTrie(db)
- for orderId, orderItem := range self.stateOrderObjects {
- if _, isDirty := self.stateOrderObjectsDirty[orderId]; isDirty {
- delete(self.stateOrderObjectsDirty, orderId)
+func (t *tradingExchanges) updateOrdersTrie(db Database) Trie {
+ tr := t.getOrdersTrie(db)
+ for orderId, orderItem := range t.stateOrderObjects {
+ if _, isDirty := t.stateOrderObjectsDirty[orderId]; isDirty {
+ delete(t.stateOrderObjectsDirty, orderId)
if orderItem.empty() {
- self.setError(tr.TryDelete(orderId[:]))
+ t.setError(tr.TryDelete(orderId[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderItem)
- self.setError(tr.TryUpdate(orderId[:], v))
+ t.setError(tr.TryUpdate(orderId[:], v))
}
}
return tr
@@ -588,71 +588,71 @@ func (self *tradingExchanges) updateOrdersTrie(db Database) Trie {
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *tradingExchanges) updateOrdersRoot(db Database) {
- self.updateOrdersTrie(db)
- self.data.OrderRoot = self.ordersTrie.Hash()
+func (t *tradingExchanges) updateOrdersRoot(db Database) {
+ t.updateOrdersTrie(db)
+ t.data.OrderRoot = t.ordersTrie.Hash()
}
// CommitAskTrie the storage trie of the object to dwb.
// This updates the trie root.
-func (self *tradingExchanges) CommitOrdersTrie(db Database) error {
- self.updateOrdersTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (t *tradingExchanges) CommitOrdersTrie(db Database) error {
+ t.updateOrdersTrie(db)
+ if t.dbErr != nil {
+ return t.dbErr
}
- root, err := self.ordersTrie.Commit(nil)
+ root, err := t.ordersTrie.Commit(nil)
if err == nil {
- self.data.OrderRoot = root
+ t.data.OrderRoot = root
}
return err
}
-func (self *tradingExchanges) MarkStateLiquidationPriceDirty(price common.Hash) {
- self.liquidationPriceStatesDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (t *tradingExchanges) MarkStateLiquidationPriceDirty(price common.Hash) {
+ t.liquidationPriceStatesDirty[price] = struct{}{}
+ if t.onDirty != nil {
+ t.onDirty(t.Hash())
+ t.onDirty = nil
}
}
-func (self *tradingExchanges) createStateLiquidationPrice(db Database, liquidationPrice common.Hash) (newobj *liquidationPriceState) {
- newobj = newLiquidationPriceState(self.db, self.orderBookHash, liquidationPrice, orderList{Volume: Zero}, self.MarkStateLiquidationPriceDirty)
- self.liquidationPriceStates[liquidationPrice] = newobj
- self.liquidationPriceStatesDirty[liquidationPrice] = struct{}{}
+func (t *tradingExchanges) createStateLiquidationPrice(db Database, liquidationPrice common.Hash) (newobj *liquidationPriceState) {
+ newobj = newLiquidationPriceState(t.db, t.orderBookHash, liquidationPrice, orderList{Volume: Zero}, t.MarkStateLiquidationPriceDirty)
+ t.liquidationPriceStates[liquidationPrice] = newobj
+ t.liquidationPriceStatesDirty[liquidationPrice] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode liquidation price object at %x: %v", liquidationPrice[:], err))
}
- self.setError(self.getLiquidationPriceTrie(db).TryUpdate(liquidationPrice[:], data))
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+ t.setError(t.getLiquidationPriceTrie(db).TryUpdate(liquidationPrice[:], data))
+ if t.onDirty != nil {
+ t.onDirty(t.Hash())
+ t.onDirty = nil
}
return newobj
}
-func (self *tradingExchanges) getLiquidationPriceTrie(db Database) Trie {
- if self.liquidationPriceTrie == nil {
+func (t *tradingExchanges) getLiquidationPriceTrie(db Database) Trie {
+ if t.liquidationPriceTrie == nil {
var err error
- self.liquidationPriceTrie, err = db.OpenStorageTrie(self.orderBookHash, self.data.LiquidationPriceRoot)
+ t.liquidationPriceTrie, err = db.OpenStorageTrie(t.orderBookHash, t.data.LiquidationPriceRoot)
if err != nil {
- self.liquidationPriceTrie, _ = db.OpenStorageTrie(self.orderBookHash, EmptyHash)
- self.setError(fmt.Errorf("can't create liquidation liquidationPrice trie: %v", err))
+ t.liquidationPriceTrie, _ = db.OpenStorageTrie(t.orderBookHash, EmptyHash)
+ t.setError(fmt.Errorf("can't create liquidation liquidationPrice trie: %v", err))
}
}
- return self.liquidationPriceTrie
+ return t.liquidationPriceTrie
}
-func (self *tradingExchanges) getStateLiquidationPrice(db Database, price common.Hash) (stateObject *liquidationPriceState) {
+func (t *tradingExchanges) getStateLiquidationPrice(db Database, price common.Hash) (stateObject *liquidationPriceState) {
// Prefer 'live' objects.
- if obj := self.liquidationPriceStates[price]; obj != nil {
+ if obj := t.liquidationPriceStates[price]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getLiquidationPriceTrie(db).TryGet(price[:])
+ enc, err := t.getLiquidationPriceTrie(db).TryGet(price[:])
if len(enc) == 0 {
- self.setError(err)
+ t.setError(err)
return nil
}
var data orderList
@@ -661,16 +661,16 @@ func (self *tradingExchanges) getStateLiquidationPrice(db Database, price common
return nil
}
// Insert into the live set.
- obj := newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty)
- self.liquidationPriceStates[price] = obj
+ obj := newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
+ t.liquidationPriceStates[price] = obj
return obj
}
-func (self *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
- trie := self.getLiquidationPriceTrie(db)
+func (t *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
+ trie := t.getLiquidationPriceTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
- log.Error("Failed find best liquidationPrice ask trie ", "orderbook", self.orderBookHash.Hex())
+ log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex())
return EmptyHash, nil
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -678,25 +678,25 @@ func (self *tradingExchanges) getLowestLiquidationPrice(db Database) (common.Has
return EmptyHash, nil
}
price := common.BytesToHash(encKey)
- obj := self.liquidationPriceStates[price]
+ obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash, nil
}
- obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty)
- self.liquidationPriceStates[price] = obj
+ obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
+ t.liquidationPriceStates[price] = obj
}
return price, obj
}
-func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit common.Hash) map[common.Hash]*liquidationPriceState {
- trie := self.getLiquidationPriceTrie(db)
+func (t *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit common.Hash) map[common.Hash]*liquidationPriceState {
+ trie := t.getLiquidationPriceTrie(db)
encKeys, encValues, err := trie.TryGetAllLeftKeyAndValue(limit.Bytes())
result := map[common.Hash]*liquidationPriceState{}
if err != nil || len(encKeys) != len(encValues) {
- log.Error("Failed get lower liquidation price trie ", "orderbook", self.orderBookHash.Hex(), "encKeys", len(encKeys), "encValues", len(encValues))
+ log.Error("Failed get lower liquidation price trie ", "orderbook", t.orderBookHash.Hex(), "encKeys", len(encKeys), "encValues", len(encValues))
return result
}
if len(encKeys) == 0 || len(encValues) == 0 {
@@ -705,15 +705,15 @@ func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit com
}
for i := range encKeys {
price := common.BytesToHash(encKeys[i])
- obj := self.liquidationPriceStates[price]
+ obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValues[i], &data); err != nil {
log.Error("Failed to decode state get all lower liquidation price trie", "price", price, "encValues", encValues[i], "err", err)
return result
}
- obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty)
- self.liquidationPriceStates[price] = obj
+ obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
+ t.liquidationPriceStates[price] = obj
}
if obj.empty() {
continue
@@ -723,11 +723,11 @@ func (self *tradingExchanges) getAllLowerLiquidationPrice(db Database, limit com
return result
}
-func (self *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
- trie := self.getLiquidationPriceTrie(db)
+func (t *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Hash, *liquidationPriceState) {
+ trie := t.getLiquidationPriceTrie(db)
encKey, encValue, err := trie.TryGetBestRightKeyAndValue()
if err != nil {
- log.Error("Failed find best liquidationPrice ask trie ", "orderbook", self.orderBookHash.Hex())
+ log.Error("Failed find best liquidationPrice ask trie ", "orderbook", t.orderBookHash.Hex())
return EmptyHash, nil
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -735,28 +735,29 @@ func (self *tradingExchanges) getHighestLiquidationPrice(db Database) (common.Ha
return EmptyHash, nil
}
price := common.BytesToHash(encKey)
- obj := self.liquidationPriceStates[price]
+ obj := t.liquidationPriceStates[price]
if obj == nil {
var data orderList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best ask trie", "err", err)
return EmptyHash, nil
}
- obj = newLiquidationPriceState(self.db, self.orderBookHash, price, data, self.MarkStateLiquidationPriceDirty)
- self.liquidationPriceStates[price] = obj
+ obj = newLiquidationPriceState(t.db, t.orderBookHash, price, data, t.MarkStateLiquidationPriceDirty)
+ t.liquidationPriceStates[price] = obj
}
if obj.empty() {
return EmptyHash, nil
}
return price, obj
}
-func (self *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie {
- tr := self.getLiquidationPriceTrie(db)
- for price, stateObject := range self.liquidationPriceStates {
- if _, isDirty := self.liquidationPriceStatesDirty[price]; isDirty {
- delete(self.liquidationPriceStatesDirty, price)
+
+func (t *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie {
+ tr := t.getLiquidationPriceTrie(db)
+ for price, stateObject := range t.liquidationPriceStates {
+ if _, isDirty := t.liquidationPriceStatesDirty[price]; isDirty {
+ delete(t.liquidationPriceStatesDirty, price)
if stateObject.empty() {
- self.setError(tr.TryDelete(price[:]))
+ t.setError(tr.TryDelete(price[:]))
continue
}
err := stateObject.updateRoot(db)
@@ -765,23 +766,23 @@ func (self *tradingExchanges) updateLiquidationPriceTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(stateObject)
- self.setError(tr.TryUpdate(price[:], v))
+ t.setError(tr.TryUpdate(price[:], v))
}
}
return tr
}
-func (self *tradingExchanges) updateLiquidationPriceRoot(db Database) {
- self.updateLiquidationPriceTrie(db)
- self.data.LiquidationPriceRoot = self.liquidationPriceTrie.Hash()
+func (t *tradingExchanges) updateLiquidationPriceRoot(db Database) {
+ t.updateLiquidationPriceTrie(db)
+ t.data.LiquidationPriceRoot = t.liquidationPriceTrie.Hash()
}
-func (self *tradingExchanges) CommitLiquidationPriceTrie(db Database) error {
- self.updateLiquidationPriceTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (t *tradingExchanges) CommitLiquidationPriceTrie(db Database) error {
+ t.updateLiquidationPriceTrie(db)
+ if t.dbErr != nil {
+ return t.dbErr
}
- root, err := self.liquidationPriceTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := t.liquidationPriceTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList orderList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -792,23 +793,23 @@ func (self *tradingExchanges) CommitLiquidationPriceTrie(db Database) error {
return nil
})
if err == nil {
- self.data.LiquidationPriceRoot = root
+ t.data.LiquidationPriceRoot = root
}
return err
}
-func (c *tradingExchanges) addLendingCount(amount *big.Int) {
- c.setLendingCount(new(big.Int).Add(c.data.LendingCount, amount))
+func (t *tradingExchanges) addLendingCount(amount *big.Int) {
+ t.setLendingCount(new(big.Int).Add(t.data.LendingCount, amount))
}
-func (c *tradingExchanges) subLendingCount(amount *big.Int) {
- c.setLendingCount(new(big.Int).Sub(c.data.LendingCount, amount))
+func (t *tradingExchanges) subLendingCount(amount *big.Int) {
+ t.setLendingCount(new(big.Int).Sub(t.data.LendingCount, amount))
}
-func (self *tradingExchanges) setLendingCount(volume *big.Int) {
- self.data.LendingCount = volume
- if self.onDirty != nil {
- self.onDirty(self.orderBookHash)
- self.onDirty = nil
+func (t *tradingExchanges) setLendingCount(volume *big.Int) {
+ t.data.LendingCount = volume
+ if t.onDirty != nil {
+ t.onDirty(t.orderBookHash)
+ t.onDirty = nil
}
}
diff --git a/XDCx/tradingstate/statedb.go b/XDCx/tradingstate/statedb.go
index 68d1278d95..036b5cac4c 100644
--- a/XDCx/tradingstate/statedb.go
+++ b/XDCx/tradingstate/statedb.go
@@ -78,55 +78,55 @@ func New(root common.Hash, db Database) (*TradingStateDB, error) {
}
// setError remembers the first non-nil error it is called with.
-func (self *TradingStateDB) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (t *TradingStateDB) setError(err error) {
+ if t.dbErr == nil {
+ t.dbErr = err
}
}
-func (self *TradingStateDB) Error() error {
- return self.dbErr
+func (t *TradingStateDB) Error() error {
+ return t.dbErr
}
// Exist reports whether the given orderId address exists in the state.
// Notably this also returns true for suicided exchanges.
-func (self *TradingStateDB) Exist(addr common.Hash) bool {
- return self.getStateExchangeObject(addr) != nil
+func (t *TradingStateDB) Exist(addr common.Hash) bool {
+ return t.getStateExchangeObject(addr) != nil
}
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
-func (self *TradingStateDB) Empty(addr common.Hash) bool {
- so := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) Empty(addr common.Hash) bool {
+ so := t.getStateExchangeObject(addr)
return so == nil || so.empty()
}
-func (self *TradingStateDB) GetNonce(addr common.Hash) uint64 {
- stateObject := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) GetNonce(addr common.Hash) uint64 {
+ stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.Nonce()
}
return 0
}
-func (self *TradingStateDB) GetLastPrice(addr common.Hash) *big.Int {
- stateObject := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) GetLastPrice(addr common.Hash) *big.Int {
+ stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.LastPrice
}
return nil
}
-func (self *TradingStateDB) GetMediumPriceBeforeEpoch(addr common.Hash) *big.Int {
- stateObject := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) GetMediumPriceBeforeEpoch(addr common.Hash) *big.Int {
+ stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.MediumPriceBeforeEpoch
}
return Zero
}
-func (self *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big.Int, *big.Int) {
- stateObject := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big.Int, *big.Int) {
+ stateObject := t.getStateExchangeObject(addr)
if stateObject != nil {
return stateObject.data.MediumPrice, stateObject.data.TotalQuantity
}
@@ -134,25 +134,25 @@ func (self *TradingStateDB) GetMediumPriceAndTotalAmount(addr common.Hash) (*big
}
// Database retrieves the low level database supporting the lower level trie ops.
-func (self *TradingStateDB) Database() Database {
- return self.db
+func (t *TradingStateDB) Database() Database {
+ return t.db
}
-func (self *TradingStateDB) SetNonce(addr common.Hash, nonce uint64) {
- stateObject := self.GetOrNewStateExchangeObject(addr)
+func (t *TradingStateDB) SetNonce(addr common.Hash, nonce uint64) {
+ stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, nonceChange{
+ t.journal = append(t.journal, nonceChange{
hash: addr,
- prev: self.GetNonce(addr),
+ prev: t.GetNonce(addr),
})
stateObject.SetNonce(nonce)
}
}
-func (self *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) {
- stateObject := self.GetOrNewStateExchangeObject(addr)
+func (t *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) {
+ stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, lastPriceChange{
+ t.journal = append(t.journal, lastPriceChange{
hash: addr,
prev: stateObject.data.LastPrice,
})
@@ -160,10 +160,10 @@ func (self *TradingStateDB) SetLastPrice(addr common.Hash, price *big.Int) {
}
}
-func (self *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, quantity *big.Int) {
- stateObject := self.GetOrNewStateExchangeObject(addr)
+func (t *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, quantity *big.Int) {
+ stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, mediumPriceChange{
+ t.journal = append(t.journal, mediumPriceChange{
hash: addr,
prevPrice: stateObject.data.MediumPrice,
prevQuantity: stateObject.data.TotalQuantity,
@@ -172,10 +172,10 @@ func (self *TradingStateDB) SetMediumPrice(addr common.Hash, price *big.Int, qua
}
}
-func (self *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *big.Int) {
- stateObject := self.GetOrNewStateExchangeObject(addr)
+func (t *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *big.Int) {
+ stateObject := t.GetOrNewStateExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, mediumPriceBeforeEpochChange{
+ t.journal = append(t.journal, mediumPriceBeforeEpochChange{
hash: addr,
prevPrice: stateObject.data.MediumPriceBeforeEpoch,
})
@@ -183,78 +183,79 @@ func (self *TradingStateDB) SetMediumPriceBeforeEpoch(addr common.Hash, price *b
}
}
-func (self *TradingStateDB) InsertOrderItem(orderBook common.Hash, orderId common.Hash, order OrderItem) {
+func (t *TradingStateDB) InsertOrderItem(orderBook common.Hash, orderId common.Hash, order OrderItem) {
priceHash := common.BigToHash(order.Price)
- stateExchange := self.getStateExchangeObject(orderBook)
+ stateExchange := t.getStateExchangeObject(orderBook)
if stateExchange == nil {
- stateExchange = self.createExchangeObject(orderBook)
+ stateExchange = t.createExchangeObject(orderBook)
}
var stateOrderList *stateOrderList
switch order.Side {
case Ask:
- stateOrderList = stateExchange.getStateOrderListAskObject(self.db, priceHash)
+ stateOrderList = stateExchange.getStateOrderListAskObject(t.db, priceHash)
if stateOrderList == nil {
- stateOrderList = stateExchange.createStateOrderListAskObject(self.db, priceHash)
+ stateOrderList = stateExchange.createStateOrderListAskObject(t.db, priceHash)
}
case Bid:
- stateOrderList = stateExchange.getStateBidOrderListObject(self.db, priceHash)
+ stateOrderList = stateExchange.getStateBidOrderListObject(t.db, priceHash)
if stateOrderList == nil {
- stateOrderList = stateExchange.createStateBidOrderListObject(self.db, priceHash)
+ stateOrderList = stateExchange.createStateBidOrderListObject(t.db, priceHash)
}
default:
return
}
- self.journal = append(self.journal, insertOrder{
+ t.journal = append(t.journal, insertOrder{
orderBook: orderBook,
orderId: orderId,
order: &order,
})
- stateExchange.createStateOrderObject(self.db, orderId, order)
- stateOrderList.insertOrderItem(self.db, orderId, common.BigToHash(order.Quantity))
+ stateExchange.createStateOrderObject(t.db, orderId, order)
+ stateOrderList.insertOrderItem(t.db, orderId, common.BigToHash(order.Quantity))
stateOrderList.AddVolume(order.Quantity)
}
-func (self *TradingStateDB) GetOrder(orderBook common.Hash, orderId common.Hash) OrderItem {
- stateObject := self.GetOrNewStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetOrder(orderBook common.Hash, orderId common.Hash) OrderItem {
+ stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
return EmptyOrder
}
- stateOrderItem := stateObject.getStateOrderObject(self.db, orderId)
+ stateOrderItem := stateObject.getStateOrderObject(t.db, orderId)
if stateOrderItem == nil {
return EmptyOrder
}
return stateOrderItem.data
}
-func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
+
+func (t *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
priceHash := common.BigToHash(price)
- stateObject := self.GetOrNewStateExchangeObject(orderBook)
+ stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
- return fmt.Errorf("Order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
var stateOrderList *stateOrderList
switch side {
case Ask:
- stateOrderList = stateObject.getStateOrderListAskObject(self.db, priceHash)
+ stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash)
case Bid:
- stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash)
+ stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash)
default:
- return fmt.Errorf("Order type not found : %s ", side)
+ return fmt.Errorf("not found order type: %s", side)
}
if stateOrderList == nil || stateOrderList.empty() {
- return fmt.Errorf("Order list empty order book : %s , order id : %s , price : %s ", orderBook, orderId.Hex(), priceHash.Hex())
+ return fmt.Errorf("empty Orderlist: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
- stateOrderItem := stateObject.getStateOrderObject(self.db, orderId)
+ stateOrderItem := stateObject.getStateOrderObject(t.db, orderId)
if stateOrderItem == nil || stateOrderItem.empty() {
- return fmt.Errorf("Order item empty order book : %s , order id : %s , price : %s ", orderBook, orderId.Hex(), priceHash.Hex())
+ return fmt.Errorf("empty OrderItem: order book: %s , order id : %s , price : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
- currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(self.db, orderId).Bytes()[:])
+ currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderId).Bytes()[:])
if currentAmount.Cmp(amount) < 0 {
- return fmt.Errorf("Order amount not enough : %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount)
+ return fmt.Errorf("not enough order amount: %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount)
}
- self.journal = append(self.journal, subAmountOrder{
+ t.journal = append(t.journal, subAmountOrder{
orderBook: orderBook,
orderId: orderId,
- order: self.GetOrder(orderBook, orderId),
+ order: t.GetOrder(orderBook, orderId),
amount: amount,
})
newAmount := new(big.Int).Sub(currentAmount, amount)
@@ -262,86 +263,86 @@ func (self *TradingStateDB) SubAmountOrderItem(orderBook common.Hash, orderId co
stateOrderList.subVolume(amount)
stateOrderItem.setVolume(newAmount)
if newAmount.Sign() == 0 {
- stateOrderList.removeOrderItem(self.db, orderId)
+ stateOrderList.removeOrderItem(t.db, orderId)
} else {
stateOrderList.setOrderItem(orderId, common.BigToHash(newAmount))
}
if stateOrderList.empty() {
switch side {
case Ask:
- stateObject.removeStateOrderListAskObject(self.db, stateOrderList)
+ stateObject.removeStateOrderListAskObject(t.db, stateOrderList)
case Bid:
- stateObject.removeStateOrderListBidObject(self.db, stateOrderList)
+ stateObject.removeStateOrderListBidObject(t.db, stateOrderList)
default:
}
}
return nil
}
-func (self *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) error {
+func (t *TradingStateDB) CancelOrder(orderBook common.Hash, order *OrderItem) error {
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID))
- stateObject := self.GetOrNewStateExchangeObject(orderBook)
+ stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject == nil {
- return fmt.Errorf("Order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
- stateOrderItem := stateObject.getStateOrderObject(self.db, orderIdHash)
+ stateOrderItem := stateObject.getStateOrderObject(t.db, orderIdHash)
if stateOrderItem == nil || stateOrderItem.empty() {
- return fmt.Errorf("Order item empty order book : %s , order id : %s ", orderBook, orderIdHash.Hex())
+ return fmt.Errorf("empty OrderItem: order book: %s , order id : %s", orderBook, orderIdHash.Hex())
}
priceHash := common.BigToHash(stateOrderItem.data.Price)
var stateOrderList *stateOrderList
switch stateOrderItem.data.Side {
case Ask:
- stateOrderList = stateObject.getStateOrderListAskObject(self.db, priceHash)
+ stateOrderList = stateObject.getStateOrderListAskObject(t.db, priceHash)
case Bid:
- stateOrderList = stateObject.getStateBidOrderListObject(self.db, priceHash)
+ stateOrderList = stateObject.getStateBidOrderListObject(t.db, priceHash)
default:
- return fmt.Errorf("Order side not found : %s ", order.Side)
+ return fmt.Errorf("not found order.Side: %s", order.Side)
}
if stateOrderList == nil || stateOrderList.empty() {
- return fmt.Errorf("Order list empty order book : %s , order id : %s , price : %s ", orderBook, orderIdHash.Hex(), priceHash.Hex())
+ return fmt.Errorf("empty OrderList: order book: %s , order id : %s , price : %s", orderBook, orderIdHash.Hex(), priceHash.Hex())
}
if stateOrderItem.data.UserAddress != order.UserAddress {
- return fmt.Errorf("Error Order User Address mismatch when cancel order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), stateOrderItem.data.UserAddress.Hex(), order.UserAddress.Hex())
+ return fmt.Errorf("error Order UserAddress mismatch when cancel: order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), stateOrderItem.data.UserAddress.Hex(), order.UserAddress.Hex())
}
if stateOrderItem.data.Hash != order.Hash {
- return fmt.Errorf("Invalid order hash : got : %s , expect : %s ", order.Hash.Hex(), stateOrderItem.data.Hash.Hex())
+ return fmt.Errorf("invalid order hash: got : %s , expect : %s", order.Hash.Hex(), stateOrderItem.data.Hash.Hex())
}
if stateOrderItem.data.ExchangeAddress != order.ExchangeAddress {
- return fmt.Errorf("Exchange Address mismatch when cancel. order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex())
+ return fmt.Errorf("mismatch ExchangeAddress when cancel: order book : %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), order.ExchangeAddress.Hex(), stateOrderItem.data.ExchangeAddress.Hex())
}
- self.journal = append(self.journal, cancelOrder{
+ t.journal = append(t.journal, cancelOrder{
orderBook: orderBook,
orderId: orderIdHash,
order: stateOrderItem.data,
})
- currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(self.db, orderIdHash).Bytes()[:])
+ currentAmount := new(big.Int).SetBytes(stateOrderList.GetOrderAmount(t.db, orderIdHash).Bytes()[:])
stateOrderItem.setVolume(big.NewInt(0))
stateOrderList.subVolume(currentAmount)
- stateOrderList.removeOrderItem(self.db, orderIdHash)
+ stateOrderList.removeOrderItem(t.db, orderIdHash)
if stateOrderList.empty() {
switch stateOrderItem.data.Side {
case Ask:
- stateObject.removeStateOrderListAskObject(self.db, stateOrderList)
+ stateObject.removeStateOrderListAskObject(t.db, stateOrderList)
case Bid:
- stateObject.removeStateOrderListBidObject(self.db, stateOrderList)
+ stateObject.removeStateOrderListBidObject(t.db, stateOrderList)
default:
}
}
return nil
}
-func (self *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, orderType string) *big.Int {
- stateObject := self.GetOrNewStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, orderType string) *big.Int {
+ stateObject := t.GetOrNewStateExchangeObject(orderBook)
var volume *big.Int = nil
if stateObject != nil {
var stateOrderList *stateOrderList
switch orderType {
case Ask:
- stateOrderList = stateObject.getStateOrderListAskObject(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price))
case Bid:
- stateOrderList = stateObject.getStateBidOrderListObject(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price))
default:
return Zero
}
@@ -352,14 +353,15 @@ func (self *TradingStateDB) GetVolume(orderBook common.Hash, price *big.Int, ord
}
return volume
}
-func (self *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big.Int) {
- stateObject := self.getStateExchangeObject(orderBook)
+
+func (t *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *big.Int) {
+ stateObject := t.getStateExchangeObject(orderBook)
if stateObject != nil {
- priceHash := stateObject.getBestPriceAsksTrie(self.db)
+ priceHash := stateObject.getBestPriceAsksTrie(t.db)
if common.EmptyHash(priceHash) {
return Zero, Zero
}
- orderList := stateObject.getStateOrderListAskObject(self.db, priceHash)
+ orderList := stateObject.getStateOrderListAskObject(t.db, priceHash)
if orderList == nil {
log.Error("order list ask not found", "price", priceHash.Hex())
return Zero, Zero
@@ -369,14 +371,14 @@ func (self *TradingStateDB) GetBestAskPrice(orderBook common.Hash) (*big.Int, *b
return Zero, Zero
}
-func (self *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big.Int) {
- stateObject := self.getStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *big.Int) {
+ stateObject := t.getStateExchangeObject(orderBook)
if stateObject != nil {
- priceHash := stateObject.getBestBidsTrie(self.db)
+ priceHash := stateObject.getBestBidsTrie(t.db)
if common.EmptyHash(priceHash) {
return Zero, Zero
}
- orderList := stateObject.getStateBidOrderListObject(self.db, priceHash)
+ orderList := stateObject.getStateBidOrderListObject(t.db, priceHash)
if orderList == nil {
log.Error("order list bid not found", "price", priceHash.Hex())
return Zero, Zero
@@ -386,52 +388,52 @@ func (self *TradingStateDB) GetBestBidPrice(orderBook common.Hash) (*big.Int, *b
return Zero, Zero
}
-func (self *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
- stateObject := self.GetOrNewStateExchangeObject(orderBook)
+func (t *TradingStateDB) GetBestOrderIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
+ stateObject := t.GetOrNewStateExchangeObject(orderBook)
if stateObject != nil {
var stateOrderList *stateOrderList
switch side {
case Ask:
- stateOrderList = stateObject.getStateOrderListAskObject(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getStateOrderListAskObject(t.db, common.BigToHash(price))
case Bid:
- stateOrderList = stateObject.getStateBidOrderListObject(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getStateBidOrderListObject(t.db, common.BigToHash(price))
default:
- return EmptyHash, Zero, fmt.Errorf("not found side :%s ", side)
+ return EmptyHash, Zero, fmt.Errorf("not found side: %s", side)
}
if stateOrderList != nil {
- key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue()
+ key, _, err := stateOrderList.getTrie(t.db).TryGetBestLeftKeyAndValue()
if err != nil {
return EmptyHash, Zero, err
}
orderId := common.BytesToHash(key)
- amount := stateOrderList.GetOrderAmount(self.db, orderId)
+ amount := stateOrderList.GetOrderAmount(t.db, orderId)
return orderId, new(big.Int).SetBytes(amount.Bytes()), nil
}
- return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook : %s , price : %d , side :%s ", orderBook.Hex(), price, side)
+ return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , price : %d , side : %s", orderBook.Hex(), price, side)
}
- return EmptyHash, Zero, fmt.Errorf("not found orderBook : %s ", orderBook.Hex())
+ return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
// updateStateExchangeObject writes the given object to the trie.
-func (self *TradingStateDB) updateStateExchangeObject(stateObject *tradingExchanges) {
+func (t *TradingStateDB) updateStateExchangeObject(stateObject *tradingExchanges) {
addr := stateObject.Hash()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
- self.setError(self.trie.TryUpdate(addr[:], data))
+ t.setError(t.trie.TryUpdate(addr[:], data))
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObject *tradingExchanges) {
+func (t *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObject *tradingExchanges) {
// Prefer 'live' objects.
- if obj := self.stateExhangeObjects[addr]; obj != nil {
+ if obj := t.stateExhangeObjects[addr]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.trie.TryGet(addr[:])
+ enc, err := t.trie.TryGet(addr[:])
if len(enc) == 0 {
- self.setError(err)
+ t.setError(err)
return nil
}
var data tradingExchangeObject
@@ -440,169 +442,169 @@ func (self *TradingStateDB) getStateExchangeObject(addr common.Hash) (stateObjec
return nil
}
// Insert into the live set.
- obj := newStateExchanges(self, addr, data, self.MarkStateExchangeObjectDirty)
- self.stateExhangeObjects[addr] = obj
+ obj := newStateExchanges(t, addr, data, t.MarkStateExchangeObjectDirty)
+ t.stateExhangeObjects[addr] = obj
return obj
}
-func (self *TradingStateDB) setStateExchangeObject(object *tradingExchanges) {
- self.stateExhangeObjects[object.Hash()] = object
- self.stateExhangeObjectsDirty[object.Hash()] = struct{}{}
+func (t *TradingStateDB) setStateExchangeObject(object *tradingExchanges) {
+ t.stateExhangeObjects[object.Hash()] = object
+ t.stateExhangeObjectsDirty[object.Hash()] = struct{}{}
}
// Retrieve a state object or create a new state object if nil.
-func (self *TradingStateDB) GetOrNewStateExchangeObject(addr common.Hash) *tradingExchanges {
- stateExchangeObject := self.getStateExchangeObject(addr)
+func (t *TradingStateDB) GetOrNewStateExchangeObject(addr common.Hash) *tradingExchanges {
+ stateExchangeObject := t.getStateExchangeObject(addr)
if stateExchangeObject == nil {
- stateExchangeObject = self.createExchangeObject(addr)
+ stateExchangeObject = t.createExchangeObject(addr)
}
return stateExchangeObject
}
// MarkStateAskObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *TradingStateDB) MarkStateExchangeObjectDirty(addr common.Hash) {
- self.stateExhangeObjectsDirty[addr] = struct{}{}
+func (t *TradingStateDB) MarkStateExchangeObjectDirty(addr common.Hash) {
+ t.stateExhangeObjectsDirty[addr] = struct{}{}
}
// createStateOrderListObject creates a new state object. If there is an existing orderId with
// the given address, it is overwritten and returned as the second return value.
-func (self *TradingStateDB) createExchangeObject(hash common.Hash) (newobj *tradingExchanges) {
- newobj = newStateExchanges(self, hash, tradingExchangeObject{LendingCount: Zero, MediumPrice: Zero, MediumPriceBeforeEpoch: Zero, TotalQuantity: Zero}, self.MarkStateExchangeObjectDirty)
+func (t *TradingStateDB) createExchangeObject(hash common.Hash) (newobj *tradingExchanges) {
+ newobj = newStateExchanges(t, hash, tradingExchangeObject{LendingCount: Zero, MediumPrice: Zero, MediumPriceBeforeEpoch: Zero, TotalQuantity: Zero}, t.MarkStateExchangeObjectDirty)
newobj.setNonce(0) // sets the object to dirty
- self.setStateExchangeObject(newobj)
+ t.setStateExchangeObject(newobj)
return newobj
}
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
-func (self *TradingStateDB) Copy() *TradingStateDB {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (t *TradingStateDB) Copy() *TradingStateDB {
+ t.lock.Lock()
+ defer t.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &TradingStateDB{
- db: self.db,
- trie: self.db.CopyTrie(self.trie),
- stateExhangeObjects: make(map[common.Hash]*tradingExchanges, len(self.stateExhangeObjectsDirty)),
- stateExhangeObjectsDirty: make(map[common.Hash]struct{}, len(self.stateExhangeObjectsDirty)),
+ db: t.db,
+ trie: t.db.CopyTrie(t.trie),
+ stateExhangeObjects: make(map[common.Hash]*tradingExchanges, len(t.stateExhangeObjectsDirty)),
+ stateExhangeObjectsDirty: make(map[common.Hash]struct{}, len(t.stateExhangeObjectsDirty)),
}
// Copy the dirty states, logs, and preimages
- for addr := range self.stateExhangeObjectsDirty {
+ for addr := range t.stateExhangeObjectsDirty {
state.stateExhangeObjectsDirty[addr] = struct{}{}
}
- for addr, exchangeObject := range self.stateExhangeObjects {
+ for addr, exchangeObject := range t.stateExhangeObjects {
state.stateExhangeObjects[addr] = exchangeObject.deepCopy(state, state.MarkStateExchangeObjectDirty)
}
return state
}
-func (s *TradingStateDB) clearJournalAndRefund() {
- s.journal = nil
- s.validRevisions = s.validRevisions[:0]
+func (t *TradingStateDB) clearJournalAndRefund() {
+ t.journal = nil
+ t.validRevisions = t.validRevisions[:0]
}
// Snapshot returns an identifier for the current revision of the state.
-func (self *TradingStateDB) Snapshot() int {
- id := self.nextRevisionId
- self.nextRevisionId++
- self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
+func (t *TradingStateDB) Snapshot() int {
+ id := t.nextRevisionId
+ t.nextRevisionId++
+ t.validRevisions = append(t.validRevisions, revision{id, len(t.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
-func (self *TradingStateDB) RevertToSnapshot(revid int) {
+func (t *TradingStateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
- idx := sort.Search(len(self.validRevisions), func(i int) bool {
- return self.validRevisions[i].id >= revid
+ idx := sort.Search(len(t.validRevisions), func(i int) bool {
+ return t.validRevisions[i].id >= revid
})
- if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
+ if idx == len(t.validRevisions) || t.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
- snapshot := self.validRevisions[idx].journalIndex
+ snapshot := t.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
- for i := len(self.journal) - 1; i >= snapshot; i-- {
- self.journal[i].undo(self)
+ for i := len(t.journal) - 1; i >= snapshot; i-- {
+ t.journal[i].undo(t)
}
- self.journal = self.journal[:snapshot]
+ t.journal = t.journal[:snapshot]
// Remove invalidated snapshots from the stack.
- self.validRevisions = self.validRevisions[:idx]
+ t.validRevisions = t.validRevisions[:idx]
}
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
-func (s *TradingStateDB) Finalise() {
+func (t *TradingStateDB) Finalise() {
// Commit objects to the trie.
- for addr, stateObject := range s.stateExhangeObjects {
- if _, isDirty := s.stateExhangeObjectsDirty[addr]; isDirty {
+ for addr, stateObject := range t.stateExhangeObjects {
+ if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
- err := stateObject.updateAsksRoot(s.db)
+ err := stateObject.updateAsksRoot(t.db)
if err != nil {
log.Warn("Finalise updateAsksRoot", "err", err, "addr", addr, "stateObject", *stateObject)
}
- stateObject.updateBidsRoot(s.db)
- stateObject.updateOrdersRoot(s.db)
- stateObject.updateLiquidationPriceRoot(s.db)
+ stateObject.updateBidsRoot(t.db)
+ stateObject.updateOrdersRoot(t.db)
+ stateObject.updateLiquidationPriceRoot(t.db)
// Update the object in the main orderId trie.
- s.updateStateExchangeObject(stateObject)
+ t.updateStateExchangeObject(stateObject)
//delete(s.stateExhangeObjectsDirty, addr)
}
}
- s.clearJournalAndRefund()
+ t.clearJournalAndRefund()
}
// IntermediateRoot computes the current root orderBookHash of the state trie.
// It is called in between transactions to get the root orderBookHash that
// goes into transaction receipts.
-func (s *TradingStateDB) IntermediateRoot() common.Hash {
- s.Finalise()
- return s.trie.Hash()
+func (t *TradingStateDB) IntermediateRoot() common.Hash {
+ t.Finalise()
+ return t.trie.Hash()
}
// Commit writes the state to the underlying in-memory trie database.
-func (s *TradingStateDB) Commit() (root common.Hash, err error) {
- defer s.clearJournalAndRefund()
+func (t *TradingStateDB) Commit() (root common.Hash, err error) {
+ defer t.clearJournalAndRefund()
// Commit objects to the trie.
- for addr, stateObject := range s.stateExhangeObjects {
- if _, isDirty := s.stateExhangeObjectsDirty[addr]; isDirty {
+ for addr, stateObject := range t.stateExhangeObjects {
+ if _, isDirty := t.stateExhangeObjectsDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
- if err := stateObject.CommitAsksTrie(s.db); err != nil {
+ if err := stateObject.CommitAsksTrie(t.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitBidsTrie(s.db); err != nil {
+ if err := stateObject.CommitBidsTrie(t.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitOrdersTrie(s.db); err != nil {
+ if err := stateObject.CommitOrdersTrie(t.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitLiquidationPriceTrie(s.db); err != nil {
+ if err := stateObject.CommitLiquidationPriceTrie(t.db); err != nil {
return EmptyHash, err
}
// Update the object in the main orderId trie.
- s.updateStateExchangeObject(stateObject)
- delete(s.stateExhangeObjectsDirty, addr)
+ t.updateStateExchangeObject(stateObject)
+ delete(t.stateExhangeObjectsDirty, addr)
}
}
// Write trie changes.
- root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err = t.trie.Commit(func(leaf []byte, parent common.Hash) error {
var exchange tradingExchangeObject
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
return nil
}
if exchange.AskRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.AskRoot, parent)
+ t.db.TrieDB().Reference(exchange.AskRoot, parent)
}
if exchange.BidRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.BidRoot, parent)
+ t.db.TrieDB().Reference(exchange.BidRoot, parent)
}
if exchange.OrderRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.OrderRoot, parent)
+ t.db.TrieDB().Reference(exchange.OrderRoot, parent)
}
if exchange.LiquidationPriceRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.LiquidationPriceRoot, parent)
+ t.db.TrieDB().Reference(exchange.LiquidationPriceRoot, parent)
}
return nil
})
@@ -610,19 +612,19 @@ func (s *TradingStateDB) Commit() (root common.Hash, err error) {
return root, err
}
-func (self *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Hash, limit *big.Int) map[*big.Int]map[common.Hash][]common.Hash {
+func (t *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Hash, limit *big.Int) map[*big.Int]map[common.Hash][]common.Hash {
result := map[*big.Int]map[common.Hash][]common.Hash{}
- orderbookState := self.getStateExchangeObject(orderBook)
+ orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
return result
}
- mapPrices := orderbookState.getAllLowerLiquidationPrice(self.db, common.BigToHash(limit))
+ mapPrices := orderbookState.getAllLowerLiquidationPrice(t.db, common.BigToHash(limit))
for priceHash, liquidationState := range mapPrices {
price := new(big.Int).SetBytes(priceHash[:])
log.Debug("GetAllLowerLiquidationPriceData", "price", price, "limit", limit)
if liquidationState != nil && price.Sign() > 0 && price.Cmp(limit) < 0 {
liquidationData := map[common.Hash][]common.Hash{}
- priceLiquidationData := liquidationState.getAllLiquidationData(self.db)
+ priceLiquidationData := liquidationState.getAllLiquidationData(t.db)
for lendingBook, data := range priceLiquidationData {
if len(data) == 0 {
continue
@@ -641,16 +643,16 @@ func (self *TradingStateDB) GetAllLowerLiquidationPriceData(orderBook common.Has
return result
}
-func (self *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash, price *big.Int) (*big.Int, map[common.Hash][]common.Hash) {
+func (t *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash, price *big.Int) (*big.Int, map[common.Hash][]common.Hash) {
liquidationData := map[common.Hash][]common.Hash{}
- orderbookState := self.getStateExchangeObject(orderBook)
+ orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
return common.Big0, liquidationData
}
- highestPriceHash, liquidationState := orderbookState.getHighestLiquidationPrice(self.db)
+ highestPriceHash, liquidationState := orderbookState.getHighestLiquidationPrice(t.db)
highestPrice := new(big.Int).SetBytes(highestPriceHash[:])
if liquidationState != nil && highestPrice.Sign() > 0 && price.Cmp(highestPrice) < 0 {
- priceLiquidationData := liquidationState.getAllLiquidationData(self.db)
+ priceLiquidationData := liquidationState.getAllLiquidationData(t.db)
for lendingBook, data := range priceLiquidationData {
if len(data) == 0 {
continue
@@ -667,26 +669,26 @@ func (self *TradingStateDB) GetHighestLiquidationPriceData(orderBook common.Hash
return highestPrice, liquidationData
}
-func (self *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) {
+func (t *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) {
tradIdHash := common.Uint64ToHash(tradeId)
priceHash := common.BigToHash(price)
- orderBookState := self.getStateExchangeObject(orderBook)
+ orderBookState := t.getStateExchangeObject(orderBook)
if orderBookState == nil {
- orderBookState = self.createExchangeObject(orderBook)
+ orderBookState = t.createExchangeObject(orderBook)
}
- liquidationPriceState := orderBookState.getStateLiquidationPrice(self.db, priceHash)
+ liquidationPriceState := orderBookState.getStateLiquidationPrice(t.db, priceHash)
if liquidationPriceState == nil {
- liquidationPriceState = orderBookState.createStateLiquidationPrice(self.db, priceHash)
+ liquidationPriceState = orderBookState.createStateLiquidationPrice(t.db, priceHash)
}
- lendingBookState := liquidationPriceState.getStateLendingBook(self.db, lendingBook)
+ lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook)
if lendingBookState == nil {
- lendingBookState = liquidationPriceState.createLendingBook(self.db, lendingBook)
+ lendingBookState = liquidationPriceState.createLendingBook(t.db, lendingBook)
}
- lendingBookState.insertTradingId(self.db, tradIdHash)
+ lendingBookState.insertTradingId(t.db, tradIdHash)
lendingBookState.AddVolume(One)
liquidationPriceState.AddVolume(One)
orderBookState.addLendingCount(One)
- self.journal = append(self.journal, insertLiquidationPrice{
+ t.journal = append(t.journal, insertLiquidationPrice{
orderBook: orderBook,
price: price,
lendingBook: lendingBook,
@@ -694,35 +696,35 @@ func (self *TradingStateDB) InsertLiquidationPrice(orderBook common.Hash, price
})
}
-func (self *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) error {
+func (t *TradingStateDB) RemoveLiquidationPrice(orderBook common.Hash, price *big.Int, lendingBook common.Hash, tradeId uint64) error {
tradeIdHash := common.Uint64ToHash(tradeId)
priceHash := common.BigToHash(price)
- orderbookState := self.getStateExchangeObject(orderBook)
+ orderbookState := t.getStateExchangeObject(orderBook)
if orderbookState == nil {
- return fmt.Errorf("order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found order book: %s", orderBook.Hex())
}
- liquidationPriceState := orderbookState.getStateLiquidationPrice(self.db, priceHash)
+ liquidationPriceState := orderbookState.getStateLiquidationPrice(t.db, priceHash)
if liquidationPriceState == nil {
- return fmt.Errorf("liquidation price not found : %s , %s ", orderBook.Hex(), priceHash.Hex())
+ return fmt.Errorf("not found liquidation price: %s , %s", orderBook.Hex(), priceHash.Hex())
}
- lendingBookState := liquidationPriceState.getStateLendingBook(self.db, lendingBook)
+ lendingBookState := liquidationPriceState.getStateLendingBook(t.db, lendingBook)
if lendingBookState == nil {
- return fmt.Errorf("lending book not found : %s , %s ,%s ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex())
+ return fmt.Errorf("not found lending book: %s , %s ,%s", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex())
}
- if !lendingBookState.Exist(self.db, tradeIdHash) {
- return fmt.Errorf("trade id not found : %s , %s ,%s , %d ", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId)
+ if !lendingBookState.Exist(t.db, tradeIdHash) {
+ return fmt.Errorf("not found trade id: %s, %s ,%s , %d", orderBook.Hex(), priceHash.Hex(), lendingBook.Hex(), tradeId)
}
- lendingBookState.removeTradingId(self.db, tradeIdHash)
+ lendingBookState.removeTradingId(t.db, tradeIdHash)
lendingBookState.subVolume(One)
liquidationPriceState.subVolume(One)
if liquidationPriceState.Volume().Sign() == 0 {
- err := orderbookState.getLiquidationPriceTrie(self.db).TryDelete(priceHash[:])
+ err := orderbookState.getLiquidationPriceTrie(t.db).TryDelete(priceHash[:])
if err != nil {
log.Warn("RemoveLiquidationPrice getLiquidationPriceTrie.TryDelete", "err", err, "priceHash", priceHash[:])
}
}
orderbookState.subLendingCount(One)
- self.journal = append(self.journal, removeLiquidationPrice{
+ t.journal = append(t.journal, removeLiquidationPrice{
orderBook: orderBook,
price: price,
lendingBook: lendingBook,
diff --git a/XDCx/tradingstate/trade.go b/XDCx/tradingstate/trade.go
index 22f6e30ea1..f8a4e5cd27 100644
--- a/XDCx/tradingstate/trade.go
+++ b/XDCx/tradingstate/trade.go
@@ -5,8 +5,8 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/globalsign/mgo/bson"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -136,7 +136,7 @@ func (t *Trade) SetBSON(raw bson.Raw) error {
// The OrderHash, Amount, Taker and TradeNonce attributes must be
// set before attempting to compute the trade orderBookHash
func (t *Trade) ComputeHash() common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(t.MakerOrderHash.Bytes())
sha.Write(t.TakerOrderHash.Bytes())
return common.BytesToHash(sha.Sum(nil))
diff --git a/XDCxDAO/leveldb.go b/XDCxDAO/leveldb.go
index 2a76162d4c..a6d90dd57a 100644
--- a/XDCxDAO/leveldb.go
+++ b/XDCxDAO/leveldb.go
@@ -8,10 +8,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
- lru "github.com/hashicorp/golang-lru"
)
type BatchItem struct {
@@ -21,7 +19,6 @@ type BatchItem struct {
type BatchDatabase struct {
db ethdb.Database
emptyKey []byte
- cacheItems *lru.Cache // Cache for reading
lock sync.RWMutex
cacheLimit int
Debug bool
@@ -44,11 +41,8 @@ func NewBatchDatabaseWithEncode(datadir string, cacheLimit int) *BatchDatabase {
itemCacheLimit = cacheLimit
}
- cacheItems, _ := lru.New(itemCacheLimit)
-
batchDB := &BatchDatabase{
db: db,
- cacheItems: cacheItems,
emptyKey: EmptyKey(), // pre alloc for comparison
cacheLimit: itemCacheLimit,
}
diff --git a/XDCxDAO/mongodb.go b/XDCxDAO/mongodb.go
index 3d2efef317..8c8e45052c 100644
--- a/XDCxDAO/mongodb.go
+++ b/XDCxDAO/mongodb.go
@@ -10,11 +10,11 @@ import (
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -32,7 +32,7 @@ type MongoDatabase struct {
Session *mgo.Session
dbName string
emptyKey []byte
- cacheItems *lru.Cache // Cache for reading
+ cacheItems *lru.Cache[string, interface{}] // Cache for reading
orderBulk *mgo.Bulk
tradeBulk *mgo.Bulk
epochPriceBulk *mgo.Bulk
@@ -64,12 +64,11 @@ func NewMongoDatabase(session *mgo.Session, dbName string, mongoURL string, repl
if cacheLimit > 0 {
itemCacheLimit = cacheLimit
}
- cacheItems, _ := lru.New(itemCacheLimit)
db := &MongoDatabase{
Session: session,
dbName: dbName,
- cacheItems: cacheItems,
+ cacheItems: lru.NewCache[string, interface{}](itemCacheLimit),
}
if err := db.EnsureIndexes(); err != nil {
return nil, err
@@ -101,7 +100,7 @@ func (db *MongoDatabase) HasObject(hash common.Hash, val interface{}) (bool, err
err error
)
query := bson.M{"hash": hash.Hex()}
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
// Find key in ordersCollection collection
count, err = sc.DB(db.dbName).C(ordersCollection).Find(query).Limit(1).Count()
@@ -126,7 +125,6 @@ func (db *MongoDatabase) HasObject(hash common.Hash, val interface{}) (bool, err
}
case *lendingstate.LendingItem:
// Find key in lendingItemsCollection collection
- item := val.(*lendingstate.LendingItem)
switch item.Type {
case lendingstate.Repay:
count, err = sc.DB(db.dbName).C(lendingRepayCollection).Find(query).Limit(1).Count()
@@ -176,7 +174,7 @@ func (db *MongoDatabase) GetObject(hash common.Hash, val interface{}) (interface
query := bson.M{"hash": hash.Hex()}
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
var oi *tradingstate.OrderItem
err := sc.DB(db.dbName).C(ordersCollection).Find(query).One(&oi)
@@ -196,7 +194,6 @@ func (db *MongoDatabase) GetObject(hash common.Hash, val interface{}) (interface
case *lendingstate.LendingItem:
var li *lendingstate.LendingItem
var err error
- item := val.(*lendingstate.LendingItem)
switch item.Type {
case lendingstate.Repay:
err = sc.DB(db.dbName).C(lendingRepayCollection).Find(query).One(&li)
@@ -230,62 +227,58 @@ func (db *MongoDatabase) PutObject(hash common.Hash, val interface{}) error {
cacheKey := db.getCacheKey(hash.Bytes())
db.cacheItems.Add(cacheKey, val)
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.Trade:
// PutObject trade into tradesCollection collection
- db.tradeBulk.Insert(val.(*tradingstate.Trade))
+ db.tradeBulk.Insert(item)
case *tradingstate.OrderItem:
// PutObject order into ordersCollection collection
- o := val.(*tradingstate.OrderItem)
- if o.Status == tradingstate.OrderStatusOpen {
- db.orderBulk.Insert(o)
+ if item.Status == tradingstate.OrderStatusOpen {
+ db.orderBulk.Insert(item)
} else {
- query := bson.M{"hash": o.Hash.Hex()}
- db.orderBulk.Upsert(query, o)
+ query := bson.M{"hash": item.Hash.Hex()}
+ db.orderBulk.Upsert(query, item)
}
return nil
case *tradingstate.EpochPriceItem:
- item := val.(*tradingstate.EpochPriceItem)
query := bson.M{"hash": item.Hash.Hex()}
db.epochPriceBulk.Upsert(query, item)
return nil
case *lendingstate.LendingTrade:
- lt := val.(*lendingstate.LendingTrade)
// PutObject LendingTrade into tradesCollection collection
if existed, err := db.HasObject(hash, val); err == nil && existed {
- query := bson.M{"hash": lt.Hash.Hex()}
- db.lendingTradeBulk.Upsert(query, lt)
+ query := bson.M{"hash": item.Hash.Hex()}
+ db.lendingTradeBulk.Upsert(query, item)
} else {
- db.lendingTradeBulk.Insert(lt)
+ db.lendingTradeBulk.Insert(item)
}
case *lendingstate.LendingItem:
// PutObject order into ordersCollection collection
- li := val.(*lendingstate.LendingItem)
- switch li.Type {
+ switch item.Type {
case lendingstate.Repay:
- if li.Status != lendingstate.LendingStatusReject {
- li.Status = lendingstate.Repay
+ if item.Status != lendingstate.LendingStatusReject {
+ item.Status = lendingstate.Repay
}
- db.repayBulk.Insert(li)
+ db.repayBulk.Insert(item)
return nil
case lendingstate.TopUp:
- if li.Status != lendingstate.LendingStatusReject {
- li.Status = lendingstate.TopUp
+ if item.Status != lendingstate.LendingStatusReject {
+ item.Status = lendingstate.TopUp
}
- db.topUpBulk.Insert(li)
+ db.topUpBulk.Insert(item)
return nil
case lendingstate.Recall:
- if li.Status != lendingstate.LendingStatusReject {
- li.Status = lendingstate.Recall
+ if item.Status != lendingstate.LendingStatusReject {
+ item.Status = lendingstate.Recall
}
- db.recallBulk.Insert(li)
+ db.recallBulk.Insert(item)
return nil
default:
- if li.Status == lendingstate.LendingStatusOpen {
- db.lendingItemBulk.Insert(li)
+ if item.Status == lendingstate.LendingStatusOpen {
+ db.lendingItemBulk.Insert(item)
} else {
- query := bson.M{"hash": li.Hash.Hex()}
- db.lendingItemBulk.Upsert(query, li)
+ query := bson.M{"hash": item.Hash.Hex()}
+ db.lendingItemBulk.Upsert(query, item)
}
return nil
}
@@ -313,7 +306,7 @@ func (db *MongoDatabase) DeleteObject(hash common.Hash, val interface{}) error {
if found {
var err error
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
err = sc.DB(db.dbName).C(ordersCollection).Remove(query)
if err != nil && err != mgo.ErrNotFound {
@@ -325,7 +318,6 @@ func (db *MongoDatabase) DeleteObject(hash common.Hash, val interface{}) error {
return fmt.Errorf("failed to delete XDCx trade. Err: %v", err)
}
case *lendingstate.LendingItem:
- item := val.(*lendingstate.LendingItem)
switch item.Type {
case lendingstate.Repay:
err = sc.DB(db.dbName).C(lendingRepayCollection).Remove(query)
@@ -424,7 +416,7 @@ func (db *MongoDatabase) DeleteItemByTxHash(txhash common.Hash, val interface{})
defer sc.Close()
query := bson.M{"txHash": txhash.Hex()}
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
if err := sc.DB(db.dbName).C(ordersCollection).Remove(query); err != nil && err != mgo.ErrNotFound {
log.Error("DeleteItemByTxHash: failed to delete order", "txhash", txhash, "err", err)
@@ -434,7 +426,6 @@ func (db *MongoDatabase) DeleteItemByTxHash(txhash common.Hash, val interface{})
log.Error("DeleteItemByTxHash: failed to delete trade", "txhash", txhash, "err", err)
}
case *lendingstate.LendingItem:
- item := val.(*lendingstate.LendingItem)
switch item.Type {
case lendingstate.Repay:
if err := sc.DB(db.dbName).C(lendingRepayCollection).Remove(query); err != nil && err != mgo.ErrNotFound {
@@ -473,7 +464,7 @@ func (db *MongoDatabase) GetListItemByTxHash(txhash common.Hash, val interface{}
defer sc.Close()
query := bson.M{"txHash": txhash.Hex()}
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
result := []*tradingstate.OrderItem{}
if err := sc.DB(db.dbName).C(ordersCollection).Find(query).All(&result); err != nil && err != mgo.ErrNotFound {
@@ -487,7 +478,6 @@ func (db *MongoDatabase) GetListItemByTxHash(txhash common.Hash, val interface{}
}
return result
case *lendingstate.LendingItem:
- item := val.(*lendingstate.LendingItem)
result := []*lendingstate.LendingItem{}
switch item.Type {
case lendingstate.Repay:
@@ -529,7 +519,7 @@ func (db *MongoDatabase) GetListItemByHashes(hashes []string, val interface{}) i
query := bson.M{"hash": bson.M{"$in": hashes}}
- switch val.(type) {
+ switch item := val.(type) {
case *tradingstate.OrderItem:
result := []*tradingstate.OrderItem{}
if err := sc.DB(db.dbName).C(ordersCollection).Find(query).All(&result); err != nil && err != mgo.ErrNotFound {
@@ -543,7 +533,6 @@ func (db *MongoDatabase) GetListItemByHashes(hashes []string, val interface{}) i
}
return result
case *lendingstate.LendingItem:
- item := val.(*lendingstate.LendingItem)
result := []*lendingstate.LendingItem{}
switch item.Type {
case lendingstate.Repay:
@@ -835,7 +824,10 @@ func (db *MongoDatabase) EnsureIndexes() error {
}
func (db *MongoDatabase) Close() error {
- return db.Close()
+ if db.Session != nil {
+ db.Session.Close()
+ }
+ return nil
}
// HasAncient returns an error as we don't have a backing chain freezer.
diff --git a/XDCxlending/XDCxlending.go b/XDCxlending/XDCxlending.go
index 48ff540778..1bf2496ff2 100644
--- a/XDCxlending/XDCxlending.go
+++ b/XDCxlending/XDCxlending.go
@@ -13,6 +13,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/XDCxDAO"
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/state"
@@ -20,7 +21,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/rpc"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -42,8 +42,8 @@ type Lending struct {
orderNonce map[common.Address]*big.Int
XDCx *XDCx.XDCX
- lendingItemHistory *lru.Cache
- lendingTradeHistory *lru.Cache
+ lendingItemHistory *lru.Cache[common.Hash, map[common.Hash]lendingstate.LendingItemHistoryItem]
+ lendingTradeHistory *lru.Cache[common.Hash, map[common.Hash]lendingstate.LendingTradeHistoryItem]
}
func (l *Lending) Protocols() []p2p.Protocol {
@@ -62,13 +62,11 @@ func (l *Lending) Stop() error {
}
func New(XDCx *XDCx.XDCX) *Lending {
- itemCache, _ := lru.New(defaultCacheLimit)
- lendingTradeCache, _ := lru.New(defaultCacheLimit)
lending := &Lending{
orderNonce: make(map[common.Address]*big.Int),
Triegc: prque.New(nil),
- lendingItemHistory: itemCache,
- lendingTradeHistory: lendingTradeCache,
+ lendingItemHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingItemHistoryItem](defaultCacheLimit),
+ lendingTradeHistory: lru.NewCache[common.Hash, map[common.Hash]lendingstate.LendingTradeHistoryItem](defaultCacheLimit),
}
lending.StateCache = lendingstate.NewDatabase(XDCx.GetLevelDB())
lending.XDCx = XDCx
@@ -311,7 +309,7 @@ func (l *Lending) SyncDataToSDKNode(chain consensus.ChainContext, statedb *state
}
// maker dirty order
makerFilledAmount := big.NewInt(0)
- makerOrderHash := common.Hash{}
+ var makerOrderHash common.Hash
if updatedTakerLendingItem.Side == lendingstate.Borrowing {
makerOrderHash = tradeRecord.InvestingOrderHash
} else {
@@ -665,7 +663,7 @@ func (l *Lending) GetLendingState(block *types.Block, author common.Address) (*l
return nil, err
}
if l.StateCache == nil {
- return nil, errors.New("Not initialized XDCx")
+ return nil, errors.New("not initialized XDCx")
}
state, err := lendingstate.New(root, l.StateCache)
if err != nil {
@@ -684,10 +682,7 @@ func (l *Lending) HasLendingState(block *types.Block, author common.Address) boo
return false
}
_, err = l.StateCache.OpenTrie(root)
- if err != nil {
- return false
- }
- return true
+ return err == nil
}
func (l *Lending) GetTriegc() *prque.Prque {
@@ -708,12 +703,9 @@ func (l *Lending) GetLendingStateRoot(block *types.Block, author common.Address)
}
func (l *Lending) UpdateLendingItemCache(LendingToken, CollateralToken common.Address, hash common.Hash, txhash common.Hash, lastState lendingstate.LendingItemHistoryItem) {
- var lendingCacheAtTxHash map[common.Hash]lendingstate.LendingItemHistoryItem
- c, ok := l.lendingItemHistory.Get(txhash)
- if !ok || c == nil {
+ lendingCacheAtTxHash, ok := l.lendingItemHistory.Get(txhash)
+ if !ok || lendingCacheAtTxHash == nil {
lendingCacheAtTxHash = make(map[common.Hash]lendingstate.LendingItemHistoryItem)
- } else {
- lendingCacheAtTxHash = c.(map[common.Hash]lendingstate.LendingItemHistoryItem)
}
orderKey := lendingstate.GetLendingItemHistoryKey(LendingToken, CollateralToken, hash)
_, ok = lendingCacheAtTxHash[orderKey]
@@ -725,11 +717,9 @@ func (l *Lending) UpdateLendingItemCache(LendingToken, CollateralToken common.Ad
func (l *Lending) UpdateLendingTradeCache(hash common.Hash, txhash common.Hash, lastState lendingstate.LendingTradeHistoryItem) {
var lendingCacheAtTxHash map[common.Hash]lendingstate.LendingTradeHistoryItem
- c, ok := l.lendingTradeHistory.Get(txhash)
- if !ok || c == nil {
+ lendingCacheAtTxHash, ok := l.lendingTradeHistory.Get(txhash)
+ if !ok || lendingCacheAtTxHash == nil {
lendingCacheAtTxHash = make(map[common.Hash]lendingstate.LendingTradeHistoryItem)
- } else {
- lendingCacheAtTxHash = c.(map[common.Hash]lendingstate.LendingTradeHistoryItem)
}
_, ok = lendingCacheAtTxHash[hash]
if !ok {
@@ -746,16 +736,15 @@ func (l *Lending) RollbackLendingData(txhash common.Hash) error {
items := db.GetListItemByTxHash(txhash, &lendingstate.LendingItem{})
if items != nil {
for _, item := range items.([]*lendingstate.LendingItem) {
- c, ok := l.lendingItemHistory.Get(txhash)
- log.Debug("XDCxlending reorg: rollback lendingItem", "txhash", txhash.Hex(), "item", lendingstate.ToJSON(item), "lendingItemHistory", c)
- if !ok {
+ cacheAtTxHash, ok := l.lendingItemHistory.Get(txhash)
+ log.Debug("XDCxlending reorg: rollback lendingItem", "txhash", txhash.Hex(), "item", lendingstate.ToJSON(item), "lendingItemHistory", cacheAtTxHash)
+ if !ok || cacheAtTxHash == nil {
log.Debug("XDCxlending reorg: remove item due to no lendingItemHistory", "item", lendingstate.ToJSON(item))
if err := db.DeleteObject(item.Hash, &lendingstate.LendingItem{}); err != nil {
return fmt.Errorf("failed to remove reorg LendingItem. Err: %v . Item: %s", err.Error(), lendingstate.ToJSON(item))
}
continue
}
- cacheAtTxHash := c.(map[common.Hash]lendingstate.LendingItemHistoryItem)
lendingItemHistory := cacheAtTxHash[lendingstate.GetLendingItemHistoryKey(item.LendingToken, item.CollateralToken, item.Hash)]
if (lendingItemHistory == lendingstate.LendingItemHistoryItem{}) {
log.Debug("XDCxlending reorg: remove item due to empty lendingItemHistory", "item", lendingstate.ToJSON(item))
@@ -779,16 +768,15 @@ func (l *Lending) RollbackLendingData(txhash common.Hash) error {
items = db.GetListItemByTxHash(txhash, &lendingstate.LendingTrade{})
if items != nil {
for _, trade := range items.([]*lendingstate.LendingTrade) {
- c, ok := l.lendingTradeHistory.Get(txhash)
- log.Debug("XDCxlending reorg: rollback LendingTrade", "txhash", txhash.Hex(), "trade", lendingstate.ToJSON(trade), "LendingTradeHistory", c)
- if !ok {
+ cacheAtTxHash, ok := l.lendingTradeHistory.Get(txhash)
+ log.Debug("XDCxlending reorg: rollback LendingTrade", "txhash", txhash.Hex(), "trade", lendingstate.ToJSON(trade), "LendingTradeHistory", cacheAtTxHash)
+ if !ok || cacheAtTxHash == nil {
log.Debug("XDCxlending reorg: remove trade due to no LendingTradeHistory", "trade", lendingstate.ToJSON(trade))
if err := db.DeleteObject(trade.Hash, &lendingstate.LendingTrade{}); err != nil {
return fmt.Errorf("failed to remove reorg LendingTrade. Err: %v . Trade: %s", err.Error(), lendingstate.ToJSON(trade))
}
continue
}
- cacheAtTxHash := c.(map[common.Hash]lendingstate.LendingTradeHistoryItem)
lendingTradeHistoryItem := cacheAtTxHash[trade.Hash]
if (lendingTradeHistoryItem == lendingstate.LendingTradeHistoryItem{}) {
log.Debug("XDCxlending reorg: remove trade due to empty LendingTradeHistory", "trade", lendingstate.ToJSON(trade))
@@ -926,7 +914,7 @@ func (l *Lending) ProcessLiquidationData(header *types.Header, chain consensus.C
trade := lendingState.GetLendingTrade(lendingBook, tradingIdHash)
log.Debug("TestRecall", "borrower", trade.Borrower.Hex(), "lendingToken", trade.LendingToken.Hex(), "collateral", trade.CollateralToken.Hex(), "price", price, "tradingIdHash", tradingIdHash.Hex())
if trade.AutoTopUp {
- err, _, newTrade := l.ProcessRecallLendingTrade(lendingState, statedb, tradingState, lendingBook, tradingIdHash, newLiquidatePrice)
+ _, newTrade, err := l.ProcessRecallLendingTrade(lendingState, statedb, tradingState, lendingBook, tradingIdHash, newLiquidatePrice)
if err != nil {
log.Error("ProcessRecallLendingTrade", "lendingBook", lendingBook.Hex(), "tradingIdHash", tradingIdHash.Hex(), "newLiquidatePrice", newLiquidatePrice, "err", err)
return updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err
diff --git a/XDCxlending/lendingstate/database.go b/XDCxlending/lendingstate/database.go
index d765a1eade..7c4934bc7f 100644
--- a/XDCxlending/lendingstate/database.go
+++ b/XDCxlending/lendingstate/database.go
@@ -23,7 +23,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/trie"
- lru "github.com/hashicorp/golang-lru"
)
// Trie cache generation limit after which to evic trie nodes from memory.
@@ -33,9 +32,6 @@ const (
// Number of past tries to keep. This value is chosen such that
// reasonable chain reorg depths will hit an existing trie.
maxPastTries = 12
-
- // Number of codehash->size associations to keep.
- codeSizeCacheSize = 100000
)
// Database wraps access to tries and contract code.
@@ -78,18 +74,16 @@ type Trie interface {
// intermediate trie-node memory pool between the low level storage layer and the
// high level trie abstraction.
func NewDatabase(db ethdb.Database) Database {
- csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{
- db: trie.NewDatabase(db),
- codeSizeCache: csc,
+ db: trie.NewDatabase(db),
+ // codeSizeCache: csc,
}
}
type cachingDB struct {
- db *trie.Database
- mu sync.Mutex
- pastTries []*XDCXTrie
- codeSizeCache *lru.Cache
+ db *trie.Database
+ mu sync.Mutex
+ pastTries []*XDCXTrie
}
// OpenTrie opens the main account trie.
diff --git a/XDCxlending/lendingstate/dump.go b/XDCxlending/lendingstate/dump.go
index 10dc070862..f3fe43742e 100644
--- a/XDCxlending/lendingstate/dump.go
+++ b/XDCxlending/lendingstate/dump.go
@@ -18,11 +18,11 @@ package lendingstate
import (
"fmt"
- "github.com/XinFinOrg/XDPoSChain/rlp"
"math/big"
"sort"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie"
)
@@ -39,13 +39,13 @@ type DumpOrderBookInfo struct {
LowestLiquidationTime *big.Int
}
-func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpOrderList{}
- it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil))
for it.Next() {
interestHash := common.BytesToHash(it.Key)
if common.EmptyHash(interestHash) {
@@ -57,15 +57,15 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I
} else {
var data itemList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest)
}
stateOrderList := newItemListState(orderBook, interestHash, data, nil)
- mapResult[interest] = stateOrderList.DumpItemList(self.db)
+ mapResult[interest] = stateOrderList.DumpItemList(ls.db)
}
}
for interestHash, itemList := range exhangeObject.investingStates {
if itemList.Volume().Sign() > 0 {
- mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(self.db)
+ mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(ls.db)
}
}
listInterest := []*big.Int{}
@@ -82,13 +82,13 @@ func (self *LendingStateDB) DumpInvestingTrie(orderBook common.Hash) (map[*big.I
return result, nil
}
-func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpOrderList{}
- it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil))
for it.Next() {
interestHash := common.BytesToHash(it.Key)
if common.EmptyHash(interestHash) {
@@ -100,15 +100,15 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I
} else {
var data itemList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest)
}
stateOrderList := newItemListState(orderBook, interestHash, data, nil)
- mapResult[interest] = stateOrderList.DumpItemList(self.db)
+ mapResult[interest] = stateOrderList.DumpItemList(ls.db)
}
}
for interestHash, itemList := range exhangeObject.borrowingStates {
if itemList.Volume().Sign() > 0 {
- mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(self.db)
+ mapResult[new(big.Int).SetBytes(interestHash.Bytes())] = itemList.DumpItemList(ls.db)
}
}
listInterest := []*big.Int{}
@@ -125,13 +125,13 @@ func (self *LendingStateDB) DumpBorrowingTrie(orderBook common.Hash) (map[*big.I
return result, nil
}
-func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]*big.Int{}
- it := trie.NewIterator(exhangeObject.getInvestingTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getInvestingTrie(ls.db).NodeIterator(nil))
for it.Next() {
interestHash := common.BytesToHash(it.Key)
if common.EmptyHash(interestHash) {
@@ -143,7 +143,7 @@ func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*
} else {
var data itemList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest)
}
stateOrderList := newItemListState(orderBook, interestHash, data, nil)
mapResult[interest] = stateOrderList.data.Volume
@@ -168,13 +168,13 @@ func (self *LendingStateDB) GetInvestings(orderBook common.Hash) (map[*big.Int]*
return result, nil
}
-func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*big.Int, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]*big.Int{}
- it := trie.NewIterator(exhangeObject.getBorrowingTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getBorrowingTrie(ls.db).NodeIterator(nil))
for it.Next() {
interestHash := common.BytesToHash(it.Key)
if common.EmptyHash(interestHash) {
@@ -186,7 +186,7 @@ func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*
} else {
var data itemList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,interest :%v ", orderBook.Hex(), interest)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , interest : %v", orderBook.Hex(), interest)
}
stateOrderList := newItemListState(orderBook, interestHash, data, nil)
mapResult[interest] = stateOrderList.data.Volume
@@ -211,22 +211,22 @@ func (self *LendingStateDB) GetBorrowings(orderBook common.Hash) (map[*big.Int]*
return result, nil
}
-func (self *itemListState) DumpItemList(db Database) DumpOrderList {
- mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
- orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil))
+func (il *itemListState) DumpItemList(db Database) DumpOrderList {
+ mapResult := DumpOrderList{Volume: il.Volume(), Orders: map[*big.Int]*big.Int{}}
+ orderListIt := trie.NewIterator(il.getTrie(db).NodeIterator(nil))
for orderListIt.Next() {
keyHash := common.BytesToHash(orderListIt.Key)
if common.EmptyHash(keyHash) {
continue
}
- if _, exist := self.cachedStorage[keyHash]; exist {
+ if _, exist := il.cachedStorage[keyHash]; exist {
continue
} else {
_, content, _, _ := rlp.Split(orderListIt.Value)
mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content)
}
}
- for key, value := range self.cachedStorage {
+ for key, value := range il.cachedStorage {
if !common.EmptyHash(value) {
mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes())
}
@@ -238,44 +238,44 @@ func (self *itemListState) DumpItemList(db Database) DumpOrderList {
sort.Slice(listIds, func(i, j int) bool {
return listIds[i].Cmp(listIds[j]) < 0
})
- result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
+ result := DumpOrderList{Volume: il.Volume(), Orders: map[*big.Int]*big.Int{}}
for _, id := range listIds {
result.Orders[id] = mapResult.Orders[id]
}
return result
}
-func (self *LendingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) DumpOrderBookInfo(orderBook common.Hash) (*DumpOrderBookInfo, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
result := &DumpOrderBookInfo{}
result.Nonce = exhangeObject.data.Nonce
result.TradeNonce = exhangeObject.data.TradeNonce
- result.BestInvesting = new(big.Int).SetBytes(exhangeObject.getBestInvestingInterest(self.db).Bytes())
- result.BestBorrowing = new(big.Int).SetBytes(exhangeObject.getBestBorrowingInterest(self.db).Bytes())
- lowestLiquidationTime, _ := exhangeObject.getLowestLiquidationTime(self.db)
+ result.BestInvesting = new(big.Int).SetBytes(exhangeObject.getBestInvestingInterest(ls.db).Bytes())
+ result.BestBorrowing = new(big.Int).SetBytes(exhangeObject.getBestBorrowingInterest(ls.db).Bytes())
+ lowestLiquidationTime, _ := exhangeObject.getLowestLiquidationTime(ls.db)
result.LowestLiquidationTime = new(big.Int).SetBytes(lowestLiquidationTime.Bytes())
return result, nil
}
-func (self *liquidationTimeState) DumpItemList(db Database) DumpOrderList {
- mapResult := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
- orderListIt := trie.NewIterator(self.getTrie(db).NodeIterator(nil))
+func (lts *liquidationTimeState) DumpItemList(db Database) DumpOrderList {
+ mapResult := DumpOrderList{Volume: lts.Volume(), Orders: map[*big.Int]*big.Int{}}
+ orderListIt := trie.NewIterator(lts.getTrie(db).NodeIterator(nil))
for orderListIt.Next() {
keyHash := common.BytesToHash(orderListIt.Key)
if common.EmptyHash(keyHash) {
continue
}
- if _, exist := self.cachedStorage[keyHash]; exist {
+ if _, exist := lts.cachedStorage[keyHash]; exist {
continue
} else {
_, content, _, _ := rlp.Split(orderListIt.Value)
mapResult.Orders[new(big.Int).SetBytes(keyHash.Bytes())] = new(big.Int).SetBytes(content)
}
}
- for key, value := range self.cachedStorage {
+ for key, value := range lts.cachedStorage {
if !common.EmptyHash(value) {
mapResult.Orders[new(big.Int).SetBytes(key.Bytes())] = new(big.Int).SetBytes(value.Bytes())
}
@@ -287,19 +287,20 @@ func (self *liquidationTimeState) DumpItemList(db Database) DumpOrderList {
sort.Slice(listIds, func(i, j int) bool {
return listIds[i].Cmp(listIds[j]) < 0
})
- result := DumpOrderList{Volume: self.Volume(), Orders: map[*big.Int]*big.Int{}}
+ result := DumpOrderList{Volume: lts.Volume(), Orders: map[*big.Int]*big.Int{}}
for _, id := range listIds {
result.Orders[id] = mapResult.Orders[id]
}
return mapResult
}
-func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+
+func (ls *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[*big.Int]DumpOrderList, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]DumpOrderList{}
- it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getLiquidationTimeTrie(ls.db).NodeIterator(nil))
for it.Next() {
unixTimeHash := common.BytesToHash(it.Key)
if common.EmptyHash(unixTimeHash) {
@@ -311,15 +312,15 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[
} else {
var data itemList
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,unixTime :%v ", orderBook.Hex(), unixTime)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , unixTime : %v", orderBook.Hex(), unixTime)
}
stateOrderList := newLiquidationTimeState(orderBook, unixTimeHash, data, nil)
- mapResult[unixTime] = stateOrderList.DumpItemList(self.db)
+ mapResult[unixTime] = stateOrderList.DumpItemList(ls.db)
}
}
for unixTimeHash, itemList := range exhangeObject.liquidationTimeStates {
if itemList.Volume().Sign() > 0 {
- mapResult[new(big.Int).SetBytes(unixTimeHash.Bytes())] = itemList.DumpItemList(self.db)
+ mapResult[new(big.Int).SetBytes(unixTimeHash.Bytes())] = itemList.DumpItemList(ls.db)
}
}
listUnixTime := []*big.Int{}
@@ -336,13 +337,13 @@ func (self *LendingStateDB) DumpLiquidationTimeTrie(orderBook common.Hash) (map[
return result, nil
}
-func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big.Int]LendingItem, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*big.Int]LendingItem, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]LendingItem{}
- it := trie.NewIterator(exhangeObject.getLendingItemTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getLendingItemTrie(ls.db).NodeIterator(nil))
for it.Next() {
orderIdHash := common.BytesToHash(it.Key)
if common.EmptyHash(orderIdHash) {
@@ -354,7 +355,7 @@ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*bi
} else {
var data LendingItem
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,orderId :%v ", orderBook.Hex(), orderId)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , orderId : %v", orderBook.Hex(), orderId)
}
mapResult[orderId] = data
}
@@ -376,13 +377,13 @@ func (self *LendingStateDB) DumpLendingOrderTrie(orderBook common.Hash) (map[*bi
return result, nil
}
-func (self *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big.Int]LendingTrade, error) {
- exhangeObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*big.Int]LendingTrade, error) {
+ exhangeObject := ls.getLendingExchange(orderBook)
if exhangeObject == nil {
- return nil, fmt.Errorf("Order book not found orderBook : %v ", orderBook.Hex())
+ return nil, fmt.Errorf("not found orderBook: %v", orderBook.Hex())
}
mapResult := map[*big.Int]LendingTrade{}
- it := trie.NewIterator(exhangeObject.getLendingTradeTrie(self.db).NodeIterator(nil))
+ it := trie.NewIterator(exhangeObject.getLendingTradeTrie(ls.db).NodeIterator(nil))
for it.Next() {
tradeIdHash := common.BytesToHash(it.Key)
if common.EmptyHash(tradeIdHash) {
@@ -394,7 +395,7 @@ func (self *LendingStateDB) DumpLendingTradeTrie(orderBook common.Hash) (map[*bi
} else {
var data LendingTrade
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
- return nil, fmt.Errorf("Fail when decode order iist orderBook : %v ,tradeId :%v ", orderBook.Hex(), tradeId)
+ return nil, fmt.Errorf("fail when decode order iist orderBook: %v , tradeId : %v", orderBook.Hex(), tradeId)
}
mapResult[tradeId] = data
}
diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go
index dd4553ab87..4d8e074b48 100644
--- a/XDCxlending/lendingstate/lendingitem.go
+++ b/XDCxlending/lendingstate/lendingitem.go
@@ -10,8 +10,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/globalsign/mgo/bson"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -207,7 +207,7 @@ func (l *LendingItem) VerifyLendingItem(state *state.StateDB) error {
if err := l.VerifyLendingStatus(); err != nil {
return err
}
- if valid, _ := IsValidPair(state, l.Relayer, l.LendingToken, l.Term); valid == false {
+ if valid, _ := IsValidPair(state, l.Relayer, l.LendingToken, l.Term); !valid {
return fmt.Errorf("invalid pair . LendToken %s . Term: %v", l.LendingToken.Hex(), l.Term)
}
if l.Status == LendingStatusNew {
@@ -308,7 +308,7 @@ func (l *LendingItem) VerifyLendingStatus() error {
}
func (l *LendingItem) ComputeHash() common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
if l.Status == LendingStatusNew {
sha.Write(l.Relayer.Bytes())
sha.Write(l.UserAddress.Bytes())
@@ -411,7 +411,7 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD
if lendTokenXDCPrice != nil && lendTokenXDCPrice.Sign() > 0 {
defaultFee := new(big.Int).Mul(quantity, new(big.Int).SetUint64(DefaultFeeRate))
defaultFee = new(big.Int).Div(defaultFee, common.XDCXBaseFee)
- defaultFeeInXDC := common.Big0
+ var defaultFeeInXDC *big.Int
if lendingToken != common.XDCNativeAddressBinary {
defaultFeeInXDC = new(big.Int).Mul(defaultFee, lendTokenXDCPrice)
defaultFeeInXDC = new(big.Int).Div(defaultFeeInXDC, lendingTokenDecimal)
@@ -429,8 +429,7 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD
// make sure actualBalance >= cancel fee
lendingBook := GetLendingOrderBookHash(lendingToken, term)
item := lendingStateDb.GetLendingOrder(lendingBook, common.BigToHash(new(big.Int).SetUint64(lendingId)))
- cancelFee := big.NewInt(0)
- cancelFee = new(big.Int).Mul(item.Quantity, borrowingFeeRate)
+ cancelFee := new(big.Int).Mul(item.Quantity, borrowingFeeRate)
cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee)
actualBalance := GetTokenBalance(userAddress, lendingToken, statedb)
@@ -459,9 +458,8 @@ func VerifyBalance(isXDCXLendingFork bool, statedb *state.StateDB, lendingStateD
case LendingStatusCancelled:
lendingBook := GetLendingOrderBookHash(lendingToken, term)
item := lendingStateDb.GetLendingOrder(lendingBook, common.BigToHash(new(big.Int).SetUint64(lendingId)))
- cancelFee := big.NewInt(0)
// Fee == quantityToLend/base lend token decimal *price*borrowFee/LendingCancelFee
- cancelFee = new(big.Int).Div(item.Quantity, collateralPrice)
+ cancelFee := new(big.Int).Div(item.Quantity, collateralPrice)
cancelFee = new(big.Int).Mul(cancelFee, borrowingFeeRate)
cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee)
actualBalance := GetTokenBalance(userAddress, collateralToken, statedb)
diff --git a/XDCxlending/lendingstate/lendingitem_test.go b/XDCxlending/lendingstate/lendingitem_test.go
index 692dff1c2f..24ad0a48b4 100644
--- a/XDCxlending/lendingstate/lendingitem_test.go
+++ b/XDCxlending/lendingstate/lendingitem_test.go
@@ -12,8 +12,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/rpc"
+ "golang.org/x/crypto/sha3"
)
func TestLendingItem_VerifyLendingSide(t *testing.T) {
@@ -515,11 +515,11 @@ func Test_CreateOrder(t *testing.T) {
func sendOrder(nonce uint64) {
rpcClient, err := rpc.DialHTTP("http://localhost:8501")
- defer rpcClient.Close()
if err != nil {
fmt.Println("rpc.DialHTTP failed", "err", err)
os.Exit(1)
}
+ defer rpcClient.Close()
rand.Seed(time.Now().UTC().UnixNano())
item := &LendingOrderMsg{
AccountNonce: nonce,
@@ -568,9 +568,8 @@ func sendOrder(nonce uint64) {
}
func computeHash(l *LendingOrderMsg) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
if l.Status == LendingStatusCancelled {
- sha := sha3.NewKeccak256()
sha.Write(l.Hash.Bytes())
sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes())
sha.Write(l.UserAddress.Bytes())
@@ -593,5 +592,4 @@ func computeHash(l *LendingOrderMsg) common.Hash {
sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes())
}
return common.BytesToHash(sha.Sum(nil))
-
}
diff --git a/XDCxlending/lendingstate/relayer.go b/XDCxlending/lendingstate/relayer.go
index 536784741b..e7ca282d1b 100644
--- a/XDCxlending/lendingstate/relayer.go
+++ b/XDCxlending/lendingstate/relayer.go
@@ -41,10 +41,7 @@ func IsResignedRelayer(relayer common.Address, statedb *state.StateDB) bool {
slot := RelayerMappingSlot["RESIGN_REQUESTS"]
locBig := GetLocMappingAtKey(relayer.Hash(), slot)
locHash := common.BigToHash(locBig)
- if statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), locHash) != (common.Hash{}) {
- return true
- }
- return false
+ return statedb.GetState(common.HexToAddress(common.RelayerRegistrationSMC), locHash) != (common.Hash{})
}
func GetBaseTokenLength(relayer common.Address, statedb *state.StateDB) uint64 {
@@ -235,7 +232,7 @@ func CheckAddTokenBalance(addr common.Address, value *big.Int, token common.Addr
newBalance := new(big.Int).Add(balance, value)
log.Debug("CheckAddTokenBalance settle balance: ADD TOKEN BALANCE ", "token", token.String(), "address", addr.String(), "balance", balance, "value", value, "newBalance", newBalance)
if common.BigToHash(newBalance).Big().Cmp(newBalance) != 0 {
- return nil, fmt.Errorf("Overflow when try add token balance , max is 2^256 , balance : %v , value:%v ", balance, value)
+ return nil, fmt.Errorf("overflow when try add token balance , max is 2^256 , balance : %v , value : %v", balance, value)
} else {
return newBalance, nil
}
diff --git a/XDCxlending/lendingstate/state_itemList.go b/XDCxlending/lendingstate/state_itemList.go
index ad48c8c857..7737e5b159 100644
--- a/XDCxlending/lendingstate/state_itemList.go
+++ b/XDCxlending/lendingstate/state_itemList.go
@@ -19,10 +19,11 @@ package lendingstate
import (
"bytes"
"fmt"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/rlp"
"io"
"math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
)
type itemListState struct {
@@ -46,8 +47,8 @@ type itemListState struct {
onDirty func(price common.Hash) // Callback method to mark a state object newly dirty
}
-func (s *itemListState) empty() bool {
- return s.data.Volume == nil || s.data.Volume.Sign() == 0
+func (il *itemListState) empty() bool {
+ return il.data.Volume == nil || il.data.Volume.Sign() == 0
}
func newItemListState(lendingBook common.Hash, key common.Hash, data itemList, onDirty func(price common.Hash)) *itemListState {
@@ -62,132 +63,132 @@ func newItemListState(lendingBook common.Hash, key common.Hash, data itemList, o
}
// EncodeRLP implements rlp.Encoder.
-func (c *itemListState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (il *itemListState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, il.data)
}
// setError remembers the first non-nil error it is called with.
-func (self *itemListState) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (il *itemListState) setError(err error) {
+ if il.dbErr == nil {
+ il.dbErr = err
}
}
-func (c *itemListState) getTrie(db Database) Trie {
- if c.trie == nil {
+func (il *itemListState) getTrie(db Database) Trie {
+ if il.trie == nil {
var err error
- c.trie, err = db.OpenStorageTrie(c.key, c.data.Root)
+ il.trie, err = db.OpenStorageTrie(il.key, il.data.Root)
if err != nil {
- c.trie, _ = db.OpenStorageTrie(c.key, EmptyHash)
- c.setError(fmt.Errorf("can't create storage trie: %v", err))
+ il.trie, _ = db.OpenStorageTrie(il.key, EmptyHash)
+ il.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
- return c.trie
+ return il.trie
}
-func (self *itemListState) GetOrderAmount(db Database, orderId common.Hash) common.Hash {
- amount, exists := self.cachedStorage[orderId]
+func (il *itemListState) GetOrderAmount(db Database, orderId common.Hash) common.Hash {
+ amount, exists := il.cachedStorage[orderId]
if exists {
return amount
}
// Load from DB in case it is missing.
- enc, err := self.getTrie(db).TryGet(orderId[:])
+ enc, err := il.getTrie(db).TryGet(orderId[:])
if err != nil {
- self.setError(err)
+ il.setError(err)
return EmptyHash
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
- self.setError(err)
+ il.setError(err)
}
amount.SetBytes(content)
}
if (amount != common.Hash{}) {
- self.cachedStorage[orderId] = amount
+ il.cachedStorage[orderId] = amount
}
return amount
}
-func (self *itemListState) insertLendingItem(db Database, orderId common.Hash, amount common.Hash) {
- self.setOrderItem(orderId, amount)
- self.setError(self.getTrie(db).TryUpdate(orderId[:], amount[:]))
+func (il *itemListState) insertLendingItem(db Database, orderId common.Hash, amount common.Hash) {
+ il.setOrderItem(orderId, amount)
+ il.setError(il.getTrie(db).TryUpdate(orderId[:], amount[:]))
}
-func (self *itemListState) removeOrderItem(db Database, orderId common.Hash) {
- tr := self.getTrie(db)
- self.setError(tr.TryDelete(orderId[:]))
- self.setOrderItem(orderId, EmptyHash)
+func (il *itemListState) removeOrderItem(db Database, orderId common.Hash) {
+ tr := il.getTrie(db)
+ il.setError(tr.TryDelete(orderId[:]))
+ il.setOrderItem(orderId, EmptyHash)
}
-func (self *itemListState) setOrderItem(orderId common.Hash, amount common.Hash) {
- self.cachedStorage[orderId] = amount
- self.dirtyStorage[orderId] = amount
+func (il *itemListState) setOrderItem(orderId common.Hash, amount common.Hash) {
+ il.cachedStorage[orderId] = amount
+ il.dirtyStorage[orderId] = amount
- if self.onDirty != nil {
- self.onDirty(self.key)
- self.onDirty = nil
+ if il.onDirty != nil {
+ il.onDirty(il.key)
+ il.onDirty = nil
}
}
// updateAskTrie writes cached storage modifications into the object's storage trie.
-func (self *itemListState) updateTrie(db Database) Trie {
- tr := self.getTrie(db)
- for orderId, amount := range self.dirtyStorage {
- delete(self.dirtyStorage, orderId)
+func (il *itemListState) updateTrie(db Database) Trie {
+ tr := il.getTrie(db)
+ for orderId, amount := range il.dirtyStorage {
+ delete(il.dirtyStorage, orderId)
if amount == EmptyHash {
- self.setError(tr.TryDelete(orderId[:]))
+ il.setError(tr.TryDelete(orderId[:]))
continue
}
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(amount[:], "\x00"))
- self.setError(tr.TryUpdate(orderId[:], v))
+ il.setError(tr.TryUpdate(orderId[:], v))
}
return tr
}
// UpdateRoot sets the trie root to the current root tradeId of
-func (self *itemListState) updateRoot(db Database) error {
- self.updateTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (il *itemListState) updateRoot(db Database) error {
+ il.updateTrie(db)
+ if il.dbErr != nil {
+ return il.dbErr
}
- root, err := self.trie.Commit(nil)
+ root, err := il.trie.Commit(nil)
if err == nil {
- self.data.Root = root
+ il.data.Root = root
}
return err
}
-func (self *itemListState) deepCopy(db *LendingStateDB, onDirty func(price common.Hash)) *itemListState {
- stateOrderList := newItemListState(self.lendingBook, self.key, self.data, onDirty)
- if self.trie != nil {
- stateOrderList.trie = db.db.CopyTrie(self.trie)
+func (il *itemListState) deepCopy(db *LendingStateDB, onDirty func(price common.Hash)) *itemListState {
+ stateOrderList := newItemListState(il.lendingBook, il.key, il.data, onDirty)
+ if il.trie != nil {
+ stateOrderList.trie = db.db.CopyTrie(il.trie)
}
- for orderId, amount := range self.dirtyStorage {
+ for orderId, amount := range il.dirtyStorage {
stateOrderList.dirtyStorage[orderId] = amount
}
- for orderId, amount := range self.cachedStorage {
+ for orderId, amount := range il.cachedStorage {
stateOrderList.cachedStorage[orderId] = amount
}
return stateOrderList
}
-func (c *itemListState) AddVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Add(c.data.Volume, amount))
+func (il *itemListState) AddVolume(amount *big.Int) {
+ il.setVolume(new(big.Int).Add(il.data.Volume, amount))
}
-func (c *itemListState) subVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Sub(c.data.Volume, amount))
+func (il *itemListState) subVolume(amount *big.Int) {
+ il.setVolume(new(big.Int).Sub(il.data.Volume, amount))
}
-func (self *itemListState) setVolume(volume *big.Int) {
- self.data.Volume = volume
- if self.onDirty != nil {
- self.onDirty(self.key)
- self.onDirty = nil
+func (il *itemListState) setVolume(volume *big.Int) {
+ il.data.Volume = volume
+ if il.onDirty != nil {
+ il.onDirty(il.key)
+ il.onDirty = nil
}
}
-func (self *itemListState) Volume() *big.Int {
- return self.data.Volume
+func (il *itemListState) Volume() *big.Int {
+ return il.data.Volume
}
diff --git a/XDCxlending/lendingstate/state_lendingbook.go b/XDCxlending/lendingstate/state_lendingbook.go
index c063f23f7f..1cc3fda077 100644
--- a/XDCxlending/lendingstate/state_lendingbook.go
+++ b/XDCxlending/lendingstate/state_lendingbook.go
@@ -108,14 +108,14 @@ func newStateExchanges(db *LendingStateDB, hash common.Hash, data lendingObject,
}
// EncodeRLP implements rlp.Encoder.
-func (self *lendingExchangeState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, self.data)
+func (le *lendingExchangeState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, le.data)
}
// setError remembers the first non-nil error it is called with.
-func (self *lendingExchangeState) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (le *lendingExchangeState) setError(err error) {
+ if le.dbErr == nil {
+ le.dbErr = err
}
}
@@ -123,63 +123,64 @@ func (self *lendingExchangeState) setError(err error) {
Get Trie
*/
-func (self *lendingExchangeState) getLendingItemTrie(db Database) Trie {
- if self.lendingItemTrie == nil {
+func (le *lendingExchangeState) getLendingItemTrie(db Database) Trie {
+ if le.lendingItemTrie == nil {
var err error
- self.lendingItemTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LendingItemRoot)
+ le.lendingItemTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LendingItemRoot)
if err != nil {
- self.lendingItemTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash)
- self.setError(fmt.Errorf("can't create Lendings trie: %v", err))
+ le.lendingItemTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash)
+ le.setError(fmt.Errorf("can't create Lendings trie: %v", err))
}
}
- return self.lendingItemTrie
+ return le.lendingItemTrie
}
-func (self *lendingExchangeState) getLendingTradeTrie(db Database) Trie {
- if self.lendingTradeTrie == nil {
+func (le *lendingExchangeState) getLendingTradeTrie(db Database) Trie {
+ if le.lendingTradeTrie == nil {
var err error
- self.lendingTradeTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LendingTradeRoot)
+ le.lendingTradeTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LendingTradeRoot)
if err != nil {
- self.lendingTradeTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash)
- self.setError(fmt.Errorf("can't create Lendings trie: %v", err))
+ le.lendingTradeTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash)
+ le.setError(fmt.Errorf("can't create Lendings trie: %v", err))
}
}
- return self.lendingTradeTrie
-}
-func (self *lendingExchangeState) getInvestingTrie(db Database) Trie {
- if self.investingTrie == nil {
- var err error
- self.investingTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.InvestingRoot)
- if err != nil {
- self.investingTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash)
- self.setError(fmt.Errorf("can't create Lendings trie: %v", err))
- }
- }
- return self.investingTrie
+ return le.lendingTradeTrie
}
-func (self *lendingExchangeState) getBorrowingTrie(db Database) Trie {
- if self.borrowingTrie == nil {
+func (le *lendingExchangeState) getInvestingTrie(db Database) Trie {
+ if le.investingTrie == nil {
var err error
- self.borrowingTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.BorrowingRoot)
+ le.investingTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.InvestingRoot)
if err != nil {
- self.borrowingTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash)
- self.setError(fmt.Errorf("can't create bids trie: %v", err))
+ le.investingTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash)
+ le.setError(fmt.Errorf("can't create Lendings trie: %v", err))
}
}
- return self.borrowingTrie
+ return le.investingTrie
}
-func (self *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie {
- if self.liquidationTimeTrie == nil {
+func (le *lendingExchangeState) getBorrowingTrie(db Database) Trie {
+ if le.borrowingTrie == nil {
var err error
- self.liquidationTimeTrie, err = db.OpenStorageTrie(self.lendingBook, self.data.LiquidationTimeRoot)
+ le.borrowingTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.BorrowingRoot)
if err != nil {
- self.liquidationTimeTrie, _ = db.OpenStorageTrie(self.lendingBook, EmptyHash)
- self.setError(fmt.Errorf("can't create bids trie: %v", err))
+ le.borrowingTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash)
+ le.setError(fmt.Errorf("can't create bids trie: %v", err))
}
}
- return self.liquidationTimeTrie
+ return le.borrowingTrie
+}
+
+func (le *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie {
+ if le.liquidationTimeTrie == nil {
+ var err error
+ le.liquidationTimeTrie, err = db.OpenStorageTrie(le.lendingBook, le.data.LiquidationTimeRoot)
+ if err != nil {
+ le.liquidationTimeTrie, _ = db.OpenStorageTrie(le.lendingBook, EmptyHash)
+ le.setError(fmt.Errorf("can't create bids trie: %v", err))
+ }
+ }
+ return le.liquidationTimeTrie
}
/*
@@ -187,16 +188,16 @@ func (self *lendingExchangeState) getLiquidationTimeTrie(db Database) Trie {
Get State
*/
-func (self *lendingExchangeState) getBorrowingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) {
+func (le *lendingExchangeState) getBorrowingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) {
// Prefer 'live' objects.
- if obj := self.borrowingStates[rate]; obj != nil {
+ if obj := le.borrowingStates[rate]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getBorrowingTrie(db).TryGet(rate[:])
+ enc, err := le.getBorrowingTrie(db).TryGet(rate[:])
if len(enc) == 0 {
- self.setError(err)
+ le.setError(err)
return nil
}
var data itemList
@@ -205,21 +206,21 @@ func (self *lendingExchangeState) getBorrowingOrderList(db Database, rate common
return nil
}
// Insert into the live set.
- obj := newItemListState(self.lendingBook, rate, data, self.MarkBorrowingDirty)
- self.borrowingStates[rate] = obj
+ obj := newItemListState(le.lendingBook, rate, data, le.MarkBorrowingDirty)
+ le.borrowingStates[rate] = obj
return obj
}
-func (self *lendingExchangeState) getInvestingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) {
+func (le *lendingExchangeState) getInvestingOrderList(db Database, rate common.Hash) (stateOrderList *itemListState) {
// Prefer 'live' objects.
- if obj := self.investingStates[rate]; obj != nil {
+ if obj := le.investingStates[rate]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getInvestingTrie(db).TryGet(rate[:])
+ enc, err := le.getInvestingTrie(db).TryGet(rate[:])
if len(enc) == 0 {
- self.setError(err)
+ le.setError(err)
return nil
}
var data itemList
@@ -228,21 +229,21 @@ func (self *lendingExchangeState) getInvestingOrderList(db Database, rate common
return nil
}
// Insert into the live set.
- obj := newItemListState(self.lendingBook, rate, data, self.MarkInvestingDirty)
- self.investingStates[rate] = obj
+ obj := newItemListState(le.lendingBook, rate, data, le.MarkInvestingDirty)
+ le.investingStates[rate] = obj
return obj
}
-func (self *lendingExchangeState) getLiquidationTimeOrderList(db Database, time common.Hash) (stateObject *liquidationTimeState) {
+func (le *lendingExchangeState) getLiquidationTimeOrderList(db Database, time common.Hash) (stateObject *liquidationTimeState) {
// Prefer 'live' objects.
- if obj := self.liquidationTimeStates[time]; obj != nil {
+ if obj := le.liquidationTimeStates[time]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getLiquidationTimeTrie(db).TryGet(time[:])
+ enc, err := le.getLiquidationTimeTrie(db).TryGet(time[:])
if len(enc) == 0 {
- self.setError(err)
+ le.setError(err)
return nil
}
var data itemList
@@ -251,21 +252,21 @@ func (self *lendingExchangeState) getLiquidationTimeOrderList(db Database, time
return nil
}
// Insert into the live set.
- obj := newLiquidationTimeState(self.lendingBook, time, data, self.MarkLiquidationTimeDirty)
- self.liquidationTimeStates[time] = obj
+ obj := newLiquidationTimeState(le.lendingBook, time, data, le.MarkLiquidationTimeDirty)
+ le.liquidationTimeStates[time] = obj
return obj
}
-func (self *lendingExchangeState) getLendingItem(db Database, lendingId common.Hash) (stateObject *lendingItemState) {
+func (le *lendingExchangeState) getLendingItem(db Database, lendingId common.Hash) (stateObject *lendingItemState) {
// Prefer 'live' objects.
- if obj := self.lendingItemStates[lendingId]; obj != nil {
+ if obj := le.lendingItemStates[lendingId]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getLendingItemTrie(db).TryGet(lendingId[:])
+ enc, err := le.getLendingItemTrie(db).TryGet(lendingId[:])
if len(enc) == 0 {
- self.setError(err)
+ le.setError(err)
return nil
}
var data LendingItem
@@ -274,21 +275,21 @@ func (self *lendingExchangeState) getLendingItem(db Database, lendingId common.H
return nil
}
// Insert into the live set.
- obj := newLendinItemState(self.lendingBook, lendingId, data, self.MarkLendingItemDirty)
- self.lendingItemStates[lendingId] = obj
+ obj := newLendinItemState(le.lendingBook, lendingId, data, le.MarkLendingItemDirty)
+ le.lendingItemStates[lendingId] = obj
return obj
}
-func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Hash) (stateObject *lendingTradeState) {
+func (le *lendingExchangeState) getLendingTrade(db Database, tradeId common.Hash) (stateObject *lendingTradeState) {
// Prefer 'live' objects.
- if obj := self.lendingTradeStates[tradeId]; obj != nil {
+ if obj := le.lendingTradeStates[tradeId]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.getLendingTradeTrie(db).TryGet(tradeId[:])
+ enc, err := le.getLendingTradeTrie(db).TryGet(tradeId[:])
if len(enc) == 0 {
- self.setError(err)
+ le.setError(err)
return nil
}
var data LendingTrade
@@ -297,8 +298,8 @@ func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Ha
return nil
}
// Insert into the live set.
- obj := newLendingTradeState(self.lendingBook, tradeId, data, self.MarkLendingTradeDirty)
- self.lendingTradeStates[tradeId] = obj
+ obj := newLendingTradeState(le.lendingBook, tradeId, data, le.MarkLendingTradeDirty)
+ le.lendingTradeStates[tradeId] = obj
return obj
}
@@ -307,46 +308,47 @@ func (self *lendingExchangeState) getLendingTrade(db Database, tradeId common.Ha
Update Trie
*/
-func (self *lendingExchangeState) updateLendingTimeTrie(db Database) Trie {
- tr := self.getLendingItemTrie(db)
- for lendingId, lendingItem := range self.lendingItemStates {
- if _, isDirty := self.lendingItemStatesDirty[lendingId]; isDirty {
- delete(self.lendingItemStatesDirty, lendingId)
+func (le *lendingExchangeState) updateLendingTimeTrie(db Database) Trie {
+ tr := le.getLendingItemTrie(db)
+ for lendingId, lendingItem := range le.lendingItemStates {
+ if _, isDirty := le.lendingItemStatesDirty[lendingId]; isDirty {
+ delete(le.lendingItemStatesDirty, lendingId)
if lendingItem.empty() {
- self.setError(tr.TryDelete(lendingId[:]))
+ le.setError(tr.TryDelete(lendingId[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(lendingItem)
- self.setError(tr.TryUpdate(lendingId[:], v))
+ le.setError(tr.TryUpdate(lendingId[:], v))
}
}
return tr
}
-func (self *lendingExchangeState) updateLendingTradeTrie(db Database) Trie {
- tr := self.getLendingTradeTrie(db)
- for tradeId, lendingTradeItem := range self.lendingTradeStates {
- if _, isDirty := self.lendingTradeStatesDirty[tradeId]; isDirty {
- delete(self.lendingTradeStatesDirty, tradeId)
+func (le *lendingExchangeState) updateLendingTradeTrie(db Database) Trie {
+ tr := le.getLendingTradeTrie(db)
+ for tradeId, lendingTradeItem := range le.lendingTradeStates {
+ if _, isDirty := le.lendingTradeStatesDirty[tradeId]; isDirty {
+ delete(le.lendingTradeStatesDirty, tradeId)
if lendingTradeItem.empty() {
- self.setError(tr.TryDelete(tradeId[:]))
+ le.setError(tr.TryDelete(tradeId[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(lendingTradeItem)
- self.setError(tr.TryUpdate(tradeId[:], v))
+ le.setError(tr.TryUpdate(tradeId[:], v))
}
}
return tr
}
-func (self *lendingExchangeState) updateBorrowingTrie(db Database) Trie {
- tr := self.getBorrowingTrie(db)
- for rate, orderList := range self.borrowingStates {
- if _, isDirty := self.borrowingStatesDirty[rate]; isDirty {
- delete(self.borrowingStatesDirty, rate)
+
+func (le *lendingExchangeState) updateBorrowingTrie(db Database) Trie {
+ tr := le.getBorrowingTrie(db)
+ for rate, orderList := range le.borrowingStates {
+ if _, isDirty := le.borrowingStatesDirty[rate]; isDirty {
+ delete(le.borrowingStatesDirty, rate)
if orderList.empty() {
- self.setError(tr.TryDelete(rate[:]))
+ le.setError(tr.TryDelete(rate[:]))
continue
}
err := orderList.updateRoot(db)
@@ -355,19 +357,19 @@ func (self *lendingExchangeState) updateBorrowingTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
- self.setError(tr.TryUpdate(rate[:], v))
+ le.setError(tr.TryUpdate(rate[:], v))
}
}
return tr
}
-func (self *lendingExchangeState) updateInvestingTrie(db Database) Trie {
- tr := self.getInvestingTrie(db)
- for rate, orderList := range self.investingStates {
- if _, isDirty := self.investingStatesDirty[rate]; isDirty {
- delete(self.investingStatesDirty, rate)
+func (le *lendingExchangeState) updateInvestingTrie(db Database) Trie {
+ tr := le.getInvestingTrie(db)
+ for rate, orderList := range le.investingStates {
+ if _, isDirty := le.investingStatesDirty[rate]; isDirty {
+ delete(le.investingStatesDirty, rate)
if orderList.empty() {
- self.setError(tr.TryDelete(rate[:]))
+ le.setError(tr.TryDelete(rate[:]))
continue
}
err := orderList.updateRoot(db)
@@ -376,19 +378,19 @@ func (self *lendingExchangeState) updateInvestingTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(orderList)
- self.setError(tr.TryUpdate(rate[:], v))
+ le.setError(tr.TryUpdate(rate[:], v))
}
}
return tr
}
-func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie {
- tr := self.getLiquidationTimeTrie(db)
- for time, itemList := range self.liquidationTimeStates {
- if _, isDirty := self.liquidationTimestatesDirty[time]; isDirty {
- delete(self.liquidationTimestatesDirty, time)
+func (le *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie {
+ tr := le.getLiquidationTimeTrie(db)
+ for time, itemList := range le.liquidationTimeStates {
+ if _, isDirty := le.liquidationTimestatesDirty[time]; isDirty {
+ delete(le.liquidationTimestatesDirty, time)
if itemList.empty() {
- self.setError(tr.TryDelete(time[:]))
+ le.setError(tr.TryDelete(time[:]))
continue
}
err := itemList.updateRoot(db)
@@ -397,7 +399,7 @@ func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie {
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(itemList)
- self.setError(tr.TryUpdate(time[:], v))
+ le.setError(tr.TryUpdate(time[:], v))
}
}
return tr
@@ -407,69 +409,69 @@ func (self *lendingExchangeState) updateLiquidationTimeTrie(db Database) Trie {
Update Root
*/
-func (self *lendingExchangeState) updateOrderRoot(db Database) {
- self.updateLendingTimeTrie(db)
- self.data.LendingItemRoot = self.lendingItemTrie.Hash()
+func (le *lendingExchangeState) updateOrderRoot(db Database) {
+ le.updateLendingTimeTrie(db)
+ le.data.LendingItemRoot = le.lendingItemTrie.Hash()
}
-func (self *lendingExchangeState) updateInvestingRoot(db Database) error {
- self.updateInvestingTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) updateInvestingRoot(db Database) error {
+ le.updateInvestingTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- self.data.InvestingRoot = self.investingTrie.Hash()
+ le.data.InvestingRoot = le.investingTrie.Hash()
return nil
}
-func (self *lendingExchangeState) updateBorrowingRoot(db Database) {
- self.updateBorrowingTrie(db)
- self.data.BorrowingRoot = self.borrowingTrie.Hash()
+func (le *lendingExchangeState) updateBorrowingRoot(db Database) {
+ le.updateBorrowingTrie(db)
+ le.data.BorrowingRoot = le.borrowingTrie.Hash()
}
-func (self *lendingExchangeState) updateLiquidationTimeRoot(db Database) {
- self.updateLiquidationTimeTrie(db)
- self.data.LiquidationTimeRoot = self.liquidationTimeTrie.Hash()
+func (le *lendingExchangeState) updateLiquidationTimeRoot(db Database) {
+ le.updateLiquidationTimeTrie(db)
+ le.data.LiquidationTimeRoot = le.liquidationTimeTrie.Hash()
}
-func (self *lendingExchangeState) updateLendingTradeRoot(db Database) {
- self.updateLendingTradeTrie(db)
- self.data.LendingTradeRoot = self.lendingTradeTrie.Hash()
+func (le *lendingExchangeState) updateLendingTradeRoot(db Database) {
+ le.updateLendingTradeTrie(db)
+ le.data.LendingTradeRoot = le.lendingTradeTrie.Hash()
}
/**
Commit Trie
*/
-func (self *lendingExchangeState) CommitLendingItemTrie(db Database) error {
- self.updateLendingTimeTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) CommitLendingItemTrie(db Database) error {
+ le.updateLendingTimeTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- root, err := self.lendingItemTrie.Commit(nil)
+ root, err := le.lendingItemTrie.Commit(nil)
if err == nil {
- self.data.LendingItemRoot = root
+ le.data.LendingItemRoot = root
}
return err
}
-func (self *lendingExchangeState) CommitLendingTradeTrie(db Database) error {
- self.updateLendingTradeTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) CommitLendingTradeTrie(db Database) error {
+ le.updateLendingTradeTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- root, err := self.lendingTradeTrie.Commit(nil)
+ root, err := le.lendingTradeTrie.Commit(nil)
if err == nil {
- self.data.LendingTradeRoot = root
+ le.data.LendingTradeRoot = root
}
return err
}
-func (self *lendingExchangeState) CommitInvestingTrie(db Database) error {
- self.updateInvestingTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) CommitInvestingTrie(db Database) error {
+ le.updateInvestingTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- root, err := self.investingTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := le.investingTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -480,17 +482,17 @@ func (self *lendingExchangeState) CommitInvestingTrie(db Database) error {
return nil
})
if err == nil {
- self.data.InvestingRoot = root
+ le.data.InvestingRoot = root
}
return err
}
-func (self *lendingExchangeState) CommitBorrowingTrie(db Database) error {
- self.updateBorrowingTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) CommitBorrowingTrie(db Database) error {
+ le.updateBorrowingTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- root, err := self.borrowingTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := le.borrowingTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -501,17 +503,17 @@ func (self *lendingExchangeState) CommitBorrowingTrie(db Database) error {
return nil
})
if err == nil {
- self.data.BorrowingRoot = root
+ le.data.BorrowingRoot = root
}
return err
}
-func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error {
- self.updateLiquidationTimeTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (le *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error {
+ le.updateLiquidationTimeTrie(db)
+ if le.dbErr != nil {
+ return le.dbErr
}
- root, err := self.liquidationTimeTrie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err := le.liquidationTimeTrie.Commit(func(leaf []byte, parent common.Hash) error {
var orderList itemList
if err := rlp.DecodeBytes(leaf, &orderList); err != nil {
return nil
@@ -522,7 +524,7 @@ func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error {
return nil
})
if err == nil {
- self.data.LiquidationTimeRoot = root
+ le.data.LiquidationTimeRoot = root
}
return err
}
@@ -532,11 +534,11 @@ func (self *lendingExchangeState) CommitLiquidationTimeTrie(db Database) error {
Get Trie Data
*/
-func (self *lendingExchangeState) getBestInvestingInterest(db Database) common.Hash {
- trie := self.getInvestingTrie(db)
+func (le *lendingExchangeState) getBestInvestingInterest(db Database) common.Hash {
+ trie := le.getInvestingTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
- log.Error("Failed find best investing rate", "orderbook", self.lendingBook.Hex())
+ log.Error("Failed find best investing rate", "orderbook", le.lendingBook.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -545,23 +547,23 @@ func (self *lendingExchangeState) getBestInvestingInterest(db Database) common.H
}
// Insert into the live set.
interest := common.BytesToHash(encKey)
- if _, exist := self.investingStates[interest]; !exist {
+ if _, exist := le.investingStates[interest]; !exist {
var data itemList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best investing rate", "err", err)
return EmptyHash
}
- obj := newItemListState(self.lendingBook, interest, data, self.MarkInvestingDirty)
- self.investingStates[interest] = obj
+ obj := newItemListState(le.lendingBook, interest, data, le.MarkInvestingDirty)
+ le.investingStates[interest] = obj
}
return interest
}
-func (self *lendingExchangeState) getBestBorrowingInterest(db Database) common.Hash {
- trie := self.getBorrowingTrie(db)
+func (le *lendingExchangeState) getBestBorrowingInterest(db Database) common.Hash {
+ trie := le.getBorrowingTrie(db)
encKey, encValue, err := trie.TryGetBestRightKeyAndValue()
if err != nil {
- log.Error("Failed find best key bid trie ", "orderbook", self.lendingBook.Hex())
+ log.Error("Failed find best key bid trie ", "orderbook", le.lendingBook.Hex())
return EmptyHash
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -570,23 +572,23 @@ func (self *lendingExchangeState) getBestBorrowingInterest(db Database) common.H
}
// Insert into the live set.
interest := common.BytesToHash(encKey)
- if _, exist := self.borrowingStates[interest]; !exist {
+ if _, exist := le.borrowingStates[interest]; !exist {
var data itemList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get best bid trie", "err", err)
return EmptyHash
}
- obj := newItemListState(self.lendingBook, interest, data, self.MarkBorrowingDirty)
- self.borrowingStates[interest] = obj
+ obj := newItemListState(le.lendingBook, interest, data, le.MarkBorrowingDirty)
+ le.borrowingStates[interest] = obj
}
return interest
}
-func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common.Hash, *liquidationTimeState) {
- trie := self.getLiquidationTimeTrie(db)
+func (le *lendingExchangeState) getLowestLiquidationTime(db Database) (common.Hash, *liquidationTimeState) {
+ trie := le.getLiquidationTimeTrie(db)
encKey, encValue, err := trie.TryGetBestLeftKeyAndValue()
if err != nil {
- log.Error("Failed find best liquidation time trie ", "orderBook", self.lendingBook.Hex())
+ log.Error("Failed find best liquidation time trie ", "orderBook", le.lendingBook.Hex())
return EmptyHash, nil
}
if len(encKey) == 0 || len(encValue) == 0 {
@@ -594,15 +596,15 @@ func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common.
return EmptyHash, nil
}
price := common.BytesToHash(encKey)
- obj, exist := self.liquidationTimeStates[price]
+ obj, exist := le.liquidationTimeStates[price]
if !exist {
var data itemList
if err := rlp.DecodeBytes(encValue, &data); err != nil {
log.Error("Failed to decode state get liquidation time trie", "err", err)
return EmptyHash, nil
}
- obj = newLiquidationTimeState(self.lendingBook, price, data, self.MarkLiquidationTimeDirty)
- self.liquidationTimeStates[price] = obj
+ obj = newLiquidationTimeState(le.lendingBook, price, data, le.MarkLiquidationTimeDirty)
+ le.liquidationTimeStates[price] = obj
}
if obj.empty() {
return EmptyHash, nil
@@ -610,193 +612,194 @@ func (self *lendingExchangeState) getLowestLiquidationTime(db Database) (common.
return price, obj
}
-func (self *lendingExchangeState) deepCopy(db *LendingStateDB, onDirty func(hash common.Hash)) *lendingExchangeState {
- stateExchanges := newStateExchanges(db, self.lendingBook, self.data, onDirty)
- if self.investingTrie != nil {
- stateExchanges.investingTrie = db.db.CopyTrie(self.investingTrie)
+func (le *lendingExchangeState) deepCopy(db *LendingStateDB, onDirty func(hash common.Hash)) *lendingExchangeState {
+ stateExchanges := newStateExchanges(db, le.lendingBook, le.data, onDirty)
+ if le.investingTrie != nil {
+ stateExchanges.investingTrie = db.db.CopyTrie(le.investingTrie)
}
- if self.borrowingTrie != nil {
- stateExchanges.borrowingTrie = db.db.CopyTrie(self.borrowingTrie)
+ if le.borrowingTrie != nil {
+ stateExchanges.borrowingTrie = db.db.CopyTrie(le.borrowingTrie)
}
- if self.lendingItemTrie != nil {
- stateExchanges.lendingItemTrie = db.db.CopyTrie(self.lendingItemTrie)
+ if le.lendingItemTrie != nil {
+ stateExchanges.lendingItemTrie = db.db.CopyTrie(le.lendingItemTrie)
}
- for key, value := range self.borrowingStates {
- stateExchanges.borrowingStates[key] = value.deepCopy(db, self.MarkBorrowingDirty)
+ for key, value := range le.borrowingStates {
+ stateExchanges.borrowingStates[key] = value.deepCopy(db, le.MarkBorrowingDirty)
}
- for key := range self.borrowingStatesDirty {
+ for key := range le.borrowingStatesDirty {
stateExchanges.borrowingStatesDirty[key] = struct{}{}
}
- for key, value := range self.investingStates {
- stateExchanges.investingStates[key] = value.deepCopy(db, self.MarkInvestingDirty)
+ for key, value := range le.investingStates {
+ stateExchanges.investingStates[key] = value.deepCopy(db, le.MarkInvestingDirty)
}
- for key := range self.investingStatesDirty {
+ for key := range le.investingStatesDirty {
stateExchanges.investingStatesDirty[key] = struct{}{}
}
- for key, value := range self.lendingItemStates {
- stateExchanges.lendingItemStates[key] = value.deepCopy(self.MarkLendingItemDirty)
+ for key, value := range le.lendingItemStates {
+ stateExchanges.lendingItemStates[key] = value.deepCopy(le.MarkLendingItemDirty)
}
- for orderId := range self.lendingItemStatesDirty {
+ for orderId := range le.lendingItemStatesDirty {
stateExchanges.lendingItemStatesDirty[orderId] = struct{}{}
}
- for key, value := range self.lendingTradeStates {
- stateExchanges.lendingTradeStates[key] = value.deepCopy(self.MarkLendingTradeDirty)
+ for key, value := range le.lendingTradeStates {
+ stateExchanges.lendingTradeStates[key] = value.deepCopy(le.MarkLendingTradeDirty)
}
- for orderId := range self.lendingTradeStatesDirty {
+ for orderId := range le.lendingTradeStatesDirty {
stateExchanges.lendingTradeStatesDirty[orderId] = struct{}{}
}
- for time, orderList := range self.liquidationTimeStates {
- stateExchanges.liquidationTimeStates[time] = orderList.deepCopy(db, self.MarkLiquidationTimeDirty)
+ for time, orderList := range le.liquidationTimeStates {
+ stateExchanges.liquidationTimeStates[time] = orderList.deepCopy(db, le.MarkLiquidationTimeDirty)
}
- for time := range self.liquidationTimestatesDirty {
+ for time := range le.liquidationTimestatesDirty {
stateExchanges.liquidationTimestatesDirty[time] = struct{}{}
}
return stateExchanges
}
// Returns the address of the contract/tradeId
-func (self *lendingExchangeState) Hash() common.Hash {
- return self.lendingBook
+func (le *lendingExchangeState) Hash() common.Hash {
+ return le.lendingBook
}
-func (self *lendingExchangeState) setNonce(nonce uint64) {
- self.data.Nonce = nonce
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) setNonce(nonce uint64) {
+ le.data.Nonce = nonce
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) Nonce() uint64 {
- return self.data.Nonce
+func (le *lendingExchangeState) Nonce() uint64 {
+ return le.data.Nonce
}
-func (self *lendingExchangeState) setTradeNonce(nonce uint64) {
- self.data.TradeNonce = nonce
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) setTradeNonce(nonce uint64) {
+ le.data.TradeNonce = nonce
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) TradeNonce() uint64 {
- return self.data.TradeNonce
+
+func (le *lendingExchangeState) TradeNonce() uint64 {
+ return le.data.TradeNonce
}
-func (self *lendingExchangeState) removeInvestingOrderList(db Database, stateOrderList *itemListState) {
- self.setError(self.investingTrie.TryDelete(stateOrderList.key[:]))
+func (le *lendingExchangeState) removeInvestingOrderList(db Database, stateOrderList *itemListState) {
+ le.setError(le.investingTrie.TryDelete(stateOrderList.key[:]))
}
-func (self *lendingExchangeState) removeBorrowingOrderList(db Database, stateOrderList *itemListState) {
- self.setError(self.borrowingTrie.TryDelete(stateOrderList.key[:]))
+func (le *lendingExchangeState) removeBorrowingOrderList(db Database, stateOrderList *itemListState) {
+ le.setError(le.borrowingTrie.TryDelete(stateOrderList.key[:]))
}
-func (self *lendingExchangeState) createInvestingOrderList(db Database, price common.Hash) (newobj *itemListState) {
- newobj = newItemListState(self.lendingBook, price, itemList{Volume: Zero}, self.MarkInvestingDirty)
- self.investingStates[price] = newobj
- self.investingStatesDirty[price] = struct{}{}
+func (le *lendingExchangeState) createInvestingOrderList(db Database, price common.Hash) (newobj *itemListState) {
+ newobj = newItemListState(le.lendingBook, price, itemList{Volume: Zero}, le.MarkInvestingDirty)
+ le.investingStates[price] = newobj
+ le.investingStatesDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
- self.setError(self.getInvestingTrie(db).TryUpdate(price[:], data))
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+ le.setError(le.getInvestingTrie(db).TryUpdate(price[:], data))
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
return newobj
}
-func (self *lendingExchangeState) MarkBorrowingDirty(price common.Hash) {
- self.borrowingStatesDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) MarkBorrowingDirty(price common.Hash) {
+ le.borrowingStatesDirty[price] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) MarkInvestingDirty(price common.Hash) {
- self.investingStatesDirty[price] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) MarkInvestingDirty(price common.Hash) {
+ le.investingStatesDirty[price] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) MarkLendingItemDirty(lending common.Hash) {
- self.lendingItemStatesDirty[lending] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) MarkLendingItemDirty(lending common.Hash) {
+ le.lendingItemStatesDirty[lending] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) MarkLendingTradeDirty(tradeId common.Hash) {
- self.lendingTradeStatesDirty[tradeId] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) MarkLendingTradeDirty(tradeId common.Hash) {
+ le.lendingTradeStatesDirty[tradeId] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) MarkLiquidationTimeDirty(orderId common.Hash) {
- self.liquidationTimestatesDirty[orderId] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+func (le *lendingExchangeState) MarkLiquidationTimeDirty(orderId common.Hash) {
+ le.liquidationTimestatesDirty[orderId] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
}
-func (self *lendingExchangeState) createBorrowingOrderList(db Database, price common.Hash) (newobj *itemListState) {
- newobj = newItemListState(self.lendingBook, price, itemList{Volume: Zero}, self.MarkBorrowingDirty)
- self.borrowingStates[price] = newobj
- self.borrowingStatesDirty[price] = struct{}{}
+func (le *lendingExchangeState) createBorrowingOrderList(db Database, price common.Hash) (newobj *itemListState) {
+ newobj = newItemListState(le.lendingBook, price, itemList{Volume: Zero}, le.MarkBorrowingDirty)
+ le.borrowingStates[price] = newobj
+ le.borrowingStatesDirty[price] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode order list object at %x: %v", price[:], err))
}
- self.setError(self.getBorrowingTrie(db).TryUpdate(price[:], data))
- if self.onDirty != nil {
- self.onDirty(self.Hash())
- self.onDirty = nil
+ le.setError(le.getBorrowingTrie(db).TryUpdate(price[:], data))
+ if le.onDirty != nil {
+ le.onDirty(le.Hash())
+ le.onDirty = nil
}
return newobj
}
-func (self *lendingExchangeState) createLendingItem(db Database, orderId common.Hash, order LendingItem) (newobj *lendingItemState) {
- newobj = newLendinItemState(self.lendingBook, orderId, order, self.MarkLendingItemDirty)
+func (le *lendingExchangeState) createLendingItem(db Database, orderId common.Hash, order LendingItem) (newobj *lendingItemState) {
+ newobj = newLendinItemState(le.lendingBook, orderId, order, le.MarkLendingItemDirty)
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId))
- self.lendingItemStates[orderIdHash] = newobj
- self.lendingItemStatesDirty[orderIdHash] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+ le.lendingItemStates[orderIdHash] = newobj
+ le.lendingItemStatesDirty[orderIdHash] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.lendingBook)
+ le.onDirty = nil
}
return newobj
}
-func (self *lendingExchangeState) createLiquidationTime(db Database, time common.Hash) (newobj *liquidationTimeState) {
- newobj = newLiquidationTimeState(time, self.lendingBook, itemList{Volume: Zero}, self.MarkLiquidationTimeDirty)
- self.liquidationTimeStates[time] = newobj
- self.liquidationTimestatesDirty[time] = struct{}{}
+func (le *lendingExchangeState) createLiquidationTime(db Database, time common.Hash) (newobj *liquidationTimeState) {
+ newobj = newLiquidationTimeState(time, le.lendingBook, itemList{Volume: Zero}, le.MarkLiquidationTimeDirty)
+ le.liquidationTimeStates[time] = newobj
+ le.liquidationTimestatesDirty[time] = struct{}{}
data, err := rlp.EncodeToBytes(newobj)
if err != nil {
panic(fmt.Errorf("can't encode liquidation time at %x: %v", time[:], err))
}
- self.setError(self.getLiquidationTimeTrie(db).TryUpdate(time[:], data))
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+ le.setError(le.getLiquidationTimeTrie(db).TryUpdate(time[:], data))
+ if le.onDirty != nil {
+ le.onDirty(le.lendingBook)
+ le.onDirty = nil
}
return newobj
}
-func (self *lendingExchangeState) insertLendingTrade(tradeId common.Hash, order LendingTrade) (newobj *lendingTradeState) {
- newobj = newLendingTradeState(self.lendingBook, tradeId, order, self.MarkLendingTradeDirty)
- self.lendingTradeStates[tradeId] = newobj
- self.lendingTradeStatesDirty[tradeId] = struct{}{}
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+func (le *lendingExchangeState) insertLendingTrade(tradeId common.Hash, order LendingTrade) (newobj *lendingTradeState) {
+ newobj = newLendingTradeState(le.lendingBook, tradeId, order, le.MarkLendingTradeDirty)
+ le.lendingTradeStates[tradeId] = newobj
+ le.lendingTradeStatesDirty[tradeId] = struct{}{}
+ if le.onDirty != nil {
+ le.onDirty(le.lendingBook)
+ le.onDirty = nil
}
return newobj
}
diff --git a/XDCxlending/lendingstate/state_lendingitem.go b/XDCxlending/lendingstate/state_lendingitem.go
index 10d95ef68b..ef9d193653 100644
--- a/XDCxlending/lendingstate/state_lendingitem.go
+++ b/XDCxlending/lendingstate/state_lendingitem.go
@@ -31,8 +31,8 @@ type lendingItemState struct {
onDirty func(orderId common.Hash) // Callback method to mark a state object newly dirty
}
-func (s *lendingItemState) empty() bool {
- return s.data.Quantity == nil || s.data.Quantity.Cmp(Zero) == 0
+func (li *lendingItemState) empty() bool {
+ return li.data.Quantity == nil || li.data.Quantity.Cmp(Zero) == 0
}
func newLendinItemState(orderBook common.Hash, orderId common.Hash, data LendingItem, onDirty func(orderId common.Hash)) *lendingItemState {
@@ -45,23 +45,23 @@ func newLendinItemState(orderBook common.Hash, orderId common.Hash, data Lending
}
// EncodeRLP implements rlp.Encoder.
-func (c *lendingItemState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (li *lendingItemState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, li.data)
}
-func (self *lendingItemState) deepCopy(onDirty func(orderId common.Hash)) *lendingItemState {
- stateOrderList := newLendinItemState(self.orderBook, self.orderId, self.data, onDirty)
+func (li *lendingItemState) deepCopy(onDirty func(orderId common.Hash)) *lendingItemState {
+ stateOrderList := newLendinItemState(li.orderBook, li.orderId, li.data, onDirty)
return stateOrderList
}
-func (self *lendingItemState) setVolume(volume *big.Int) {
- self.data.Quantity = volume
- if self.onDirty != nil {
- self.onDirty(self.orderId)
- self.onDirty = nil
+func (li *lendingItemState) setVolume(volume *big.Int) {
+ li.data.Quantity = volume
+ if li.onDirty != nil {
+ li.onDirty(li.orderId)
+ li.onDirty = nil
}
}
-func (self *lendingItemState) Quantity() *big.Int {
- return self.data.Quantity
+func (li *lendingItemState) Quantity() *big.Int {
+ return li.data.Quantity
}
diff --git a/XDCxlending/lendingstate/state_lendingtrade.go b/XDCxlending/lendingstate/state_lendingtrade.go
index 893a6063eb..ab3ce84e2d 100644
--- a/XDCxlending/lendingstate/state_lendingtrade.go
+++ b/XDCxlending/lendingstate/state_lendingtrade.go
@@ -17,10 +17,11 @@
package lendingstate
import (
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/rlp"
"io"
"math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
)
type lendingTradeState struct {
@@ -30,8 +31,8 @@ type lendingTradeState struct {
onDirty func(orderId common.Hash) // Callback method to mark a state object newly dirty
}
-func (s *lendingTradeState) empty() bool {
- return s.data.Amount.Sign() == 0
+func (lt *lendingTradeState) empty() bool {
+ return lt.data.Amount.Sign() == 0
}
func newLendingTradeState(orderBook common.Hash, tradeId common.Hash, data LendingTrade, onDirty func(orderId common.Hash)) *lendingTradeState {
@@ -44,35 +45,35 @@ func newLendingTradeState(orderBook common.Hash, tradeId common.Hash, data Lendi
}
// EncodeRLP implements rlp.Encoder.
-func (c *lendingTradeState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, c.data)
+func (lt *lendingTradeState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, lt.data)
}
-func (self *lendingTradeState) deepCopy(onDirty func(orderId common.Hash)) *lendingTradeState {
- stateOrderList := newLendingTradeState(self.orderBook, self.tradeId, self.data, onDirty)
+func (lt *lendingTradeState) deepCopy(onDirty func(orderId common.Hash)) *lendingTradeState {
+ stateOrderList := newLendingTradeState(lt.orderBook, lt.tradeId, lt.data, onDirty)
return stateOrderList
}
-func (self *lendingTradeState) SetCollateralLockedAmount(amount *big.Int) {
- self.data.CollateralLockedAmount = amount
- if self.onDirty != nil {
- self.onDirty(self.tradeId)
- self.onDirty = nil
+func (lt *lendingTradeState) SetCollateralLockedAmount(amount *big.Int) {
+ lt.data.CollateralLockedAmount = amount
+ if lt.onDirty != nil {
+ lt.onDirty(lt.tradeId)
+ lt.onDirty = nil
}
}
-func (self *lendingTradeState) SetLiquidationPrice(price *big.Int) {
- self.data.LiquidationPrice = price
- if self.onDirty != nil {
- self.onDirty(self.tradeId)
- self.onDirty = nil
+func (lt *lendingTradeState) SetLiquidationPrice(price *big.Int) {
+ lt.data.LiquidationPrice = price
+ if lt.onDirty != nil {
+ lt.onDirty(lt.tradeId)
+ lt.onDirty = nil
}
}
-func (self *lendingTradeState) SetAmount(amount *big.Int) {
- self.data.Amount = amount
- if self.onDirty != nil {
- self.onDirty(self.tradeId)
- self.onDirty = nil
+func (lt *lendingTradeState) SetAmount(amount *big.Int) {
+ lt.data.Amount = amount
+ if lt.onDirty != nil {
+ lt.onDirty(lt.tradeId)
+ lt.onDirty = nil
}
}
diff --git a/XDCxlending/lendingstate/state_liquidationtime.go b/XDCxlending/lendingstate/state_liquidationtime.go
index 3661b46287..558650989a 100644
--- a/XDCxlending/lendingstate/state_liquidationtime.go
+++ b/XDCxlending/lendingstate/state_liquidationtime.go
@@ -19,11 +19,12 @@ package lendingstate
import (
"bytes"
"fmt"
+ "io"
+ "math/big"
+
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie"
- "io"
- "math/big"
)
type liquidationTimeState struct {
@@ -47,8 +48,8 @@ type liquidationTimeState struct {
onDirty func(time common.Hash) // Callback method to mark a state object newly dirty
}
-func (s *liquidationTimeState) empty() bool {
- return s.data.Volume == nil || s.data.Volume.Sign() == 0
+func (lt *liquidationTimeState) empty() bool {
+ return lt.data.Volume == nil || lt.data.Volume.Sign() == 0
}
func newLiquidationTimeState(time common.Hash, lendingBook common.Hash, data itemList, onDirty func(time common.Hash)) *liquidationTimeState {
@@ -62,59 +63,59 @@ func newLiquidationTimeState(time common.Hash, lendingBook common.Hash, data ite
}
}
-func (self *liquidationTimeState) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, self.data)
+func (lt *liquidationTimeState) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, lt.data)
}
-func (self *liquidationTimeState) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (lt *liquidationTimeState) setError(err error) {
+ if lt.dbErr == nil {
+ lt.dbErr = err
}
}
-func (self *liquidationTimeState) getTrie(db Database) Trie {
- if self.trie == nil {
+func (lt *liquidationTimeState) getTrie(db Database) Trie {
+ if lt.trie == nil {
var err error
- self.trie, err = db.OpenStorageTrie(self.lendingBook, self.data.Root)
+ lt.trie, err = db.OpenStorageTrie(lt.lendingBook, lt.data.Root)
if err != nil {
- self.trie, _ = db.OpenStorageTrie(self.time, EmptyHash)
- self.setError(fmt.Errorf("can't create storage trie: %v", err))
+ lt.trie, _ = db.OpenStorageTrie(lt.time, EmptyHash)
+ lt.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
- return self.trie
+ return lt.trie
}
-func (self *liquidationTimeState) Exist(db Database, tradeId common.Hash) bool {
- amount, exists := self.cachedStorage[tradeId]
+func (lt *liquidationTimeState) Exist(db Database, tradeId common.Hash) bool {
+ amount, exists := lt.cachedStorage[tradeId]
if exists {
return true
}
// Load from DB in case it is missing.
- enc, err := self.getTrie(db).TryGet(tradeId[:])
+ enc, err := lt.getTrie(db).TryGet(tradeId[:])
if err != nil {
- self.setError(err)
+ lt.setError(err)
return false
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
- self.setError(err)
+ lt.setError(err)
}
amount.SetBytes(content)
}
if (amount != common.Hash{}) {
- self.cachedStorage[tradeId] = amount
+ lt.cachedStorage[tradeId] = amount
}
return true
}
-func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash {
+func (lt *liquidationTimeState) getAllTradeIds(db Database) []common.Hash {
tradeIds := []common.Hash{}
- lendingBookTrie := self.getTrie(db)
+ lendingBookTrie := lt.getTrie(db)
if lendingBookTrie == nil {
return tradeIds
}
- for id, value := range self.cachedStorage {
+ for id, value := range lt.cachedStorage {
if !common.EmptyHash(value) {
tradeIds = append(tradeIds, id)
}
@@ -122,7 +123,7 @@ func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash {
orderListIt := trie.NewIterator(lendingBookTrie.NodeIterator(nil))
for orderListIt.Next() {
id := common.BytesToHash(orderListIt.Key)
- if _, exist := self.cachedStorage[id]; exist {
+ if _, exist := lt.cachedStorage[id]; exist {
continue
}
tradeIds = append(tradeIds, id)
@@ -130,83 +131,83 @@ func (self *liquidationTimeState) getAllTradeIds(db Database) []common.Hash {
return tradeIds
}
-func (self *liquidationTimeState) insertTradeId(db Database, tradeId common.Hash) {
- self.setTradeId(tradeId, tradeId)
- self.setError(self.getTrie(db).TryUpdate(tradeId[:], tradeId[:]))
+func (lt *liquidationTimeState) insertTradeId(db Database, tradeId common.Hash) {
+ lt.setTradeId(tradeId, tradeId)
+ lt.setError(lt.getTrie(db).TryUpdate(tradeId[:], tradeId[:]))
}
-func (self *liquidationTimeState) removeTradeId(db Database, tradeId common.Hash) {
- tr := self.getTrie(db)
- self.setError(tr.TryDelete(tradeId[:]))
- self.setTradeId(tradeId, EmptyHash)
+func (lt *liquidationTimeState) removeTradeId(db Database, tradeId common.Hash) {
+ tr := lt.getTrie(db)
+ lt.setError(tr.TryDelete(tradeId[:]))
+ lt.setTradeId(tradeId, EmptyHash)
}
-func (self *liquidationTimeState) setTradeId(tradeId common.Hash, value common.Hash) {
- self.cachedStorage[tradeId] = value
- self.dirtyStorage[tradeId] = value
+func (lt *liquidationTimeState) setTradeId(tradeId common.Hash, value common.Hash) {
+ lt.cachedStorage[tradeId] = value
+ lt.dirtyStorage[tradeId] = value
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+ if lt.onDirty != nil {
+ lt.onDirty(lt.lendingBook)
+ lt.onDirty = nil
}
}
-func (self *liquidationTimeState) updateTrie(db Database) Trie {
- tr := self.getTrie(db)
- for key, value := range self.dirtyStorage {
- delete(self.dirtyStorage, key)
+func (lt *liquidationTimeState) updateTrie(db Database) Trie {
+ tr := lt.getTrie(db)
+ for key, value := range lt.dirtyStorage {
+ delete(lt.dirtyStorage, key)
if value == EmptyHash {
- self.setError(tr.TryDelete(key[:]))
+ lt.setError(tr.TryDelete(key[:]))
continue
}
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
- self.setError(tr.TryUpdate(key[:], v))
+ lt.setError(tr.TryUpdate(key[:], v))
}
return tr
}
-func (self *liquidationTimeState) updateRoot(db Database) error {
- self.updateTrie(db)
- if self.dbErr != nil {
- return self.dbErr
+func (lt *liquidationTimeState) updateRoot(db Database) error {
+ lt.updateTrie(db)
+ if lt.dbErr != nil {
+ return lt.dbErr
}
- root, err := self.trie.Commit(nil)
+ root, err := lt.trie.Commit(nil)
if err == nil {
- self.data.Root = root
+ lt.data.Root = root
}
return err
}
-func (self *liquidationTimeState) deepCopy(db *LendingStateDB, onDirty func(time common.Hash)) *liquidationTimeState {
- stateLendingBook := newLiquidationTimeState(self.lendingBook, self.time, self.data, onDirty)
- if self.trie != nil {
- stateLendingBook.trie = db.db.CopyTrie(self.trie)
+func (lt *liquidationTimeState) deepCopy(db *LendingStateDB, onDirty func(time common.Hash)) *liquidationTimeState {
+ stateLendingBook := newLiquidationTimeState(lt.lendingBook, lt.time, lt.data, onDirty)
+ if lt.trie != nil {
+ stateLendingBook.trie = db.db.CopyTrie(lt.trie)
}
- for key, value := range self.dirtyStorage {
+ for key, value := range lt.dirtyStorage {
stateLendingBook.dirtyStorage[key] = value
}
- for key, value := range self.cachedStorage {
+ for key, value := range lt.cachedStorage {
stateLendingBook.cachedStorage[key] = value
}
return stateLendingBook
}
-func (c *liquidationTimeState) AddVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Add(c.data.Volume, amount))
+func (lt *liquidationTimeState) AddVolume(amount *big.Int) {
+ lt.setVolume(new(big.Int).Add(lt.data.Volume, amount))
}
-func (c *liquidationTimeState) subVolume(amount *big.Int) {
- c.setVolume(new(big.Int).Sub(c.data.Volume, amount))
+func (lt *liquidationTimeState) subVolume(amount *big.Int) {
+ lt.setVolume(new(big.Int).Sub(lt.data.Volume, amount))
}
-func (self *liquidationTimeState) setVolume(volume *big.Int) {
- self.data.Volume = volume
- if self.onDirty != nil {
- self.onDirty(self.lendingBook)
- self.onDirty = nil
+func (lt *liquidationTimeState) setVolume(volume *big.Int) {
+ lt.data.Volume = volume
+ if lt.onDirty != nil {
+ lt.onDirty(lt.lendingBook)
+ lt.onDirty = nil
}
}
-func (self *liquidationTimeState) Volume() *big.Int {
- return self.data.Volume
+func (lt *liquidationTimeState) Volume() *big.Int {
+ return lt.data.Volume
}
diff --git a/XDCxlending/lendingstate/statedb.go b/XDCxlending/lendingstate/statedb.go
index f514f61ba0..e5a5cbf6ed 100644
--- a/XDCxlending/lendingstate/statedb.go
+++ b/XDCxlending/lendingstate/statedb.go
@@ -73,39 +73,39 @@ func New(root common.Hash, db Database) (*LendingStateDB, error) {
}
// setError remembers the first non-nil error it is called with.
-func (self *LendingStateDB) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (ls *LendingStateDB) setError(err error) {
+ if ls.dbErr == nil {
+ ls.dbErr = err
}
}
-func (self *LendingStateDB) Error() error {
- return self.dbErr
+func (ls *LendingStateDB) Error() error {
+ return ls.dbErr
}
// Exist reports whether the given tradeId address exists in the state.
// Notably this also returns true for suicided lenddinges.
-func (self *LendingStateDB) Exist(addr common.Hash) bool {
- return self.getLendingExchange(addr) != nil
+func (ls *LendingStateDB) Exist(addr common.Hash) bool {
+ return ls.getLendingExchange(addr) != nil
}
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
-func (self *LendingStateDB) Empty(addr common.Hash) bool {
- so := self.getLendingExchange(addr)
+func (ls *LendingStateDB) Empty(addr common.Hash) bool {
+ so := ls.getLendingExchange(addr)
return so == nil || so.empty()
}
-func (self *LendingStateDB) GetNonce(addr common.Hash) uint64 {
- stateObject := self.getLendingExchange(addr)
+func (ls *LendingStateDB) GetNonce(addr common.Hash) uint64 {
+ stateObject := ls.getLendingExchange(addr)
if stateObject != nil {
return stateObject.Nonce()
}
return 0
}
-func (self *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 {
- stateObject := self.getLendingExchange(addr)
+func (ls *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 {
+ stateObject := ls.getLendingExchange(addr)
if stateObject != nil {
return stateObject.TradeNonce()
}
@@ -113,14 +113,14 @@ func (self *LendingStateDB) GetTradeNonce(addr common.Hash) uint64 {
}
// Database retrieves the low level database supporting the lower level trie ops.
-func (self *LendingStateDB) Database() Database {
- return self.db
+func (ls *LendingStateDB) Database() Database {
+ return ls.db
}
-func (self *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) {
- stateObject := self.GetOrNewLendingExchangeObject(addr)
+func (ls *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) {
+ stateObject := ls.GetOrNewLendingExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, nonceChange{
+ ls.journal = append(ls.journal, nonceChange{
hash: addr,
prev: stateObject.Nonce(),
})
@@ -128,10 +128,10 @@ func (self *LendingStateDB) SetNonce(addr common.Hash, nonce uint64) {
}
}
-func (self *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) {
- stateObject := self.GetOrNewLendingExchangeObject(addr)
+func (ls *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) {
+ stateObject := ls.GetOrNewLendingExchangeObject(addr)
if stateObject != nil {
- self.journal = append(self.journal, tradeNonceChange{
+ ls.journal = append(ls.journal, tradeNonceChange{
hash: addr,
prev: stateObject.TradeNonce(),
})
@@ -139,45 +139,45 @@ func (self *LendingStateDB) SetTradeNonce(addr common.Hash, nonce uint64) {
}
}
-func (self *LendingStateDB) InsertLendingItem(orderBook common.Hash, orderId common.Hash, order LendingItem) {
+func (ls *LendingStateDB) InsertLendingItem(orderBook common.Hash, orderId common.Hash, order LendingItem) {
interestHash := common.BigToHash(order.Interest)
- stateExchange := self.getLendingExchange(orderBook)
+ stateExchange := ls.getLendingExchange(orderBook)
if stateExchange == nil {
- stateExchange = self.createLendingExchangeObject(orderBook)
+ stateExchange = ls.createLendingExchangeObject(orderBook)
}
var stateOrderList *itemListState
switch order.Side {
case Investing:
- stateOrderList = stateExchange.getInvestingOrderList(self.db, interestHash)
+ stateOrderList = stateExchange.getInvestingOrderList(ls.db, interestHash)
if stateOrderList == nil {
- stateOrderList = stateExchange.createInvestingOrderList(self.db, interestHash)
+ stateOrderList = stateExchange.createInvestingOrderList(ls.db, interestHash)
}
case Borrowing:
- stateOrderList = stateExchange.getBorrowingOrderList(self.db, interestHash)
+ stateOrderList = stateExchange.getBorrowingOrderList(ls.db, interestHash)
if stateOrderList == nil {
- stateOrderList = stateExchange.createBorrowingOrderList(self.db, interestHash)
+ stateOrderList = stateExchange.createBorrowingOrderList(ls.db, interestHash)
}
default:
return
}
- self.journal = append(self.journal, insertOrder{
+ ls.journal = append(ls.journal, insertOrder{
orderBook: orderBook,
orderId: orderId,
order: &order,
})
- stateExchange.createLendingItem(self.db, orderId, order)
- stateOrderList.insertLendingItem(self.db, orderId, common.BigToHash(order.Quantity))
+ stateExchange.createLendingItem(ls.db, orderId, order)
+ stateOrderList.insertLendingItem(ls.db, orderId, common.BigToHash(order.Quantity))
stateOrderList.AddVolume(order.Quantity)
}
-func (self *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uint64, order LendingTrade) {
+func (ls *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uint64, order LendingTrade) {
tradeIdHash := common.Uint64ToHash(tradeId)
- stateExchange := self.getLendingExchange(orderBook)
+ stateExchange := ls.getLendingExchange(orderBook)
if stateExchange == nil {
- stateExchange = self.createLendingExchangeObject(orderBook)
+ stateExchange = ls.createLendingExchangeObject(orderBook)
}
- prvTrade := self.GetLendingTrade(orderBook, tradeIdHash)
- self.journal = append(self.journal, insertTrading{
+ prvTrade := ls.GetLendingTrade(orderBook, tradeIdHash)
+ ls.journal = append(ls.journal, insertTrading{
orderBook: orderBook,
tradeId: tradeId,
prvTrade: &prvTrade,
@@ -185,88 +185,90 @@ func (self *LendingStateDB) InsertTradingItem(orderBook common.Hash, tradeId uin
stateExchange.insertLendingTrade(tradeIdHash, order)
}
-func (self *LendingStateDB) UpdateLiquidationPrice(orderBook common.Hash, tradeId uint64, price *big.Int) {
+func (ls *LendingStateDB) UpdateLiquidationPrice(orderBook common.Hash, tradeId uint64, price *big.Int) {
tradeIdHash := common.Uint64ToHash(tradeId)
- stateExchange := self.getLendingExchange(orderBook)
+ stateExchange := ls.getLendingExchange(orderBook)
if stateExchange == nil {
- stateExchange = self.createLendingExchangeObject(orderBook)
+ stateExchange = ls.createLendingExchangeObject(orderBook)
}
- stateLendingTrade := stateExchange.getLendingTrade(self.db, tradeIdHash)
- self.journal = append(self.journal, liquidationPriceChange{
+ stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash)
+ ls.journal = append(ls.journal, liquidationPriceChange{
orderBook: orderBook,
tradeId: tradeIdHash,
prev: stateLendingTrade.data.LiquidationPrice,
})
stateLendingTrade.SetLiquidationPrice(price)
}
-func (self *LendingStateDB) UpdateCollateralLockedAmount(orderBook common.Hash, tradeId uint64, amount *big.Int) {
+
+func (ls *LendingStateDB) UpdateCollateralLockedAmount(orderBook common.Hash, tradeId uint64, amount *big.Int) {
tradeIdHash := common.Uint64ToHash(tradeId)
- stateExchange := self.getLendingExchange(orderBook)
+ stateExchange := ls.getLendingExchange(orderBook)
if stateExchange == nil {
- stateExchange = self.createLendingExchangeObject(orderBook)
+ stateExchange = ls.createLendingExchangeObject(orderBook)
}
- stateLendingTrade := stateExchange.getLendingTrade(self.db, tradeIdHash)
- self.journal = append(self.journal, collateralLockedAmount{
+ stateLendingTrade := stateExchange.getLendingTrade(ls.db, tradeIdHash)
+ ls.journal = append(ls.journal, collateralLockedAmount{
orderBook: orderBook,
tradeId: tradeIdHash,
prev: stateLendingTrade.data.CollateralLockedAmount,
})
stateLendingTrade.SetCollateralLockedAmount(amount)
}
-func (self *LendingStateDB) GetLendingOrder(orderBook common.Hash, orderId common.Hash) LendingItem {
- stateObject := self.GetOrNewLendingExchangeObject(orderBook)
+
+func (ls *LendingStateDB) GetLendingOrder(orderBook common.Hash, orderId common.Hash) LendingItem {
+ stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
if stateObject == nil {
return EmptyLendingOrder
}
- stateOrderItem := stateObject.getLendingItem(self.db, orderId)
+ stateOrderItem := stateObject.getLendingItem(ls.db, orderId)
if stateOrderItem == nil {
return EmptyLendingOrder
}
return stateOrderItem.data
}
-func (self *LendingStateDB) GetLendingTrade(orderBook common.Hash, tradeId common.Hash) LendingTrade {
- stateObject := self.GetOrNewLendingExchangeObject(orderBook)
+func (ls *LendingStateDB) GetLendingTrade(orderBook common.Hash, tradeId common.Hash) LendingTrade {
+ stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
if stateObject == nil {
return EmptyLendingTrade
}
- stateOrderItem := stateObject.getLendingTrade(self.db, tradeId)
+ stateOrderItem := stateObject.getLendingTrade(ls.db, tradeId)
if stateOrderItem == nil || stateOrderItem.empty() {
return EmptyLendingTrade
}
return stateOrderItem.data
}
-func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
+func (ls *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId common.Hash, price *big.Int, amount *big.Int, side string) error {
priceHash := common.BigToHash(price)
- lendingExchange := self.GetOrNewLendingExchangeObject(orderBook)
+ lendingExchange := ls.GetOrNewLendingExchangeObject(orderBook)
if lendingExchange == nil {
- return fmt.Errorf("Order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found order book: %s", orderBook.Hex())
}
var orderList *itemListState
switch side {
case Investing:
- orderList = lendingExchange.getInvestingOrderList(self.db, priceHash)
+ orderList = lendingExchange.getInvestingOrderList(ls.db, priceHash)
case Borrowing:
- orderList = lendingExchange.getBorrowingOrderList(self.db, priceHash)
+ orderList = lendingExchange.getBorrowingOrderList(ls.db, priceHash)
default:
- return fmt.Errorf("Order type not found : %s ", side)
+ return fmt.Errorf("not found order type: %s", side)
}
if orderList == nil || orderList.empty() {
- return fmt.Errorf("Order list empty order book : %s , order id : %s , key : %s ", orderBook, orderId.Hex(), priceHash.Hex())
+ return fmt.Errorf("empty orderList: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
- lendingItem := lendingExchange.getLendingItem(self.db, orderId)
+ lendingItem := lendingExchange.getLendingItem(ls.db, orderId)
if lendingItem == nil || lendingItem.empty() {
- return fmt.Errorf("Order item empty order book : %s , order id : %s , key : %s ", orderBook, orderId.Hex(), priceHash.Hex())
+ return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderId.Hex(), priceHash.Hex())
}
- currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(self.db, orderId).Bytes()[:])
+ currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderId).Bytes()[:])
if currentAmount.Cmp(amount) < 0 {
- return fmt.Errorf("Order amount not enough : %s , have : %d , want : %d ", orderId.Hex(), currentAmount, amount)
+ return fmt.Errorf("not enough order amount %s: have : %d , want : %d", orderId.Hex(), currentAmount, amount)
}
- self.journal = append(self.journal, subAmountOrder{
+ ls.journal = append(ls.journal, subAmountOrder{
orderBook: orderBook,
orderId: orderId,
- order: self.GetLendingOrder(orderBook, orderId),
+ order: ls.GetLendingOrder(orderBook, orderId),
amount: amount,
})
newAmount := new(big.Int).Sub(currentAmount, amount)
@@ -274,77 +276,77 @@ func (self *LendingStateDB) SubAmountLendingItem(orderBook common.Hash, orderId
log.Debug("SubAmountOrderItem", "tradeId", orderId.Hex(), "side", side, "key", price.Uint64(), "amount", amount.Uint64(), "new amount", newAmount.Uint64())
orderList.subVolume(amount)
if newAmount.Sign() == 0 {
- orderList.removeOrderItem(self.db, orderId)
+ orderList.removeOrderItem(ls.db, orderId)
} else {
orderList.setOrderItem(orderId, common.BigToHash(newAmount))
}
if orderList.empty() {
switch side {
case Investing:
- lendingExchange.removeInvestingOrderList(self.db, orderList)
+ lendingExchange.removeInvestingOrderList(ls.db, orderList)
case Borrowing:
- lendingExchange.removeBorrowingOrderList(self.db, orderList)
+ lendingExchange.removeBorrowingOrderList(ls.db, orderList)
default:
}
}
return nil
}
-func (self *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *LendingItem) error {
+func (ls *LendingStateDB) CancelLendingOrder(orderBook common.Hash, order *LendingItem) error {
interestHash := common.BigToHash(order.Interest)
orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.LendingId))
- stateObject := self.GetOrNewLendingExchangeObject(orderBook)
+ stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
if stateObject == nil {
- return fmt.Errorf("Order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found order book: %s", orderBook.Hex())
}
- lendingItem := stateObject.getLendingItem(self.db, orderIdHash)
+ lendingItem := stateObject.getLendingItem(ls.db, orderIdHash)
var orderList *itemListState
switch lendingItem.data.Side {
case Investing:
- orderList = stateObject.getInvestingOrderList(self.db, interestHash)
+ orderList = stateObject.getInvestingOrderList(ls.db, interestHash)
case Borrowing:
- orderList = stateObject.getBorrowingOrderList(self.db, interestHash)
+ orderList = stateObject.getBorrowingOrderList(ls.db, interestHash)
default:
- return fmt.Errorf("Order side not found : %s ", order.Side)
+ return fmt.Errorf("not found order side: %s", order.Side)
}
if orderList == nil || orderList.empty() {
- return fmt.Errorf("Order list empty order book : %s , order id : %s , key : %s ", orderBook, orderIdHash.Hex(), interestHash.Hex())
+ return fmt.Errorf("empty OrderList: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex())
}
if lendingItem == nil || lendingItem.empty() {
- return fmt.Errorf("Order item empty order book : %s , order id : %s , key : %s ", orderBook, orderIdHash.Hex(), interestHash.Hex())
+ return fmt.Errorf("empty order item: order book : %s , order id : %s , key : %s", orderBook, orderIdHash.Hex(), interestHash.Hex())
}
if lendingItem.data.UserAddress != order.UserAddress {
- return fmt.Errorf("Error Order User Address mismatch when cancel order book : %s , order id : %s , got : %s , expect : %s ", orderBook, orderIdHash.Hex(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex())
+ return fmt.Errorf("error Order UserAddress mismatch when cancel order book: %s , order id : %s , got : %s , expect : %s", orderBook, orderIdHash.Hex(), lendingItem.data.UserAddress.Hex(), order.UserAddress.Hex())
}
- self.journal = append(self.journal, cancelOrder{
+ ls.journal = append(ls.journal, cancelOrder{
orderBook: orderBook,
orderId: orderIdHash,
- order: self.GetLendingOrder(orderBook, orderIdHash),
+ order: ls.GetLendingOrder(orderBook, orderIdHash),
})
lendingItem.setVolume(big.NewInt(0))
- currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(self.db, orderIdHash).Bytes()[:])
+ currentAmount := new(big.Int).SetBytes(orderList.GetOrderAmount(ls.db, orderIdHash).Bytes()[:])
orderList.subVolume(currentAmount)
- orderList.removeOrderItem(self.db, orderIdHash)
+ orderList.removeOrderItem(ls.db, orderIdHash)
if orderList.empty() {
switch order.Side {
case Investing:
- stateObject.removeInvestingOrderList(self.db, orderList)
+ stateObject.removeInvestingOrderList(ls.db, orderList)
case Borrowing:
- stateObject.removeBorrowingOrderList(self.db, orderList)
+ stateObject.removeBorrowingOrderList(ls.db, orderList)
default:
}
}
return nil
}
-func (self *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, *big.Int) {
- stateObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.Int, *big.Int) {
+ stateObject := ls.getLendingExchange(orderBook)
if stateObject != nil {
- investingHash := stateObject.getBestInvestingInterest(self.db)
+ investingHash := stateObject.getBestInvestingInterest(ls.db)
if common.EmptyHash(investingHash) {
return Zero, Zero
}
- orderList := stateObject.getInvestingOrderList(self.db, investingHash)
+ orderList := stateObject.getInvestingOrderList(ls.db, investingHash)
if orderList == nil {
log.Error("order list investing not found", "key", investingHash.Hex())
return Zero, Zero
@@ -354,14 +356,14 @@ func (self *LendingStateDB) GetBestInvestingRate(orderBook common.Hash) (*big.In
return Zero, Zero
}
-func (self *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *big.Int) {
- stateObject := self.getLendingExchange(orderBook)
+func (ls *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int, *big.Int) {
+ stateObject := ls.getLendingExchange(orderBook)
if stateObject != nil {
- priceHash := stateObject.getBestBorrowingInterest(self.db)
+ priceHash := stateObject.getBestBorrowingInterest(ls.db)
if common.EmptyHash(priceHash) {
return Zero, Zero
}
- orderList := stateObject.getBorrowingOrderList(self.db, priceHash)
+ orderList := stateObject.getBorrowingOrderList(ls.db, priceHash)
if orderList == nil {
log.Error("order list ask not found", "key", priceHash.Hex())
return Zero, Zero
@@ -371,52 +373,52 @@ func (self *LendingStateDB) GetBestBorrowRate(orderBook common.Hash) (*big.Int,
return Zero, Zero
}
-func (self *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
- stateObject := self.GetOrNewLendingExchangeObject(orderBook)
+func (ls *LendingStateDB) GetBestLendingIdAndAmount(orderBook common.Hash, price *big.Int, side string) (common.Hash, *big.Int, error) {
+ stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
if stateObject != nil {
var stateOrderList *itemListState
switch side {
case Investing:
- stateOrderList = stateObject.getInvestingOrderList(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getInvestingOrderList(ls.db, common.BigToHash(price))
case Borrowing:
- stateOrderList = stateObject.getBorrowingOrderList(self.db, common.BigToHash(price))
+ stateOrderList = stateObject.getBorrowingOrderList(ls.db, common.BigToHash(price))
default:
- return EmptyHash, Zero, fmt.Errorf("not found side :%s ", side)
+ return EmptyHash, Zero, fmt.Errorf("not found side: %s", side)
}
if stateOrderList != nil {
- key, _, err := stateOrderList.getTrie(self.db).TryGetBestLeftKeyAndValue()
+ key, _, err := stateOrderList.getTrie(ls.db).TryGetBestLeftKeyAndValue()
if err != nil {
return EmptyHash, Zero, err
}
orderId := common.BytesToHash(key)
- amount := stateOrderList.GetOrderAmount(self.db, orderId)
+ amount := stateOrderList.GetOrderAmount(ls.db, orderId)
return orderId, new(big.Int).SetBytes(amount.Bytes()), nil
}
- return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook : %s , key : %d , side :%s ", orderBook.Hex(), price, side)
+ return EmptyHash, Zero, fmt.Errorf("not found order list with orderBook: %s , key : %d , side : %s", orderBook.Hex(), price, side)
}
- return EmptyHash, Zero, fmt.Errorf("not found orderBook : %s ", orderBook.Hex())
+ return EmptyHash, Zero, fmt.Errorf("not found orderBook: %s", orderBook.Hex())
}
// updateLendingExchange writes the given object to the trie.
-func (self *LendingStateDB) updateLendingExchange(stateObject *lendingExchangeState) {
+func (ls *LendingStateDB) updateLendingExchange(stateObject *lendingExchangeState) {
addr := stateObject.Hash()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
- self.setError(self.trie.TryUpdate(addr[:], data))
+ ls.setError(ls.trie.TryUpdate(addr[:], data))
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *lendingExchangeState) {
+func (ls *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *lendingExchangeState) {
// Prefer 'live' objects.
- if obj := self.lendingExchangeStates[addr]; obj != nil {
+ if obj := ls.lendingExchangeStates[addr]; obj != nil {
return obj
}
// Load the object from the database.
- enc, err := self.trie.TryGet(addr[:])
+ enc, err := ls.trie.TryGet(addr[:])
if len(enc) == 0 {
- self.setError(err)
+ ls.setError(err)
return nil
}
var data lendingObject
@@ -425,176 +427,176 @@ func (self *LendingStateDB) getLendingExchange(addr common.Hash) (stateObject *l
return nil
}
// Insert into the live set.
- obj := newStateExchanges(self, addr, data, self.MarkLendingExchangeObjectDirty)
- self.lendingExchangeStates[addr] = obj
+ obj := newStateExchanges(ls, addr, data, ls.MarkLendingExchangeObjectDirty)
+ ls.lendingExchangeStates[addr] = obj
return obj
}
-func (self *LendingStateDB) setLendingExchangeObject(object *lendingExchangeState) {
- self.lendingExchangeStates[object.Hash()] = object
- self.lendingExchangeStatesDirty[object.Hash()] = struct{}{}
+func (ls *LendingStateDB) setLendingExchangeObject(object *lendingExchangeState) {
+ ls.lendingExchangeStates[object.Hash()] = object
+ ls.lendingExchangeStatesDirty[object.Hash()] = struct{}{}
}
// Retrieve a state object or create a new state object if nil.
-func (self *LendingStateDB) GetOrNewLendingExchangeObject(addr common.Hash) *lendingExchangeState {
- stateExchangeObject := self.getLendingExchange(addr)
+func (ls *LendingStateDB) GetOrNewLendingExchangeObject(addr common.Hash) *lendingExchangeState {
+ stateExchangeObject := ls.getLendingExchange(addr)
if stateExchangeObject == nil {
- stateExchangeObject = self.createLendingExchangeObject(addr)
+ stateExchangeObject = ls.createLendingExchangeObject(addr)
}
return stateExchangeObject
}
// MarkStateLendObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *LendingStateDB) MarkLendingExchangeObjectDirty(addr common.Hash) {
- self.lendingExchangeStatesDirty[addr] = struct{}{}
+func (ls *LendingStateDB) MarkLendingExchangeObjectDirty(addr common.Hash) {
+ ls.lendingExchangeStatesDirty[addr] = struct{}{}
}
// createStateOrderListObject creates a new state object. If there is an existing tradeId with
// the given address, it is overwritten and returned as the second return value.
-func (self *LendingStateDB) createLendingExchangeObject(hash common.Hash) (newobj *lendingExchangeState) {
- newobj = newStateExchanges(self, hash, lendingObject{}, self.MarkLendingExchangeObjectDirty)
+func (ls *LendingStateDB) createLendingExchangeObject(hash common.Hash) (newobj *lendingExchangeState) {
+ newobj = newStateExchanges(ls, hash, lendingObject{}, ls.MarkLendingExchangeObjectDirty)
newobj.setNonce(0) // sets the object to dirty
- self.setLendingExchangeObject(newobj)
+ ls.setLendingExchangeObject(newobj)
return newobj
}
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
-func (self *LendingStateDB) Copy() *LendingStateDB {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (ls *LendingStateDB) Copy() *LendingStateDB {
+ ls.lock.Lock()
+ defer ls.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &LendingStateDB{
- db: self.db,
- trie: self.db.CopyTrie(self.trie),
- lendingExchangeStates: make(map[common.Hash]*lendingExchangeState, len(self.lendingExchangeStatesDirty)),
- lendingExchangeStatesDirty: make(map[common.Hash]struct{}, len(self.lendingExchangeStatesDirty)),
+ db: ls.db,
+ trie: ls.db.CopyTrie(ls.trie),
+ lendingExchangeStates: make(map[common.Hash]*lendingExchangeState, len(ls.lendingExchangeStatesDirty)),
+ lendingExchangeStatesDirty: make(map[common.Hash]struct{}, len(ls.lendingExchangeStatesDirty)),
}
// Copy the dirty states, logs, and preimages
- for addr := range self.lendingExchangeStatesDirty {
+ for addr := range ls.lendingExchangeStatesDirty {
state.lendingExchangeStatesDirty[addr] = struct{}{}
}
- for addr, exchangeObject := range self.lendingExchangeStates {
+ for addr, exchangeObject := range ls.lendingExchangeStates {
state.lendingExchangeStates[addr] = exchangeObject.deepCopy(state, state.MarkLendingExchangeObjectDirty)
}
return state
}
-func (s *LendingStateDB) clearJournalAndRefund() {
- s.journal = nil
- s.validRevisions = s.validRevisions[:0]
+func (ls *LendingStateDB) clearJournalAndRefund() {
+ ls.journal = nil
+ ls.validRevisions = ls.validRevisions[:0]
}
// Snapshot returns an identifier for the current revision of the state.
-func (self *LendingStateDB) Snapshot() int {
- id := self.nextRevisionId
- self.nextRevisionId++
- self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
+func (ls *LendingStateDB) Snapshot() int {
+ id := ls.nextRevisionId
+ ls.nextRevisionId++
+ ls.validRevisions = append(ls.validRevisions, revision{id, len(ls.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
-func (self *LendingStateDB) RevertToSnapshot(revid int) {
+func (ls *LendingStateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
- idx := sort.Search(len(self.validRevisions), func(i int) bool {
- return self.validRevisions[i].id >= revid
+ idx := sort.Search(len(ls.validRevisions), func(i int) bool {
+ return ls.validRevisions[i].id >= revid
})
- if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
+ if idx == len(ls.validRevisions) || ls.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
- snapshot := self.validRevisions[idx].journalIndex
+ snapshot := ls.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
- for i := len(self.journal) - 1; i >= snapshot; i-- {
- self.journal[i].undo(self)
+ for i := len(ls.journal) - 1; i >= snapshot; i-- {
+ ls.journal[i].undo(ls)
}
- self.journal = self.journal[:snapshot]
+ ls.journal = ls.journal[:snapshot]
// Remove invalidated snapshots from the stack.
- self.validRevisions = self.validRevisions[:idx]
+ ls.validRevisions = ls.validRevisions[:idx]
}
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
-func (s *LendingStateDB) Finalise() {
+func (ls *LendingStateDB) Finalise() {
// Commit objects to the trie.
- for addr, stateObject := range s.lendingExchangeStates {
- if _, isDirty := s.lendingExchangeStatesDirty[addr]; isDirty {
+ for addr, stateObject := range ls.lendingExchangeStates {
+ if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
- err := stateObject.updateInvestingRoot(s.db)
+ err := stateObject.updateInvestingRoot(ls.db)
if err != nil {
log.Warn("Finalise updateInvestingRoot", "err", err, "addr", addr, "stateObject", *stateObject)
}
- stateObject.updateBorrowingRoot(s.db)
- stateObject.updateOrderRoot(s.db)
- stateObject.updateLendingTradeRoot(s.db)
- stateObject.updateLiquidationTimeRoot(s.db)
+ stateObject.updateBorrowingRoot(ls.db)
+ stateObject.updateOrderRoot(ls.db)
+ stateObject.updateLendingTradeRoot(ls.db)
+ stateObject.updateLiquidationTimeRoot(ls.db)
// Update the object in the main tradeId trie.
- s.updateLendingExchange(stateObject)
+ ls.updateLendingExchange(stateObject)
//delete(s.investingStatesDirty, addr)
}
}
- s.clearJournalAndRefund()
+ ls.clearJournalAndRefund()
}
// IntermediateRoot computes the current root orderBook of the state trie.
// It is called in between transactions to get the root orderBook that
// goes into transaction receipts.
-func (s *LendingStateDB) IntermediateRoot() common.Hash {
- s.Finalise()
- return s.trie.Hash()
+func (ls *LendingStateDB) IntermediateRoot() common.Hash {
+ ls.Finalise()
+ return ls.trie.Hash()
}
// Commit writes the state to the underlying in-memory trie database.
-func (s *LendingStateDB) Commit() (root common.Hash, err error) {
- defer s.clearJournalAndRefund()
+func (ls *LendingStateDB) Commit() (root common.Hash, err error) {
+ defer ls.clearJournalAndRefund()
// Commit objects to the trie.
- for addr, stateObject := range s.lendingExchangeStates {
- if _, isDirty := s.lendingExchangeStatesDirty[addr]; isDirty {
+ for addr, stateObject := range ls.lendingExchangeStates {
+ if _, isDirty := ls.lendingExchangeStatesDirty[addr]; isDirty {
// Write any storage changes in the state object to its storage trie.
- if err := stateObject.CommitInvestingTrie(s.db); err != nil {
+ if err := stateObject.CommitInvestingTrie(ls.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitBorrowingTrie(s.db); err != nil {
+ if err := stateObject.CommitBorrowingTrie(ls.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitLendingItemTrie(s.db); err != nil {
+ if err := stateObject.CommitLendingItemTrie(ls.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitLendingTradeTrie(s.db); err != nil {
+ if err := stateObject.CommitLendingTradeTrie(ls.db); err != nil {
return EmptyHash, err
}
- if err := stateObject.CommitLiquidationTimeTrie(s.db); err != nil {
+ if err := stateObject.CommitLiquidationTimeTrie(ls.db); err != nil {
return EmptyHash, err
}
// Update the object in the main tradeId trie.
- s.updateLendingExchange(stateObject)
- delete(s.lendingExchangeStatesDirty, addr)
+ ls.updateLendingExchange(stateObject)
+ delete(ls.lendingExchangeStatesDirty, addr)
}
}
// Write trie changes.
- root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ root, err = ls.trie.Commit(func(leaf []byte, parent common.Hash) error {
var exchange lendingObject
if err := rlp.DecodeBytes(leaf, &exchange); err != nil {
return nil
}
if exchange.InvestingRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.InvestingRoot, parent)
+ ls.db.TrieDB().Reference(exchange.InvestingRoot, parent)
}
if exchange.BorrowingRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.BorrowingRoot, parent)
+ ls.db.TrieDB().Reference(exchange.BorrowingRoot, parent)
}
if exchange.LendingItemRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.LendingItemRoot, parent)
+ ls.db.TrieDB().Reference(exchange.LendingItemRoot, parent)
}
if exchange.LendingTradeRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.LendingTradeRoot, parent)
+ ls.db.TrieDB().Reference(exchange.LendingTradeRoot, parent)
}
if exchange.LiquidationTimeRoot != EmptyRoot {
- s.db.TrieDB().Reference(exchange.LiquidationTimeRoot, parent)
+ ls.db.TrieDB().Reference(exchange.LiquidationTimeRoot, parent)
}
return nil
})
@@ -602,38 +604,38 @@ func (s *LendingStateDB) Commit() (root common.Hash, err error) {
return root, err
}
-func (self *LendingStateDB) InsertLiquidationTime(lendingBook common.Hash, time *big.Int, tradeId uint64) {
+func (ls *LendingStateDB) InsertLiquidationTime(lendingBook common.Hash, time *big.Int, tradeId uint64) {
timeHash := common.BigToHash(time)
- lendingExchangeState := self.getLendingExchange(lendingBook)
+ lendingExchangeState := ls.getLendingExchange(lendingBook)
if lendingExchangeState == nil {
- lendingExchangeState = self.createLendingExchangeObject(lendingBook)
+ lendingExchangeState = ls.createLendingExchangeObject(lendingBook)
}
- liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(self.db, timeHash)
+ liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash)
if liquidationTime == nil {
- liquidationTime = lendingExchangeState.createLiquidationTime(self.db, timeHash)
+ liquidationTime = lendingExchangeState.createLiquidationTime(ls.db, timeHash)
}
- liquidationTime.insertTradeId(self.db, common.Uint64ToHash(tradeId))
+ liquidationTime.insertTradeId(ls.db, common.Uint64ToHash(tradeId))
liquidationTime.AddVolume(One)
}
-func (self *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, tradeId uint64, time uint64) error {
+func (ls *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, tradeId uint64, time uint64) error {
timeHash := common.Uint64ToHash(time)
tradeIdHash := common.Uint64ToHash(tradeId)
- lendingExchangeState := self.getLendingExchange(lendingBook)
+ lendingExchangeState := ls.getLendingExchange(lendingBook)
if lendingExchangeState == nil {
- return fmt.Errorf("lending book not found : %s ", lendingBook.Hex())
+ return fmt.Errorf("lending book not found: %s", lendingBook.Hex())
}
- liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(self.db, timeHash)
+ liquidationTime := lendingExchangeState.getLiquidationTimeOrderList(ls.db, timeHash)
if liquidationTime == nil {
- return fmt.Errorf("liquidation time not found : %s , %d ", lendingBook.Hex(), time)
+ return fmt.Errorf("not found liquidation time: %s , %d", lendingBook.Hex(), time)
}
- if !liquidationTime.Exist(self.db, tradeIdHash) {
- return fmt.Errorf("tradeId not exist : %s , %d , %d ", lendingBook.Hex(), time, tradeId)
+ if !liquidationTime.Exist(ls.db, tradeIdHash) {
+ return fmt.Errorf("not exist tradeId: %s, %d, %d", lendingBook.Hex(), time, tradeId)
}
- liquidationTime.removeTradeId(self.db, tradeIdHash)
+ liquidationTime.removeTradeId(ls.db, tradeIdHash)
liquidationTime.subVolume(One)
if liquidationTime.Volume().Sign() == 0 {
- err := lendingExchangeState.getLiquidationTimeTrie(self.db).TryDelete(timeHash[:])
+ err := lendingExchangeState.getLiquidationTimeTrie(ls.db).TryDelete(timeHash[:])
if err != nil {
log.Warn("RemoveLiquidationTime getLiquidationTimeTrie.TryDelete", "err", err, "timeHash[:]", timeHash[:])
}
@@ -641,33 +643,33 @@ func (self *LendingStateDB) RemoveLiquidationTime(lendingBook common.Hash, trade
return nil
}
-func (self *LendingStateDB) GetLowestLiquidationTime(lendingBook common.Hash, time *big.Int) (*big.Int, []common.Hash) {
+func (ls *LendingStateDB) GetLowestLiquidationTime(lendingBook common.Hash, time *big.Int) (*big.Int, []common.Hash) {
liquidationData := []common.Hash{}
- lendingExchangeState := self.getLendingExchange(lendingBook)
+ lendingExchangeState := ls.getLendingExchange(lendingBook)
if lendingExchangeState == nil {
return common.Big0, liquidationData
}
- lowestPriceHash, liquidationState := lendingExchangeState.getLowestLiquidationTime(self.db)
+ lowestPriceHash, liquidationState := lendingExchangeState.getLowestLiquidationTime(ls.db)
lowestTime := new(big.Int).SetBytes(lowestPriceHash[:])
if liquidationState != nil && lowestTime.Sign() > 0 && lowestTime.Cmp(time) <= 0 {
- liquidationData = liquidationState.getAllTradeIds(self.db)
+ liquidationData = liquidationState.getAllTradeIds(ls.db)
}
return lowestTime, liquidationData
}
-func (self *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId uint64) error {
+func (ls *LendingStateDB) CancelLendingTrade(orderBook common.Hash, tradeId uint64) error {
tradeIdHash := common.Uint64ToHash(tradeId)
- stateObject := self.GetOrNewLendingExchangeObject(orderBook)
+ stateObject := ls.GetOrNewLendingExchangeObject(orderBook)
if stateObject == nil {
- return fmt.Errorf("Order book not found : %s ", orderBook.Hex())
+ return fmt.Errorf("not found order book: %s", orderBook.Hex())
}
- lendingTrade := stateObject.getLendingTrade(self.db, tradeIdHash)
+ lendingTrade := stateObject.getLendingTrade(ls.db, tradeIdHash)
if lendingTrade == nil || lendingTrade.empty() {
- return fmt.Errorf("lending trade empty order book : %s , trade id : %s , trade id hash : %s ", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex())
+ return fmt.Errorf("lending trade empty order book: %s , trade id : %s , trade id hash : %s", orderBook, tradeIdHash.Hex(), tradeIdHash.Hex())
}
- self.journal = append(self.journal, cancelTrading{
+ ls.journal = append(ls.journal, cancelTrading{
orderBook: orderBook,
- order: self.GetLendingTrade(orderBook, tradeIdHash),
+ order: ls.GetLendingTrade(orderBook, tradeIdHash),
})
lendingTrade.SetAmount(Zero)
return nil
diff --git a/XDCxlending/lendingstate/statedb_test.go b/XDCxlending/lendingstate/statedb_test.go
index 0ea231e5ac..07207d5704 100644
--- a/XDCxlending/lendingstate/statedb_test.go
+++ b/XDCxlending/lendingstate/statedb_test.go
@@ -18,10 +18,11 @@ package lendingstate
import (
"fmt"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"testing"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
)
func TestEchangeStates(t *testing.T) {
@@ -225,9 +226,7 @@ func TestDumpStates(t *testing.T) {
orderBook := common.StringToHash("BTC/XDC")
numberOrder := 20
orderItems := []LendingItem{}
- relayers := []common.Hash{}
for i := 0; i < numberOrder; i++ {
- relayers = append(relayers, common.BigToHash(big.NewInt(int64(i))))
id := new(big.Int).SetUint64(uint64(i) + 1)
orderItems = append(orderItems, LendingItem{LendingId: id.Uint64(), Quantity: big.NewInt(int64(2*i + 1)), Interest: big.NewInt(1), Side: Investing, Signature: &Signature{V: 1, R: common.HexToHash("111111"), S: common.HexToHash("222222222222")}})
orderItems = append(orderItems, LendingItem{LendingId: id.Uint64(), Quantity: big.NewInt(int64(2*i + 1)), Interest: big.NewInt(1), Side: Borrowing, Signature: &Signature{V: 1, R: common.HexToHash("3333333333"), S: common.HexToHash("22222222222222222")}})
diff --git a/XDCxlending/lendingstate/trade.go b/XDCxlending/lendingstate/trade.go
index 0416ca4c26..89542381fe 100644
--- a/XDCxlending/lendingstate/trade.go
+++ b/XDCxlending/lendingstate/trade.go
@@ -2,14 +2,14 @@ package lendingstate
import (
"fmt"
- "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"math/big"
"strconv"
"time"
+ "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/globalsign/mgo/bson"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -183,7 +183,7 @@ func (t *LendingTrade) SetBSON(raw bson.Raw) error {
}
func (t *LendingTrade) ComputeHash() common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(t.InvestingOrderHash.Bytes())
sha.Write(t.BorrowingOrderHash.Bytes())
return common.BytesToHash(sha.Sum(nil))
diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go
index 5af2d27713..0a41347acd 100644
--- a/XDCxlending/order_processor.go
+++ b/XDCxlending/order_processor.go
@@ -66,7 +66,7 @@ func (l *Lending) ApplyOrder(header *types.Header, coinbase common.Address, chai
switch order.Type {
case lendingstate.TopUp:
- err, reject, newLendingTrade := l.ProcessTopUp(lendingStateDB, statedb, tradingStateDb, order)
+ reject, newLendingTrade, err := l.ProcessTopUp(lendingStateDB, statedb, tradingStateDb, order)
if err != nil || reject {
rejects = append(rejects, order)
}
@@ -438,7 +438,7 @@ func (l *Lending) getLendQuantity(
}
LendingTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, makerOrder.LendingToken)
if err != nil || LendingTokenDecimal.Sign() == 0 {
- return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.LendingToken.String(), err)
+ return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", makerOrder.LendingToken.String(), err)
}
collateralToken := makerOrder.CollateralToken
if takerOrder.Side == lendingstate.Borrowing {
@@ -446,7 +446,7 @@ func (l *Lending) getLendQuantity(
}
collateralTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, collateralToken)
if err != nil || collateralTokenDecimal.Sign() == 0 {
- return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal. Token: %v . Err: %v", collateralToken.String(), err)
+ return lendingstate.Zero, lendingstate.Zero, false, nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", collateralToken.String(), err)
}
if takerOrder.Relayer == makerOrder.Relayer {
if err := lendingstate.CheckRelayerFee(takerOrder.Relayer, new(big.Int).Mul(common.RelayerLendingFee, big.NewInt(2)), statedb); err != nil {
@@ -582,7 +582,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *lendingsta
matchingFee = new(big.Int).Add(matchingFee, common.RelayerLendingFee)
if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) {
- return fmt.Errorf("Echange owner empty , Taker: %v , maker : %v ", takerExOwner, makerExOwner)
+ return fmt.Errorf("empty echange owner: taker: %v , maker : %v", takerExOwner, makerExOwner)
}
mapBalances := map[common.Address]map[common.Address]*big.Int{}
//Checking balance
@@ -727,9 +727,10 @@ func (l *Lending) ProcessCancelOrder(header *types.Header, lendingStateDB *lendi
}
}
feeRate := lendingstate.GetFee(statedb, originOrder.Relayer)
- tokenCancelFee, tokenPriceInXDC := common.Big0, common.Big0
+ var tokenCancelFee, tokenPriceInXDC *big.Int
if !chain.Config().IsTIPXDCXCancellationFee(header.Number) {
tokenCancelFee = getCancelFeeV1(collateralTokenDecimal, collateralPrice, feeRate, &originOrder)
+ tokenPriceInXDC = common.Big0
} else {
tokenCancelFee, tokenPriceInXDC = l.getCancelFee(chain, statedb, tradingStateDb, &originOrder, feeRate)
}
@@ -783,22 +784,22 @@ func (l *Lending) ProcessCancelOrder(header *types.Header, lendingStateDB *lendi
return nil, false
}
-func (l *Lending) ProcessTopUp(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, order *lendingstate.LendingItem) (error, bool, *lendingstate.LendingTrade) {
+func (l *Lending) ProcessTopUp(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, order *lendingstate.LendingItem) (bool, *lendingstate.LendingTrade, error) {
lendingTradeId := common.Uint64ToHash(order.LendingTradeId)
lendingBook := lendingstate.GetLendingOrderBookHash(order.LendingToken, order.Term)
lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId)
if lendingTrade == lendingstate.EmptyLendingTrade {
- return fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil
+ return true, nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex())
}
if order.UserAddress != lendingTrade.Borrower {
- return fmt.Errorf("ProcessTopUp: invalid userAddress . UserAddress: %s . Borrower: %s", order.UserAddress.Hex(), lendingTrade.Borrower.Hex()), true, nil
+ return true, nil, fmt.Errorf("ProcessTopUp: invalid userAddress . UserAddress: %s . Borrower: %s", order.UserAddress.Hex(), lendingTrade.Borrower.Hex())
}
if order.Relayer != lendingTrade.BorrowingRelayer {
- return fmt.Errorf("ProcessTopUp: invalid relayerAddress . Got: %s . Expect: %s", order.Relayer.Hex(), lendingTrade.BorrowingRelayer.Hex()), true, nil
+ return true, nil, fmt.Errorf("ProcessTopUp: invalid relayerAddress . Got: %s . Expect: %s", order.Relayer.Hex(), lendingTrade.BorrowingRelayer.Hex())
}
if order.Quantity.Sign() <= 0 || lendingTrade.TradeId != lendingTradeId.Big().Uint64() {
log.Debug("ProcessTopUp: invalid quantity", "Quantity", order.Quantity, "lendingTradeId", lendingTradeId.Hex())
- return nil, true, nil
+ return true, nil, nil
}
return l.ProcessTopUpLendingTrade(lendingStateDB, statedb, tradingStateDb, lendingTradeId, lendingBook, order.Quantity)
}
@@ -927,7 +928,7 @@ func (l *Lending) LiquidationTrade(lendingStateDB *lendingstate.LendingStateDB,
// cancellation fee = 1/10 borrowing fee
// deprecated after hardfork at TIPXDCXCancellationFee
func getCancelFeeV1(collateralTokenDecimal *big.Int, collateralPrice, borrowFee *big.Int, order *lendingstate.LendingItem) *big.Int {
- cancelFee := big.NewInt(0)
+ var cancelFee *big.Int
if order.Side == lendingstate.Investing {
// cancel fee = quantityToLend*borrowFee/LendingCancelFee
cancelFee = new(big.Int).Mul(order.Quantity, borrowFee)
@@ -947,7 +948,7 @@ func (l *Lending) getCancelFee(chain consensus.ChainContext, statedb *state.Stat
if feeRate == nil || feeRate.Sign() == 0 {
return common.Big0, common.Big0
}
- cancelFee, tokenPriceInXDC := common.Big0, common.Big0
+ var cancelFee, tokenPriceInXDC *big.Int
var err error
if order.Side == lendingstate.Investing {
cancelFee, tokenPriceInXDC, err = l.XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.LendingToken, common.RelayerLendingCancelFee)
@@ -971,11 +972,11 @@ func (l *Lending) GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, s
if inversePrice != nil && inversePrice.Sign() > 0 {
quoteTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, quoteToken)
if err != nil || quoteTokenDecimal.Sign() == 0 {
- return nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", quoteToken.String(), err)
+ return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", quoteToken.String(), err)
}
baseTokenDecimal, err := l.XDCx.GetTokenDecimal(chain, statedb, baseToken)
if err != nil || baseTokenDecimal.Sign() == 0 {
- return nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", baseToken, err)
+ return nil, fmt.Errorf("fail to get tokenDecimal: Token: %v . Err: %v", baseToken, err)
}
price = new(big.Int).Mul(baseTokenDecimal, quoteTokenDecimal)
price = new(big.Int).Div(price, inversePrice)
@@ -1098,7 +1099,7 @@ func (l *Lending) AutoTopUp(statedb *state.StateDB, tradingState *tradingstate.T
return nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex())
}
if currentPrice.Cmp(lendingTrade.LiquidationPrice) >= 0 {
- return nil, fmt.Errorf("CurrentPrice is still higher than or equal to LiquidationPrice. current price: %v , liquidation price : %v ", currentPrice, lendingTrade.LiquidationPrice)
+ return nil, fmt.Errorf("currentPrice is still higher than or equal to LiquidationPrice. current price: %v , liquidation price : %v ", currentPrice, lendingTrade.LiquidationPrice)
}
// newLiquidationPrice = currentPrice * 90%
newLiquidationPrice := new(big.Int).Mul(currentPrice, common.RateTopUp)
@@ -1112,23 +1113,23 @@ func (l *Lending) AutoTopUp(statedb *state.StateDB, tradingState *tradingstate.T
if tokenBalance.Cmp(requiredDepositAmount) < 0 {
return nil, fmt.Errorf("not enough balance to AutoTopUp. requiredDepositAmount: %v . tokenBalance: %v . Token: %s", requiredDepositAmount, tokenBalance, lendingTrade.CollateralToken.Hex())
}
- err, _, newTrade := l.ProcessTopUpLendingTrade(lendingState, statedb, tradingState, lendingTradeId, lendingBook, requiredDepositAmount)
+ _, newTrade, err := l.ProcessTopUpLendingTrade(lendingState, statedb, tradingState, lendingTradeId, lendingBook, requiredDepositAmount)
return newTrade, err
}
-func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingTradeId common.Hash, lendingBook common.Hash, quantity *big.Int) (error, bool, *lendingstate.LendingTrade) {
+func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingTradeId common.Hash, lendingBook common.Hash, quantity *big.Int) (bool, *lendingstate.LendingTrade, error) {
lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId)
if lendingTrade == lendingstate.EmptyLendingTrade {
- return fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil
+ return true, nil, fmt.Errorf("process deposit for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex())
}
tokenBalance := lendingstate.GetTokenBalance(lendingTrade.Borrower, lendingTrade.CollateralToken, statedb)
if tokenBalance.Cmp(quantity) < 0 {
log.Debug("not enough balance deposit", "Quantity", quantity, "tokenBalance", tokenBalance)
- return fmt.Errorf("not enough balance deposit. lendingTradeId: %v , Quantity : %v , tokenBalance : %v", lendingTradeId.Hex(), quantity, tokenBalance), true, nil
+ return true, nil, fmt.Errorf("not enough balance deposit. lendingTradeId: %v , Quantity : %v , tokenBalance : %v", lendingTradeId.Hex(), quantity, tokenBalance)
}
err := tradingStateDb.RemoveLiquidationPrice(tradingstate.GetTradingOrderBookHash(lendingTrade.CollateralToken, lendingTrade.LendingToken), lendingTrade.LiquidationPrice, lendingBook, lendingTrade.TradeId)
if err != nil {
- return err, true, nil
+ return true, nil, err
}
err = lendingstate.SubTokenBalance(lendingTrade.Borrower, quantity, lendingTrade.CollateralToken, statedb)
if err != nil {
@@ -1149,7 +1150,7 @@ func (l *Lending) ProcessTopUpLendingTrade(lendingStateDB *lendingstate.LendingS
newLendingTrade.LiquidationPrice = newLiquidationPrice
newLendingTrade.CollateralLockedAmount = newLockedAmount
log.Debug("ProcessTopUp successfully", "price", newLiquidationPrice, "lockAmount", newLockedAmount)
- return nil, false, &newLendingTrade
+ return false, &newLendingTrade, nil
}
func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus.ChainContext, lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingstateDB *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId uint64) (trade *lendingstate.LendingTrade, err error) {
@@ -1165,7 +1166,7 @@ func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus
if tokenBalance.Cmp(paymentBalance) < 0 {
if lendingTrade.LiquidationTime > time {
- return nil, fmt.Errorf("Not enough balance need : %s , have : %s ", paymentBalance, tokenBalance)
+ return nil, fmt.Errorf("not enough balance need : %s , have : %s", paymentBalance, tokenBalance)
}
newLendingTrade := &lendingstate.LendingTrade{}
var err error
@@ -1235,14 +1236,14 @@ func (l *Lending) ProcessRepayLendingTrade(header *types.Header, chain consensus
return &lendingTrade, nil
}
-func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId common.Hash, newLiquidationPrice *big.Int) (error, bool, *lendingstate.LendingTrade) {
+func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.LendingStateDB, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, lendingBook common.Hash, lendingTradeId common.Hash, newLiquidationPrice *big.Int) (bool, *lendingstate.LendingTrade, error) {
log.Debug("ProcessRecallLendingTrade", "lendingTradeId", lendingTradeId.Hex(), "lendingBook", lendingBook.Hex(), "newLiquidationPrice", newLiquidationPrice)
lendingTrade := lendingStateDB.GetLendingTrade(lendingBook, lendingTradeId)
if lendingTrade == lendingstate.EmptyLendingTrade {
- return fmt.Errorf("process recall for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex()), true, nil
+ return true, nil, fmt.Errorf("process recall for emptyLendingTrade is not allowed. lendingTradeId: %v", lendingTradeId.Hex())
}
if newLiquidationPrice.Cmp(lendingTrade.LiquidationPrice) <= 0 {
- return fmt.Errorf("New liquidation price must higher than old liquidation price. current liquidation price: %v , new liquidation price : %v ", lendingTrade.LiquidationPrice, newLiquidationPrice), true, nil
+ return true, nil, fmt.Errorf("New liquidation price must higher than old liquidation price. current liquidation price: %v , new liquidation price : %v ", lendingTrade.LiquidationPrice, newLiquidationPrice)
}
newLockedAmount := new(big.Int).Mul(lendingTrade.CollateralLockedAmount, lendingTrade.LiquidationPrice)
newLockedAmount = new(big.Int).Div(newLockedAmount, newLiquidationPrice)
@@ -1250,7 +1251,7 @@ func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.Lending
log.Debug("ProcessRecallLendingTrade", "newLockedAmount", newLockedAmount, "recallAmount", recallAmount, "oldLiquidationPrice", lendingTrade.LiquidationPrice, "newLiquidationPrice", newLiquidationPrice)
err := tradingStateDb.RemoveLiquidationPrice(tradingstate.GetTradingOrderBookHash(lendingTrade.CollateralToken, lendingTrade.LendingToken), lendingTrade.LiquidationPrice, lendingBook, lendingTrade.TradeId)
if err != nil {
- return err, true, nil
+ return true, nil, err
}
err = lendingstate.AddTokenBalance(lendingTrade.Borrower, recallAmount, lendingTrade.CollateralToken, statedb)
if err != nil {
@@ -1268,5 +1269,5 @@ func (l *Lending) ProcessRecallLendingTrade(lendingStateDB *lendingstate.Lending
newLendingTrade.LiquidationPrice = newLiquidationPrice
newLendingTrade.CollateralLockedAmount = newLockedAmount
log.Debug("ProcessRecall", "price", newLiquidationPrice, "lockAmount", newLockedAmount, "recall amount", recallAmount)
- return nil, false, &newLendingTrade
+ return false, &newLendingTrade, nil
}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 4cf5668ff6..790505b8c0 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -19,6 +19,7 @@ package abi
import (
"bytes"
"encoding/hex"
+ "errors"
"fmt"
"log"
"math/big"
@@ -713,3 +714,34 @@ func TestABI_MethodById(t *testing.T) {
}
}
+
+func TestUnpackRevert(t *testing.T) {
+ t.Parallel()
+
+ var cases = []struct {
+ input string
+ expect string
+ expectErr error
+ }{
+ {"", "", errors.New("invalid data for unpacking")},
+ {"08c379a1", "", errors.New("invalid data for unpacking")},
+ {"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
+ }
+ for index, c := range cases {
+ t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
+ got, err := UnpackRevert(common.Hex2Bytes(c.input))
+ if c.expectErr != nil {
+ if err == nil {
+ t.Fatalf("Expected non-nil error")
+ }
+ if err.Error() != c.expectErr.Error() {
+ t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
+ }
+ return
+ }
+ if c.expect != got {
+ t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
+ }
+ })
+ }
+}
diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go
index 1db4a496af..a53aeef366 100644
--- a/accounts/abi/bind/auth.go
+++ b/accounts/abi/bind/auth.go
@@ -20,7 +20,6 @@ import (
"crypto/ecdsa"
"errors"
"io"
- "io/ioutil"
"math/big"
"github.com/XinFinOrg/XDPoSChain/accounts"
@@ -80,7 +79,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
// NewTransactorWithChainID is a utility method to easily create a transaction signer from
// an encrypted json key stream and the associated passphrase.
func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
- json, err := ioutil.ReadAll(keyin)
+ json, err := io.ReadAll(keyin)
if err != nil {
return nil, err
}
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
index 126787963a..25ac008c03 100644
--- a/accounts/abi/bind/backend.go
+++ b/accounts/abi/bind/backend.go
@@ -32,12 +32,12 @@ var (
// have any code associated with it (i.e. suicided).
ErrNoCode = errors.New("no contract code at given address")
- // This error is raised when attempting to perform a pending state action
+ // ErrNoPendingState is raised when attempting to perform a pending state action
// on a backend that doesn't implement PendingContractCaller.
ErrNoPendingState = errors.New("backend does not support pending state")
- // This error is returned by WaitDeployed if contract creation leaves an
- // empty contract behind.
+ // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
+ // an empty contract behind.
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
)
@@ -47,7 +47,8 @@ type ContractCaller interface {
// CodeAt returns the code of the given account. This is needed to differentiate
// between contract internal errors and the local chain being out of sync.
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
- // ContractCall executes an Ethereum contract call with the specified data as the
+
+ // CallContract executes an Ethereum contract call with the specified data as the
// input.
CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error)
}
@@ -58,6 +59,7 @@ type ContractCaller interface {
type PendingContractCaller interface {
// PendingCodeAt returns the code of the given account in the pending state.
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
+
// PendingCallContract executes an Ethereum contract call against the pending state.
PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error)
}
@@ -67,19 +69,31 @@ type PendingContractCaller interface {
// used when the user does not provide some needed values, but rather leaves it up
// to the transactor to decide.
type ContractTransactor interface {
+ // HeaderByNumber returns a block header from the current canonical chain. If
+ // number is nil, the latest known header is returned.
+ HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
+
// PendingCodeAt returns the code of the given account in the pending state.
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
+
// PendingNonceAt retrieves the current pending nonce associated with an account.
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
+
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction.
SuggestGasPrice(ctx context.Context) (*big.Int, error)
+
+ // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
+ // a timely execution of a transaction.
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+
// EstimateGas tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as other
// transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default.
EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error)
+
// SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(ctx context.Context, tx *types.Transaction) error
}
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 08a7260d26..12005705c7 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
"github.com/XinFinOrg/XDPoSChain/accounts"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi"
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
"github.com/XinFinOrg/XDPoSChain/common"
@@ -42,6 +43,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/eth/filters"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/event"
@@ -53,7 +55,6 @@ import (
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
-var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
@@ -86,10 +87,10 @@ func SimulateWalletAddressAndSignFn() (common.Address, func(account accounts.Acc
pass := "" // not used but required by API
a1, err := ks.NewAccount(pass)
if err != nil {
- return common.Address{}, nil, fmt.Errorf(err.Error())
+ return common.Address{}, nil, err
}
if err := ks.Unlock(a1, ""); err != nil {
- return a1.Address, nil, fmt.Errorf(err.Error())
+ return a1.Address, nil, err
}
return a1.Address, ks.SignHash, nil
}
@@ -101,7 +102,7 @@ func NewXDCSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64, chainConfi
GasLimit: gasLimit, // need this big, support initial smart contract
Config: chainConfig,
Alloc: alloc,
- ExtraData: append(make([]byte, 32), make([]byte, 65)...),
+ ExtraData: append(make([]byte, 32), make([]byte, crypto.SignatureLength)...),
}
genesis.MustCommit(database)
consensus := XDPoS.NewFaker(database, chainConfig)
@@ -160,6 +161,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
return backend
}
+// Close terminates the underlying blockchain's update loop.
+func (b *SimulatedBackend) Close() error {
+ b.blockchain.Stop()
+ return nil
+}
+
// Commit imports all the pending transactions as a single block and starts a
// fresh new state.
func (b *SimulatedBackend) Commit() {
@@ -256,6 +263,19 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
return receipt, nil
}
+// HeaderByNumber returns a block header from the current canonical chain. If number is
+// nil, the latest known header is returned.
+func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
+ return b.blockchain.CurrentHeader(), nil
+ }
+
+ return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
+}
+
// PendingCodeAt returns the code associated with an account in the pending state.
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
b.mu.Lock()
@@ -276,8 +296,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call XDPoSChain.Cal
if err != nil {
return nil, err
}
- rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
- return rval, err
+ res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
+ if err != nil {
+ return nil, err
+ }
+ return res.Return(), nil
}
// PendingCallContract executes a contract call on the pending state.
@@ -286,8 +309,11 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call XDPoSCh
defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
- rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
- return rval, err
+ res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
+ if err != nil {
+ return nil, err
+ }
+ return res.Return(), nil
}
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
@@ -300,8 +326,17 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
}
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
-// chain doens't have miners, we just return a gas price of 1 for any call.
+// chain doesn't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
+ if b.pendingBlock.Header().BaseFee != nil {
+ return b.pendingBlock.Header().BaseFee, nil
+ }
+ return big.NewInt(1), nil
+}
+
+// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
+// chain doesn't have miners, we just return a gas tip of 1 for any call.
+func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}
@@ -325,22 +360,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
- executable := func(gas uint64) bool {
+ executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
call.Gas = gas
snapshot := b.pendingState.Snapshot()
- _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
+ res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
b.pendingState.RevertToSnapshot(snapshot)
- if err != nil || failed {
- return false
+ if err != nil {
+ if err == core.ErrIntrinsicGas {
+ return true, nil, nil // Special case, raise gas limit
+ }
+ return true, nil, err // Bail out
}
- return true
+ return res.Failed(), res, nil
}
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
mid := (hi + lo) / 2
- if !executable(mid) {
+ failed, _, err := executable(mid)
+
+ // If the error is not nil(consensus error), it means the provided message
+ // call or transaction will never be accepted no matter how much gas it is
+ // assigned. Return the error directly, don't struggle any more
+ if err != nil {
+ return 0, err
+ }
+ if failed {
lo = mid
} else {
hi = mid
@@ -348,8 +394,25 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
}
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
- if !executable(hi) {
- return 0, errGasEstimationFailed
+ failed, result, err := executable(hi)
+ if err != nil {
+ return 0, err
+ }
+ if failed {
+ if result != nil && result.Err != vm.ErrOutOfGas {
+ errMsg := fmt.Sprintf("always failing transaction (%v)", result.Err)
+ if len(result.Revert()) > 0 {
+ ret, err := abi.UnpackRevert(result.Revert())
+ if err != nil {
+ errMsg += fmt.Sprintf(" (%#x)", result.Revert())
+ } else {
+ errMsg += fmt.Sprintf(" (%s)", ret)
+ }
+ }
+ return 0, errors.New(errMsg)
+ }
+ // Otherwise, the specified gas cap is too low
+ return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
}
}
return hi, nil
@@ -357,11 +420,39 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
// callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary.
-func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (ret []byte, usedGas uint64, failed bool, err error) {
- // Ensure message is initialized properly.
- if call.GasPrice == nil {
- call.GasPrice = big.NewInt(1)
+func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
+ // Gas prices post 1559 need to be initialized
+ if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
+ return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
+ head := b.blockchain.CurrentHeader()
+ if !b.blockchain.Config().IsEIP1559(head.Number) {
+ // If there's no basefee, then it must be a non-1559 execution
+ if call.GasPrice == nil {
+ call.GasPrice = new(big.Int)
+ }
+ call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
+ } else {
+ // A basefee is provided, necessitating 1559-type execution
+ if call.GasPrice != nil {
+ // User specified the legacy gas field, convert to 1559 gas typing
+ call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
+ } else {
+ // User specified 1559 gas feilds (or none), use those
+ if call.GasFeeCap == nil {
+ call.GasFeeCap = new(big.Int)
+ }
+ if call.GasTipCap == nil {
+ call.GasTipCap = new(big.Int)
+ }
+ // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
+ call.GasPrice = new(big.Int)
+ if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
+ call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap)
+ }
+ }
+ }
+ // Ensure message is initialized properly.
if call.Gas == 0 {
call.Gas = 50000000
}
@@ -379,14 +470,16 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal
msg.CallMsg.BalanceTokenFee = value
}
}
- evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
+
+ txContext := core.NewEVMTxContext(msg)
+ evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
- vmenv := vm.NewEVM(evmContext, statedb, nil, b.config, vm.Config{})
+ vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true})
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
- ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
- return
+ res, err, _ := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
+ return res, err
}
// SendTransaction updates the pending block to include the given transaction.
@@ -520,9 +613,11 @@ type callMsg struct {
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
-func (m callMsg) CheckNonce() bool { return false }
+func (m callMsg) IsFake() bool { return true }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
+func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
@@ -552,7 +647,11 @@ func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*t
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
- return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
+ number := rawdb.ReadHeaderNumber(fb.db, hash)
+ if number == nil {
+ return nil, nil
+ }
+ return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
}
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go
new file mode 100644
index 0000000000..5681898523
--- /dev/null
+++ b/accounts/abi/bind/backends/simulated_test.go
@@ -0,0 +1,154 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package backends
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "strings"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/params"
+)
+
+func TestSimulatedBackend_EstimateGas(t *testing.T) {
+ /*
+ pragma solidity ^0.6.4;
+ contract GasEstimation {
+ function PureRevert() public { revert(); }
+ function Revert() public { revert("revert reason");}
+ function OOG() public { for (uint i = 0; ; i++) {}}
+ function Assert() public { assert(false);}
+ function Valid() public {}
+ }*/
+ const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
+ const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
+
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ opts := bind.NewKeyedTransactor(key)
+
+ sim := NewXDCSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000, ¶ms.ChainConfig{
+ ConstantinopleBlock: big.NewInt(0),
+ XDPoS: ¶ms.XDPoSConfig{
+ Epoch: 900,
+ SkipV1Validation: true,
+ V2: ¶ms.V2{
+ SwitchBlock: big.NewInt(900),
+ CurrentConfig: params.UnitTestV2Configs[0],
+ },
+ },
+ })
+
+ defer sim.Close()
+
+ parsed, _ := abi.JSON(strings.NewReader(contractAbi))
+ contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
+ sim.Commit()
+
+ var cases = []struct {
+ name string
+ message XDPoSChain.CallMsg
+ expect uint64
+ expectError error
+ }{
+ {"plain transfer(valid)", XDPoSChain.CallMsg{
+ From: addr,
+ To: &addr,
+ Gas: 0,
+ GasPrice: big.NewInt(0),
+ Value: big.NewInt(1),
+ Data: nil,
+ }, params.TxGas, nil},
+
+ {"plain transfer(invalid)", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 0,
+ GasPrice: big.NewInt(0),
+ Value: big.NewInt(1),
+ Data: nil,
+ }, 0, errors.New("always failing transaction (execution reverted)")},
+
+ {"Revert", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 0,
+ GasPrice: big.NewInt(0),
+ Value: nil,
+ Data: common.Hex2Bytes("d8b98391"),
+ }, 0, errors.New("always failing transaction (execution reverted) (revert reason)")},
+
+ {"PureRevert", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 0,
+ GasPrice: big.NewInt(0),
+ Value: nil,
+ Data: common.Hex2Bytes("aa8b1d30"),
+ }, 0, errors.New("always failing transaction (execution reverted)")},
+
+ {"OOG", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 100000,
+ GasPrice: big.NewInt(0),
+ Value: nil,
+ Data: common.Hex2Bytes("50f6fe34"),
+ }, 0, errors.New("gas required exceeds allowance (100000)")},
+
+ {"Assert", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 100000,
+ GasPrice: big.NewInt(0),
+ Value: nil,
+ Data: common.Hex2Bytes("b9b046f9"),
+ }, 0, errors.New("always failing transaction (invalid opcode: INVALID)")},
+
+ {"Valid", XDPoSChain.CallMsg{
+ From: addr,
+ To: &contractAddr,
+ Gas: 100000,
+ GasPrice: big.NewInt(0),
+ Value: nil,
+ Data: common.Hex2Bytes("e09fface"),
+ }, 21483, nil},
+ }
+ for _, c := range cases {
+ got, err := sim.EstimateGas(context.Background(), c.message)
+ if c.expectError != nil {
+ if err == nil {
+ t.Fatalf("Expect error, got nil")
+ }
+ if c.expectError.Error() != err.Error() {
+ t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
+ }
+ continue
+ }
+ if got != c.expect {
+ t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
+ }
+ }
+}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 4d5390d15b..ebdd9b6cc3 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -53,11 +53,15 @@ type TransactOpts struct {
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
Signer SignerFn // Method to use for signing the transaction (mandatory)
- Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
- GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
- GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
+ Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
+ GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
+ GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
+ GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+
+ NoSend bool // Do all transact steps but do not send the transaction
}
// FilterOpts is the collection of options to fine tune filtering for events
@@ -184,57 +188,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
return c.transact(opts, &c.address, nil)
}
-// transact executes an actual transaction invocation, first deriving any missing
-// authorization fields, and then scheduling the transaction for execution.
-func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
- var err error
-
- // Ensure a valid value field and resolve the account nonce
+func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
+ // Normalize value
value := opts.Value
if value == nil {
value = new(big.Int)
}
- var nonce uint64
- if opts.Nonce == nil {
- nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+ // Estimate TipCap
+ gasTipCap := opts.GasTipCap
+ if gasTipCap == nil {
+ tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
if err != nil {
- return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
+ return nil, err
}
- } else {
- nonce = opts.Nonce.Uint64()
+ gasTipCap = tip
}
- // Figure out the gas allowance and gas price values
+ // Estimate FeeCap
+ gasFeeCap := opts.GasFeeCap
+ if gasFeeCap == nil {
+ gasFeeCap = new(big.Int).Add(
+ gasTipCap,
+ new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
+ )
+ }
+ if gasFeeCap.Cmp(gasTipCap) < 0 {
+ return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
+ }
+ baseTx := &types.DynamicFeeTx{
+ To: contract,
+ Nonce: nonce,
+ GasFeeCap: gasFeeCap,
+ GasTipCap: gasTipCap,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return types.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
+ if opts.GasFeeCap != nil || opts.GasTipCap != nil {
+ return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but EIP-1559 is not active yet")
+ }
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = new(big.Int)
+ }
+ // Estimate GasPrice
gasPrice := opts.GasPrice
if gasPrice == nil {
- gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
+ price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
- return nil, fmt.Errorf("failed to suggest gas price: %v", err)
+ return nil, err
}
+ gasPrice = price
}
+ // Estimate GasLimit
gasLimit := opts.GasLimit
- if gasLimit == 0 {
- // Gas estimation cannot succeed without code for method invocations
- if contract != nil {
- if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
- return nil, err
- } else if len(code) == 0 {
- return nil, ErrNoCode
- }
- }
- // If the contract surely has code (or code is not needed), estimate the transaction
- msg := XDPoSChain.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
- gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
if err != nil {
- return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
+ return nil, err
}
}
- // Create the transaction, sign it and schedule it for execution
- var rawTx *types.Transaction
- if contract == nil {
- rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
- } else {
- rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
+ // create the transaction
+ nonce, err := c.getNonce(opts)
+ if err != nil {
+ return nil, err
}
+ baseTx := &types.LegacyTx{
+ To: contract,
+ Nonce: nonce,
+ GasPrice: gasPrice,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
+ }
+ return types.NewTx(baseTx), nil
+}
+
+func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
+ if contract != nil {
+ // Gas estimation cannot succeed without code for method invocations.
+ if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
+ return 0, err
+ } else if len(code) == 0 {
+ return 0, ErrNoCode
+ }
+ }
+ msg := XDPoSChain.CallMsg{
+ From: opts.From,
+ To: contract,
+ GasPrice: gasPrice,
+ GasTipCap: gasTipCap,
+ GasFeeCap: gasFeeCap,
+ Value: value,
+ Data: input,
+ }
+ return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+}
+
+func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
+ if opts.Nonce == nil {
+ return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+ } else {
+ return opts.Nonce.Uint64(), nil
+ }
+}
+
+// transact executes an actual transaction invocation, first deriving any missing
+// authorization fields, and then scheduling the transaction for execution.
+func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
+ if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
+ return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ }
+ // Create the transaction
+ var (
+ rawTx *types.Transaction
+ err error
+ )
+ if opts.GasPrice != nil {
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ } else {
+ // Only query for basefee if gasPrice not specified
+ if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
+ return nil, errHead
+ } else if head.BaseFee != nil {
+ rawTx, err = c.createDynamicTx(opts, contract, input, head)
+ } else {
+ // Chain is not London ready -> use legacy transaction
+ rawTx, err = c.createLegacyTx(opts, contract, input)
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ // Sign the transaction and schedule it for execution
if opts.Signer == nil {
return nil, errors.New("no signer to authorize the transaction with")
}
@@ -242,6 +347,9 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if err != nil {
return nil, err
}
+ if opts.NoSend {
+ return signedTx, nil
+ }
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
return nil, err
}
@@ -280,7 +388,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
if err != nil {
return nil, nil, err
}
- sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
+ sub := event.NewSubscription(func(quit <-chan struct{}) error {
for _, log := range buff {
select {
case logs <- log:
@@ -289,11 +397,8 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
}
}
return nil
- }), nil
+ })
- if err != nil {
- return nil, nil, err
- }
return logs, sub, nil
}
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
new file mode 100644
index 0000000000..037f1d24e8
--- /dev/null
+++ b/accounts/abi/bind/base_test.go
@@ -0,0 +1,177 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bind_test
+
+import (
+ "context"
+ "math/big"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/stretchr/testify/assert"
+)
+
+func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
+
+type mockTransactor struct {
+ baseFee *big.Int
+ gasTipCap *big.Int
+ gasPrice *big.Int
+ suggestGasTipCapCalled bool
+ suggestGasPriceCalled bool
+}
+
+func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
+ return &types.Header{BaseFee: mt.baseFee}, nil
+}
+
+func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
+ return []byte{1}, nil
+}
+
+func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
+ return 0, nil
+}
+
+func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
+ mt.suggestGasPriceCalled = true
+ return mt.gasPrice, nil
+}
+
+func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ mt.suggestGasTipCapCalled = true
+ return mt.gasTipCap, nil
+}
+
+func (mt *mockTransactor) EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error) {
+ return 0, nil
+}
+
+func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
+ return nil
+}
+
+type mockCaller struct {
+ codeAtBlockNumber *big.Int
+ callContractBlockNumber *big.Int
+ pendingCodeAtCalled bool
+ pendingCallContractCalled bool
+}
+
+func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
+ mc.codeAtBlockNumber = blockNumber
+ return []byte{1, 2, 3}, nil
+}
+
+func (mc *mockCaller) CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error) {
+ mc.callContractBlockNumber = blockNumber
+ return nil, nil
+}
+
+func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
+ mc.pendingCodeAtCalled = true
+ return nil, nil
+}
+
+func (mc *mockCaller) PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error) {
+ mc.pendingCallContractCalled = true
+ return nil, nil
+}
+func TestPassingBlockNumber(t *testing.T) {
+
+ mc := &mockCaller{}
+
+ bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
+ Methods: map[string]abi.Method{
+ "something": {
+ Name: "something",
+ Outputs: abi.Arguments{},
+ },
+ },
+ }, mc, nil, nil)
+
+ bc.Call(&bind.CallOpts{}, nil, "something")
+
+ bc.Call(&bind.CallOpts{}, nil, "something")
+
+ if mc.callContractBlockNumber != nil {
+ t.Fatalf("CallContract() was passed a block number when it should not have been")
+ }
+
+ if mc.codeAtBlockNumber != nil {
+ t.Fatalf("CodeAt() was passed a block number when it should not have been")
+ }
+
+ bc.Call(&bind.CallOpts{Pending: true}, nil, "something")
+
+ if !mc.pendingCallContractCalled {
+ t.Fatalf("CallContract() was not passed the block number")
+ }
+
+ if !mc.pendingCodeAtCalled {
+ t.Fatalf("CodeAt() was not passed the block number")
+ }
+}
+
+func TestTransactGasFee(t *testing.T) {
+ assert := assert.New(t)
+
+ // GasTipCap and GasFeeCap
+ // When opts.GasTipCap and opts.GasFeeCap are nil
+ mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
+ bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
+ opts := &bind.TransactOpts{Signer: mockSign}
+ tx, err := bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(5), tx.GasTipCap())
+ assert.Equal(big.NewInt(205), tx.GasFeeCap())
+ assert.Nil(opts.GasTipCap)
+ assert.Nil(opts.GasFeeCap)
+ assert.True(mt.suggestGasTipCapCalled)
+
+ // Second call to Transact should use latest suggested GasTipCap
+ mt.gasTipCap = big.NewInt(6)
+ mt.suggestGasTipCapCalled = false
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(6), tx.GasTipCap())
+ assert.Equal(big.NewInt(206), tx.GasFeeCap())
+ assert.True(mt.suggestGasTipCapCalled)
+
+ // GasPrice
+ // When opts.GasPrice is nil
+ mt = &mockTransactor{gasPrice: big.NewInt(5)}
+ bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
+ opts = &bind.TransactOpts{Signer: mockSign}
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(5), tx.GasPrice())
+ assert.Nil(opts.GasPrice)
+ assert.True(mt.suggestGasPriceCalled)
+
+ // Second call to Transact should use latest suggested GasPrice
+ mt.gasPrice = big.NewInt(6)
+ mt.suggestGasPriceCalled = false
+ tx, err = bc.Transact(opts, "")
+ assert.Nil(err)
+ assert.Equal(big.NewInt(6), tx.GasPrice())
+ assert.True(mt.suggestGasPriceCalled)
+}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index 1fd65acef5..79d05afb44 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -37,8 +37,6 @@ type Lang int
const (
LangGo Lang = iota
- LangJava
- LangObjC
)
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
@@ -119,7 +117,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
}
contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]),
- InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
+ InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
InputBin: strings.TrimSpace(bytecodes[i]),
Constructor: evmABI.Constructor,
Calls: calls,
@@ -160,15 +158,15 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
// bindType is a set of type binders that convert Solidity types to some supported
// programming language types.
var bindType = map[Lang]func(kind abi.Type) string{
- LangGo: bindTypeGo,
- LangJava: bindTypeJava,
+ LangGo: bindTypeGo,
}
// Helper function for the binding generators.
// It reads the unmatched characters after the inner type-match,
-// (since the inner type is a prefix of the total type declaration),
-// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
-// and returns the sizes of these arrays.
+//
+// (since the inner type is a prefix of the total type declaration),
+// looks for valid arrays (possibly a dynamic one) wrapping the inner type,
+// and returns the sizes of these arrays.
//
// Returned array sizes are in the same order as solidity signatures; inner array size first.
// Array sizes may also be "", indicating a dynamic array.
@@ -244,15 +242,6 @@ func arrayBindingJava(inner string, arraySizes []string) string {
return inner + strings.Repeat("[]", len(arraySizes))
}
-// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
-// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
-// mapped will use an upscaled type (e.g. BigDecimal).
-func bindTypeJava(kind abi.Type) string {
- stringKind := kind.String()
- innerLen, innerMapping := bindUnnestedTypeJava(stringKind)
- return arrayBindingJava(wrapArray(stringKind, innerLen, innerMapping))
-}
-
// The inner function of bindTypeJava, this finds the inner type of stringKind.
// (Or just the type itself if it is not an array or slice)
// The length of the matched part is returned, with the the translated type.
@@ -311,8 +300,7 @@ func bindUnnestedTypeJava(stringKind string) (int, string) {
// bindTopicType is a set of type binders that convert Solidity types to some
// supported programming language topic types.
var bindTopicType = map[Lang]func(kind abi.Type) string{
- LangGo: bindTopicTypeGo,
- LangJava: bindTopicTypeJava,
+ LangGo: bindTopicTypeGo,
}
// bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
@@ -325,64 +313,16 @@ func bindTopicTypeGo(kind abi.Type) string {
return bound
}
-// bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
-// funcionality as for simple types, but dynamic types get converted to hashes.
-func bindTopicTypeJava(kind abi.Type) string {
- bound := bindTypeJava(kind)
- if bound == "String" || bound == "Bytes" {
- bound = "Hash"
- }
- return bound
-}
-
// namedType is a set of functions that transform language specific types to
// named versions that my be used inside method names.
var namedType = map[Lang]func(string, abi.Type) string{
- LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
- LangJava: namedTypeJava,
-}
-
-// namedTypeJava converts some primitive data types to named variants that can
-// be used as parts of method names.
-func namedTypeJava(javaKind string, solKind abi.Type) string {
- switch javaKind {
- case "byte[]":
- return "Binary"
- case "byte[][]":
- return "Binaries"
- case "string":
- return "String"
- case "string[]":
- return "Strings"
- case "boolean":
- return "Bool"
- case "boolean[]":
- return "Bools"
- case "BigInt[]":
- return "BigInts"
- default:
- parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
- if len(parts) != 4 {
- return javaKind
- }
- switch parts[2] {
- case "8", "16", "32", "64":
- if parts[3] == "" {
- return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
- }
- return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
-
- default:
- return javaKind
- }
- }
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
}
// methodNormalizer is a name transformer that modifies Solidity method names to
// conform to target language naming concentions.
var methodNormalizer = map[Lang]func(string) string{
- LangGo: capitalise,
- LangJava: decapitalise,
+ LangGo: capitalise,
}
// capitalise makes a camel-case string which starts with an upper case character.
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index ce6e36bffd..2c477882eb 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -229,7 +229,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy an interaction tester contract and call a transaction on it
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
@@ -270,7 +270,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a tuple tester contract and execute a structured call on it
_, _, getter, err := DeployGetter(auth, sim)
@@ -302,7 +302,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a tuple tester contract and execute a structured call on it
_, _, tupler, err := DeployTupler(auth, sim)
@@ -344,7 +344,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a slice tester contract and execute a n array call on it
_, _, slicer, err := DeploySlicer(auth, sim)
@@ -378,7 +378,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a default method invoker contract and execute its default method
_, _, defaulter, err := DeployDefaulter(auth, sim)
@@ -447,7 +447,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a funky gas pattern contract
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
@@ -482,7 +482,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a sender tester contract and execute a structured call on it
_, _, callfrom, err := DeployCallFrom(auth, sim)
@@ -542,7 +542,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy a underscorer tester contract and execute a structured call on it
_, _, underscorer, err := DeployUnderscorer(auth, sim)
@@ -612,7 +612,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
// Deploy an eventer contract
_, _, eventer, err := DeployEventer(auth, sim)
@@ -761,7 +761,7 @@ var bindTests = []struct {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
+ sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
//deploy the test contract
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
@@ -821,7 +821,7 @@ func TestBindings(t *testing.T) {
}
t.Log("Using config", params.TestXDPoSMockChainConfig)
// Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845)
- linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}")
+ linkTestCode := "package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}"
linkTestDeps, err := imports.Process(os.TempDir(), []byte(linkTestCode), nil)
if err != nil {
t.Fatalf("failed check for goimports symlink bug: %v", err)
diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go
index eca7a6882e..61951dcd7c 100644
--- a/accounts/abi/bind/template.go
+++ b/accounts/abi/bind/template.go
@@ -52,8 +52,7 @@ type tmplEvent struct {
// tmplSource is language to template mapping containing all the supported
// programming languages the package can generate to.
var tmplSource = map[Lang]string{
- LangGo: tmplSourceGo,
- LangJava: tmplSourceJava,
+ LangGo: tmplSourceGo,
}
// tmplSourceGo is the Go source template use to generate the contract binding
@@ -418,105 +417,3 @@ package {{.Package}}
{{end}}
{{end}}
`
-
-// tmplSourceJava is the Java source template use to generate the contract binding
-// based on.
-const tmplSourceJava = `
-// This file is an automatically generated Java binding. Do not modify as any
-// change will likely be lost upon the next re-generation!
-
-package {{.Package}};
-
-import org.ethereum.geth.*;
-import org.ethereum.geth.internal.*;
-
-{{range $contract := .Contracts}}
- public class {{.Type}} {
- // ABI is the input ABI used to generate the binding from.
- public final static String ABI = "{{.InputABI}}";
-
- {{if .InputBin}}
- // BYTECODE is the compiled bytecode used for deploying new contracts.
- public final static byte[] BYTECODE = "{{.InputBin}}".getBytes();
-
- // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
- public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
- Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
- {{range $index, $element := .Constructor.Inputs}}
- args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
- {{end}}
- return new {{.Type}}(Geth.deployContract(auth, ABI, BYTECODE, client, args));
- }
-
- // Internal constructor used by contract deployment.
- private {{.Type}}(BoundContract deployment) {
- this.Address = deployment.getAddress();
- this.Deployer = deployment.getDeployer();
- this.Contract = deployment;
- }
- {{end}}
-
- // Ethereum address where this contract is located at.
- public final Address Address;
-
- // Ethereum transaction in which this contract was deployed (if known!).
- public final Transaction Deployer;
-
- // Contract instance bound to a blockchain address.
- private final BoundContract Contract;
-
- // Creates a new instance of {{.Type}}, bound to a specific deployed contract.
- public {{.Type}}(Address address, EthereumClient client) throws Exception {
- this(Geth.bindContract(address, ABI, client));
- }
-
- {{range .Calls}}
- {{if gt (len .Normalized.Outputs) 1}}
- // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
- public class {{capitalise .Normalized.Name}}Results {
- {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
- {{end}}
- }
- {{end}}
-
- // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}.
- //
- // Solidity: {{.Original.String}}
- public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
- Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
- {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
- {{end}}
-
- Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
- {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type) .Type}}(); results.set({{$index}}, result{{$index}});
- {{end}}
-
- if (opts == null) {
- opts = Geth.newCallOpts();
- }
- this.Contract.call(opts, results, "{{.Original.Name}}", args);
- {{if gt (len .Normalized.Outputs) 1}}
- {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
- {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type) .Type}}();
- {{end}}
- return result;
- {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type) .Type}}();{{end}}
- {{end}}
- }
- {{end}}
-
- {{range .Transacts}}
- // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}.
- //
- // Solidity: {{.Original.String}}
- public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type}} {{.Name}}{{end}}) throws Exception {
- Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
- {{range $index, $item := .Normalized.Inputs}}args.set({{$index}}, Geth.newInterface()); args.get({{$index}}).set{{namedtype (bindtype .Type) .Type}}({{.Name}});
- {{end}}
-
- return this.Contract.transact(opts, "{{.Original.Name}}" , args);
- }
- {{end}}
- }
-{{end}}
-`
diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go
index 06efcd980c..5b52249209 100644
--- a/accounts/abi/bind/util_test.go
+++ b/accounts/abi/bind/util_test.go
@@ -53,15 +53,20 @@ var waitDeployedTests = map[string]struct {
}
func TestWaitDeployed(t *testing.T) {
+ config := *params.TestXDPoSMockChainConfig
+ config.Eip1559Block = big.NewInt(0)
for name, test := range waitDeployedTests {
backend := backends.NewXDCSimulatedBackend(
core.GenesisAlloc{
- crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
- }, 10000000, params.TestXDPoSMockChainConfig,
+ crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(100000000000000000)},
+ }, 10000000, &config,
)
- // Create the transaction.
- tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
+ // Create the transaction
+ head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
+ gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
+
+ tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
// Wait for it to get mined in the background.
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index 7f6f0b4a0a..cb9787854c 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -102,7 +102,7 @@ func TestTypeRegexp(t *testing.T) {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
}
if !reflect.DeepEqual(typ, tt.kind) {
- t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
+ t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
}
}
}
diff --git a/accounts/keystore/keystore_passphrase.go b/accounts/keystore/keystore_passphrase.go
index 9e597678da..a58d152b1c 100644
--- a/accounts/keystore/keystore_passphrase.go
+++ b/accounts/keystore/keystore_passphrase.go
@@ -201,11 +201,11 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
if keyProtected.Version != version {
- return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
+ return nil, nil, fmt.Errorf("not supported Version: %v", keyProtected.Version)
}
if keyProtected.Crypto.Cipher != "aes-128-ctr" {
- return nil, nil, fmt.Errorf("Cipher not supported: %v", keyProtected.Crypto.Cipher)
+ return nil, nil, fmt.Errorf("not supported Cipher: %v", keyProtected.Crypto.Cipher)
}
keyId = uuid.Parse(keyProtected.Id)
@@ -293,13 +293,13 @@ func getKDFKey(cryptoJSON cryptoJSON, auth string) ([]byte, error) {
c := ensureInt(cryptoJSON.KDFParams["c"])
prf := cryptoJSON.KDFParams["prf"].(string)
if prf != "hmac-sha256" {
- return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
+ return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
}
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
return key, nil
}
- return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
+ return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
}
// TODO: can we do without this when unmarshalling dynamic JSON?
diff --git a/accounts/keystore/keystore_plain_test.go b/accounts/keystore/keystore_plain_test.go
index 73a58b507f..4a977d2eac 100644
--- a/accounts/keystore/keystore_plain_test.go
+++ b/accounts/keystore/keystore_plain_test.go
@@ -211,7 +211,7 @@ func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
}
privHex := hex.EncodeToString(privBytes)
if test.Priv != privHex {
- t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
+ t.Fatal(fmt.Errorf("decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
}
}
@@ -222,7 +222,7 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
}
privHex := hex.EncodeToString(privBytes)
if test.Priv != privHex {
- t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
+ t.Fatal(fmt.Errorf("decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
}
}
diff --git a/accounts/url.go b/accounts/url.go
index 47f9d8ee4b..d5c9c645c7 100644
--- a/accounts/url.go
+++ b/accounts/url.go
@@ -64,7 +64,7 @@ func (u URL) String() string {
func (u URL) TerminalString() string {
url := u.String()
if len(url) > 32 {
- return url[:31] + "…"
+ return url[:31] + ".."
}
return url
}
@@ -76,10 +76,9 @@ func (u URL) MarshalJSON() ([]byte, error) {
// Cmp compares x and y and returns:
//
-// -1 if x < y
-// 0 if x == y
-// +1 if x > y
-//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
func (u URL) Cmp(url URL) int {
if u.Scheme == url.Scheme {
return strings.Compare(u.Path, url.Path)
diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go
index d2259eacd0..f9d06aa032 100644
--- a/accounts/usbwallet/ledger.go
+++ b/accounts/usbwallet/ledger.go
@@ -32,6 +32,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
@@ -164,7 +165,7 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
}
// Ensure the wallet is capable of signing the given transaction
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
- return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
+ return common.Address{}, nil, fmt.Errorf("ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
}
// All infos gathered and metadata checks out, request signing
return w.ledgerSign(path, tx, chainID)
@@ -175,18 +176,18 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
//
// The version retrieval protocol is defined as follows:
//
-// CLA | INS | P1 | P2 | Lc | Le
-// ----+-----+----+----+----+---
-// E0 | 06 | 00 | 00 | 00 | 04
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+----+---
+// E0 | 06 | 00 | 00 | 00 | 04
//
// With no input data, and the output data being:
//
-// Description | Length
-// ---------------------------------------------------+--------
-// Flags 01: arbitrary data signature enabled by user | 1 byte
-// Application major version | 1 byte
-// Application minor version | 1 byte
-// Application patch version | 1 byte
+// Description | Length
+// ---------------------------------------------------+--------
+// Flags 01: arbitrary data signature enabled by user | 1 byte
+// Application major version | 1 byte
+// Application minor version | 1 byte
+// Application patch version | 1 byte
func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
// Send the request and wait for the response
reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
@@ -207,32 +208,32 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
//
// The address derivation protocol is defined as follows:
//
-// CLA | INS | P1 | P2 | Lc | Le
-// ----+-----+----+----+-----+---
-// E0 | 02 | 00 return address
-// 01 display address and confirm before returning
-// | 00: do not return the chain code
-// | 01: return the chain code
-// | var | 00
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 02 | 00 return address
+// 01 display address and confirm before returning
+// | 00: do not return the chain code
+// | 01: return the chain code
+// | var | 00
//
// Where the input data is:
//
-// Description | Length
-// -------------------------------------------------+--------
-// Number of BIP 32 derivations to perform (max 10) | 1 byte
-// First derivation index (big endian) | 4 bytes
-// ... | 4 bytes
-// Last derivation index (big endian) | 4 bytes
+// Description | Length
+// -------------------------------------------------+--------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
//
// And the output data is:
//
-// Description | Length
-// ------------------------+-------------------
-// Public Key length | 1 byte
-// Uncompressed Public Key | arbitrary
-// Ethereum address length | 1 byte
-// Ethereum address | 40 bytes hex ascii
-// Chain code if requested | 32 bytes
+// Description | Length
+// ------------------------+-------------------
+// Public Key length | 1 byte
+// Uncompressed Public Key | arbitrary
+// Ethereum address length | 1 byte
+// Ethereum address | 40 bytes hex ascii
+// Chain code if requested | 32 bytes
func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) {
// Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath))
@@ -268,35 +269,35 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
//
// The transaction signing protocol is defined as follows:
//
-// CLA | INS | P1 | P2 | Lc | Le
-// ----+-----+----+----+-----+---
-// E0 | 04 | 00: first transaction data block
-// 80: subsequent transaction data block
-// | 00 | variable | variable
+// CLA | INS | P1 | P2 | Lc | Le
+// ----+-----+----+----+-----+---
+// E0 | 04 | 00: first transaction data block
+// 80: subsequent transaction data block
+// | 00 | variable | variable
//
// Where the input for the first transaction block (first 255 bytes) is:
//
-// Description | Length
-// -------------------------------------------------+----------
-// Number of BIP 32 derivations to perform (max 10) | 1 byte
-// First derivation index (big endian) | 4 bytes
-// ... | 4 bytes
-// Last derivation index (big endian) | 4 bytes
-// RLP transaction chunk | arbitrary
+// Description | Length
+// -------------------------------------------------+----------
+// Number of BIP 32 derivations to perform (max 10) | 1 byte
+// First derivation index (big endian) | 4 bytes
+// ... | 4 bytes
+// Last derivation index (big endian) | 4 bytes
+// RLP transaction chunk | arbitrary
//
// And the input for subsequent transaction blocks (first 255 bytes) are:
//
-// Description | Length
-// ----------------------+----------
-// RLP transaction chunk | arbitrary
+// Description | Length
+// ----------------------+----------
+// RLP transaction chunk | arbitrary
//
// And the output data is:
//
-// Description | Length
-// ------------+---------
-// signature V | 1 byte
-// signature R | 32 bytes
-// signature S | 32 bytes
+// Description | Length
+// ------------+---------
+// signature V | 1 byte
+// signature R | 32 bytes
+// signature S | 32 bytes
func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
// Flatten the derivation path into the Ledger request
path := make([]byte, 1+4*len(derivationPath))
@@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
op = ledgerP1ContTransactionData
}
// Extract the Ethereum signature and do a sanity validation
- if len(reply) != 65 {
+ if len(reply) != crypto.SignatureLength {
return common.Address{}, nil, errors.New("reply lacks signature")
}
signature := append(reply[1:], reply[0])
@@ -352,7 +353,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
signer = new(types.HomesteadSigner)
} else {
signer = types.NewEIP155Signer(chainID)
- signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
+ signature[crypto.RecoveryIDOffset] = signature[crypto.RecoveryIDOffset] - byte(chainID.Uint64()*2+35)
}
signed, err := tx.WithSignature(signer, signature)
if err != nil {
@@ -370,12 +371,12 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
//
// The common transport header is defined as follows:
//
-// Description | Length
-// --------------------------------------+----------
-// Communication channel ID (big endian) | 2 bytes
-// Command tag | 1 byte
-// Packet sequence index (big endian) | 2 bytes
-// Payload | arbitrary
+// Description | Length
+// --------------------------------------+----------
+// Communication channel ID (big endian) | 2 bytes
+// Command tag | 1 byte
+// Packet sequence index (big endian) | 2 bytes
+// Payload | arbitrary
//
// The Communication channel ID allows commands multiplexing over the same
// physical link. It is not used for the time being, and should be set to 0101
@@ -389,15 +390,15 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
//
// APDU Command payloads are encoded as follows:
//
-// Description | Length
-// -----------------------------------
-// APDU length (big endian) | 2 bytes
-// APDU CLA | 1 byte
-// APDU INS | 1 byte
-// APDU P1 | 1 byte
-// APDU P2 | 1 byte
-// APDU length | 1 byte
-// Optional APDU data | arbitrary
+// Description | Length
+// -----------------------------------
+// APDU length (big endian) | 2 bytes
+// APDU CLA | 1 byte
+// APDU INS | 1 byte
+// APDU P1 | 1 byte
+// APDU P2 | 1 byte
+// APDU length | 1 byte
+// Optional APDU data | arbitrary
func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
// Construct the message payload, possibly split into multiple chunks
apdu := make([]byte, 2, 7+len(data))
diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go
index 04e57292ba..8bc51bb835 100644
--- a/accounts/usbwallet/trezor.go
+++ b/accounts/usbwallet/trezor.go
@@ -32,6 +32,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/golang/protobuf/proto"
)
@@ -222,7 +223,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
} else {
// Trezor backend does not support typed transactions yet.
signer = types.NewEIP155Signer(chainID)
- signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
+ signature[crypto.RecoveryIDOffset] = signature[crypto.RecoveryIDOffset] - byte(chainID.Uint64()*2+35)
}
// Inject the final signature into the transaction and sanity check the sender
diff --git a/bmt/bmt.go b/bmt/bmt.go
index 4b65b1d94a..aa36885769 100644
--- a/bmt/bmt.go
+++ b/bmt/bmt.go
@@ -150,29 +150,29 @@ func NewTreePool(hasher BaseHasher, segmentCount, capacity int) *TreePool {
}
// Drain drains the pool uptil it has no more than n resources
-func (self *TreePool) Drain(n int) {
- self.lock.Lock()
- defer self.lock.Unlock()
- for len(self.c) > n {
- <-self.c
- self.count--
+func (tp *TreePool) Drain(n int) {
+ tp.lock.Lock()
+ defer tp.lock.Unlock()
+ for len(tp.c) > n {
+ <-tp.c
+ tp.count--
}
}
// Reserve is blocking until it returns an available Tree
// it reuses free Trees or creates a new one if size is not reached
-func (self *TreePool) Reserve() *Tree {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (tp *TreePool) Reserve() *Tree {
+ tp.lock.Lock()
+ defer tp.lock.Unlock()
var t *Tree
- if self.count == self.Capacity {
- return <-self.c
+ if tp.count == tp.Capacity {
+ return <-tp.c
}
select {
- case t = <-self.c:
+ case t = <-tp.c:
default:
- t = NewTree(self.hasher, self.SegmentSize, self.SegmentCount)
- self.count++
+ t = NewTree(tp.hasher, tp.SegmentSize, tp.SegmentCount)
+ tp.count++
}
return t
}
@@ -180,8 +180,8 @@ func (self *TreePool) Reserve() *Tree {
// Release gives back a Tree to the pool.
// This Tree is guaranteed to be in reusable state
// does not need locking
-func (self *TreePool) Release(t *Tree) {
- self.c <- t // can never fail but...
+func (tp *TreePool) Release(t *Tree) {
+ tp.c <- t // can never fail but...
}
// Tree is a reusable control structure representing a BMT
@@ -193,17 +193,14 @@ type Tree struct {
}
// Draw draws the BMT (badly)
-func (self *Tree) Draw(hash []byte, d int) string {
+func (t *Tree) Draw(hash []byte, d int) string {
var left, right []string
var anc []*Node
- for i, n := range self.leaves {
+ for _, n := range t.leaves {
left = append(left, fmt.Sprintf("%v", hashstr(n.left)))
- if i%2 == 0 {
- anc = append(anc, n.parent)
- }
right = append(right, fmt.Sprintf("%v", hashstr(n.right)))
}
- anc = self.leaves
+ anc = t.leaves
var hashes [][]string
for l := 0; len(anc) > 0; l++ {
var nodes []*Node
@@ -277,42 +274,42 @@ func NewTree(hasher BaseHasher, segmentSize, segmentCount int) *Tree {
// methods needed by hash.Hash
// Size returns the size
-func (self *Hasher) Size() int {
- return self.size
+func (ha *Hasher) Size() int {
+ return ha.size
}
// BlockSize returns the block size
-func (self *Hasher) BlockSize() int {
- return self.blocksize
+func (ha *Hasher) BlockSize() int {
+ return ha.blocksize
}
// Sum returns the hash of the buffer
// hash.Hash interface Sum method appends the byte slice to the underlying
// data before it calculates and returns the hash of the chunk
-func (self *Hasher) Sum(b []byte) (r []byte) {
- t := self.bmt
- i := self.cur
+func (ha *Hasher) Sum(b []byte) (r []byte) {
+ t := ha.bmt
+ i := ha.cur
n := t.leaves[i]
j := i
// must run strictly before all nodes calculate
// datanodes are guaranteed to have a parent
- if len(self.segment) > self.size && i > 0 && n.parent != nil {
+ if len(ha.segment) > ha.size && i > 0 && n.parent != nil {
n = n.parent
} else {
i *= 2
}
- d := self.finalise(n, i)
- self.writeSegment(j, self.segment, d)
- c := <-self.result
- self.releaseTree()
+ d := ha.finalise(n, i)
+ ha.writeSegment(j, ha.segment, d)
+ c := <-ha.result
+ ha.releaseTree()
// sha3(length + BMT(pure_chunk))
- if self.blockLength == nil {
+ if ha.blockLength == nil {
return c
}
- res := self.pool.hasher()
+ res := ha.pool.hasher()
res.Reset()
- res.Write(self.blockLength)
+ res.Write(ha.blockLength)
res.Write(c)
return res.Sum(nil)
}
@@ -321,8 +318,8 @@ func (self *Hasher) Sum(b []byte) (r []byte) {
// Hash waits for the hasher result and returns it
// caller must call this on a BMT Hasher being written to
-func (self *Hasher) Hash() []byte {
- return <-self.result
+func (ha *Hasher) Hash() []byte {
+ return <-ha.result
}
// Hasher implements the io.Writer interface
@@ -330,16 +327,16 @@ func (self *Hasher) Hash() []byte {
// Write fills the buffer to hash
// with every full segment complete launches a hasher go routine
// that shoots up the BMT
-func (self *Hasher) Write(b []byte) (int, error) {
+func (ha *Hasher) Write(b []byte) (int, error) {
l := len(b)
if l <= 0 {
return 0, nil
}
- s := self.segment
- i := self.cur
- count := (self.count + 1) / 2
- need := self.count*self.size - self.cur*2*self.size
- size := self.size
+ s := ha.segment
+ i := ha.cur
+ count := (ha.count + 1) / 2
+ need := ha.count*ha.size - ha.cur*2*ha.size
+ size := ha.size
if need > size {
size *= 2
}
@@ -356,7 +353,7 @@ func (self *Hasher) Write(b []byte) (int, error) {
// read full segments and the last possibly partial segment
for need > 0 && i < count-1 {
// push all finished chunks we read
- self.writeSegment(i, s, self.depth)
+ ha.writeSegment(i, s, ha.depth)
need -= size
if need < 0 {
size += need
@@ -365,8 +362,8 @@ func (self *Hasher) Write(b []byte) (int, error) {
rest += size
i++
}
- self.segment = s
- self.cur = i
+ ha.segment = s
+ ha.cur = i
// otherwise, we can assume len(s) == 0, so all buffer is read and chunk is not yet full
return l, nil
}
@@ -376,8 +373,8 @@ func (self *Hasher) Write(b []byte) (int, error) {
// ReadFrom reads from io.Reader and appends to the data to hash using Write
// it reads so that chunk to hash is maximum length or reader reaches EOF
// caller must Reset the hasher prior to call
-func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
- bufsize := self.size*self.count - self.size*self.cur - len(self.segment)
+func (ha *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
+ bufsize := ha.size*ha.count - ha.size*ha.cur - len(ha.segment)
buf := make([]byte, bufsize)
var read int
for {
@@ -385,7 +382,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
n, err = r.Read(buf)
read += n
if err == io.EOF || read == len(buf) {
- hash := self.Sum(buf[:n])
+ hash := ha.Sum(buf[:n])
if read == len(buf) {
err = NewEOC(hash)
}
@@ -394,7 +391,7 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
if err != nil {
break
}
- n, err = self.Write(buf[:n])
+ _, err = ha.Write(buf[:n])
if err != nil {
break
}
@@ -403,9 +400,9 @@ func (self *Hasher) ReadFrom(r io.Reader) (m int64, err error) {
}
// Reset needs to be called before writing to the hasher
-func (self *Hasher) Reset() {
- self.getTree()
- self.blockLength = nil
+func (ha *Hasher) Reset() {
+ ha.getTree()
+ ha.blockLength = nil
}
// Hasher implements the SwarmHash interface
@@ -413,53 +410,53 @@ func (self *Hasher) Reset() {
// ResetWithLength needs to be called before writing to the hasher
// the argument is supposed to be the byte slice binary representation of
// the legth of the data subsumed under the hash
-func (self *Hasher) ResetWithLength(l []byte) {
- self.Reset()
- self.blockLength = l
+func (ha *Hasher) ResetWithLength(l []byte) {
+ ha.Reset()
+ ha.blockLength = l
}
// Release gives back the Tree to the pool whereby it unlocks
// it resets tree, segment and index
-func (self *Hasher) releaseTree() {
- if self.bmt != nil {
- n := self.bmt.leaves[self.cur]
+func (ha *Hasher) releaseTree() {
+ if ha.bmt != nil {
+ n := ha.bmt.leaves[ha.cur]
for ; n != nil; n = n.parent {
n.unbalanced = false
if n.parent != nil {
n.root = false
}
}
- self.pool.Release(self.bmt)
- self.bmt = nil
+ ha.pool.Release(ha.bmt)
+ ha.bmt = nil
}
- self.cur = 0
- self.segment = nil
+ ha.cur = 0
+ ha.segment = nil
}
-func (self *Hasher) writeSegment(i int, s []byte, d int) {
- h := self.pool.hasher()
- n := self.bmt.leaves[i]
+func (ha *Hasher) writeSegment(i int, s []byte, d int) {
+ h := ha.pool.hasher()
+ n := ha.bmt.leaves[i]
- if len(s) > self.size && n.parent != nil {
+ if len(s) > ha.size && n.parent != nil {
go func() {
h.Reset()
h.Write(s)
s = h.Sum(nil)
if n.root {
- self.result <- s
+ ha.result <- s
return
}
- self.run(n.parent, h, d, n.index, s)
+ ha.run(n.parent, h, d, n.index, s)
}()
return
}
- go self.run(n, h, d, i*2, s)
+ go ha.run(n, h, d, i*2, s)
}
-func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
+func (ha *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
isLeft := i%2 == 0
for {
if isLeft {
@@ -480,9 +477,9 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
s = append(n.left, n.right...)
}
- self.hash = s
+ ha.hash = s
if n.root {
- self.result <- s
+ ha.result <- s
return
}
@@ -493,20 +490,20 @@ func (self *Hasher) run(n *Node, h hash.Hash, d int, i int, s []byte) {
}
// getTree obtains a BMT resource by reserving one from the pool
-func (self *Hasher) getTree() *Tree {
- if self.bmt != nil {
- return self.bmt
+func (ha *Hasher) getTree() *Tree {
+ if ha.bmt != nil {
+ return ha.bmt
}
- t := self.pool.Reserve()
- self.bmt = t
+ t := ha.pool.Reserve()
+ ha.bmt = t
return t
}
// atomic bool toggle implementing a concurrent reusable 2-state object
// atomic addint with %2 implements atomic bool toggle
// it returns true if the toggler just put it in the active/waiting state
-func (self *Node) toggle() bool {
- return atomic.AddInt32(&self.state, 1)%2 == 1
+func (n *Node) toggle() bool {
+ return atomic.AddInt32(&n.state, 1)%2 == 1
}
func hashstr(b []byte) string {
@@ -526,7 +523,7 @@ func depth(n int) (d int) {
// finalise is following the zigzags on the tree belonging
// to the final datasegment
-func (self *Hasher) finalise(n *Node, i int) (d int) {
+func (ha *Hasher) finalise(n *Node, i int) (d int) {
isLeft := i%2 == 0
for {
// when the final segment's path is going via left segments
@@ -551,8 +548,8 @@ type EOC struct {
}
// Error returns the error string
-func (self *EOC) Error() string {
- return fmt.Sprintf("hasher limit reached, chunk hash: %x", self.Hash)
+func (e *EOC) Error() string {
+ return fmt.Sprintf("hasher limit reached, chunk hash: %x", e.Hash)
}
// NewEOC creates new end of chunk error with the hash
diff --git a/bmt/bmt_test.go b/bmt/bmt_test.go
index ac762993c5..3030cea640 100644
--- a/bmt/bmt_test.go
+++ b/bmt/bmt_test.go
@@ -29,7 +29,7 @@ import (
"testing"
"time"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -39,7 +39,7 @@ const (
// TestRefHasher tests that the RefHasher computes the expected BMT hash for
// all data lengths between 0 and 256 bytes
func TestRefHasher(t *testing.T) {
- hashFunc := sha3.NewKeccak256
+ hashFunc := sha3.NewLegacyKeccak256
sha3 := func(data ...[]byte) []byte {
h := hashFunc()
@@ -212,7 +212,7 @@ func testHasher(f func(BaseHasher, []byte, int, int) error) error {
tdata := testDataReader(4128)
data := make([]byte, 4128)
tdata.Read(data)
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
size := hasher().Size()
counts := []int{1, 2, 3, 4, 5, 8, 16, 32, 64, 128}
@@ -239,7 +239,7 @@ func TestHasherReuseWithRelease(t *testing.T) {
}
func testHasherReuse(i int, t *testing.T) {
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
pool := NewTreePool(hasher, 128, i)
defer pool.Drain(0)
bmt := New(pool)
@@ -258,7 +258,7 @@ func testHasherReuse(i int, t *testing.T) {
}
func TestHasherConcurrency(t *testing.T) {
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
pool := NewTreePool(hasher, 128, maxproccnt)
defer pool.Drain(0)
wg := sync.WaitGroup{}
@@ -379,7 +379,7 @@ func benchmarkBMTBaseline(n int, t *testing.B) {
tdata := testDataReader(64)
data := make([]byte, 64)
tdata.Read(data)
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
t.ReportAllocs()
t.ResetTimer()
@@ -409,7 +409,7 @@ func benchmarkHasher(n int, t *testing.B) {
tdata.Read(data)
size := 1
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
segmentCount := 128
pool := NewTreePool(hasher, segmentCount, size)
bmt := New(pool)
@@ -428,7 +428,7 @@ func benchmarkHasherReuse(poolsize, n int, t *testing.B) {
data := make([]byte, n)
tdata.Read(data)
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
segmentCount := 128
pool := NewTreePool(hasher, segmentCount, poolsize)
cycles := 200
@@ -455,7 +455,7 @@ func benchmarkSHA3(n int, t *testing.B) {
data := make([]byte, n)
tdata := testDataReader(n)
tdata.Read(data)
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
h := hasher()
t.ReportAllocs()
@@ -471,7 +471,7 @@ func benchmarkRefHasher(n int, t *testing.B) {
data := make([]byte, n)
tdata := testDataReader(n)
tdata.Read(data)
- hasher := sha3.NewKeccak256
+ hasher := sha3.NewLegacyKeccak256
rbmt := NewRefHasher(hasher, 128)
t.ReportAllocs()
diff --git a/build/ci.go b/build/ci.go
index 7d09ec7c9d..ff3ea89d10 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -115,7 +115,7 @@ func doInstall(cmdline []string) {
if minor < 21 {
log.Println("You have Go version", runtime.Version())
- log.Println("XDC requires at least Go version 1.21 and cannot")
+ log.Println("XDC requires at least Go version 1.22 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1)
}
diff --git a/cicd/Dockerfile b/cicd/Dockerfile
index bf5b4b5c79..055492e548 100644
--- a/cicd/Dockerfile
+++ b/cicd/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.21-alpine as builder
+FROM golang:1.22-alpine as builder
RUN apk add make build-base linux-headers
diff --git a/cicd/devnet/bootnodes.list b/cicd/devnet/bootnodes.list
index 72592bff40..618052572b 100644
--- a/cicd/devnet/bootnodes.list
+++ b/cicd/devnet/bootnodes.list
@@ -1,5 +1,2 @@
-enode://207f812067141e038439acbd18a6e3b5f38fcff7de362d8fe0eb5f153f828ae99cae3270ed653beaa197e3946223d9e2f4a22a5e948177209d046fd095b89626@66.94.121.151:30301
-enode://ec569f5d52cefee5c5405a0c5db720dc7061f3085e0682dd8321413430ddda6a177b85db75b0daf83d2e68760ba3f5beb4ba9e333e7d52072fba4d39b05a0451@194.233.77.19:30301
-enode://cab457c58bc9b94ea9afa428d018179b4773fe90fec16ef958951f5a23adb9627350cea8b56709351766183d85574fc54ba504816b200fa31675a04c25226e9f@66.94.98.186:30301
-enode://751de9e39fe0b8f18585bb4a8b50aa5631fe262e14f579753709631eddcac30be901ca0b5a3878b7686838da7ddca2c61e6d2c5eb3745e6b855289e692068e8d@144.126.140.3:30301
-enode://6bd073ee1085c1dc09427d9f65f0125a75393ac89f0db36f884cc22d61f441403a4c06d8f9a9d3c8e8c08368122bceeeac5f48c83f3d2e87c0da6d5c0eb7cd7e@194.163.167.177:30301
\ No newline at end of file
+enode://00d49d72a48164681906ad61924568da0d3049937efdbaed0b7533e34a99f55814f1839d909cdc82f78e04a36ac04737d80b41b22905c7d6cac3c80bb5cdbbc4@66.94.98.186:30301
+enode://d6793b02a478f13ed6d01c30778935f6f8f7461a75aebedcb310def4ed9b066f995a0dca046d0c7ea7f5ffdd8e3f1f53c6b6dce909d1693650504921aad62f1a@194.163.167.177:30301
diff --git a/cicd/devnet/start-local-devnet.sh b/cicd/devnet/start-local-devnet.sh
index c0a9448a86..af2b09f6fc 100755
--- a/cicd/devnet/start-local-devnet.sh
+++ b/cicd/devnet/start-local-devnet.sh
@@ -46,17 +46,17 @@ else
log_level=$LOG_LEVEL
fi
-netstats="${NODE_NAME}-${wallet}-local:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000"
+netstats="${NODE_NAME}-${wallet}-local:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlabs.apothem.network:1999"
echo "Running a node with wallet: ${wallet} at local"
../../build/bin/XDC --ethstats ${netstats} --gcmode=archive \
--bootnodes ${bootnodes} --syncmode full \
--datadir ./tmp/xdcchain --networkid 551 \
--port 30303 --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
---rpcport 8545 \
---rpcapi db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \
---rpcvhosts "*" --unlock "${wallet}" --password ./tmp/.pwd --mine \
---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
---ws --wsaddr=0.0.0.0 --wsport 8555 \
---wsorigins "*" 2>&1 >>./tmp/xdc.log
+--port 30303 --http --http-corsdomain "*" --http-addr 0.0.0.0 \
+--http-port 8545 \
+--http-api db,eth,debug,miner,net,shh,txpool,personal,web3,XDPoS \
+--http-vhosts "*" --unlock "${wallet}" --password ./tmp/.pwd --mine \
+--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \
+--ws --ws-addr=0.0.0.0 --ws-port 8555 \
+--ws-origins "*" 2>&1 >>./tmp/xdc.log
diff --git a/cicd/devnet/start.sh b/cicd/devnet/start.sh
index 707b8b32a5..8e52b1c923 100755
--- a/cicd/devnet/start.sh
+++ b/cicd/devnet/start.sh
@@ -24,6 +24,13 @@ do
bootnodes="${bootnodes},$line"
fi
done < "$input"
+#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line
+if [ -z "${bootnodes}" ]
+then
+ bootnodes=$line
+else
+ bootnodes="${bootnodes},$line"
+fi
log_level=3
if test -z "$LOG_LEVEL"
@@ -62,7 +69,7 @@ else
fi
INSTANCE_IP=$(curl https://checkip.amazonaws.com)
-netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.apothem.network:2000"
+netstats="${NODE_NAME}-${wallet}-${INSTANCE_IP}:xinfin_xdpos_hybrid_network_stats@devnetstats.hashlabs.apothem.network:1999"
echo "Running a node with wallet: ${wallet} at IP: ${INSTANCE_IP}"
@@ -75,11 +82,12 @@ XDC --ethstats ${netstats} --gcmode archive \
--nat extip:${INSTANCE_IP} \
--bootnodes ${bootnodes} --syncmode full \
--datadir /work/xdcchain --networkid 551 \
--port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
---rpcport $rpc_port \
---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
+--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \
+--http-port $rpc_port \
+--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
+--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
+--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \
--debugdatadir /work/xdcchain \
---ws --wsaddr=0.0.0.0 --wsport $ws_port \
---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
+--store-reward \
+--ws --ws-addr=0.0.0.0 --ws-port $ws_port \
+--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
diff --git a/cicd/devnet/terraform/main.tf b/cicd/devnet/terraform/main.tf
index e7723398bf..fc998b799c 100644
--- a/cicd/devnet/terraform/main.tf
+++ b/cicd/devnet/terraform/main.tf
@@ -2,7 +2,6 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = "~> 5.13.1"
}
}
}
diff --git a/cicd/devnet/terraform/module/region/main.tf b/cicd/devnet/terraform/module/region/main.tf
index 8e2ac0dd55..10fc5b08e6 100644
--- a/cicd/devnet/terraform/module/region/main.tf
+++ b/cicd/devnet/terraform/module/region/main.tf
@@ -2,7 +2,6 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = "~> 5.13.1"
}
}
}
diff --git a/cicd/devnet/terraform/s3.tf b/cicd/devnet/terraform/s3.tf
index f04eeeb7d8..cd30e5ce32 100644
--- a/cicd/devnet/terraform/s3.tf
+++ b/cicd/devnet/terraform/s3.tf
@@ -1,4 +1,3 @@
-# Bucket need to be created first. If first time run terraform init, need to comment out the below section
terraform {
backend "s3" {
bucket = "tf-xinfin-bucket" // This name need to be updated to be the same as local.s3BucketName. We can't use variable here.
diff --git a/cicd/devnet/terraform/variables.tf b/cicd/devnet/terraform/variables.tf
index 0d9b3bb125..12285d8ca7 100644
--- a/cicd/devnet/terraform/variables.tf
+++ b/cicd/devnet/terraform/variables.tf
@@ -5,15 +5,6 @@ variable docker_tag {
}
locals {
- /**
- Load the nodes data from s3
- Below is the the format the config needs to follow:
- {{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: {
- pk: {{Value of the node private key}},
- ... any other configuration we want to pass.
- }
- Note: No `n` is allowed in the node name
- **/
predefinedNodesConfig = jsondecode(data.aws_s3_object.devnet_xdc_node_config.body)
envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] }
logLevel = local.envs["log_level"]
@@ -37,12 +28,12 @@ locals {
]
keyNames = {
- for r in local.regions :
+ for r in local.regions :
r.name => [for i in range(r.start, r.end+1) : "xdc${i}"]
}
devnetNodeKeys = {
- for r in local.regions :
+ for r in local.regions :
r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] }
}
}
diff --git a/cicd/mainnet/start.sh b/cicd/mainnet/start.sh
index 0cfbe26e27..66bb772166 100755
--- a/cicd/mainnet/start.sh
+++ b/cicd/mainnet/start.sh
@@ -24,6 +24,13 @@ do
bootnodes="${bootnodes},$line"
fi
done < "$input"
+#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line
+if [ -z "${bootnodes}" ]
+then
+ bootnodes=$line
+else
+ bootnodes="${bootnodes},$line"
+fi
log_level=3
if test -z "$LOG_LEVEL"
@@ -74,11 +81,12 @@ XDC --ethstats ${netstats} --gcmode archive \
--nat extip:${INSTANCE_IP} \
--bootnodes ${bootnodes} --syncmode full \
--datadir /work/xdcchain --networkid 50 \
--port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
---rpcport $rpc_port \
---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
+--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \
+--http-port $rpc_port \
+--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
+--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
+--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \
--debugdatadir /work/xdcchain \
---ws --wsaddr=0.0.0.0 --wsport $ws_port \
---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
+--store-reward \
+--ws --ws-addr=0.0.0.0 --ws-port $ws_port \
+--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
diff --git a/cicd/terraform/main.tf b/cicd/terraform/main.tf
index e34f8c5ffe..7fc541fb7f 100644
--- a/cicd/terraform/main.tf
+++ b/cicd/terraform/main.tf
@@ -12,8 +12,6 @@ provider "aws" {
region = "us-east-1"
}
-# WARNING: APSE-1 will only be used to host rpc node
-# Workaround to avoid conflicts with existing ecs cluster in existing regions
provider "aws" {
alias = "ap-southeast-1"
region = "ap-southeast-1"
@@ -25,9 +23,9 @@ module "devnet_rpc" {
vpc_id = local.vpc_id
aws_subnet_id = local.aws_subnet_id
ami_id = local.ami_id
- instance_type = "t3.xlarge"
+ instance_type = "t3.large"
ssh_key_name = local.ssh_key_name
- rpc_image = local.rpc_image
+ rpc_image = local.rpc_image
volume_size = 1500
providers = {
@@ -43,7 +41,7 @@ module "testnet_rpc" {
ami_id = local.ami_id
instance_type = "t3.large"
ssh_key_name = local.ssh_key_name
- rpc_image = local.rpc_image
+ rpc_image = local.rpc_image
volume_size = 1500
providers = {
@@ -59,7 +57,7 @@ module "mainnet_rpc" {
ami_id = local.ami_id
instance_type = "t3.large"
ssh_key_name = local.ssh_key_name
- rpc_image = local.rpc_image
+ rpc_image = local.rpc_image
volume_size = 3000
providers = {
diff --git a/cicd/terraform/variables.tf b/cicd/terraform/variables.tf
index c5a1eb8970..02bf5bdcf4 100644
--- a/cicd/terraform/variables.tf
+++ b/cicd/terraform/variables.tf
@@ -1,41 +1,14 @@
locals {
- /**
- Load the nodes data from s3
- Below is the the format the config needs to follow:
- {{Name of the node, in a pattern of 'xdc'+ number. i.e xdc50}}: {
- pk: {{Value of the node private key}},
- ... any other configuration we want to pass.
- }
- Note: No `n` is allowed in the node name
- **/
predefinedNodesConfig = jsondecode(data.aws_s3_object.xdc_node_config.body)
envs = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] }
logLevel = local.envs["log_level"]
- # regions = [
- # {
- # "name": "us-east-2", // Ohio
- # "start": local.envs["us_east_2_start"],
- # "end": local.envs["us_east_2_end"],
- # }
- # ]
-
- # keyNames = {
- # for r in local.regions :
- # r.name => [for i in range(r.start, r.end+1) : "xdc${i}"]
- # }
-
- # nodeKeys = {
- # for r in local.regions :
- # r.name => { for i in local.keyNames[r.name]: i => local.predefinedNodesConfig[i] }
- # }
-
rpcDevnetNodeKeys = { "devnet-rpc1": local.predefinedNodesConfig["devnet-rpc1"]} // we hardcode the rpc to a single node for now
rpcTestnetNodeKeys = { "testnet-rpc1": local.predefinedNodesConfig["testnet-rpc1"]} // we hardcode the rpc to a single node for now
rpcMainnetNodeKeys = { "mainnet-rpc1": local.predefinedNodesConfig["mainnet-rpc1"]} // we hardcode the rpc to a single node for now
}
-locals { //ec2_rpc values
+locals {
ami_id = "ami-097c4e1feeea169e5"
rpc_image = "xinfinorg/xdposchain:v2.2.0-beta1"
vpc_id = "vpc-20a06846"
diff --git a/cicd/testnet/start.sh b/cicd/testnet/start.sh
index abfd91cadf..6dbd47fbf2 100755
--- a/cicd/testnet/start.sh
+++ b/cicd/testnet/start.sh
@@ -25,6 +25,13 @@ do
bootnodes="${bootnodes},$line"
fi
done < "$input"
+#check last line since it's not included in "read" command https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line
+if [ -z "${bootnodes}" ]
+then
+ bootnodes=$line
+else
+ bootnodes="${bootnodes},$line"
+fi
log_level=3
if test -z "$LOG_LEVEL"
@@ -76,11 +83,12 @@ XDC --ethstats ${netstats} --gcmode archive \
--nat extip:${INSTANCE_IP} \
--bootnodes ${bootnodes} --syncmode full \
--datadir /work/xdcchain --networkid 51 \
--port $port --rpc --rpccorsdomain "*" --rpcaddr 0.0.0.0 \
---rpcport $rpc_port \
---rpcapi db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
---rpcvhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
---gasprice "1" --targetgaslimit "420000000" --verbosity ${log_level} \
+--port $port --http --http-corsdomain "*" --http-addr 0.0.0.0 \
+--http-port $rpc_port \
+--http-api db,eth,debug,net,shh,txpool,personal,web3,XDPoS \
+--http-vhosts "*" --unlock "${wallet}" --password /work/.pwd --mine \
+--miner-gasprice "1" --miner-gaslimit "420000000" --verbosity ${log_level} \
--debugdatadir /work/xdcchain \
---ws --wsaddr=0.0.0.0 --wsport $ws_port \
---wsorigins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
+--store-reward \
+--ws --ws-addr=0.0.0.0 --ws-port $ws_port \
+--ws-origins "*" 2>&1 >>/work/xdcchain/xdc.log | tee -a /work/xdcchain/xdc.log
diff --git a/cmd/XDC/accountcmd.go b/cmd/XDC/accountcmd.go
index d54828ab8e..46909bdf94 100644
--- a/cmd/XDC/accountcmd.go
+++ b/cmd/XDC/accountcmd.go
@@ -26,31 +26,31 @@ import (
"github.com/XinFinOrg/XDPoSChain/console"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
var (
- walletCommand = cli.Command{
+ walletCommand = &cli.Command{
Name: "wallet",
Usage: "Manage XDPoSChain presale wallets",
ArgsUsage: "",
- Category: "ACCOUNT COMMANDS",
Description: `
XDC wallet import /path/to/my/presale.wallet
will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.`,
- Subcommands: []cli.Command{
+ Subcommands: []*cli.Command{
{
Name: "import",
Usage: "Import XDPoSChain presale wallet",
ArgsUsage: "",
- Action: utils.MigrateFlags(importWallet),
+ Action: importWallet,
Category: "ACCOUNT COMMANDS",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
@@ -65,10 +65,9 @@ passwordfile as argument containing the wallet password in plaintext.`,
},
}
- accountCommand = cli.Command{
- Name: "account",
- Usage: "Manage accounts",
- Category: "ACCOUNT COMMANDS",
+ accountCommand = &cli.Command{
+ Name: "account",
+ Usage: "Manage accounts",
Description: `
Manage accounts, list all existing accounts, import a private key into a new
@@ -89,13 +88,14 @@ It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes by simply copying.
Make sure you backup your keys regularly.`,
- Subcommands: []cli.Command{
+ Subcommands: []*cli.Command{
{
Name: "list",
Usage: "Print summary of existing accounts",
- Action: utils.MigrateFlags(accountList),
+ Action: accountList,
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.KeyStoreDirFlag,
},
Description: `
@@ -104,9 +104,10 @@ Print a short summary of all accounts`,
{
Name: "new",
Usage: "Create a new account",
- Action: utils.MigrateFlags(accountCreate),
+ Action: accountCreate,
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
@@ -129,10 +130,11 @@ password to file or expose in any other way.
{
Name: "update",
Usage: "Update an existing account",
- Action: utils.MigrateFlags(accountUpdate),
+ Action: accountUpdate,
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.KeyStoreDirFlag,
utils.LightKDFFlag,
},
@@ -158,9 +160,10 @@ changing your password is only possible interactively.
{
Name: "import",
Usage: "Import a private key into a new account",
- Action: utils.MigrateFlags(accountImport),
+ Action: accountImport,
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.KeyStoreDirFlag,
utils.PasswordFileFlag,
utils.LightKDFFlag,
@@ -293,7 +296,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
func accountCreate(ctx *cli.Context) error {
cfg := XDCConfig{Node: defaultNodeConfig()}
// Load config file.
- if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if file := ctx.String(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
@@ -319,13 +322,13 @@ func accountCreate(ctx *cli.Context) error {
// accountUpdate transitions an account from a previous format to the current
// one, also providing the possibility to change the pass-phrase.
func accountUpdate(ctx *cli.Context) error {
- if len(ctx.Args()) == 0 {
+ if ctx.Args().Len() == 0 {
utils.Fatalf("No accounts specified to update")
}
stack, _ := makeConfigNode(ctx)
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- for _, addr := range ctx.Args() {
+ for _, addr := range ctx.Args().Slice() {
account, oldPassword := unlockAccount(ctx, ks, addr, 0, nil)
newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
if err := ks.Update(account, oldPassword, newPassword); err != nil {
@@ -336,10 +339,10 @@ func accountUpdate(ctx *cli.Context) error {
}
func importWallet(ctx *cli.Context) error {
- keyfile := ctx.Args().First()
- if len(keyfile) == 0 {
- utils.Fatalf("keyfile must be given as argument")
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
}
+ keyfile := ctx.Args().First()
keyJson, err := os.ReadFile(keyfile)
if err != nil {
utils.Fatalf("Could not read wallet file: %v", err)
@@ -358,10 +361,10 @@ func importWallet(ctx *cli.Context) error {
}
func accountImport(ctx *cli.Context) error {
- keyfile := ctx.Args().First()
- if len(keyfile) == 0 {
- utils.Fatalf("keyfile must be given as argument")
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
}
+ keyfile := ctx.Args().First()
key, err := crypto.LoadECDSA(keyfile)
if err != nil {
utils.Fatalf("Failed to load the private key: %v", err)
diff --git a/cmd/XDC/accountcmd_test.go b/cmd/XDC/accountcmd_test.go
index 4577ce5d65..668739594d 100644
--- a/cmd/XDC/accountcmd_test.go
+++ b/cmd/XDC/accountcmd_test.go
@@ -43,26 +43,31 @@ func tmpDatadirWithKeystore(t *testing.T) string {
}
func TestAccountListEmpty(t *testing.T) {
- XDC := runXDC(t, "account", "list")
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
+ XDC := runXDC(t, "account", "list", "--datadir", datadir)
XDC.ExpectExit()
}
func TestAccountList(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
- XDC := runXDC(t, "account", "list", "--datadir", datadir)
- defer XDC.ExpectExit()
- if runtime.GOOS == "windows" {
- XDC.Expect(`
-Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
-Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
-Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
-`)
- } else {
- XDC.Expect(`
+ defer os.RemoveAll(datadir)
+ var want = `
Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa
Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz
-`)
+`
+ if runtime.GOOS == "windows" {
+ want = `
+Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
+Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
+`
+ }
+ {
+ geth := runXDC(t, "account", "list", "--datadir", datadir)
+ geth.Expect(want)
+ geth.ExpectExit()
}
}
@@ -92,9 +97,8 @@ Fatal: Passphrases do not match
func TestAccountUpdate(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
- XDC := runXDC(t, "account", "update",
- "--datadir", datadir, "--lightkdf",
- "f466859ead1932d743d622cb74fc058882e8648a")
+ defer os.RemoveAll(datadir)
+ XDC := runXDC(t, "account", "update", "--datadir", datadir, "--lightkdf", "f466859ead1932d743d622cb74fc058882e8648a")
defer XDC.ExpectExit()
XDC.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
@@ -107,7 +111,9 @@ Repeat passphrase: {{.InputLine "foobar2"}}
}
func TestWalletImport(t *testing.T) {
- XDC := runXDC(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
+ XDC := runXDC(t, "wallet", "import", "--datadir", datadir, "--lightkdf", "testdata/guswallet.json")
defer XDC.ExpectExit()
XDC.Expect(`
!! Unsupported terminal, password will be echoed.
@@ -121,6 +127,36 @@ Address: {xdcd4584b5f6229b7be90727b0fc8c6b91bb427821f}
}
}
+func TestAccountHelp(t *testing.T) {
+ geth := runXDC(t, "account", "-h")
+ geth.WaitExit()
+ if have, want := geth.ExitStatus(), 0; have != want {
+ t.Errorf("exit error, have %d want %d", have, want)
+ }
+
+ geth = runXDC(t, "account", "import", "-h")
+ geth.WaitExit()
+ if have, want := geth.ExitStatus(), 0; have != want {
+ t.Errorf("exit error, have %d want %d", have, want)
+ }
+}
+
+func importAccountWithExpect(t *testing.T, key string, expected string) {
+ dir := t.TempDir()
+ defer os.RemoveAll(dir)
+ keyfile := filepath.Join(dir, "key.prv")
+ if err := os.WriteFile(keyfile, []byte(key), 0600); err != nil {
+ t.Error(err)
+ }
+ passwordFile := filepath.Join(dir, "password.txt")
+ if err := os.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil {
+ t.Error(err)
+ }
+ geth := runXDC(t, "account", "import", "--lightkdf", "-password", passwordFile, keyfile)
+ defer geth.ExpectExit()
+ geth.Expect(expected)
+}
+
func TestWalletImportBadPassword(t *testing.T) {
XDC := runXDC(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
defer XDC.ExpectExit()
@@ -133,10 +169,11 @@ Fatal: could not decrypt key with given passphrase
func TestUnlockFlag(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
- "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
- "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
- "js", "testdata/empty.js")
+ "js", "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0",
+ "--port", "0", "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
+ "testdata/empty.js")
XDC.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed.
@@ -157,6 +194,7 @@ Passphrase: {{.InputLine "foobar"}}
func TestUnlockFlagWrongPassword(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
"--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
"--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
@@ -176,10 +214,11 @@ Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could
// https://github.com/XinFinOrg/XDPoSChain/issues/1785
func TestUnlockFlagMultiIndex(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
- "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
- "--unlock", "0,2",
- "js", "testdata/empty.js")
+ "js", "--datadir", datadir, "--nat", "none", "--nodiscover",
+ "--maxpeers", "0", "--port", "0", "--unlock", "0,2",
+ "testdata/empty.js")
XDC.Expect(`
Unlocking account 0 | Attempt 1/3
!! Unsupported terminal, password will be echoed.
@@ -203,10 +242,11 @@ Passphrase: {{.InputLine "foobar"}}
func TestUnlockFlagPasswordFile(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
- "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
- "--password", "testdata/passwords.txt", "--unlock", "0,2",
- "js", "testdata/empty.js")
+ "js", "--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0",
+ "--port", "0", "--password", "testdata/passwords.txt", "--unlock", "0,2",
+ "testdata/empty.js")
XDC.ExpectExit()
wantMessages := []string{
@@ -223,6 +263,7 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
datadir := tmpDatadirWithKeystore(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
"--datadir", datadir, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
@@ -235,9 +276,9 @@ Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase)
func TestUnlockFlagAmbiguous(t *testing.T) {
store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
XDC := runXDC(t,
- "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0", "--port", "0",
- "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
- "js", "testdata/empty.js")
+ "js", "--keystore", store, "--nat", "none", "--nodiscover", "--maxpeers", "0",
+ "--port", "0", "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
+ "testdata/empty.js")
defer XDC.ExpectExit()
// Helper for the expect template, returns absolute keystore path.
diff --git a/cmd/XDC/bugcmd.go b/cmd/XDC/bugcmd.go
index c4bb574946..201883f8c9 100644
--- a/cmd/XDC/bugcmd.go
+++ b/cmd/XDC/bugcmd.go
@@ -28,17 +28,14 @@ import (
"github.com/XinFinOrg/XDPoSChain/cmd/internal/browser"
"github.com/XinFinOrg/XDPoSChain/params"
-
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- cli "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var bugCommand = cli.Command{
- Action: utils.MigrateFlags(reportBug),
+var bugCommand = &cli.Command{
+ Action: reportBug,
Name: "bug",
Usage: "opens a window to report a bug on the XDC repo",
ArgsUsage: " ",
- Category: "MISCELLANEOUS COMMANDS",
}
const issueUrl = "https://github.com/XinFinOrg/XDPoSChain/issues/new"
diff --git a/cmd/XDC/chaincmd.go b/cmd/XDC/chaincmd.go
index ae4221e4b2..aa050dfd3c 100644
--- a/cmd/XDC/chaincmd.go
+++ b/cmd/XDC/chaincmd.go
@@ -35,22 +35,21 @@ import (
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/event"
"github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/metrics"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
var (
- initCommand = cli.Command{
- Action: utils.MigrateFlags(initGenesis),
+ initCommand = &cli.Command{
+ Action: initGenesis,
Name: "init",
Usage: "Bootstrap and initialize a new genesis block",
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.LightModeFlag,
utils.XDCTestnetFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
The init command initializes a new genesis block and definition for the network.
This is a destructive action and changes the network in which you will be
@@ -58,20 +57,32 @@ participating.
It expects the genesis file as argument.`,
}
- importCommand = cli.Command{
- Action: utils.MigrateFlags(importChain),
+ importCommand = &cli.Command{
+ Action: importChain,
Name: "import",
Usage: "Import a blockchain file",
ArgsUsage: " ( ... ) ",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
utils.GCModeFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
+ utils.MetricsEnabledFlag,
+ utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsEnableInfluxDBFlag,
+ utils.MetricsEnableInfluxDBV2Flag,
+ utils.MetricsInfluxDBEndpointFlag,
+ utils.MetricsInfluxDBDatabaseFlag,
+ utils.MetricsInfluxDBUsernameFlag,
+ utils.MetricsInfluxDBPasswordFlag,
+ utils.MetricsInfluxDBTagsFlag,
+ utils.MetricsInfluxDBTokenFlag,
+ utils.MetricsInfluxDBBucketFlag,
+ utils.MetricsInfluxDBOrganizationFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
The import command imports blocks from an RLP-encoded form. The form can be one file
with several RLP-encoded blocks, or several files can be used.
@@ -79,30 +90,31 @@ with several RLP-encoded blocks, or several files can be used.
If only one file is used, import error will result in failure. If several files are used,
processing will proceed even if an individual RLP-file import failure occurs.`,
}
- exportCommand = cli.Command{
- Action: utils.MigrateFlags(exportChain),
+ exportCommand = &cli.Command{
+ Action: exportChain,
Name: "export",
Usage: "Export blockchain into file",
ArgsUsage: " [ ]",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
Requires a first argument of the file to write to.
Optional second and third arguments control the first and
last block to write. In this mode, the file will be appended
if already existing.`,
}
- importPreimagesCommand = cli.Command{
- Action: utils.MigrateFlags(importPreimages),
+ importPreimagesCommand = &cli.Command{
+ Action: importPreimages,
Name: "import-preimages",
Usage: "Import the preimage database from an RLP stream",
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
@@ -110,57 +122,58 @@ if already existing.`,
Description: `
The import-preimages command imports hash preimages from an RLP encoded stream.`,
}
- exportPreimagesCommand = cli.Command{
- Action: utils.MigrateFlags(exportPreimages),
+ exportPreimagesCommand = &cli.Command{
+ Action: exportPreimages,
Name: "export-preimages",
Usage: "Export the preimage database into an RLP stream",
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
The export-preimages command export hash preimages to an RLP encoded stream`,
}
- copydbCommand = cli.Command{
- Action: utils.MigrateFlags(copyDb),
+ copydbCommand = &cli.Command{
+ Action: copyDb,
Name: "copydb",
Usage: "Create a local chain from a target chaindata folder",
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.SyncModeFlag,
utils.FakePoWFlag,
utils.TestnetFlag,
utils.RinkebyFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
The first argument must be the directory containing the blockchain to download from`,
}
- removedbCommand = cli.Command{
- Action: utils.MigrateFlags(removeDB),
+ removedbCommand = &cli.Command{
+ Action: removeDB,
Name: "removedb",
Usage: "Remove blockchain and state databases",
ArgsUsage: " ",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.LightModeFlag,
},
- Category: "BLOCKCHAIN COMMANDS",
Description: `
Remove blockchain and state databases`,
}
- dumpCommand = cli.Command{
- Action: utils.MigrateFlags(dump),
+ dumpCommand = &cli.Command{
+ Action: dump,
Name: "dump",
Usage: "Dump a specific block from storage",
ArgsUsage: "[ | ]...",
Flags: []cli.Flag{
utils.DataDirFlag,
+ utils.XDCXDataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
},
@@ -174,12 +187,10 @@ Use "ethereum dump 0" to dump the genesis block.`,
// initGenesis will initialise the given JSON format genesis file and writes it as
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
func initGenesis(ctx *cli.Context) error {
- // Make sure we have a valid genesis JSON
- genesisPath := ctx.Args().First()
genesis := new(core.Genesis)
- if ctx.GlobalBool(utils.XDCTestnetFlag.Name) {
- if len(genesisPath) > 0 {
+ if ctx.Bool(utils.XDCTestnetFlag.Name) {
+ if ctx.Args().Len() > 0 {
utils.Fatalf("Flags --apothem and genesis file can't be used at the same time")
}
err := json.Unmarshal(apothemGenesis, &genesis)
@@ -187,8 +198,12 @@ func initGenesis(ctx *cli.Context) error {
utils.Fatalf("invalid genesis json: %v", err)
}
} else {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("need genesis.json file as the only argument")
+ }
+ genesisPath := ctx.Args().First()
if len(genesisPath) == 0 {
- utils.Fatalf("Must supply path to genesis JSON file")
+ utils.Fatalf("invalid path to genesis file")
}
file, err := os.Open(genesisPath)
if err != nil {
@@ -203,6 +218,8 @@ func initGenesis(ctx *cli.Context) error {
// Open an initialise both full and light databases
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
for _, name := range []string{"chaindata", "lightchaindata"} {
chaindb, err := stack.OpenDatabase(name, 0, 0, "")
if err != nil {
@@ -218,16 +235,15 @@ func initGenesis(ctx *cli.Context) error {
}
func importChain(ctx *cli.Context) error {
- if len(ctx.Args()) < 1 {
+ if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
}
+ stack, cfg := makeFullNode(ctx)
+ defer stack.Close()
// Start metrics export if enabled
- utils.SetupMetrics(ctx)
- // Start system runtime metrics collection
- go metrics.CollectProcessMetrics(3 * time.Second)
+ utils.SetupMetrics(&cfg.Metrics)
- stack, _ := makeFullNode(ctx)
chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close()
@@ -249,12 +265,12 @@ func importChain(ctx *cli.Context) error {
// Import the chain
start := time.Now()
- if len(ctx.Args()) == 1 {
+ if ctx.Args().Len() == 1 {
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
log.Error("Import error", "err", err)
}
} else {
- for _, arg := range ctx.Args() {
+ for _, arg := range ctx.Args().Slice() {
if err := utils.ImportChain(chain, arg); err != nil {
log.Error("Import error", "file", arg, "err", err)
}
@@ -287,7 +303,7 @@ func importChain(ctx *cli.Context) error {
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
- if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) {
+ if ctx.IsSet(utils.NoCompactionFlag.Name) {
return nil
}
@@ -315,17 +331,20 @@ func importChain(ctx *cli.Context) error {
}
func exportChain(ctx *cli.Context) error {
- if len(ctx.Args()) < 1 {
+ if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
}
+
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
chain, db := utils.MakeChain(ctx, stack)
defer db.Close()
start := time.Now()
var err error
fp := ctx.Args().First()
- if len(ctx.Args()) < 3 {
+ if ctx.Args().Len() < 3 {
err = utils.ExportChain(chain, fp)
} else {
// This can be improved to allow for numbers larger than 9223372036854775807
@@ -349,10 +368,13 @@ func exportChain(ctx *cli.Context) error {
// importPreimages imports preimage data from the specified file.
func importPreimages(ctx *cli.Context) error {
- if len(ctx.Args()) < 1 {
+ if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
}
+
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
diskdb := utils.MakeChainDatabase(ctx, stack)
defer diskdb.Close()
@@ -366,10 +388,12 @@ func importPreimages(ctx *cli.Context) error {
// exportPreimages dumps the preimage data to specified json file in streaming way.
func exportPreimages(ctx *cli.Context) error {
- if len(ctx.Args()) < 1 {
+ if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
}
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
diskdb := utils.MakeChainDatabase(ctx, stack)
defer diskdb.Close()
@@ -383,19 +407,24 @@ func exportPreimages(ctx *cli.Context) error {
func copyDb(ctx *cli.Context) error {
// Ensure we have a source chain directory to copy
- if len(ctx.Args()) != 1 {
+ if ctx.Args().Len() != 1 {
utils.Fatalf("Source chaindata directory path argument missing")
}
// Initialize a new chain for the running node to sync into
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close()
- syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode)
+ var syncmode downloader.SyncMode
+ if err := syncmode.UnmarshalText([]byte(ctx.String(utils.SyncModeFlag.Name))); err != nil {
+ utils.Fatalf("invalid --syncmode flag: %v", err)
+ }
dl := downloader.New(syncmode, chainDb, new(event.TypeMux), chain, nil, nil, nil)
// Create a source peer to satisfy downloader requests from
- db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.GlobalInt(utils.CacheFlag.Name), 256, "")
+ db, err := rawdb.NewLevelDBDatabase(ctx.Args().First(), ctx.Int(utils.CacheFlag.Name), 256, "")
if err != nil {
return err
}
@@ -461,10 +490,12 @@ func removeDB(ctx *cli.Context) error {
func dump(ctx *cli.Context) error {
stack, _ := makeFullNode(ctx)
+ defer stack.Close()
+
chain, chainDb := utils.MakeChain(ctx, stack)
defer chainDb.Close()
-
- for _, arg := range ctx.Args() {
+
+ for _, arg := range ctx.Args().Slice() {
var block *types.Block
if hashish(arg) {
block = chain.GetBlockByHash(common.HexToHash(arg))
diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go
index a47f19c7bf..ef0dbbb00d 100644
--- a/cmd/XDC/config.go
+++ b/cmd/XDC/config.go
@@ -27,34 +27,33 @@ import (
"strings"
"unicode"
- "gopkg.in/urfave/cli.v1"
-
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
- "github.com/XinFinOrg/XDPoSChain/internal/debug"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/node"
"github.com/XinFinOrg/XDPoSChain/params"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
"github.com/naoina/toml"
+ "github.com/urfave/cli/v2"
)
var (
- dumpConfigCommand = cli.Command{
- Action: utils.MigrateFlags(dumpConfig),
+ dumpConfigCommand = &cli.Command{
+ Action: dumpConfig,
Name: "dumpconfig",
Usage: "Show configuration values",
ArgsUsage: "",
- Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...),
- Category: "MISCELLANEOUS COMMANDS",
+ Flags: utils.GroupFlags(nodeFlags, rpcFlags),
Description: `The dumpconfig command shows configuration values.`,
}
- configFileFlag = cli.StringFlag{
- Name: "config",
- Usage: "TOML configuration file",
+ configFileFlag = &cli.StringFlag{
+ Name: "config",
+ Usage: "TOML configuration file",
+ Category: flags.EthCategory,
}
)
@@ -91,9 +90,9 @@ type Bootnodes struct {
type XDCConfig struct {
Eth ethconfig.Config
- Shh whisper.Config
Node node.Config
Ethstats ethstatsConfig
+ Metrics metrics.Config
XDCX XDCx.Config
Account account
StakeEnable bool
@@ -120,42 +119,43 @@ func defaultNodeConfig() node.Config {
cfg := node.DefaultConfig
cfg.Name = clientIdentifier
cfg.Version = params.VersionWithCommit(gitCommit)
- cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
- cfg.WSModules = append(cfg.WSModules, "eth", "shh")
+ cfg.HTTPModules = append(cfg.HTTPModules, "eth")
+ cfg.WSModules = append(cfg.WSModules, "eth")
cfg.IPCPath = "XDC.ipc"
return cfg
}
+// makeConfigNode loads geth configuration and creates a blank node instance.
func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) {
// Load defaults.
cfg := XDCConfig{
Eth: ethconfig.Defaults,
- Shh: whisper.DefaultConfig,
XDCX: XDCx.DefaultConfig,
Node: defaultNodeConfig(),
+ Metrics: metrics.DefaultConfig,
StakeEnable: true,
Verbosity: 3,
NAT: "",
}
// Load config file.
- if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if file := ctx.String(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
}
- if ctx.GlobalIsSet(utils.StakingEnabledFlag.Name) {
- cfg.StakeEnable = ctx.GlobalBool(utils.StakingEnabledFlag.Name)
- }
- if !ctx.GlobalIsSet(debug.VerbosityFlag.Name) {
- debug.Glogger.Verbosity(log.Lvl(cfg.Verbosity))
+ if ctx.IsSet(utils.MiningEnabledFlag.Name) {
+ cfg.StakeEnable = ctx.Bool(utils.MiningEnabledFlag.Name)
}
+ // if !ctx.IsSet(debug.VerbosityFlag.Name) {
+ // debug.Verbosity(log.Lvl(cfg.Verbosity))
+ // }
- if !ctx.GlobalIsSet(utils.NATFlag.Name) && cfg.NAT != "" {
+ if !ctx.IsSet(utils.NATFlag.Name) && cfg.NAT != "" {
ctx.Set(utils.NATFlag.Name, cfg.NAT)
}
// Check testnet is enable.
- if ctx.GlobalBool(utils.XDCTestnetFlag.Name) {
+ if ctx.Bool(utils.XDCTestnetFlag.Name) {
common.IsTestnet = true
common.TRC21IssuerSMC = common.TRC21IssuerSMCTestNet
cfg.Eth.NetworkId = 51
@@ -164,24 +164,24 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) {
common.TIPXDCXCancellationFee = common.TIPXDCXCancellationFeeTestnet
}
- if ctx.GlobalBool(utils.EnableXDCPrefixFlag.Name) {
+ if ctx.Bool(utils.EnableXDCPrefixFlag.Name) {
common.Enable0xPrefix = false
}
// Rewound
- if rewound := ctx.GlobalInt(utils.RewoundFlag.Name); rewound != 0 {
+ if rewound := ctx.Int(utils.RewoundFlag.Name); rewound != 0 {
common.Rewound = uint64(rewound)
}
// Check rollback hash exist.
- if rollbackHash := ctx.GlobalString(utils.RollbackFlag.Name); rollbackHash != "" {
+ if rollbackHash := ctx.String(utils.RollbackFlag.Name); rollbackHash != "" {
common.RollbackHash = common.HexToHash(rollbackHash)
}
// Check GasPrice
common.MinGasPrice = big.NewInt(common.DefaultMinGasPrice)
- if ctx.GlobalIsSet(utils.GasPriceFlag.Name) {
- if gasPrice := int64(ctx.GlobalInt(utils.GasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice {
+ if ctx.IsSet(utils.MinerGasPriceFlag.Name) {
+ if gasPrice := int64(ctx.Int(utils.MinerGasPriceFlag.Name)); gasPrice > common.DefaultMinGasPrice {
common.MinGasPrice = big.NewInt(gasPrice)
}
}
@@ -208,12 +208,14 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) {
utils.Fatalf("Failed to create the protocol stack: %v", err)
}
utils.SetEthConfig(ctx, stack, &cfg.Eth)
- if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
- cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
+ if ctx.IsSet(utils.EthStatsURLFlag.Name) {
+ cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name)
}
- utils.SetShhConfig(ctx, stack, &cfg.Shh)
utils.SetXDCXConfig(ctx, &cfg.XDCX, cfg.Node.DataDir)
+
+ applyMetricConfig(ctx, &cfg)
+
return stack, cfg
}
@@ -230,36 +232,16 @@ func applyValues(values []string, params *[]string) {
}
-// enableWhisper returns true in case one of the whisper flags is set.
-func enableWhisper(ctx *cli.Context) bool {
- for _, flag := range whisperFlags {
- if ctx.GlobalIsSet(flag.GetName()) {
- return true
- }
- }
- return false
-}
-
func makeFullNode(ctx *cli.Context) (*node.Node, XDCConfig) {
stack, cfg := makeConfigNode(ctx)
+ // Start metrics export if enabled
+ utils.SetupMetrics(&cfg.Metrics)
+
// Register XDCX's OrderBook service if requested.
// enable in default
utils.RegisterXDCXService(stack, &cfg.XDCX)
- utils.RegisterEthService(stack, &cfg.Eth)
-
- // Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
- shhEnabled := enableWhisper(ctx)
- shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)
- if shhEnabled || shhAutoEnabled {
- if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {
- cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))
- }
- if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
- cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
- }
- utils.RegisterShhService(stack, &cfg.Shh)
- }
+ utils.RegisterEthService(stack, &cfg.Eth, cfg.Node.Version)
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
@@ -287,3 +269,69 @@ func dumpConfig(ctx *cli.Context) error {
os.Stdout.Write(out)
return nil
}
+
+func applyMetricConfig(ctx *cli.Context, cfg *XDCConfig) {
+ if ctx.IsSet(utils.MetricsEnabledFlag.Name) {
+ cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) {
+ log.Warn("Expensive metrics are collected by default, please remove this flag", "flag", utils.MetricsEnabledExpensiveFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsHTTPFlag.Name) {
+ cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsPortFlag.Name) {
+ cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) {
+ cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) {
+ cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) {
+ cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) {
+ cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) {
+ cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) {
+ cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
+ cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) {
+ cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) {
+ cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) {
+ cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name)
+ }
+ // Sanity-check the commandline flags. It is fine if some unused fields is part
+ // of the toml-config, but we expect the commandline to only contain relevant
+ // arguments, otherwise it indicates an error.
+ var (
+ enableExport = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name)
+ enableExportV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
+ )
+ if enableExport || enableExportV2 {
+ v1FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) ||
+ ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name)
+
+ v2FlagIsSet := ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) ||
+ ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) ||
+ ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name)
+
+ if enableExport && v2FlagIsSet {
+ utils.Fatalf("Flags --influxdb-metrics.organization, --influxdb-metrics.token, --influxdb-metrics.bucket are only available for influxdb-v2")
+ } else if enableExportV2 && v1FlagIsSet {
+ utils.Fatalf("Flags --influxdb-metrics.username, --influxdb-metrics.password are only available for influxdb-v1")
+ }
+ }
+}
diff --git a/cmd/XDC/consolecmd.go b/cmd/XDC/consolecmd.go
index 6eb2b52fc9..f6b0630293 100644
--- a/cmd/XDC/consolecmd.go
+++ b/cmd/XDC/consolecmd.go
@@ -28,31 +28,29 @@ import (
"github.com/XinFinOrg/XDPoSChain/console"
"github.com/XinFinOrg/XDPoSChain/node"
"github.com/XinFinOrg/XDPoSChain/rpc"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
var (
consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag}
- consoleCommand = cli.Command{
- Action: utils.MigrateFlags(localConsole),
- Name: "console",
- Usage: "Start an interactive JavaScript environment",
- Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...),
- Category: "CONSOLE COMMANDS",
+ consoleCommand = &cli.Command{
+ Action: localConsole,
+ Name: "console",
+ Usage: "Start an interactive JavaScript environment",
+ Flags: utils.GroupFlags(nodeFlags, rpcFlags, consoleFlags),
Description: `
The XDC console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console.`,
}
- attachCommand = cli.Command{
- Action: utils.MigrateFlags(remoteConsole),
+ attachCommand = &cli.Command{
+ Action: remoteConsole,
Name: "attach",
Usage: "Start an interactive JavaScript environment (connect to node)",
ArgsUsage: "[endpoint]",
- Flags: append(consoleFlags, utils.DataDirFlag),
- Category: "CONSOLE COMMANDS",
+ Flags: utils.GroupFlags([]cli.Flag{utils.DataDirFlag}, consoleFlags),
Description: `
The XDC console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
@@ -60,13 +58,12 @@ See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console.
This command allows to open a console on a running XDC node.`,
}
- javascriptCommand = cli.Command{
- Action: utils.MigrateFlags(ephemeralConsole),
+ javascriptCommand = &cli.Command{
+ Action: ephemeralConsole,
Name: "js",
Usage: "Execute the specified JavaScript files",
ArgsUsage: " [jsfile...]",
- Flags: append(nodeFlags, consoleFlags...),
- Category: "CONSOLE COMMANDS",
+ Flags: utils.GroupFlags(nodeFlags, consoleFlags),
Description: `
The JavaScript VM exposes a node admin interface as well as the Ðapp
JavaScript API. See https://github.com/XinFinOrg/XDPoSChain/wiki/JavaScript-Console`,
@@ -79,7 +76,7 @@ func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
node, cfg := makeFullNode(ctx)
startNode(ctx, node, cfg)
- defer node.Stop()
+ defer node.Close()
// Attach to the newly started node and start the JavaScript console
client, err := node.Attach()
@@ -88,7 +85,7 @@ func localConsole(ctx *cli.Context) error {
}
config := console.Config{
DataDir: utils.MakeDataDir(ctx),
- DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
+ DocRoot: ctx.String(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
@@ -100,7 +97,7 @@ func localConsole(ctx *cli.Context) error {
defer console.Stop(false)
// If only a short execution was requested, evaluate and return
- if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
+ if script := ctx.String(utils.ExecFlag.Name); script != "" {
console.Evaluate(script)
return nil
}
@@ -114,17 +111,20 @@ func localConsole(ctx *cli.Context) error {
// remoteConsole will connect to a remote XDC instance, attaching a JavaScript
// console to it.
func remoteConsole(ctx *cli.Context) error {
- // Attach to a remotely running XDC instance and start the JavaScript console
+ if ctx.Args().Len() > 1 {
+ utils.Fatalf("invalid command-line: too many arguments")
+ }
+
endpoint := ctx.Args().First()
if endpoint == "" {
path := node.DefaultDataDir()
- if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
- path = ctx.GlobalString(utils.DataDirFlag.Name)
+ if ctx.IsSet(utils.DataDirFlag.Name) {
+ path = ctx.String(utils.DataDirFlag.Name)
}
if path != "" {
- if ctx.GlobalBool(utils.TestnetFlag.Name) {
+ if ctx.Bool(utils.TestnetFlag.Name) {
path = filepath.Join(path, "testnet")
- } else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
+ } else if ctx.Bool(utils.RinkebyFlag.Name) {
path = filepath.Join(path, "rinkeby")
}
}
@@ -137,7 +137,7 @@ func remoteConsole(ctx *cli.Context) error {
}
config := console.Config{
DataDir: utils.MakeDataDir(ctx),
- DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
+ DocRoot: ctx.String(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
@@ -148,7 +148,7 @@ func remoteConsole(ctx *cli.Context) error {
}
defer console.Stop(false)
- if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
+ if script := ctx.String(utils.ExecFlag.Name); script != "" {
console.Evaluate(script)
return nil
}
@@ -181,7 +181,7 @@ func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
node, cfg := makeFullNode(ctx)
startNode(ctx, node, cfg)
- defer node.Stop()
+ defer node.Close()
// Attach to the newly started node and start the JavaScript console
client, err := node.Attach()
@@ -190,7 +190,7 @@ func ephemeralConsole(ctx *cli.Context) error {
}
config := console.Config{
DataDir: utils.MakeDataDir(ctx),
- DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
+ DocRoot: ctx.String(utils.JSpathFlag.Name),
Client: client,
Preload: utils.MakeConsolePreloads(ctx),
}
@@ -202,7 +202,7 @@ func ephemeralConsole(ctx *cli.Context) error {
defer console.Stop(false)
// Evaluate each of the specified JavaScript files
- for _, file := range ctx.Args() {
+ for _, file := range ctx.Args().Slice() {
if err = console.Execute(file); err != nil {
utils.Fatalf("Failed to execute %s: %v", file, err)
}
diff --git a/cmd/XDC/consolecmd_test.go b/cmd/XDC/consolecmd_test.go
index ecbd11762a..23988bc2c2 100644
--- a/cmd/XDC/consolecmd_test.go
+++ b/cmd/XDC/consolecmd_test.go
@@ -39,13 +39,14 @@ const (
// then terminated by closing the input stream.
func TestConsoleWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
// Start a XDC console, make sure it's cleaned up and terminate the console
XDC := runXDC(t,
- "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(),
+ "console", "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(),
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
- "--etherbase", coinbase,
- "console")
+ "--miner-etherbase", coinbase)
// Gather all the infos the welcome message needs to contain
XDC.SetTemplateFunc("goos", func() string { return runtime.GOOS })
@@ -76,18 +77,18 @@ at block: 0 ({{niltime}})
func TestIPCAttachWelcome(t *testing.T) {
// Configure the instance for IPC attachement
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
var ipc string
if runtime.GOOS == "windows" {
ipc = `\\.\pipe\XDC` + strconv.Itoa(trulyRandInt(100000, 999999))
} else {
- ws := tmpdir(t)
- defer os.RemoveAll(ws)
- ipc = filepath.Join(ws, "XDC.ipc")
+ ipc = filepath.Join(datadir, "XDC.ipc")
}
XDC := runXDC(t,
- "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(),
+ "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(),
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
- "--etherbase", coinbase, "--ipcpath", ipc)
+ "--miner-etherbase", coinbase, "--ipcpath", ipc)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, XDC, "ipc:"+ipc, ipcAPIs)
@@ -99,10 +100,12 @@ func TestIPCAttachWelcome(t *testing.T) {
func TestHTTPAttachWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
- "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(),
+ "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(),
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
- "--etherbase", coinbase, "--rpc", "--rpcport", port)
+ "--miner-etherbase", coinbase, "--http", "--http-port", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, XDC, "http://localhost:"+port, httpAPIs)
@@ -114,11 +117,12 @@ func TestHTTPAttachWelcome(t *testing.T) {
func TestWSAttachWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
-
+ datadir := tmpdir(t)
+ defer os.RemoveAll(datadir)
XDC := runXDC(t,
- "--XDCx.datadir", tmpdir(t)+"XDCx/"+time.Now().String(),
+ "--datadir", datadir, "--XDCx-datadir", datadir+"/XDCx/"+time.Now().String(),
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
- "--etherbase", coinbase, "--ws", "--wsport", port)
+ "--miner-etherbase", coinbase, "--ws", "--ws-port", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
testAttachWelcome(t, XDC, "ws://localhost:"+port, httpAPIs)
diff --git a/cmd/XDC/dao_test.go b/cmd/XDC/dao_test.go
index 030e255517..5444a4b137 100644
--- a/cmd/XDC/dao_test.go
+++ b/cmd/XDC/dao_test.go
@@ -111,11 +111,10 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
if err := os.WriteFile(json, []byte(genesis), 0600); err != nil {
t.Fatalf("test %d: failed to write genesis file: %v", test, err)
}
- runXDC(t, "--datadir", datadir, "init", json).WaitExit()
+ runXDC(t, "init", "--datadir", datadir, json).WaitExit()
} else {
// Force chain initialization
- args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
- XDC := runXDC(t, append(args, []string{"--exec", "2+2", "console"}...)...)
+ XDC := runXDC(t, "console", "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir, "--exec", "2+2")
XDC.WaitExit()
}
// Retrieve the DAO config flag from the database
diff --git a/cmd/XDC/main.go b/cmd/XDC/main.go
index 864e5e1cd0..f3b9bdba13 100644
--- a/cmd/XDC/main.go
+++ b/cmd/XDC/main.go
@@ -33,14 +33,14 @@ import (
"github.com/XinFinOrg/XDPoSChain/eth"
"github.com/XinFinOrg/XDPoSChain/ethclient"
"github.com/XinFinOrg/XDPoSChain/internal/debug"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/node"
+ "github.com/urfave/cli/v2"
// Force-load the native, to trigger registration
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native"
-
- "gopkg.in/urfave/cli.v1"
)
const (
@@ -51,7 +51,7 @@ var (
// Git SHA1 commit hash of the release (set via linker flags)
gitCommit = ""
// The app that holds all commands and flags.
- app = utils.NewApp(gitCommit, "the XDPoSChain command line interface")
+ app = flags.NewApp(gitCommit, "the XDPoSChain command line interface")
// flags that configure the node
nodeFlags = []cli.Flag{
utils.IdentityFlag,
@@ -98,14 +98,15 @@ var (
//utils.TrieCacheGenFlag,
utils.CacheLogSizeFlag,
utils.FDLimitFlag,
+ utils.CryptoKZGFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
- utils.EtherbaseFlag,
- utils.GasPriceFlag,
- utils.StakerThreadsFlag,
- utils.StakingEnabledFlag,
- utils.TargetGasLimitFlag,
+ utils.MinerEtherbaseFlag,
+ utils.MinerGasPriceFlag,
+ utils.MinerThreadsFlag,
+ utils.MiningEnabledFlag,
+ utils.MinerGasLimitFlag,
utils.NATFlag,
utils.NoDiscoverFlag,
//utils.DiscoveryV5Flag,
@@ -122,12 +123,9 @@ var (
utils.EnableXDCPrefixFlag,
utils.RewoundFlag,
utils.NetworkIdFlag,
- utils.RPCCORSDomainFlag,
- utils.RPCVirtualHostsFlag,
+ utils.HTTPCORSDomainFlag,
+ utils.HTTPVirtualHostsFlag,
utils.EthStatsURLFlag,
- utils.MetricsEnabledFlag,
- utils.MetricsHTTPFlag,
- utils.MetricsPortFlag,
//utils.FakePoWFlag,
//utils.NoCompactionFlag,
//utils.GpoBlocksFlag,
@@ -136,6 +134,8 @@ var (
utils.GpoIgnoreGasPriceFlag,
//utils.ExtraDataFlag,
configFileFlag,
+ utils.LogDebugFlag,
+ utils.LogBacktraceAtFlag,
utils.AnnounceTxsFlag,
utils.StoreRewardFlag,
utils.RollbackFlag,
@@ -143,12 +143,14 @@ var (
}
rpcFlags = []cli.Flag{
- utils.RPCEnabledFlag,
+ utils.HTTPEnabledFlag,
utils.RPCGlobalGasCapFlag,
- utils.RPCListenAddrFlag,
- utils.RPCPortFlag,
- utils.RPCHttpWriteTimeoutFlag,
- utils.RPCApiFlag,
+ utils.HTTPListenAddrFlag,
+ utils.HTTPPortFlag,
+ utils.HTTPReadTimeoutFlag,
+ utils.HTTPWriteTimeoutFlag,
+ utils.HTTPIdleTimeoutFlag,
+ utils.HTTPApiFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,
@@ -159,19 +161,29 @@ var (
utils.RPCGlobalTxFeeCap,
}
- whisperFlags = []cli.Flag{
- utils.WhisperEnabledFlag,
- utils.WhisperMaxMessageSizeFlag,
- utils.WhisperMinPOWFlag,
+ metricsFlags = []cli.Flag{
+ utils.MetricsEnabledFlag,
+ utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsHTTPFlag,
+ utils.MetricsPortFlag,
+ utils.MetricsEnableInfluxDBFlag,
+ utils.MetricsInfluxDBEndpointFlag,
+ utils.MetricsInfluxDBDatabaseFlag,
+ utils.MetricsInfluxDBUsernameFlag,
+ utils.MetricsInfluxDBPasswordFlag,
+ utils.MetricsInfluxDBTagsFlag,
+ utils.MetricsEnableInfluxDBV2Flag,
+ utils.MetricsInfluxDBTokenFlag,
+ utils.MetricsInfluxDBBucketFlag,
+ utils.MetricsInfluxDBOrganizationFlag,
}
)
func init() {
// Initialize the CLI app and start XDC
app.Action = XDC
- app.HideVersion = true // we have a command to print the version
- app.Copyright = "Copyright (c) 2018 XDPoSChain"
- app.Commands = []cli.Command{
+ app.Copyright = "Copyright (c) 2024 XDPoSChain"
+ app.Commands = []*cli.Command{
// See chaincmd.go:
initCommand,
importCommand,
@@ -196,13 +208,17 @@ func init() {
app.Flags = append(app.Flags, rpcFlags...)
app.Flags = append(app.Flags, consoleFlags...)
app.Flags = append(app.Flags, debug.Flags...)
- app.Flags = append(app.Flags, whisperFlags...)
+ app.Flags = append(app.Flags, metricsFlags...)
+ flags.AutoEnvVars(app.Flags, "XDC")
app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU())
+ flags.MigrateGlobalFlags(ctx)
if err := debug.Setup(ctx); err != nil {
return err
}
+ flags.CheckEnvVars(ctx, app.Flags, "XDC")
+
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
@@ -229,6 +245,7 @@ func main() {
// blocking mode, waiting for it to be shut down.
func XDC(ctx *cli.Context) error {
node, cfg := makeFullNode(ctx)
+ defer node.Close()
startNode(ctx, node, cfg)
node.Wait()
return nil
@@ -244,11 +261,11 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
// Unlock any account specifically requested
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- if ctx.GlobalIsSet(utils.UnlockedAccountFlag.Name) {
- cfg.Account.Unlocks = strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
+ if ctx.IsSet(utils.UnlockedAccountFlag.Name) {
+ cfg.Account.Unlocks = strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",")
}
- if ctx.GlobalIsSet(utils.PasswordFileFlag.Name) {
+ if ctx.IsSet(utils.PasswordFileFlag.Name) {
cfg.Account.Passwords = utils.MakePasswordList(ctx)
}
@@ -301,13 +318,9 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
// Start auxiliary services if enabled
// Mining only makes sense if a full Ethereum node is running
- if ctx.GlobalBool(utils.LightModeFlag.Name) || ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
+ if ctx.Bool(utils.LightModeFlag.Name) || ctx.String(utils.SyncModeFlag.Name) == "light" {
utils.Fatalf("Light clients do not support staking")
}
- // Start metrics export if enabled
- utils.SetupMetrics(ctx)
- // Start system runtime metrics collection
- go metrics.CollectProcessMetrics(3 * time.Second)
var ethereum *eth.Ethereum
if err := stack.Service(ðereum); err != nil {
@@ -317,7 +330,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
go func() {
started := false
ok := false
- slaveMode := ctx.GlobalIsSet(utils.XDCSlaveModeFlag.Name)
+ slaveMode := ctx.IsSet(utils.XDCSlaveModeFlag.Name)
var err error
ok, err = ethereum.ValidateMasternode()
if err != nil {
@@ -330,7 +343,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
} else {
log.Info("Masternode found. Enabling staking mode...")
// Use a reduced number of threads if requested
- if threads := ctx.GlobalInt(utils.StakerThreadsFlag.Name); threads > 0 {
+ if threads := ctx.Int(utils.MinerThreadsFlag.Name); threads > 0 {
type threaded interface {
SetThreads(threads int)
}
@@ -372,7 +385,7 @@ func startNode(ctx *cli.Context, stack *node.Node, cfg XDCConfig) {
} else {
log.Info("Masternode found. Enabling staking mode...")
// Use a reduced number of threads if requested
- if threads := ctx.GlobalInt(utils.StakerThreadsFlag.Name); threads > 0 {
+ if threads := ctx.Int(utils.MinerThreadsFlag.Name); threads > 0 {
type threaded interface {
SetThreads(threads int)
}
diff --git a/cmd/XDC/misccmd.go b/cmd/XDC/misccmd.go
index e86f7c27cc..65a0ae0d80 100644
--- a/cmd/XDC/misccmd.go
+++ b/cmd/XDC/misccmd.go
@@ -28,16 +28,15 @@ import (
"github.com/XinFinOrg/XDPoSChain/eth"
"github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
"github.com/XinFinOrg/XDPoSChain/params"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
var (
- makecacheCommand = cli.Command{
- Action: utils.MigrateFlags(makecache),
+ makecacheCommand = &cli.Command{
+ Action: makecache,
Name: "makecache",
Usage: "Generate ethash verification cache (for testing)",
ArgsUsage: " ",
- Category: "MISCELLANEOUS COMMANDS",
Description: `
The makecache command generates an ethash cache in .
@@ -45,12 +44,11 @@ This command exists to support the system testing project.
Regular users do not need to execute it.
`,
}
- makedagCommand = cli.Command{
- Action: utils.MigrateFlags(makedag),
+ makedagCommand = &cli.Command{
+ Action: makedag,
Name: "makedag",
Usage: "Generate ethash mining DAG (for testing)",
ArgsUsage: " ",
- Category: "MISCELLANEOUS COMMANDS",
Description: `
The makedag command generates an ethash DAG in .
@@ -58,28 +56,26 @@ This command exists to support the system testing project.
Regular users do not need to execute it.
`,
}
- versionCommand = cli.Command{
- Action: utils.MigrateFlags(version),
+ versionCommand = &cli.Command{
+ Action: version,
Name: "version",
Usage: "Print version numbers",
ArgsUsage: " ",
- Category: "MISCELLANEOUS COMMANDS",
Description: `
The output of this command is supposed to be machine-readable.
`,
}
- licenseCommand = cli.Command{
- Action: utils.MigrateFlags(license),
+ licenseCommand = &cli.Command{
+ Action: license,
Name: "license",
Usage: "Display license information",
ArgsUsage: " ",
- Category: "MISCELLANEOUS COMMANDS",
}
)
// makecache generates an ethash verification cache into the provided folder.
func makecache(ctx *cli.Context) error {
- args := ctx.Args()
+ args := ctx.Args().Slice()
if len(args) != 2 {
utils.Fatalf(`Usage: XDC makecache `)
}
@@ -94,7 +90,7 @@ func makecache(ctx *cli.Context) error {
// makedag generates an ethash mining DAG into the provided folder.
func makedag(ctx *cli.Context) error {
- args := ctx.Args()
+ args := ctx.Args().Slice()
if len(args) != 2 {
utils.Fatalf(`Usage: XDC makedag `)
}
diff --git a/cmd/XDC/monitorcmd.go b/cmd/XDC/monitorcmd.go
deleted file mode 100644
index 42decc574f..0000000000
--- a/cmd/XDC/monitorcmd.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-package main
-
-import (
- "fmt"
- "math"
- "reflect"
- "runtime"
- "sort"
- "strings"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "github.com/XinFinOrg/XDPoSChain/node"
- "github.com/XinFinOrg/XDPoSChain/rpc"
- "github.com/gizak/termui"
- "gopkg.in/urfave/cli.v1"
-)
-
-var (
- monitorCommandAttachFlag = cli.StringFlag{
- Name: "attach",
- Value: node.DefaultIPCEndpoint(clientIdentifier),
- Usage: "API endpoint to attach to",
- }
- monitorCommandRowsFlag = cli.IntFlag{
- Name: "rows",
- Value: 5,
- Usage: "Maximum rows in the chart grid",
- }
- monitorCommandRefreshFlag = cli.IntFlag{
- Name: "refresh",
- Value: 3,
- Usage: "Refresh interval in seconds",
- }
- monitorCommand = cli.Command{
- Action: utils.MigrateFlags(monitor), // keep track of migration progress
- Name: "monitor",
- Usage: "Monitor and visualize node metrics",
- ArgsUsage: " ",
- Category: "MONITOR COMMANDS",
- Description: `
-The XDC monitor is a tool to collect and visualize various internal metrics
-gathered by the node, supporting different chart types as well as the capacity
-to display multiple metrics simultaneously.
-`,
- Flags: []cli.Flag{
- monitorCommandAttachFlag,
- monitorCommandRowsFlag,
- monitorCommandRefreshFlag,
- },
- }
-)
-
-// monitor starts a terminal UI based monitoring tool for the requested metrics.
-func monitor(ctx *cli.Context) error {
- var (
- client *rpc.Client
- err error
- )
- // Attach to an Ethereum node over IPC or RPC
- endpoint := ctx.String(monitorCommandAttachFlag.Name)
- if client, err = dialRPC(endpoint); err != nil {
- utils.Fatalf("Unable to attach to XDC node: %v", err)
- }
- defer client.Close()
-
- // Retrieve all the available metrics and resolve the user pattens
- metrics, err := retrieveMetrics(client)
- if err != nil {
- utils.Fatalf("Failed to retrieve system metrics: %v", err)
- }
- monitored := resolveMetrics(metrics, ctx.Args())
- if len(monitored) == 0 {
- list := expandMetrics(metrics, "")
- sort.Strings(list)
-
- if len(list) > 0 {
- utils.Fatalf("No metrics specified.\n\nAvailable:\n - %s", strings.Join(list, "\n - "))
- } else {
- utils.Fatalf("No metrics collected by XDC (--%s).\n", utils.MetricsEnabledFlag.Name)
- }
- }
- sort.Strings(monitored)
- if cols := len(monitored) / ctx.Int(monitorCommandRowsFlag.Name); cols > 6 {
- utils.Fatalf("Requested metrics (%d) spans more that 6 columns:\n - %s", len(monitored), strings.Join(monitored, "\n - "))
- }
- // Create and configure the chart UI defaults
- if err := termui.Init(); err != nil {
- utils.Fatalf("Unable to initialize terminal UI: %v", err)
- }
- defer termui.Close()
-
- rows := len(monitored)
- if max := ctx.Int(monitorCommandRowsFlag.Name); rows > max {
- rows = max
- }
- cols := (len(monitored) + rows - 1) / rows
- for i := 0; i < rows; i++ {
- termui.Body.AddRows(termui.NewRow())
- }
- // Create each individual data chart
- footer := termui.NewPar("")
- footer.Block.Border = true
- footer.Height = 3
-
- charts := make([]*termui.LineChart, len(monitored))
- units := make([]int, len(monitored))
- data := make([][]float64, len(monitored))
- for i := 0; i < len(monitored); i++ {
- charts[i] = createChart((termui.TermHeight() - footer.Height) / rows)
- row := termui.Body.Rows[i%rows]
- row.Cols = append(row.Cols, termui.NewCol(12/cols, 0, charts[i]))
- }
- termui.Body.AddRows(termui.NewRow(termui.NewCol(12, 0, footer)))
-
- refreshCharts(client, monitored, data, units, charts, ctx, footer)
- termui.Body.Align()
- termui.Render(termui.Body)
-
- // Watch for various system events, and periodically refresh the charts
- termui.Handle("/sys/kbd/C-c", func(termui.Event) {
- termui.StopLoop()
- })
- termui.Handle("/sys/wnd/resize", func(termui.Event) {
- termui.Body.Width = termui.TermWidth()
- for _, chart := range charts {
- chart.Height = (termui.TermHeight() - footer.Height) / rows
- }
- termui.Body.Align()
- termui.Render(termui.Body)
- })
- go func() {
- tick := time.NewTicker(time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second)
- for range tick.C {
- if refreshCharts(client, monitored, data, units, charts, ctx, footer) {
- termui.Body.Align()
- }
- termui.Render(termui.Body)
- }
- }()
- termui.Loop()
- return nil
-}
-
-// retrieveMetrics contacts the attached XDC node and retrieves the entire set
-// of collected system metrics.
-func retrieveMetrics(client *rpc.Client) (map[string]interface{}, error) {
- var metrics map[string]interface{}
- err := client.Call(&metrics, "debug_metrics", true)
- return metrics, err
-}
-
-// resolveMetrics takes a list of input metric patterns, and resolves each to one
-// or more canonical metric names.
-func resolveMetrics(metrics map[string]interface{}, patterns []string) []string {
- res := []string{}
- for _, pattern := range patterns {
- res = append(res, resolveMetric(metrics, pattern, "")...)
- }
- return res
-}
-
-// resolveMetrics takes a single of input metric pattern, and resolves it to one
-// or more canonical metric names.
-func resolveMetric(metrics map[string]interface{}, pattern string, path string) []string {
- results := []string{}
-
- // If a nested metric was requested, recurse optionally branching (via comma)
- parts := strings.SplitN(pattern, "/", 2)
- if len(parts) > 1 {
- for _, variation := range strings.Split(parts[0], ",") {
- if submetrics, ok := metrics[variation].(map[string]interface{}); !ok {
- utils.Fatalf("Failed to retrieve system metrics: %s", path+variation)
- return nil
- } else {
- results = append(results, resolveMetric(submetrics, parts[1], path+variation+"/")...)
- }
- }
- return results
- }
- // Depending what the last link is, return or expand
- for _, variation := range strings.Split(pattern, ",") {
- switch metric := metrics[variation].(type) {
- case float64:
- // Final metric value found, return as singleton
- results = append(results, path+variation)
-
- case map[string]interface{}:
- results = append(results, expandMetrics(metric, path+variation+"/")...)
-
- default:
- utils.Fatalf("Metric pattern resolved to unexpected type: %v", reflect.TypeOf(metric))
- return nil
- }
- }
- return results
-}
-
-// expandMetrics expands the entire tree of metrics into a flat list of paths.
-func expandMetrics(metrics map[string]interface{}, path string) []string {
- // Iterate over all fields and expand individually
- list := []string{}
- for name, metric := range metrics {
- switch metric := metric.(type) {
- case float64:
- // Final metric value found, append to list
- list = append(list, path+name)
-
- case map[string]interface{}:
- // Tree of metrics found, expand recursively
- list = append(list, expandMetrics(metric, path+name+"/")...)
-
- default:
- utils.Fatalf("Metric pattern %s resolved to unexpected type: %v", path+name, reflect.TypeOf(metric))
- return nil
- }
- }
- return list
-}
-
-// fetchMetric iterates over the metrics map and retrieves a specific one.
-func fetchMetric(metrics map[string]interface{}, metric string) float64 {
- parts := strings.Split(metric, "/")
- for _, part := range parts[:len(parts)-1] {
- var found bool
- metrics, found = metrics[part].(map[string]interface{})
- if !found {
- return 0
- }
- }
- if v, ok := metrics[parts[len(parts)-1]].(float64); ok {
- return v
- }
- return 0
-}
-
-// refreshCharts retrieves a next batch of metrics, and inserts all the new
-// values into the active datasets and charts
-func refreshCharts(client *rpc.Client, metrics []string, data [][]float64, units []int, charts []*termui.LineChart, ctx *cli.Context, footer *termui.Par) (realign bool) {
- values, err := retrieveMetrics(client)
- for i, metric := range metrics {
- if len(data) < 512 {
- data[i] = append([]float64{fetchMetric(values, metric)}, data[i]...)
- } else {
- data[i] = append([]float64{fetchMetric(values, metric)}, data[i][:len(data[i])-1]...)
- }
- if updateChart(metric, data[i], &units[i], charts[i], err) {
- realign = true
- }
- }
- updateFooter(ctx, err, footer)
- return
-}
-
-// updateChart inserts a dataset into a line chart, scaling appropriately as to
-// not display weird labels, also updating the chart label accordingly.
-func updateChart(metric string, data []float64, base *int, chart *termui.LineChart, err error) (realign bool) {
- dataUnits := []string{"", "K", "M", "G", "T", "E"}
- timeUnits := []string{"ns", "µs", "ms", "s", "ks", "ms"}
- colors := []termui.Attribute{termui.ColorBlue, termui.ColorCyan, termui.ColorGreen, termui.ColorYellow, termui.ColorRed, termui.ColorRed}
-
- // Extract only part of the data that's actually visible
- if chart.Width*2 < len(data) {
- data = data[:chart.Width*2]
- }
- // Find the maximum value and scale under 1K
- high := 0.0
- if len(data) > 0 {
- high = data[0]
- for _, value := range data[1:] {
- high = math.Max(high, value)
- }
- }
- unit, scale := 0, 1.0
- for high >= 1000 && unit+1 < len(dataUnits) {
- high, unit, scale = high/1000, unit+1, scale*1000
- }
- // If the unit changes, re-create the chart (hack to set max height...)
- if unit != *base {
- realign, *base, *chart = true, unit, *createChart(chart.Height)
- }
- // Update the chart's data points with the scaled values
- if cap(chart.Data) < len(data) {
- chart.Data = make([]float64, len(data))
- }
- chart.Data = chart.Data[:len(data)]
- for i, value := range data {
- chart.Data[i] = value / scale
- }
- // Update the chart's label with the scale units
- units := dataUnits
- if strings.Contains(metric, "/Percentiles/") || strings.Contains(metric, "/pauses/") || strings.Contains(metric, "/time/") {
- units = timeUnits
- }
- chart.BorderLabel = metric
- if len(units[unit]) > 0 {
- chart.BorderLabel += " [" + units[unit] + "]"
- }
- chart.LineColor = colors[unit] | termui.AttrBold
- if err != nil {
- chart.LineColor = termui.ColorRed | termui.AttrBold
- }
- return
-}
-
-// createChart creates an empty line chart with the default configs.
-func createChart(height int) *termui.LineChart {
- chart := termui.NewLineChart()
- if runtime.GOOS == "windows" {
- chart.Mode = "dot"
- }
- chart.DataLabels = []string{""}
- chart.Height = height
- chart.AxesColor = termui.ColorWhite
- chart.PaddingBottom = -2
-
- chart.BorderLabelFg = chart.BorderFg | termui.AttrBold
- chart.BorderFg = chart.BorderBg
-
- return chart
-}
-
-// updateFooter updates the footer contents based on any encountered errors.
-func updateFooter(ctx *cli.Context, err error, footer *termui.Par) {
- // Generate the basic footer
- refresh := time.Duration(ctx.Int(monitorCommandRefreshFlag.Name)) * time.Second
- footer.Text = fmt.Sprintf("Press Ctrl+C to quit. Refresh interval: %v.", refresh)
- footer.TextFgColor = termui.ThemeAttr("par.fg") | termui.AttrBold
-
- // Append any encountered errors
- if err != nil {
- footer.Text = fmt.Sprintf("Error: %v.", err)
- footer.TextFgColor = termui.ColorRed | termui.AttrBold
- }
-}
diff --git a/cmd/XDC/run_test.go b/cmd/XDC/run_test.go
index 82b4c8fd58..98f6092fed 100644
--- a/cmd/XDC/run_test.go
+++ b/cmd/XDC/run_test.go
@@ -66,12 +66,12 @@ func runXDC(t *testing.T, args ...string) *testXDC {
tt := &testXDC{}
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
for i, arg := range args {
- switch {
- case arg == "-datadir" || arg == "--datadir":
+ switch arg {
+ case "--datadir":
if i < len(args)-1 {
tt.Datadir = args[i+1]
}
- case arg == "-etherbase" || arg == "--etherbase":
+ case "--miner-etherbase":
if i < len(args)-1 {
tt.Etherbase = args[i+1]
}
@@ -80,7 +80,7 @@ func runXDC(t *testing.T, args ...string) *testXDC {
if tt.Datadir == "" {
tt.Datadir = tmpdir(t)
tt.Cleanup = func() { os.RemoveAll(tt.Datadir) }
- args = append([]string{"-datadir", tt.Datadir}, args...)
+ args = append([]string{"--datadir", tt.Datadir}, args...)
// Remove the temporary datadir if something fails below.
defer func() {
if t.Failed() {
diff --git a/cmd/XDC/testdata/config.toml b/cmd/XDC/testdata/config.toml
index 939c87c12c..b94576dddb 100644
--- a/cmd/XDC/testdata/config.toml
+++ b/cmd/XDC/testdata/config.toml
@@ -5,24 +5,24 @@ NAT = "" # flag --nat
[Eth]
NetworkId = 89 # flag --networkid
SyncMode = "full" # flag --syncmode
-GasPrice = 1 # flag --gasprice
+GasPrice = 1 # flag --miner-gasprice
[Shh]
[Node]
DataDir = "node1/" # flag --datadir
-HTTPPort = 8501 # flag --rpcport
-HTTPHost = "localhost" # flags --rpcaddr & --rpc
+HTTPPort = 8501 # flag --http-port
+HTTPHost = "localhost" # flags --http-addr & --http
# in 3 cases :
- # HTTPHost is "" == --rpc & --rpcaddr is not set
- # HTTPHost is "localhost" or "127.0.0.1" == only set --rpc
- # HTTPHost is other IP (ex : 192.168.1.1) = set 2 flags --rpc & --rpcaddr
-WSHost = "localhost" # flags --wsaddr & --ws . same option HTTPHost
-WSPort = 8546 # flag --wsport
-WSModules = ["eth","ssh"] #flag --wsapi
+ # HTTPHost is "" == --http & --http-addr is not set
+ # HTTPHost is "localhost" or "127.0.0.1" == only set --http
+ # HTTPHost is other IP (ex : 192.168.1.1) = set 2 flags --http & --http-addr
+WSHost = "localhost" # flags --ws-addr & --ws . same option HTTPHost
+WSPort = 8546 # flag --ws-port
+WSModules = ["eth","ssh"] #flag --ws-api
-HTTPModules = ["personal","db","eth","net","web3","txpool","miner"] # flag --rpcapi
+HTTPModules = ["personal","db","eth","net","web3","txpool","miner"] # flag --http-api
KeyStoreDir = "" # flag --keystore
UserIdent = "" # flag --identity
diff --git a/cmd/XDC/usage.go b/cmd/XDC/usage.go
deleted file mode 100644
index b724155162..0000000000
--- a/cmd/XDC/usage.go
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// Contains the XDC command usage template and generator.
-
-package main
-
-import (
- "io"
- "sort"
-
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "github.com/XinFinOrg/XDPoSChain/internal/debug"
- "gopkg.in/urfave/cli.v1"
-)
-
-// AppHelpTemplate is the test template for the default, global app help topic.
-var AppHelpTemplate = `NAME:
- {{.App.Name}} - {{.App.Usage}}
-
- Copyright (c) 2018 XDPoSChain
-
-USAGE:
- {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
- {{if .App.Version}}
-VERSION:
- {{.App.Version}}
- {{end}}{{if len .App.Authors}}
-AUTHOR(S):
- {{range .App.Authors}}{{ . }}{{end}}
- {{end}}{{if .App.Commands}}
-COMMANDS:
- {{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
- {{end}}{{end}}{{if .FlagGroups}}
-{{range .FlagGroups}}{{.Name}} OPTIONS:
- {{range .Flags}}{{.}}
- {{end}}
-{{end}}{{end}}{{if .App.Copyright }}
-COPYRIGHT:
- {{.App.Copyright}}
- {{end}}
-`
-
-// flagGroup is a collection of flags belonging to a single topic.
-type flagGroup struct {
- Name string
- Flags []cli.Flag
-}
-
-// AppHelpFlagGroups is the application flags, grouped by functionality.
-var AppHelpFlagGroups = []flagGroup{
- {
- Name: "XDPoSChain",
- Flags: []cli.Flag{
- configFileFlag,
- utils.DataDirFlag,
- utils.KeyStoreDirFlag,
- //utils.NoUSBFlag,
- utils.NetworkIdFlag,
- //utils.TestnetFlag,
- //utils.RinkebyFlag,
- utils.SyncModeFlag,
- utils.GCModeFlag,
- utils.EthStatsURLFlag,
- utils.IdentityFlag,
- //utils.LightServFlag,
- //utils.LightPeersFlag,
- //utils.LightKDFFlag,
- },
- },
- //{Name: "DEVELOPER CHAIN",
- // Flags: []cli.Flag{
- // utils.DeveloperFlag,
- // utils.DeveloperPeriodFlag,
- // },
- //},
- //{
- // Name: "ETHASH",
- // Flags: []cli.Flag{
- // utils.EthashCacheDirFlag,
- // utils.EthashCachesInMemoryFlag,
- // utils.EthashCachesOnDiskFlag,
- // utils.EthashDatasetDirFlag,
- // utils.EthashDatasetsInMemoryFlag,
- // utils.EthashDatasetsOnDiskFlag,
- // },
- //},
- //{
- // Name: "DASHBOARD",
- // Flags: []cli.Flag{
- // utils.DashboardEnabledFlag,
- // utils.DashboardAddrFlag,
- // utils.DashboardPortFlag,
- // utils.DashboardRefreshFlag,
- // utils.DashboardAssetsFlag,
- // },
- //},
- //{
- // Name: "TRANSACTION POOL",
- // Flags: []cli.Flag{
- // utils.TxPoolNoLocalsFlag,
- // utils.TxPoolJournalFlag,
- // utils.TxPoolRejournalFlag,
- // utils.TxPoolPriceLimitFlag,
- // utils.TxPoolPriceBumpFlag,
- // utils.TxPoolAccountSlotsFlag,
- // utils.TxPoolGlobalSlotsFlag,
- // utils.TxPoolAccountQueueFlag,
- // utils.TxPoolGlobalQueueFlag,
- // utils.TxPoolLifetimeFlag,
- // },
- //},
- {
- Name: "PERFORMANCE TUNING",
- Flags: []cli.Flag{
- utils.CacheFlag,
- utils.CacheDatabaseFlag,
- // utils.CacheGCFlag,
- // utils.TrieCacheGenFlag,
- utils.FDLimitFlag,
- },
- },
- {
- Name: "ACCOUNT",
- Flags: []cli.Flag{
- utils.UnlockedAccountFlag,
- utils.PasswordFileFlag,
- },
- },
- {
- Name: "API AND CONSOLE",
- Flags: []cli.Flag{
- utils.RPCEnabledFlag,
- utils.RPCGlobalGasCapFlag,
- utils.RPCListenAddrFlag,
- utils.RPCPortFlag,
- utils.RPCHttpWriteTimeoutFlag,
- utils.RPCApiFlag,
- utils.WSEnabledFlag,
- utils.WSListenAddrFlag,
- utils.WSPortFlag,
- utils.WSApiFlag,
- utils.WSAllowedOriginsFlag,
- utils.IPCDisabledFlag,
- utils.IPCPathFlag,
- utils.RPCCORSDomainFlag,
- utils.RPCVirtualHostsFlag,
- utils.RPCGlobalTxFeeCap,
- utils.JSpathFlag,
- utils.ExecFlag,
- utils.PreloadJSFlag,
- },
- },
- {
- Name: "NETWORKING",
- Flags: []cli.Flag{
- utils.BootnodesFlag,
- utils.BootnodesV4Flag,
- utils.BootnodesV5Flag,
- utils.ListenPortFlag,
- utils.MaxPeersFlag,
- utils.MaxPendingPeersFlag,
- utils.NATFlag,
- utils.NoDiscoverFlag,
- //utils.DiscoveryV5Flag,
- //utils.NetrestrictFlag,
- utils.NodeKeyFileFlag,
- utils.NodeKeyHexFlag,
- },
- },
- {
- Name: "STAKER",
- Flags: []cli.Flag{
- utils.StakingEnabledFlag,
- utils.StakerThreadsFlag,
- utils.EtherbaseFlag,
- utils.TargetGasLimitFlag,
- utils.GasPriceFlag,
- utils.ExtraDataFlag,
- },
- },
- //{
- // Name: "GAS PRICE ORACLE",
- // Flags: []cli.Flag{
- // utils.GpoBlocksFlag,
- // utils.GpoPercentileFlag,
- // utils.GpoMaxGasPriceFlag,
- // utils.GpoIgnoreGasPriceFlag,
- // },
- //},
- //{
- // Name: "VIRTUAL MACHINE",
- // Flags: []cli.Flag{
- // utils.VMEnableDebugFlag,
- // },
- //},
- {
- Name: "LOGGING AND DEBUGGING",
- Flags: append([]cli.Flag{
- utils.MetricsEnabledFlag,
- //utils.FakePoWFlag,
- //utils.NoCompactionFlag,
- }, debug.Flags...),
- },
- //{
- // Name: "WHISPER (EXPERIMENTAL)",
- // Flags: whisperFlags,
- //},
- {
- Name: "DEPRECATED",
- Flags: []cli.Flag{
- utils.FastSyncFlag,
- utils.LightModeFlag,
- },
- },
- {
- Name: "MISC",
- },
-}
-
-// byCategory sorts an array of flagGroup by Name in the order
-// defined in AppHelpFlagGroups.
-type byCategory []flagGroup
-
-func (a byCategory) Len() int { return len(a) }
-func (a byCategory) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a byCategory) Less(i, j int) bool {
- iCat, jCat := a[i].Name, a[j].Name
- iIdx, jIdx := len(AppHelpFlagGroups), len(AppHelpFlagGroups) // ensure non categorized flags come last
-
- for i, group := range AppHelpFlagGroups {
- if iCat == group.Name {
- iIdx = i
- }
- if jCat == group.Name {
- jIdx = i
- }
- }
-
- return iIdx < jIdx
-}
-
-func flagCategory(flag cli.Flag) string {
- for _, category := range AppHelpFlagGroups {
- for _, flg := range category.Flags {
- if flg.GetName() == flag.GetName() {
- return category.Name
- }
- }
- }
- return "MISC"
-}
-
-func init() {
- // Override the default app help template
- cli.AppHelpTemplate = AppHelpTemplate
-
- // Define a one shot struct to pass to the usage template
- type helpData struct {
- App interface{}
- FlagGroups []flagGroup
- }
-
- // Override the default app help printer, but only for the global app help
- originalHelpPrinter := cli.HelpPrinter
- cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
- if tmpl == AppHelpTemplate {
- // Iterate over all the flags and add any uncategorized ones
- categorized := make(map[string]struct{})
- for _, group := range AppHelpFlagGroups {
- for _, flag := range group.Flags {
- categorized[flag.String()] = struct{}{}
- }
- }
- uncategorized := []cli.Flag{}
- for _, flag := range data.(*cli.App).Flags {
- if _, ok := categorized[flag.String()]; !ok {
- uncategorized = append(uncategorized, flag)
- }
- }
- if len(uncategorized) > 0 {
- // Append all ungategorized options to the misc group
- miscs := len(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags)
- AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = append(AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags, uncategorized...)
-
- // Make sure they are removed afterwards
- defer func() {
- AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags = AppHelpFlagGroups[len(AppHelpFlagGroups)-1].Flags[:miscs]
- }()
- }
- // Render out custom usage screen
- originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups})
- } else if tmpl == utils.CommandHelpTemplate {
- // Iterate over all command specific flags and categorize them
- categorized := make(map[string][]cli.Flag)
- for _, flag := range data.(cli.Command).Flags {
- if _, ok := categorized[flag.String()]; !ok {
- categorized[flagCategory(flag)] = append(categorized[flagCategory(flag)], flag)
- }
- }
-
- // sort to get a stable ordering
- sorted := make([]flagGroup, 0, len(categorized))
- for cat, flgs := range categorized {
- sorted = append(sorted, flagGroup{cat, flgs})
- }
- sort.Sort(byCategory(sorted))
-
- // add sorted array to data and render with default printer
- originalHelpPrinter(w, tmpl, map[string]interface{}{
- "cmd": data,
- "categorizedFlags": sorted,
- })
- } else {
- originalHelpPrinter(w, tmpl, data)
- }
- }
-}
diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go
index 8d91b3ddc6..9a19efe96f 100644
--- a/cmd/abigen/main.go
+++ b/cmd/abigen/main.go
@@ -18,54 +18,101 @@ package main
import (
"encoding/json"
- "flag"
"fmt"
"os"
"strings"
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/common/compiler"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/urfave/cli/v2"
)
var (
- abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind")
- binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)")
- typFlag = flag.String("type", "", "Struct name for the binding (default = package name)")
+ // Git SHA1 commit hash of the release (set via linker flags)
+ gitCommit = ""
- solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind")
- solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
- excFlag = flag.String("exc", "", "Comma separated types to exclude from binding")
-
- pkgFlag = flag.String("pkg", "", "Package name to generate the binding into")
- outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)")
- langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)")
+ app *cli.App
)
-func main() {
- // Parse and ensure all needed inputs are specified
- flag.Parse()
+var (
+ // Flags needed by abigen
+ abiFlag = &cli.StringFlag{
+ Name: "abi",
+ Usage: "Path to the Ethereum contract ABI json to bind",
+ }
+ binFlag = &cli.StringFlag{
+ Name: "bin",
+ Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
+ }
+ typeFlag = &cli.StringFlag{
+ Name: "type",
+ Usage: "Struct name for the binding (default = package name)",
+ }
+ solFlag = &cli.StringFlag{
+ Name: "sol",
+ Usage: "Path to the Ethereum contract Solidity source to build and bind",
+ }
+ solcFlag = &cli.StringFlag{
+ Name: "solc",
+ Usage: "Solidity compiler to use if source builds are requested",
+ Value: "solc",
+ }
+ excFlag = &cli.StringFlag{
+ Name: "exc",
+ Usage: "Comma separated types to exclude from binding",
+ }
+ pkgFlag = &cli.StringFlag{
+ Name: "pkg",
+ Usage: "Package name to generate the binding into",
+ }
+ outFlag = &cli.StringFlag{
+ Name: "out",
+ Usage: "Output file for the generated binding (default = stdout)",
+ }
+ langFlag = &cli.StringFlag{
+ Name: "lang",
+ Usage: "Destination language for the bindings (go)",
+ Value: "go",
+ }
+)
- if *abiFlag == "" && *solFlag == "" {
+func init() {
+ app = flags.NewApp(gitCommit, "ethereum checkpoint helper tool")
+ app.Name = "abigen"
+ app.Flags = []cli.Flag{
+ abiFlag,
+ binFlag,
+ typeFlag,
+ solFlag,
+ solcFlag,
+ excFlag,
+ pkgFlag,
+ outFlag,
+ langFlag,
+ }
+ app.Action = abigen
+}
+
+func abigen(c *cli.Context) error {
+ if c.String(abiFlag.Name) == "" && c.String(solFlag.Name) == "" {
fmt.Printf("No contract ABI (--abi) or Solidity source (--sol) specified\n")
os.Exit(-1)
- } else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && *solFlag != "" {
+ } else if (c.String(abiFlag.Name) != "" || c.String(binFlag.Name) != "" || c.String(typeFlag.Name) != "") && c.String(solFlag.Name) != "" {
fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity source (--sol) flag\n")
os.Exit(-1)
}
- if *pkgFlag == "" {
+ if c.String(pkgFlag.Name) == "" {
fmt.Printf("No destination package specified (--pkg)\n")
os.Exit(-1)
}
var lang bind.Lang
- switch *langFlag {
+ switch c.String(langFlag.Name) {
case "go":
lang = bind.LangGo
- case "java":
- lang = bind.LangJava
- case "objc":
- lang = bind.LangObjC
default:
- fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag)
+ fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", c.String(langFlag.Name))
os.Exit(-1)
}
// If the entire solidity code was specified, build and bind based on that
@@ -74,13 +121,13 @@ func main() {
bins []string
types []string
)
- if *solFlag != "" {
+ if c.String(solFlag.Name) != "" {
// Generate the list of types to exclude from binding
exclude := make(map[string]bool)
- for _, kind := range strings.Split(*excFlag, ",") {
+ for _, kind := range strings.Split(c.String(excFlag.Name), ",") {
exclude[strings.ToLower(kind)] = true
}
- contracts, err := compiler.CompileSolidity(*solcFlag, *solFlag)
+ contracts, err := compiler.CompileSolidity(c.String(solcFlag.Name), c.String(solFlag.Name))
if err != nil {
fmt.Printf("Failed to build Solidity contract: %v\n", err)
os.Exit(-1)
@@ -99,7 +146,7 @@ func main() {
}
} else {
// Otherwise load up the ABI, optional bytecode and type name from the parameters
- abi, err := os.ReadFile(*abiFlag)
+ abi, err := os.ReadFile(c.String(abiFlag.Name))
if err != nil {
fmt.Printf("Failed to read input ABI: %v\n", err)
os.Exit(-1)
@@ -107,33 +154,43 @@ func main() {
abis = append(abis, string(abi))
bin := []byte{}
- if *binFlag != "" {
- if bin, err = os.ReadFile(*binFlag); err != nil {
+ if c.String(binFlag.Name) != "" {
+ if bin, err = os.ReadFile(c.String(binFlag.Name)); err != nil {
fmt.Printf("Failed to read input bytecode: %v\n", err)
os.Exit(-1)
}
}
bins = append(bins, string(bin))
- kind := *typFlag
+ kind := c.String(typeFlag.Name)
if kind == "" {
- kind = *pkgFlag
+ kind = c.String(pkgFlag.Name)
}
types = append(types, kind)
}
// Generate the contract binding
- code, err := bind.Bind(types, abis, bins, *pkgFlag, lang)
+ code, err := bind.Bind(types, abis, bins, c.String(pkgFlag.Name), lang)
if err != nil {
fmt.Printf("Failed to generate ABI binding: %v\n", err)
os.Exit(-1)
}
// Either flush it out to a file or display on the standard output
- if *outFlag == "" {
+ if c.String(outFlag.Name) == "" {
fmt.Printf("%s\n", code)
- return
+ return nil
}
- if err := os.WriteFile(*outFlag, []byte(code), 0600); err != nil {
+ if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil {
fmt.Printf("Failed to write ABI binding: %v\n", err)
os.Exit(-1)
}
+ return nil
+}
+
+func main() {
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
+
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
}
diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go
index c78a1a6c52..eaf91dc51a 100644
--- a/cmd/bootnode/main.go
+++ b/cmd/bootnode/main.go
@@ -51,10 +51,10 @@ func main() {
)
flag.Parse()
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(*verbosity))
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(log.FromLegacyLevel(*verbosity))
glogger.Vmodule(*vmodule)
- log.Root().SetHandler(glogger)
+ log.SetDefault(log.NewLogger(glogger))
natm, err := nat.Parse(*natdesc)
if err != nil {
diff --git a/cmd/ethkey/generate.go b/cmd/ethkey/generate.go
index 2f61c7a8f1..6da3f8beb6 100644
--- a/cmd/ethkey/generate.go
+++ b/cmd/ethkey/generate.go
@@ -26,7 +26,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/pborman/uuid"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
type outputGenerate struct {
@@ -34,7 +34,14 @@ type outputGenerate struct {
AddressEIP55 string
}
-var commandGenerate = cli.Command{
+var (
+ privateKeyFlag = &cli.StringFlag{
+ Name: "privatekey",
+ Usage: "file containing a raw private key to encrypt",
+ }
+)
+
+var commandGenerate = &cli.Command{
Name: "generate",
Usage: "generate new keyfile",
ArgsUsage: "[ ]",
@@ -47,10 +54,7 @@ If you want to encrypt an existing private key, it can be specified by setting
Flags: []cli.Flag{
passphraseFlag,
jsonFlag,
- cli.StringFlag{
- Name: "privatekey",
- Usage: "file containing a raw private key to encrypt",
- },
+ privateKeyFlag,
},
Action: func(ctx *cli.Context) error {
// Check if keyfile path given and make sure it doesn't already exist.
diff --git a/cmd/ethkey/inspect.go b/cmd/ethkey/inspect.go
index efd6b4589d..f8baa953c9 100644
--- a/cmd/ethkey/inspect.go
+++ b/cmd/ethkey/inspect.go
@@ -24,7 +24,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
type outputInspect struct {
@@ -33,7 +33,14 @@ type outputInspect struct {
PrivateKey string
}
-var commandInspect = cli.Command{
+var (
+ privateFlag = &cli.BoolFlag{
+ Name: "private",
+ Usage: "include the private key in the output",
+ }
+)
+
+var commandInspect = &cli.Command{
Name: "inspect",
Usage: "inspect a keyfile",
ArgsUsage: "",
@@ -45,10 +52,7 @@ make sure to use this feature with great caution!`,
Flags: []cli.Flag{
passphraseFlag,
jsonFlag,
- cli.BoolFlag{
- Name: "private",
- Usage: "include the private key in the output",
- },
+ privateFlag,
},
Action: func(ctx *cli.Context) error {
keyfilepath := ctx.Args().First()
diff --git a/cmd/ethkey/main.go b/cmd/ethkey/main.go
index 323bb9e1ab..f1840227a3 100644
--- a/cmd/ethkey/main.go
+++ b/cmd/ethkey/main.go
@@ -20,8 +20,8 @@ import (
"fmt"
"os"
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "gopkg.in/urfave/cli.v1"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
+ "github.com/urfave/cli/v2"
)
const (
@@ -34,8 +34,8 @@ var gitCommit = ""
var app *cli.App
func init() {
- app = utils.NewApp(gitCommit, "an Ethereum key manager")
- app.Commands = []cli.Command{
+ app = flags.NewApp(gitCommit, "an Ethereum key manager")
+ app.Commands = []*cli.Command{
commandGenerate,
commandInspect,
commandSignMessage,
@@ -45,15 +45,15 @@ func init() {
// Commonly used command line flags.
var (
- passphraseFlag = cli.StringFlag{
+ passphraseFlag = &cli.StringFlag{
Name: "passwordfile",
- Usage: "the file that contains the passphrase for the keyfile",
+ Usage: "the file that contains the password for the keyfile",
}
- jsonFlag = cli.BoolFlag{
+ jsonFlag = &cli.BoolFlag{
Name: "json",
Usage: "output JSON instead of human-readable format",
}
- messageFlag = cli.StringFlag{
+ messageFlag = &cli.StringFlag{
Name: "message",
Usage: "the file that contains the message to sign/verify",
}
diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go
index 9442785f68..3566475c7c 100644
--- a/cmd/ethkey/message.go
+++ b/cmd/ethkey/message.go
@@ -25,19 +25,19 @@ import (
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
type outputSign struct {
Signature string
}
-var msgfileFlag = cli.StringFlag{
+var msgfileFlag = &cli.StringFlag{
Name: "msgfile",
Usage: "file containing the message to sign/verify",
}
-var commandSignMessage = cli.Command{
+var commandSignMessage = &cli.Command{
Name: "signmessage",
Usage: "sign a message",
ArgsUsage: " ",
@@ -88,7 +88,7 @@ type outputVerify struct {
RecoveredPublicKey string
}
-var commandVerifyMessage = cli.Command{
+var commandVerifyMessage = &cli.Command{
Name: "verifymessage",
Usage: "verify the signature of a signed message",
ArgsUsage: " ",
@@ -143,7 +143,7 @@ It is possible to refer to a file containing the message.`,
func getMessage(ctx *cli.Context, msgarg int) []byte {
if file := ctx.String("msgfile"); file != "" {
- if len(ctx.Args()) > msgarg {
+ if ctx.NArg() > msgarg {
utils.Fatalf("Can't use --msgfile and message argument at the same time.")
}
msg, err := os.ReadFile(file)
@@ -151,9 +151,9 @@ func getMessage(ctx *cli.Context, msgarg int) []byte {
utils.Fatalf("Can't read message file: %v", err)
}
return msg
- } else if len(ctx.Args()) == msgarg+1 {
+ } else if ctx.NArg() == msgarg+1 {
return []byte(ctx.Args().Get(msgarg))
}
- utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args()))
+ utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, ctx.NArg())
return nil
}
diff --git a/cmd/ethkey/utils.go b/cmd/ethkey/utils.go
index cd32c5d801..6f1b681ef0 100644
--- a/cmd/ethkey/utils.go
+++ b/cmd/ethkey/utils.go
@@ -25,7 +25,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/console"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
// getPassPhrase obtains a passphrase given by the user. It first checks the
diff --git a/cmd/evm/compiler.go b/cmd/evm/compiler.go
index 0206fed0ef..b2f45e2f91 100644
--- a/cmd/evm/compiler.go
+++ b/cmd/evm/compiler.go
@@ -22,11 +22,10 @@ import (
"os"
"github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler"
-
- cli "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var compileCommand = cli.Command{
+var compileCommand = &cli.Command{
Action: compileCmd,
Name: "compile",
Usage: "compiles easm source to evm binary",
@@ -34,7 +33,7 @@ var compileCommand = cli.Command{
}
func compileCmd(ctx *cli.Context) error {
- debug := ctx.GlobalBool(DebugFlag.Name)
+ debug := ctx.Bool(DebugFlag.Name)
if len(ctx.Args().First()) == 0 {
return errors.New("filename required")
diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go
index 72ca7683d6..2e09f5aab6 100644
--- a/cmd/evm/disasm.go
+++ b/cmd/evm/disasm.go
@@ -23,10 +23,10 @@ import (
"strings"
"github.com/XinFinOrg/XDPoSChain/core/asm"
- cli "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var disasmCommand = cli.Command{
+var disasmCommand = &cli.Command{
Action: disasmCmd,
Name: "disasm",
Usage: "disassembles evm binary",
@@ -34,17 +34,22 @@ var disasmCommand = cli.Command{
}
func disasmCmd(ctx *cli.Context) error {
- if len(ctx.Args().First()) == 0 {
- return errors.New("filename required")
+ var in string
+ switch {
+ case len(ctx.Args().First()) > 0:
+ fn := ctx.Args().First()
+ input, err := os.ReadFile(fn)
+ if err != nil {
+ return err
+ }
+ in = string(input)
+ case ctx.IsSet(InputFlag.Name):
+ in = ctx.String(InputFlag.Name)
+ default:
+ return errors.New("missing filename or --input value")
}
- fn := ctx.Args().First()
- in, err := os.ReadFile(fn)
- if err != nil {
- return err
- }
-
- code := strings.TrimSpace(string(in[:]))
+ code := strings.TrimSpace(in)
fmt.Printf("%v\n", code)
return asm.PrintDisassembled(code)
}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index b5dfbf528b..0ac8e896e2 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -22,93 +22,126 @@ import (
"math/big"
"os"
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "gopkg.in/urfave/cli.v1"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
+ "github.com/urfave/cli/v2"
)
-var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
+var (
+ gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
+
+ app = flags.NewApp(gitCommit, "the evm command line interface")
+)
var (
- app = utils.NewApp(gitCommit, "the evm command line interface")
-
- DebugFlag = cli.BoolFlag{
- Name: "debug",
- Usage: "output full trace logs",
+ DebugFlag = &cli.BoolFlag{
+ Name: "debug",
+ Usage: "output full trace logs",
+ Category: flags.VMCategory,
}
- MemProfileFlag = cli.StringFlag{
- Name: "memprofile",
- Usage: "creates a memory profile at the given path",
+ MemProfileFlag = &cli.StringFlag{
+ Name: "memprofile",
+ Usage: "creates a memory profile at the given path",
+ Category: flags.VMCategory,
}
- CPUProfileFlag = cli.StringFlag{
- Name: "cpuprofile",
- Usage: "creates a CPU profile at the given path",
+ CPUProfileFlag = &cli.StringFlag{
+ Name: "cpuprofile",
+ Usage: "creates a CPU profile at the given path",
+ Category: flags.VMCategory,
}
- StatDumpFlag = cli.BoolFlag{
- Name: "statdump",
- Usage: "displays stack and heap memory information",
+ StatDumpFlag = &cli.BoolFlag{
+ Name: "statdump",
+ Usage: "displays stack and heap memory information",
+ Category: flags.VMCategory,
}
- CodeFlag = cli.StringFlag{
- Name: "code",
- Usage: "EVM code",
+ CodeFlag = &cli.StringFlag{
+ Name: "code",
+ Usage: "EVM code",
+ Category: flags.VMCategory,
}
- CodeFileFlag = cli.StringFlag{
- Name: "codefile",
- Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
+ CodeFileFlag = &cli.StringFlag{
+ Name: "codefile",
+ Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
+ Category: flags.VMCategory,
}
- GasFlag = cli.Uint64Flag{
- Name: "gas",
- Usage: "gas limit for the evm",
- Value: 10000000000,
+ GasFlag = &cli.Uint64Flag{
+ Name: "gas",
+ Usage: "gas limit for the evm",
+ Value: 10000000000,
+ Category: flags.VMCategory,
}
- PriceFlag = utils.BigFlag{
- Name: "price",
- Usage: "price set for the evm",
- Value: new(big.Int),
+ PriceFlag = &flags.BigFlag{
+ Name: "price",
+ Usage: "price set for the evm",
+ Value: new(big.Int),
+ Category: flags.VMCategory,
}
- ValueFlag = utils.BigFlag{
- Name: "value",
- Usage: "value set for the evm",
- Value: new(big.Int),
+ ValueFlag = &flags.BigFlag{
+ Name: "value",
+ Usage: "value set for the evm",
+ Value: new(big.Int),
+ Category: flags.VMCategory,
}
- DumpFlag = cli.BoolFlag{
- Name: "dump",
- Usage: "dumps the state after the run",
+ DumpFlag = &cli.BoolFlag{
+ Name: "dump",
+ Usage: "dumps the state after the run",
+ Category: flags.VMCategory,
}
- InputFlag = cli.StringFlag{
- Name: "input",
- Usage: "input for the EVM",
+ InputFlag = &cli.StringFlag{
+ Name: "input",
+ Usage: "input for the EVM",
+ Category: flags.VMCategory,
}
- VerbosityFlag = cli.IntFlag{
- Name: "verbosity",
- Usage: "sets the verbosity level",
+ VerbosityFlag = &cli.IntFlag{
+ Name: "verbosity",
+ Usage: "sets the verbosity level",
+ Category: flags.VMCategory,
}
- CreateFlag = cli.BoolFlag{
- Name: "create",
- Usage: "indicates the action should be create rather than call",
+ CreateFlag = &cli.BoolFlag{
+ Name: "create",
+ Usage: "indicates the action should be create rather than call",
+ Category: flags.VMCategory,
}
- GenesisFlag = cli.StringFlag{
- Name: "prestate",
- Usage: "JSON file with prestate (genesis) config",
+ GenesisFlag = &cli.StringFlag{
+ Name: "prestate",
+ Usage: "JSON file with prestate (genesis) config",
+ Category: flags.VMCategory,
}
- MachineFlag = cli.BoolFlag{
- Name: "json",
- Usage: "output trace logs in machine readable format (json)",
+ MachineFlag = &cli.BoolFlag{
+ Name: "json",
+ Usage: "output trace logs in machine readable format (json)",
+ Category: flags.VMCategory,
}
- SenderFlag = cli.StringFlag{
- Name: "sender",
- Usage: "The transaction origin",
+ SenderFlag = &cli.StringFlag{
+ Name: "sender",
+ Usage: "The transaction origin",
+ Category: flags.VMCategory,
}
- ReceiverFlag = cli.StringFlag{
- Name: "receiver",
- Usage: "The transaction receiver (execution context)",
+ ReceiverFlag = &cli.StringFlag{
+ Name: "receiver",
+ Usage: "The transaction receiver (execution context)",
+ Category: flags.VMCategory,
}
- DisableMemoryFlag = cli.BoolFlag{
- Name: "nomemory",
- Usage: "disable memory output",
+ DisableMemoryFlag = &cli.BoolFlag{
+ Name: "nomemory",
+ Value: true,
+ Usage: "disable memory output",
+ Category: flags.VMCategory,
}
- DisableStackFlag = cli.BoolFlag{
- Name: "nostack",
- Usage: "disable stack output",
+ DisableStackFlag = &cli.BoolFlag{
+ Name: "nostack",
+ Usage: "disable stack output",
+ Category: flags.VMCategory,
+ }
+ DisableStorageFlag = &cli.BoolFlag{
+ Name: "nostorage",
+ Usage: "disable storage output",
+ Category: flags.VMCategory,
+ }
+ DisableReturnDataFlag = &cli.BoolFlag{
+ Name: "noreturndata",
+ Value: true,
+ Usage: "enable return data output",
+ Category: flags.VMCategory,
}
)
@@ -134,7 +167,7 @@ func init() {
DisableMemoryFlag,
DisableStackFlag,
}
- app.Commands = []cli.Command{
+ app.Commands = []*cli.Command{
compileCommand,
disasmCommand,
runCommand,
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 101069aff4..05adeae2be 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -22,26 +22,24 @@ import (
"fmt"
"io"
"os"
+ goruntime "runtime"
"runtime/pprof"
"time"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
- goruntime "runtime"
-
"github.com/XinFinOrg/XDPoSChain/cmd/evm/internal/compiler"
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
- "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/params"
- cli "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var runCommand = cli.Command{
+var runCommand = &cli.Command{
Action: runCmd,
Name: "run",
Usage: "run arbitrary evm binary",
@@ -71,12 +69,12 @@ func readGenesis(genesisPath string) *core.Genesis {
}
func runCmd(ctx *cli.Context) error {
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
- log.Root().SetHandler(glogger)
logconfig := &vm.LogConfig{
- EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
- DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
+ EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
+ DisableStack: ctx.Bool(DisableStackFlag.Name),
+ DisableStorage: ctx.Bool(DisableStorageFlag.Name),
+ EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
+ Debug: ctx.Bool(DebugFlag.Name),
}
var (
@@ -87,16 +85,17 @@ func runCmd(ctx *cli.Context) error {
sender = common.StringToAddress("sender")
receiver = common.StringToAddress("receiver")
)
- if ctx.GlobalBool(MachineFlag.Name) {
+ if ctx.Bool(MachineFlag.Name) {
tracer = vm.NewJSONLogger(logconfig, os.Stdout)
- } else if ctx.GlobalBool(DebugFlag.Name) {
+ } else if ctx.Bool(DebugFlag.Name) {
debugLogger = vm.NewStructLogger(logconfig)
tracer = debugLogger
} else {
debugLogger = vm.NewStructLogger(logconfig)
}
- if ctx.GlobalString(GenesisFlag.Name) != "" {
- gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
+
+ if ctx.String(GenesisFlag.Name) != "" {
+ gen := readGenesis(ctx.String(GenesisFlag.Name))
db := rawdb.NewMemoryDatabase()
genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
@@ -105,13 +104,13 @@ func runCmd(ctx *cli.Context) error {
db := rawdb.NewMemoryDatabase()
statedb, _ = state.New(common.Hash{}, state.NewDatabase(db))
}
- if ctx.GlobalString(SenderFlag.Name) != "" {
- sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
+ if ctx.String(SenderFlag.Name) != "" {
+ sender = common.HexToAddress(ctx.String(SenderFlag.Name))
}
statedb.CreateAccount(sender)
- if ctx.GlobalString(ReceiverFlag.Name) != "" {
- receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name))
+ if ctx.String(ReceiverFlag.Name) != "" {
+ receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name))
}
var (
@@ -120,11 +119,11 @@ func runCmd(ctx *cli.Context) error {
err error
)
// The '--code' or '--codefile' flag overrides code in state
- if ctx.GlobalString(CodeFileFlag.Name) != "" {
+ if ctx.String(CodeFileFlag.Name) != "" {
var hexcode []byte
var err error
// If - is specified, it means that code comes from stdin
- if ctx.GlobalString(CodeFileFlag.Name) == "-" {
+ if ctx.String(CodeFileFlag.Name) == "-" {
//Try reading from stdin
if hexcode, err = io.ReadAll(os.Stdin); err != nil {
fmt.Printf("Could not load code from stdin: %v\n", err)
@@ -132,15 +131,15 @@ func runCmd(ctx *cli.Context) error {
}
} else {
// Codefile with hex assembly
- if hexcode, err = os.ReadFile(ctx.GlobalString(CodeFileFlag.Name)); err != nil {
+ if hexcode, err = os.ReadFile(ctx.String(CodeFileFlag.Name)); err != nil {
fmt.Printf("Could not load code from file: %v\n", err)
os.Exit(1)
}
}
code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n")))
- } else if ctx.GlobalString(CodeFlag.Name) != "" {
- code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
+ } else if ctx.String(CodeFlag.Name) != "" {
+ code = common.Hex2Bytes(ctx.String(CodeFlag.Name))
} else if fn := ctx.Args().First(); len(fn) > 0 {
// EASM-file to compile
src, err := os.ReadFile(fn)
@@ -154,20 +153,20 @@ func runCmd(ctx *cli.Context) error {
code = common.Hex2Bytes(bin)
}
- initialGas := ctx.GlobalUint64(GasFlag.Name)
+ initialGas := ctx.Uint64(GasFlag.Name)
runtimeConfig := runtime.Config{
Origin: sender,
State: statedb,
GasLimit: initialGas,
- GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
- Value: utils.GlobalBig(ctx, ValueFlag.Name),
+ GasPrice: flags.GlobalBig(ctx, PriceFlag.Name),
+ Value: flags.GlobalBig(ctx, ValueFlag.Name),
EVMConfig: vm.Config{
Tracer: tracer,
- Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
+ Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name),
},
}
- if cpuProfilePath := ctx.GlobalString(CPUProfileFlag.Name); cpuProfilePath != "" {
+ if cpuProfilePath := ctx.String(CPUProfileFlag.Name); cpuProfilePath != "" {
f, err := os.Create(cpuProfilePath)
if err != nil {
fmt.Println("could not create CPU profile: ", err)
@@ -185,23 +184,23 @@ func runCmd(ctx *cli.Context) error {
}
tstart := time.Now()
var leftOverGas uint64
- if ctx.GlobalBool(CreateFlag.Name) {
- input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
+ if ctx.Bool(CreateFlag.Name) {
+ input := append(code, common.Hex2Bytes(ctx.String(InputFlag.Name))...)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig)
} else {
if len(code) > 0 {
statedb.SetCode(receiver, code)
}
- ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
+ ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.String(InputFlag.Name)), &runtimeConfig)
}
execTime := time.Since(tstart)
- if ctx.GlobalBool(DumpFlag.Name) {
+ if ctx.Bool(DumpFlag.Name) {
statedb.IntermediateRoot(true)
fmt.Println(string(statedb.Dump()))
}
- if memProfilePath := ctx.GlobalString(MemProfileFlag.Name); memProfilePath != "" {
+ if memProfilePath := ctx.String(MemProfileFlag.Name); memProfilePath != "" {
f, err := os.Create(memProfilePath)
if err != nil {
fmt.Println("could not create memory profile: ", err)
@@ -214,7 +213,7 @@ func runCmd(ctx *cli.Context) error {
f.Close()
}
- if ctx.GlobalBool(DebugFlag.Name) {
+ if ctx.Bool(DebugFlag.Name) {
if debugLogger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
vm.WriteTrace(os.Stderr, debugLogger.StructLogs())
@@ -223,7 +222,7 @@ func runCmd(ctx *cli.Context) error {
vm.WriteLogs(os.Stderr, statedb.Logs())
}
- if ctx.GlobalBool(StatDumpFlag.Name) {
+ if ctx.Bool(StatDumpFlag.Name) {
var mem goruntime.MemStats
goruntime.ReadMemStats(&mem)
fmt.Fprintf(os.Stderr, `evm execution time: %v
@@ -238,7 +237,7 @@ Gas used: %d
if tracer != nil {
tracer.CaptureEnd(ret, initialGas-leftOverGas, execTime, err)
} else {
- fmt.Printf("0x%x\n", ret)
+ fmt.Printf("%#x\n", ret)
if err != nil {
fmt.Printf(" error: %v\n", err)
}
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 1a236c0001..80dfdddf40 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -24,13 +24,11 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/vm"
- "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/tests"
-
- cli "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var stateTestCommand = cli.Command{
+var stateTestCommand = &cli.Command{
Action: stateTestCmd,
Name: "statetest",
Usage: "executes the given state tests",
@@ -49,25 +47,24 @@ func stateTestCmd(ctx *cli.Context) error {
if len(ctx.Args().First()) == 0 {
return errors.New("path-to-test argument required")
}
- // Configure the go-ethereum logger
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
- log.Root().SetHandler(glogger)
// Configure the EVM logger
config := &vm.LogConfig{
- EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
- DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
+ EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
+ DisableStack: ctx.Bool(DisableStackFlag.Name),
+ DisableStorage: ctx.Bool(DisableStorageFlag.Name),
+ EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
}
+
var (
tracer vm.EVMLogger
debugger *vm.StructLogger
)
switch {
- case ctx.GlobalBool(MachineFlag.Name):
+ case ctx.Bool(MachineFlag.Name):
tracer = vm.NewJSONLogger(config, os.Stderr)
- case ctx.GlobalBool(DebugFlag.Name):
+ case ctx.Bool(DebugFlag.Name):
debugger = vm.NewStructLogger(config)
tracer = debugger
@@ -86,7 +83,7 @@ func stateTestCmd(ctx *cli.Context) error {
// Iterate over all the tests, run them and aggregate the results
cfg := vm.Config{
Tracer: tracer,
- Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
+ Debug: ctx.Bool(DebugFlag.Name) || ctx.Bool(MachineFlag.Name),
}
results := make([]StatetestResult, 0, len(tests))
for key, test := range tests {
@@ -97,20 +94,20 @@ func stateTestCmd(ctx *cli.Context) error {
if err != nil {
// Test failed, mark as so and dump any state to aid debugging
result.Pass, result.Error = false, err.Error()
- if ctx.GlobalBool(DumpFlag.Name) && state != nil {
+ if ctx.Bool(DumpFlag.Name) && state != nil {
dump := state.RawDump()
result.State = &dump
}
}
// print state root for evmlab tracing (already committed above, so no need to delete objects again
- if ctx.GlobalBool(MachineFlag.Name) && state != nil {
+ if ctx.Bool(MachineFlag.Name) && state != nil {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false))
}
results = append(results, *result)
// Print any structured logs collected
- if ctx.GlobalBool(DebugFlag.Name) {
+ if ctx.Bool(DebugFlag.Name) {
if debugger != nil {
fmt.Fprintln(os.Stderr, "#### TRACE ####")
vm.WriteTrace(os.Stderr, debugger.StructLogs())
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index f61909393d..36388d7996 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -94,7 +94,7 @@ var (
func main() {
// Parse the flags and set up the logger to print everything requested
flag.Parse()
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*logFlag), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.FromLegacyLevel(*logFlag), true)))
// Construct the payout tiers
amounts := make([]string, *tiersFlag)
@@ -287,7 +287,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
// close terminates the Ethereum connection and tears down the faucet.
func (f *faucet) close() error {
- return f.stack.Stop()
+ return f.stack.Close()
}
// listenAndServe registers the HTTP handlers for the faucet and boots it up
@@ -396,7 +396,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
continue
}
if msg.Tier >= uint(*tiersFlag) {
- if err = sendError(wsconn, errors.New("Invalid funding tier requested")); err != nil {
+ if err = sendError(wsconn, errors.New("invalid funding tier requested")); err != nil {
log.Warn("Failed to send tier error to client", "err", err)
return
}
@@ -433,7 +433,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
}
if !result.Success {
log.Warn("Captcha verification failed", "err", string(result.Errors))
- if err = sendError(wsconn, errors.New("Beep-bop, you're a robot!")); err != nil {
+ if err = sendError(wsconn, errors.New("beep-bop, you're a robot")); err != nil {
log.Warn("Failed to send captcha failure to client", "err", err)
return
}
@@ -462,7 +462,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
case *noauthFlag:
username, avatar, address, err = authNoAuth(msg.URL)
default:
- err = errors.New("Something funky happened, please open an issue at https://github.com/XinFinOrg/XDPoSChain/issues")
+ err = errors.New("something funky happened, please open an issue at https://github.com/XinFinOrg/XDPoSChain/issues")
}
if err != nil {
if err = sendError(wsconn, err); err != nil {
@@ -517,7 +517,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
// Send an error if too frequent funding, othewise a success
if !fund {
- if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { // nolint: gosimple
+ if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil {
log.Warn("Failed to send funding error to client", "err", err)
return
}
@@ -657,7 +657,7 @@ func sendSuccess(conn *wsConn, msg string) error {
func authGitHub(url string) (string, string, common.Address, error) {
// Retrieve the gist from the GitHub Gist APIs
parts := strings.Split(url, "/")
- req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil)
+ req, _ := http.NewRequest(http.MethodGet, "https://api.github.com/gists/"+parts[len(parts)-1], nil)
if *githubUser != "" {
req.SetBasicAuth(*githubUser, *githubToken)
}
@@ -679,7 +679,7 @@ func authGitHub(url string) (string, string, common.Address, error) {
return "", "", common.Address{}, err
}
if gist.Owner.Login == "" {
- return "", "", common.Address{}, errors.New("Anonymous Gists not allowed")
+ return "", "", common.Address{}, errors.New("anonymous gists not allowed")
}
// Iterate over all the files and look for Ethereum addresses
var address common.Address
@@ -690,7 +690,7 @@ func authGitHub(url string) (string, string, common.Address, error) {
}
}
if address == (common.Address{}) {
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+ return "", "", common.Address{}, errors.New("no Ethereum address found to fund")
}
// Validate the user's existence since the API is unhelpful here
if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil {
@@ -699,7 +699,7 @@ func authGitHub(url string) (string, string, common.Address, error) {
res.Body.Close()
if res.StatusCode != 200 {
- return "", "", common.Address{}, errors.New("Invalid user... boom!")
+ return "", "", common.Address{}, errors.New("invalid user... boom")
}
// Everything passed validation, return the gathered infos
return gist.Owner.Login + "@github", fmt.Sprintf("https://github.com/%s.png?size=64", gist.Owner.Login), address, nil
@@ -711,7 +711,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
// Ensure the user specified a meaningful URL, no fancy nonsense
parts := strings.Split(url, "/")
if len(parts) < 4 || parts[len(parts)-2] != "status" {
- return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
+ return "", "", common.Address{}, errors.New("invalid Twitter status URL")
}
// Twitter's API isn't really friendly with direct links. Still, we don't
// want to do ask read permissions from users, so just load the public posts and
@@ -725,7 +725,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
// Resolve the username from the final redirect, no intermediate junk
parts = strings.Split(res.Request.URL.String(), "/")
if len(parts) < 4 || parts[len(parts)-2] != "status" {
- return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
+ return "", "", common.Address{}, errors.New("invalid Twitter status URL")
}
username := parts[len(parts)-3]
@@ -735,7 +735,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
}
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+ return "", "", common.Address{}, errors.New("no Ethereum address found to fund")
}
var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
@@ -750,7 +750,7 @@ func authGooglePlus(url string) (string, string, common.Address, error) {
// Ensure the user specified a meaningful URL, no fancy nonsense
parts := strings.Split(url, "/")
if len(parts) < 4 || parts[len(parts)-2] != "posts" {
- return "", "", common.Address{}, errors.New("Invalid Google+ post URL")
+ return "", "", common.Address{}, errors.New("invalid Google+ post URL")
}
username := parts[len(parts)-3]
@@ -769,7 +769,7 @@ func authGooglePlus(url string) (string, string, common.Address, error) {
}
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+ return "", "", common.Address{}, errors.New("no Ethereum address found to fund")
}
var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+googleusercontent.com[^\"]+photo.jpg)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
@@ -784,7 +784,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
// Ensure the user specified a meaningful URL, no fancy nonsense
parts := strings.Split(url, "/")
if len(parts) < 4 || parts[len(parts)-2] != "posts" {
- return "", "", common.Address{}, errors.New("Invalid Facebook post URL")
+ return "", "", common.Address{}, errors.New("invalid Facebook post URL")
}
username := parts[len(parts)-3]
@@ -803,7 +803,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
}
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+ return "", "", common.Address{}, errors.New("no Ethereum address found to fund")
}
var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
@@ -818,7 +818,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
func authNoAuth(url string) (string, string, common.Address, error) {
address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
if address == (common.Address{}) {
- return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
+ return "", "", common.Address{}, errors.New("no Ethereum address found to fund")
}
return address.Hex() + "@noauth", "", address, nil
}
diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go
index cbdede5df7..0404f38e9a 100644
--- a/cmd/faucet/website.go
+++ b/cmd/faucet/website.go
@@ -92,15 +92,15 @@ func faucetHtml() (*asset, error) {
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err)
}
return a.bytes, nil
}
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
// AssetString returns the asset contents as a string (instead of a []byte).
@@ -130,29 +130,29 @@ func MustAssetString(name string) string {
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err)
}
return a.info, nil
}
- return nil, fmt.Errorf("AssetInfo %s not found", name)
+ return nil, fmt.Errorf("not found AssetInfo %s", name)
}
// AssetDigest returns the digest of the file with the given name. It returns an
// error if the asset could not be found or the digest could not be loaded.
func AssetDigest(name string) ([sha256.Size]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
+ return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err)
}
return a.digest, nil
}
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
+ return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name)
}
// Digests returns a map of all known files and their checksums.
@@ -203,17 +203,17 @@ const AssetDebug = false
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
pathList := strings.Split(canonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
}
}
if node.Func != nil {
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
@@ -270,6 +270,6 @@ func RestoreAssets(dir, name string) error {
}
func _filePath(dir, name string) string {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
}
diff --git a/cmd/gc/main.go b/cmd/gc/main.go
index d0532e3f0d..4d1ba3a141 100644
--- a/cmd/gc/main.go
+++ b/cmd/gc/main.go
@@ -12,6 +12,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/cmd/utils"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
@@ -20,7 +21,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/ethdb/leveldb"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie"
- "github.com/XinFinOrg/XDPoSChain/common/lru"
)
var (
@@ -170,7 +170,7 @@ func getAllChilds(n StateNode, db *leveldb.Database) ([17]*StateNode, error) {
}
if err == nil {
childs[i] = &StateNode{node: childNode, path: append(n.path, byte(i))}
- } else if err != nil {
+ } else {
_, ok := err.(*trie.MissingNodeError)
if !ok {
return childs, err
@@ -187,7 +187,7 @@ func getAllChilds(n StateNode, db *leveldb.Database) ([17]*StateNode, error) {
}
if err == nil {
childs[0] = &StateNode{node: childNode, path: append(n.path, node.Key...)}
- } else if err != nil {
+ } else {
_, ok := err.(*trie.MissingNodeError)
if !ok {
return childs, err
diff --git a/cmd/p2psim/main.go b/cmd/p2psim/main.go
index 539b29cb73..6536d6657d 100644
--- a/cmd/p2psim/main.go
+++ b/cmd/p2psim/main.go
@@ -19,21 +19,20 @@
// Here is an example of creating a 2 node network with the first node
// connected to the second:
//
-// $ p2psim node create
-// Created node01
+// $ p2psim node create
+// Created node01
//
-// $ p2psim node start node01
-// Started node01
+// $ p2psim node start node01
+// Started node01
//
-// $ p2psim node create
-// Created node02
+// $ p2psim node create
+// Created node02
//
-// $ p2psim node start node02
-// Started node02
-//
-// $ p2psim node connect node01 node02
-// Connected node01 to node02
+// $ p2psim node start node02
+// Started node02
//
+// $ p2psim node connect node01 node02
+// Connected node01 to node02
package main
import (
@@ -46,32 +45,77 @@ import (
"text/tabwriter"
"github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
"github.com/XinFinOrg/XDPoSChain/p2p/simulations"
"github.com/XinFinOrg/XDPoSChain/p2p/simulations/adapters"
"github.com/XinFinOrg/XDPoSChain/rpc"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
var client *simulations.Client
+var (
+ // global command flags
+ apiFlag = &cli.StringFlag{
+ Name: "api",
+ Value: "http://localhost:8888",
+ Usage: "simulation API URL",
+ EnvVars: []string{"P2PSIM_API_URL"},
+ }
+
+ // events subcommand flags
+ currentFlag = &cli.BoolFlag{
+ Name: "current",
+ Usage: "get existing nodes and conns first",
+ }
+ filterFlag = &cli.StringFlag{
+ Name: "filter",
+ Value: "",
+ Usage: "message filter",
+ }
+
+ // node create subcommand flags
+ nameFlag = &cli.StringFlag{
+ Name: "name",
+ Value: "",
+ Usage: "node name",
+ }
+ servicesFlag = &cli.StringFlag{
+ Name: "services",
+ Value: "",
+ Usage: "node services (comma separated)",
+ }
+ keyFlag = &cli.StringFlag{
+ Name: "key",
+ Value: "",
+ Usage: "node private key (hex encoded)",
+ }
+
+ // node rpc subcommand flags
+ subscribeFlag = &cli.BoolFlag{
+ Name: "subscribe",
+ Usage: "method is a subscription",
+ }
+)
+
+var (
+ // Git information set by linker when building with ci.go.
+ gitCommit string
+)
+
func main() {
- app := cli.NewApp()
+ app := flags.NewApp(gitCommit, "devp2p simulation command-line client")
app.Usage = "devp2p simulation command-line client"
app.Flags = []cli.Flag{
- cli.StringFlag{
- Name: "api",
- Value: "http://localhost:8888",
- Usage: "simulation API URL",
- EnvVar: "P2PSIM_API_URL",
- },
+ apiFlag,
}
app.Before = func(ctx *cli.Context) error {
- client = simulations.NewClient(ctx.GlobalString("api"))
+ client = simulations.NewClient(ctx.String("api"))
return nil
}
- app.Commands = []cli.Command{
+ app.Commands = []*cli.Command{
{
Name: "show",
Usage: "show network information",
@@ -82,15 +126,8 @@ func main() {
Usage: "stream network events",
Action: streamNetwork,
Flags: []cli.Flag{
- cli.BoolFlag{
- Name: "current",
- Usage: "get existing nodes and conns first",
- },
- cli.StringFlag{
- Name: "filter",
- Value: "",
- Usage: "message filter",
- },
+ currentFlag,
+ filterFlag,
},
},
{
@@ -107,7 +144,7 @@ func main() {
Name: "node",
Usage: "manage simulation nodes",
Action: listNodes,
- Subcommands: []cli.Command{
+ Subcommands: []*cli.Command{
{
Name: "list",
Usage: "list nodes",
@@ -118,21 +155,9 @@ func main() {
Usage: "create a node",
Action: createNode,
Flags: []cli.Flag{
- cli.StringFlag{
- Name: "name",
- Value: "",
- Usage: "node name",
- },
- cli.StringFlag{
- Name: "services",
- Value: "",
- Usage: "node services (comma separated)",
- },
- cli.StringFlag{
- Name: "key",
- Value: "",
- Usage: "node private key (hex encoded)",
- },
+ nameFlag,
+ servicesFlag,
+ keyFlag,
},
},
{
@@ -171,10 +196,7 @@ func main() {
Usage: "call a node RPC method",
Action: rpcNode,
Flags: []cli.Flag{
- cli.BoolFlag{
- Name: "subscribe",
- Usage: "method is a subscription",
- },
+ subscribeFlag,
},
},
},
@@ -184,7 +206,7 @@ func main() {
}
func showNetwork(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
network, err := client.GetNetwork()
@@ -199,7 +221,7 @@ func showNetwork(ctx *cli.Context) error {
}
func streamNetwork(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
events := make(chan *simulations.Event)
@@ -225,7 +247,7 @@ func streamNetwork(ctx *cli.Context) error {
}
func createSnapshot(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
snap, err := client.CreateSnapshot()
@@ -236,7 +258,7 @@ func createSnapshot(ctx *cli.Context) error {
}
func loadSnapshot(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
snap := &simulations.Snapshot{}
@@ -247,7 +269,7 @@ func loadSnapshot(ctx *cli.Context) error {
}
func listNodes(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
nodes, err := client.GetNodes()
@@ -272,7 +294,7 @@ func protocolList(node *p2p.NodeInfo) []string {
}
func createNode(ctx *cli.Context) error {
- if len(ctx.Args()) != 0 {
+ if ctx.NArg() != 0 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
config := &adapters.NodeConfig{
@@ -298,11 +320,10 @@ func createNode(ctx *cli.Context) error {
}
func showNode(ctx *cli.Context) error {
- args := ctx.Args()
- if len(args) != 1 {
+ if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
+ nodeName := ctx.Args().First()
node, err := client.GetNode(nodeName)
if err != nil {
return err
@@ -323,11 +344,10 @@ func showNode(ctx *cli.Context) error {
}
func startNode(ctx *cli.Context) error {
- args := ctx.Args()
- if len(args) != 1 {
+ if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
+ nodeName := ctx.Args().First()
if err := client.StartNode(nodeName); err != nil {
return err
}
@@ -336,11 +356,10 @@ func startNode(ctx *cli.Context) error {
}
func stopNode(ctx *cli.Context) error {
- args := ctx.Args()
- if len(args) != 1 {
+ if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
+ nodeName := ctx.Args().First()
if err := client.StopNode(nodeName); err != nil {
return err
}
@@ -349,12 +368,12 @@ func stopNode(ctx *cli.Context) error {
}
func connectNode(ctx *cli.Context) error {
- args := ctx.Args()
- if len(args) != 2 {
+ if ctx.NArg() != 2 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
- peerName := args[1]
+ args := ctx.Args()
+ nodeName := args.Get(0)
+ peerName := args.Get(1)
if err := client.ConnectNode(nodeName, peerName); err != nil {
return err
}
@@ -364,11 +383,11 @@ func connectNode(ctx *cli.Context) error {
func disconnectNode(ctx *cli.Context) error {
args := ctx.Args()
- if len(args) != 2 {
+ if args.Len() != 2 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
- peerName := args[1]
+ nodeName := args.Get(0)
+ peerName := args.Get(1)
if err := client.DisconnectNode(nodeName, peerName); err != nil {
return err
}
@@ -378,21 +397,21 @@ func disconnectNode(ctx *cli.Context) error {
func rpcNode(ctx *cli.Context) error {
args := ctx.Args()
- if len(args) < 2 {
+ if args.Len() < 2 {
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
}
- nodeName := args[0]
- method := args[1]
+ nodeName := args.Get(0)
+ method := args.Get(1)
rpcClient, err := client.RPCClient(context.Background(), nodeName)
if err != nil {
return err
}
if ctx.Bool("subscribe") {
- return rpcSubscribe(rpcClient, ctx.App.Writer, method, args[3:]...)
+ return rpcSubscribe(rpcClient, ctx.App.Writer, method, args.Slice()[3:]...)
}
var result interface{}
- params := make([]interface{}, len(args[3:]))
- for i, v := range args[3:] {
+ params := make([]interface{}, len(args.Slice()[3:]))
+ for i, v := range args.Slice()[3:] {
params[i] = v
}
if err := rpcClient.Call(&result, method, params...); err != nil {
diff --git a/cmd/puppeth/module_explorer.go b/cmd/puppeth/module_explorer.go
index c6ff5c7767..0bed1107ec 100644
--- a/cmd/puppeth/module_explorer.go
+++ b/cmd/puppeth/module_explorer.go
@@ -197,7 +197,7 @@ func checkExplorer(client *sshClient, network string) (*explorerInfos, error) {
// Run a sanity check to see if the devp2p is reachable
nodePort := infos.portmap[infos.envvars["NODE_PORT"]]
if err = checkPort(client.server, nodePort); err != nil {
- log.Warn(fmt.Sprintf("Explorer devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err)
+ log.Warn("Explorer devp2p port seems unreachable", "server", client.server, "port", nodePort, "err", err)
}
// Assemble and return the useful infos
stats := &explorerInfos{
diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go
index a52a959ced..c592e59d31 100644
--- a/cmd/puppeth/module_node.go
+++ b/cmd/puppeth/module_node.go
@@ -42,7 +42,7 @@ ADD genesis.json /genesis.json
RUN \
echo 'XDC --cache 512 init /genesis.json' > XDC.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> XDC.sh && \{{end}}
- echo $'XDC --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> XDC.sh
+ echo $'XDC --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--miner-etherbase {{.Etherbase}} --mine --miner-threads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --miner-gaslimit {{.GasTarget}} --miner-gasprice {{.GasPrice}}' >> XDC.sh
ENTRYPOINT ["/bin/sh", "XDC.sh"]
`
@@ -92,7 +92,7 @@ func deployNode(client *sshClient, network string, bootnodes []string, config *n
lightFlag := ""
if config.peersLight > 0 {
- lightFlag = fmt.Sprintf("--lightpeers=%d --lightserv=50", config.peersLight)
+ lightFlag = fmt.Sprintf("--light-peers=%d --light-serv=50", config.peersLight)
}
dockerfile := new(bytes.Buffer)
template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{
diff --git a/cmd/puppeth/module_wallet.go b/cmd/puppeth/module_wallet.go
index 25c630e245..c714072e6e 100644
--- a/cmd/puppeth/module_wallet.go
+++ b/cmd/puppeth/module_wallet.go
@@ -37,7 +37,7 @@ ADD genesis.json /genesis.json
RUN \
echo 'node server.js &' > wallet.sh && \
echo 'XDC --cache 512 init /genesis.json' >> wallet.sh && \
- echo $'XDC --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh
+ echo $'XDC --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http-addr=0.0.0.0 --http-corsdomain "*" --http-vhosts "*"' >> wallet.sh
RUN \
sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \
@@ -181,11 +181,11 @@ func checkWallet(client *sshClient, network string) (*walletInfos, error) {
// Run a sanity check to see if the devp2p and RPC ports are reachable
nodePort := infos.portmap[infos.envvars["NODE_PORT"]]
if err = checkPort(client.server, nodePort); err != nil {
- log.Warn(fmt.Sprintf("Wallet devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err)
+ log.Warn("Wallet devp2p port seems unreachable", "server", client.server, "port", nodePort, "err", err)
}
rpcPort := infos.portmap["8545/tcp"]
if err = checkPort(client.server, rpcPort); err != nil {
- log.Warn(fmt.Sprintf("Wallet RPC port seems unreachable"), "server", client.server, "port", rpcPort, "err", err)
+ log.Warn("Wallet RPC port seems unreachable", "server", client.server, "port", rpcPort, "err", err)
}
// Assemble and return the useful infos
stats := &walletInfos{
diff --git a/cmd/puppeth/puppeth.go b/cmd/puppeth/puppeth.go
index 5fa0addb21..6989f95f62 100644
--- a/cmd/puppeth/puppeth.go
+++ b/cmd/puppeth/puppeth.go
@@ -24,7 +24,7 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/log"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
// main is just a boring entry point to set up the CLI app.
@@ -33,11 +33,11 @@ func main() {
app.Name = "puppeth"
app.Usage = "assemble and maintain private Ethereum networks"
app.Flags = []cli.Flag{
- cli.StringFlag{
+ &cli.StringFlag{
Name: "network",
Usage: "name of the network to administer (no spaces or hyphens, please)",
},
- cli.IntFlag{
+ &cli.IntFlag{
Name: "loglevel",
Value: 3,
Usage: "log level to emit to the screen",
@@ -45,7 +45,7 @@ func main() {
}
app.Action = func(c *cli.Context) error {
// Set up the logger to print everything and the random generator
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stdout, log.FromLegacyLevel(c.Int("loglevel")), true)))
rand.Seed(time.Now().UnixNano())
network := c.String("network")
diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go
index c1e077c595..d7a8f93cec 100644
--- a/cmd/puppeth/wizard_genesis.go
+++ b/cmd/puppeth/wizard_genesis.go
@@ -105,7 +105,7 @@ func (w *wizard) makeGenesis() {
}
}
}
- genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
+ genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+crypto.SignatureLength)
for i, signer := range signers {
copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
}
@@ -179,7 +179,7 @@ func (w *wizard) makeGenesis() {
validatorCap := new(big.Int)
validatorCap.SetString("50000000000000000000000", 10)
var validatorCaps []*big.Int
- genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
+ genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+crypto.SignatureLength)
for i, signer := range signers {
validatorCaps = append(validatorCaps, validatorCap)
copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go
index b8bdd19fb0..37bdb0e812 100644
--- a/cmd/puppeth/wizard_netstats.go
+++ b/cmd/puppeth/wizard_netstats.go
@@ -82,7 +82,6 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
logger.Info("Starting remote server health-check")
stat := &serverStat{
- address: client.address,
services: make(map[string]map[string]string),
}
if client == nil {
@@ -94,6 +93,8 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
}
client = conn
}
+ stat.address = client.address
+
// Client connected one way or another, run health-checks
logger.Debug("Checking for nginx availability")
if infos, err := checkNginx(client, w.network); err != nil {
diff --git a/cmd/utils/customflags.go b/cmd/utils/customflags.go
deleted file mode 100644
index 5f7833daf9..0000000000
--- a/cmd/utils/customflags.go
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-package utils
-
-import (
- "encoding"
- "errors"
- "flag"
- "fmt"
- "math/big"
- "os"
- "os/user"
- "path"
- "strings"
-
- "github.com/XinFinOrg/XDPoSChain/common/math"
- "gopkg.in/urfave/cli.v1"
-)
-
-// Custom type which is registered in the flags library which cli uses for
-// argument parsing. This allows us to expand Value to an absolute path when
-// the argument is parsed
-type DirectoryString struct {
- Value string
-}
-
-func (self *DirectoryString) String() string {
- return self.Value
-}
-
-func (self *DirectoryString) Set(value string) error {
- self.Value = expandPath(value)
- return nil
-}
-
-// Custom cli.Flag type which expand the received string to an absolute path.
-// e.g. ~/.ethereum -> /home/username/.ethereum
-type DirectoryFlag struct {
- Name string
- Value DirectoryString
- Usage string
-}
-
-func (self DirectoryFlag) String() string {
- fmtString := "%s %v\t%v"
- if len(self.Value.Value) > 0 {
- fmtString = "%s \"%v\"\t%v"
- }
- return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage)
-}
-
-func eachName(longName string, fn func(string)) {
- parts := strings.Split(longName, ",")
- for _, name := range parts {
- name = strings.Trim(name, " ")
- fn(name)
- }
-}
-
-// called by cli library, grabs variable from environment (if in env)
-// and adds variable to flag set for parsing.
-func (self DirectoryFlag) Apply(set *flag.FlagSet) {
- eachName(self.Name, func(name string) {
- set.Var(&self.Value, self.Name, self.Usage)
- })
-}
-
-type TextMarshaler interface {
- encoding.TextMarshaler
- encoding.TextUnmarshaler
-}
-
-// textMarshalerVal turns a TextMarshaler into a flag.Value
-type textMarshalerVal struct {
- v TextMarshaler
-}
-
-func (v textMarshalerVal) String() string {
- if v.v == nil {
- return ""
- }
- text, _ := v.v.MarshalText()
- return string(text)
-}
-
-func (v textMarshalerVal) Set(s string) error {
- return v.v.UnmarshalText([]byte(s))
-}
-
-// TextMarshalerFlag wraps a TextMarshaler value.
-type TextMarshalerFlag struct {
- Name string
- Value TextMarshaler
- Usage string
-}
-
-func (f TextMarshalerFlag) GetName() string {
- return f.Name
-}
-
-func (f TextMarshalerFlag) String() string {
- return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)
-}
-
-func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
- eachName(f.Name, func(name string) {
- set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
- })
-}
-
-// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
-func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
- val := ctx.GlobalGeneric(name)
- if val == nil {
- return nil
- }
- return val.(textMarshalerVal).v
-}
-
-// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
-// hexadecimal syntax.
-type BigFlag struct {
- Name string
- Value *big.Int
- Usage string
-}
-
-// bigValue turns *big.Int into a flag.Value
-type bigValue big.Int
-
-func (b *bigValue) String() string {
- if b == nil {
- return ""
- }
- return (*big.Int)(b).String()
-}
-
-func (b *bigValue) Set(s string) error {
- int, ok := math.ParseBig256(s)
- if !ok {
- return errors.New("invalid integer syntax")
- }
- *b = (bigValue)(*int)
- return nil
-}
-
-func (f BigFlag) GetName() string {
- return f.Name
-}
-
-func (f BigFlag) String() string {
- fmtString := "%s %v\t%v"
- if f.Value != nil {
- fmtString = "%s \"%v\"\t%v"
- }
- return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)
-}
-
-func (f BigFlag) Apply(set *flag.FlagSet) {
- eachName(f.Name, func(name string) {
- set.Var((*bigValue)(f.Value), f.Name, f.Usage)
- })
-}
-
-// GlobalBig returns the value of a BigFlag from the global flag set.
-func GlobalBig(ctx *cli.Context, name string) *big.Int {
- val := ctx.GlobalGeneric(name)
- if val == nil {
- return nil
- }
- return (*big.Int)(val.(*bigValue))
-}
-
-func prefixFor(name string) (prefix string) {
- if len(name) == 1 {
- prefix = "-"
- } else {
- prefix = "--"
- }
-
- return
-}
-
-func prefixedNames(fullName string) (prefixed string) {
- parts := strings.Split(fullName, ",")
- for i, name := range parts {
- name = strings.Trim(name, " ")
- prefixed += prefixFor(name) + name
- if i < len(parts)-1 {
- prefixed += ", "
- }
- }
- return
-}
-
-func (self DirectoryFlag) GetName() string {
- return self.Name
-}
-
-func (self *DirectoryFlag) Set(value string) {
- self.Value.Value = value
-}
-
-// Expands a file path
-// 1. replace tilde with users home dir
-// 2. expands embedded environment variables
-// 3. cleans the path, e.g. /a/b/../c -> /a/c
-// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
-func expandPath(p string) string {
- if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
- if home := homeDir(); home != "" {
- p = home + p[1:]
- }
- }
- return path.Clean(os.ExpandEnv(p))
-}
-
-func homeDir() string {
- if home := os.Getenv("HOME"); home != "" {
- return home
- }
- if usr, err := user.Current(); err == nil {
- return usr.HomeDir
- }
- return ""
-}
diff --git a/cmd/utils/customflags_test.go b/cmd/utils/customflags_test.go
deleted file mode 100644
index de39ca36a1..0000000000
--- a/cmd/utils/customflags_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-package utils
-
-import (
- "os"
- "os/user"
- "testing"
-)
-
-func TestPathExpansion(t *testing.T) {
- user, _ := user.Current()
- tests := map[string]string{
- "/home/someuser/tmp": "/home/someuser/tmp",
- "~/tmp": user.HomeDir + "/tmp",
- "~thisOtherUser/b/": "~thisOtherUser/b",
- "$DDDXXX/a/b": "/tmp/a/b",
- "/a/b/": "/a/b",
- }
- os.Setenv("DDDXXX", "/tmp")
- for test, expected := range tests {
- got := expandPath(test)
- if got != expected {
- t.Errorf("test %s, got %s, expected %s\n", test, got, expected)
- }
- }
-}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 272ecbef57..18ce92a23f 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -22,12 +22,14 @@ import (
"fmt"
"math"
"math/big"
+ "net"
"os"
"path/filepath"
"runtime"
godebug "runtime/debug"
"strconv"
"strings"
+ "time"
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/accounts"
@@ -38,17 +40,21 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/crypto/kzg4844"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
"github.com/XinFinOrg/XDPoSChain/eth/filters"
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/internal/ethapi"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/metrics/exp"
+ "github.com/XinFinOrg/XDPoSChain/metrics/influxdb"
"github.com/XinFinOrg/XDPoSChain/node"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
@@ -57,56 +63,10 @@ import (
"github.com/XinFinOrg/XDPoSChain/p2p/netutil"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rpc"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
gopsutil "github.com/shirou/gopsutil/mem"
- "gopkg.in/urfave/cli.v1"
+ "github.com/urfave/cli/v2"
)
-var (
- CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...]
-{{if .cmd.Description}}{{.cmd.Description}}
-{{end}}{{if .cmd.Subcommands}}
-SUBCOMMANDS:
- {{range .cmd.Subcommands}}{{.cmd.Name}}{{with .cmd.ShortName}}, {{.cmd}}{{end}}{{ "\t" }}{{.cmd.Usage}}
- {{end}}{{end}}{{if .categorizedFlags}}
-{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS:
-{{range $categorized.Flags}}{{"\t"}}{{.}}
-{{end}}
-{{end}}{{end}}`
-)
-
-func init() {
- cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
-
-VERSION:
- {{.Version}}
-
-COMMANDS:
- {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
- {{end}}{{if .Flags}}
-GLOBAL OPTIONS:
- {{range .Flags}}{{.}}
- {{end}}{{end}}
-`
-
- cli.CommandHelpTemplate = CommandHelpTemplate
-}
-
-// NewApp creates an app with sane defaults.
-func NewApp(gitCommit, usage string) *cli.App {
- app := cli.NewApp()
- app.Name = filepath.Base(os.Args[0])
- app.Author = ""
- //app.Authors = nil
- app.Email = ""
- app.Version = params.Version
- if len(gitCommit) >= 8 {
- app.Version += "-" + gitCommit[:8]
- }
- app.Usage = usage
- return app
-}
-
// These are all the command line flags we support.
// If you add to this list, please remember to include the
// flag in the appropriate command definition.
@@ -115,524 +75,763 @@ func NewApp(gitCommit, usage string) *cli.App {
// are the same for all commands.
var (
- // XDC flags.
- RollbackFlag = cli.StringFlag{
- Name: "rollback",
- Usage: "Rollback chain at hash",
- Value: "",
- }
- Enable0xPrefixFlag = cli.BoolFlag{
- Name: "enable-0x-prefix",
- Usage: "Addres use 0x-prefix (Deprecated: this is on by default, to use xdc prefix use --enable-xdc-prefix)",
- }
- EnableXDCPrefixFlag = cli.BoolFlag{
- Name: "enable-xdc-prefix",
- Usage: "Addres use xdc-prefix (default = false)",
- }
// General settings
- AnnounceTxsFlag = cli.BoolFlag{
- Name: "announce-txs",
- Usage: "Always commit transactions",
+ DataDirFlag = &flags.DirectoryFlag{
+ Name: "datadir",
+ Usage: "Data directory for the databases and keystore",
+ Value: flags.DirectoryString(node.DefaultDataDir()),
+ Category: flags.EthCategory,
}
- StoreRewardFlag = cli.BoolFlag{
- Name: "store-reward",
- Usage: "Store reward to file",
+ KeyStoreDirFlag = &flags.DirectoryFlag{
+ Name: "keystore",
+ Usage: "Directory for the keystore (default = inside the datadir)",
+ Category: flags.AccountCategory,
}
- DataDirFlag = DirectoryFlag{
- Name: "datadir",
- Usage: "Data directory for the databases and keystore",
- Value: DirectoryString{node.DefaultDataDir()},
+ NetworkIdFlag = &cli.Uint64Flag{
+ Name: "networkid",
+ Usage: "Network identifier (integer, 89=XDPoSChain)",
+ Value: ethconfig.Defaults.NetworkId,
+ Category: flags.EthCategory,
}
- KeyStoreDirFlag = DirectoryFlag{
- Name: "keystore",
- Usage: "Directory for the keystore (default = inside the datadir)",
+ TestnetFlag = &cli.BoolFlag{
+ Name: "testnet",
+ Usage: "Ropsten network: pre-configured proof-of-work test network",
+ Category: flags.EthCategory,
}
- NoUSBFlag = cli.BoolFlag{
- Name: "nousb",
- Usage: "Disables monitoring for and managing USB hardware wallets",
+ XDCTestnetFlag = &cli.BoolFlag{
+ Name: "apothem",
+ Usage: "XDC Apothem Network",
+ Category: flags.EthCategory,
}
- NetworkIdFlag = cli.Uint64Flag{
- Name: "networkid",
- Usage: "Network identifier (integer, 89=XDPoSChain)",
- Value: ethconfig.Defaults.NetworkId,
- }
- TestnetFlag = cli.BoolFlag{
- Name: "testnet",
- Usage: "Ropsten network: pre-configured proof-of-work test network",
- }
- XDCTestnetFlag = cli.BoolFlag{
- Name: "apothem",
- Usage: "XDC Apothem Network",
- }
- RinkebyFlag = cli.BoolFlag{
- Name: "rinkeby",
- Usage: "Rinkeby network: pre-configured proof-of-authority test network",
- }
- DeveloperFlag = cli.BoolFlag{
- Name: "dev",
- Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
- }
- DeveloperPeriodFlag = cli.IntFlag{
- Name: "dev.period",
- Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
- }
- IdentityFlag = cli.StringFlag{
- Name: "identity",
- Usage: "Custom node name",
- }
- DocRootFlag = DirectoryFlag{
- Name: "docroot",
- Usage: "Document Root for HTTPClient file scheme",
- Value: DirectoryString{homeDir()},
- }
- FastSyncFlag = cli.BoolFlag{
- Name: "fast",
- Usage: "Enable fast syncing through state downloads",
- }
- LightModeFlag = cli.BoolFlag{
- Name: "light",
- Usage: "Enable light client mode",
- }
- defaultSyncMode = ethconfig.Defaults.SyncMode
- SyncModeFlag = TextMarshalerFlag{
- Name: "syncmode",
- Usage: `Blockchain sync mode ("fast", "full", or "light")`,
- Value: &defaultSyncMode,
- }
- GCModeFlag = cli.StringFlag{
- Name: "gcmode",
- Usage: `Blockchain garbage collection mode ("full", "archive")`,
- Value: "full",
- }
- LightServFlag = cli.IntFlag{
- Name: "lightserv",
- Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
- Value: 0,
- }
- LightPeersFlag = cli.IntFlag{
- Name: "lightpeers",
- Usage: "Maximum number of LES client peers",
- Value: ethconfig.Defaults.LightPeers,
- }
- LightKDFFlag = cli.BoolFlag{
- Name: "lightkdf",
- Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
- }
- // XDCX settings
- XDCXEnabledFlag = cli.BoolFlag{
- Name: "XDCx",
- Usage: "Enable the XDCX protocol",
- }
- // Ethash settings
- EthashCacheDirFlag = DirectoryFlag{
- Name: "ethash.cachedir",
- Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
- }
- EthashCachesInMemoryFlag = cli.IntFlag{
- Name: "ethash.cachesinmem",
- Usage: "Number of recent ethash caches to keep in memory (16MB each)",
- Value: ethconfig.Defaults.Ethash.CachesInMem,
- }
- EthashCachesOnDiskFlag = cli.IntFlag{
- Name: "ethash.cachesondisk",
- Usage: "Number of recent ethash caches to keep on disk (16MB each)",
- Value: ethconfig.Defaults.Ethash.CachesOnDisk,
- }
- EthashDatasetDirFlag = DirectoryFlag{
- Name: "ethash.dagdir",
- Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
- Value: DirectoryString{ethconfig.Defaults.Ethash.DatasetDir},
- }
- EthashDatasetsInMemoryFlag = cli.IntFlag{
- Name: "ethash.dagsinmem",
- Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
- Value: ethconfig.Defaults.Ethash.DatasetsInMem,
- }
- EthashDatasetsOnDiskFlag = cli.IntFlag{
- Name: "ethash.dagsondisk",
- Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
- Value: ethconfig.Defaults.Ethash.DatasetsOnDisk,
- }
- // Transaction pool settings
- TxPoolNoLocalsFlag = cli.BoolFlag{
- Name: "txpool.nolocals",
- Usage: "Disables price exemptions for locally submitted transactions",
- }
- TxPoolJournalFlag = cli.StringFlag{
- Name: "txpool.journal",
- Usage: "Disk journal for local transaction to survive node restarts",
- Value: core.DefaultTxPoolConfig.Journal,
- }
- TxPoolRejournalFlag = cli.DurationFlag{
- Name: "txpool.rejournal",
- Usage: "Time interval to regenerate the local transaction journal",
- Value: core.DefaultTxPoolConfig.Rejournal,
- }
- TxPoolPriceLimitFlag = cli.Uint64Flag{
- Name: "txpool.pricelimit",
- Usage: "Minimum gas price limit to enforce for acceptance into the pool",
- Value: ethconfig.Defaults.TxPool.PriceLimit,
- }
- TxPoolPriceBumpFlag = cli.Uint64Flag{
- Name: "txpool.pricebump",
- Usage: "Price bump percentage to replace an already existing transaction",
- Value: ethconfig.Defaults.TxPool.PriceBump,
- }
- TxPoolAccountSlotsFlag = cli.Uint64Flag{
- Name: "txpool.accountslots",
- Usage: "Minimum number of executable transaction slots guaranteed per account",
- Value: ethconfig.Defaults.TxPool.AccountSlots,
- }
- TxPoolGlobalSlotsFlag = cli.Uint64Flag{
- Name: "txpool.globalslots",
- Usage: "Maximum number of executable transaction slots for all accounts",
- Value: ethconfig.Defaults.TxPool.GlobalSlots,
- }
- TxPoolAccountQueueFlag = cli.Uint64Flag{
- Name: "txpool.accountqueue",
- Usage: "Maximum number of non-executable transaction slots permitted per account",
- Value: ethconfig.Defaults.TxPool.AccountQueue,
- }
- TxPoolGlobalQueueFlag = cli.Uint64Flag{
- Name: "txpool.globalqueue",
- Usage: "Maximum number of non-executable transaction slots for all accounts",
- Value: ethconfig.Defaults.TxPool.GlobalQueue,
- }
- TxPoolLifetimeFlag = cli.DurationFlag{
- Name: "txpool.lifetime",
- Usage: "Maximum amount of time non-executable transaction are queued",
- Value: ethconfig.Defaults.TxPool.Lifetime,
- }
- // Performance tuning settings
- CacheFlag = cli.IntFlag{
- Name: "cache",
- Usage: "Megabytes of memory allocated to internal caching",
- Value: 1024,
- }
- CacheDatabaseFlag = cli.IntFlag{
- Name: "cache.database",
- Usage: "Percentage of cache memory allowance to use for database io",
- Value: 75,
- }
- CacheGCFlag = cli.IntFlag{
- Name: "cache.gc",
- Usage: "Percentage of cache memory allowance to use for trie pruning",
- Value: 25,
- }
- CacheLogSizeFlag = &cli.IntFlag{
- Name: "cache.blocklogs",
- Usage: "Size (in number of blocks) of the log cache for filtering",
- Value: ethconfig.Defaults.FilterLogCacheSize,
- }
- FDLimitFlag = cli.IntFlag{
- Name: "fdlimit",
- Usage: "Raise the open file descriptor resource limit (default = system fd limit)",
- }
- // Miner settings
- StakingEnabledFlag = cli.BoolFlag{
- Name: "mine",
- Usage: "Enable staking",
- }
- StakerThreadsFlag = cli.IntFlag{
- Name: "minerthreads",
- Usage: "Number of CPU threads to use for staking",
- Value: runtime.NumCPU(),
- }
- TargetGasLimitFlag = cli.Uint64Flag{
- Name: "targetgaslimit",
- Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
- Value: params.XDCGenesisGasLimit,
- }
- EtherbaseFlag = cli.StringFlag{
- Name: "etherbase",
- Usage: "Public address for block mining rewards (default = first account created)",
- Value: "0",
- }
- GasPriceFlag = BigFlag{
- Name: "gasprice",
- Usage: "Minimal gas price to accept for mining a transactions",
- Value: ethconfig.Defaults.GasPrice,
- }
- ExtraDataFlag = cli.StringFlag{
- Name: "extradata",
- Usage: "Block extra data set by the miner (default = client version)",
- }
- // Account settings
- UnlockedAccountFlag = cli.StringFlag{
- Name: "unlock",
- Usage: "Comma separated list of accounts to unlock",
- Value: "",
- }
- PasswordFileFlag = cli.StringFlag{
- Name: "password",
- Usage: "Password file to use for non-interactive password input",
- Value: "",
+ RinkebyFlag = &cli.BoolFlag{
+ Name: "rinkeby",
+ Usage: "Rinkeby network: pre-configured proof-of-authority test network",
+ Category: flags.EthCategory,
}
- VMEnableDebugFlag = cli.BoolFlag{
- Name: "vmdebug",
- Usage: "Record information useful for VM and contract debugging",
+ // Dev mode
+ DeveloperFlag = &cli.BoolFlag{
+ Name: "dev",
+ Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
+ Category: flags.DevCategory,
}
- RPCGlobalGasCapFlag = cli.Uint64Flag{
- Name: "rpc-gascap",
- Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
- Value: ethconfig.Defaults.RPCGasCap,
+ DeveloperPeriodFlag = &cli.IntFlag{
+ Name: "dev-period",
+ Aliases: []string{"dev.period"},
+ Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
+ Category: flags.DevCategory,
}
- RPCGlobalTxFeeCap = cli.Float64Flag{
- Name: "rpc.txfeecap",
- Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)",
- Value: ethconfig.Defaults.RPCTxFeeCap,
+ IdentityFlag = &cli.StringFlag{
+ Name: "identity",
+ Usage: "Custom node name",
+ Category: flags.NetworkingCategory,
}
+ DocRootFlag = &flags.DirectoryFlag{
+ Name: "docroot",
+ Usage: "Document Root for HTTPClient file scheme",
+ Value: flags.DirectoryString(flags.HomeDir()),
+ Category: flags.APICategory,
+ }
+
+ SyncModeFlag = &cli.StringFlag{
+ Name: "syncmode",
+ Usage: `Blockchain sync mode ("fast", "full", or "light")`,
+ Value: ethconfig.Defaults.SyncMode.String(),
+ Category: flags.EthCategory,
+ }
+ GCModeFlag = &cli.StringFlag{
+ Name: "gcmode",
+ Usage: `Blockchain garbage collection mode ("full", "archive")`,
+ Value: "full",
+ Category: flags.EthCategory,
+ }
+ LightKDFFlag = &cli.BoolFlag{
+ Name: "lightkdf",
+ Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
+ Category: flags.AccountCategory,
+ }
+
+ // Light server and client settings
+ LightServFlag = &cli.IntFlag{
+ Name: "light-serv",
+ Aliases: []string{"lightserv"},
+ Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
+ Value: ethconfig.Defaults.LightServ,
+ Category: flags.LightCategory,
+ }
+ LightPeersFlag = &cli.IntFlag{
+ Name: "light-peers",
+ Aliases: []string{"lightpeers"},
+ Usage: "Maximum number of LES client peers",
+ Value: ethconfig.Defaults.LightPeers,
+ Category: flags.LightCategory,
+ }
+
+ // Ethash settings
+ EthashCacheDirFlag = &flags.DirectoryFlag{
+ Name: "ethash-cachedir",
+ Aliases: []string{"ethash.cachedir"},
+ Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
+ Category: flags.EthashCategory,
+ }
+ EthashCachesInMemoryFlag = &cli.IntFlag{
+ Name: "ethash-cachesinmem",
+ Aliases: []string{"ethash.cachesinmem"},
+ Usage: "Number of recent ethash caches to keep in memory (16MB each)",
+ Value: ethconfig.Defaults.Ethash.CachesInMem,
+ Category: flags.EthashCategory,
+ }
+ EthashCachesOnDiskFlag = &cli.IntFlag{
+ Name: "ethash-cachesondisk",
+ Aliases: []string{"ethash.cachesondisk"},
+ Usage: "Number of recent ethash caches to keep on disk (16MB each)",
+ Value: ethconfig.Defaults.Ethash.CachesOnDisk,
+ Category: flags.EthashCategory,
+ }
+ EthashDatasetDirFlag = &flags.DirectoryFlag{
+ Name: "ethash-dagdir",
+ Aliases: []string{"ethash.dagdir"},
+ Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
+ Value: flags.DirectoryString(ethconfig.Defaults.Ethash.DatasetDir),
+ Category: flags.EthashCategory,
+ }
+ EthashDatasetsInMemoryFlag = &cli.IntFlag{
+ Name: "ethash-dagsinmem",
+ Aliases: []string{"ethash.dagsinmem"},
+ Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
+ Value: ethconfig.Defaults.Ethash.DatasetsInMem,
+ Category: flags.EthashCategory,
+ }
+ EthashDatasetsOnDiskFlag = &cli.IntFlag{
+ Name: "ethash-dagsondisk",
+ Aliases: []string{"ethash.dagsondisk"},
+ Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
+ Value: ethconfig.Defaults.Ethash.DatasetsOnDisk,
+ Category: flags.EthashCategory,
+ }
+
+ // Transaction pool settings
+ TxPoolNoLocalsFlag = &cli.BoolFlag{
+ Name: "txpool-nolocals",
+ Aliases: []string{"txpool.nolocals"},
+ Usage: "Disables price exemptions for locally submitted transactions",
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolJournalFlag = &cli.StringFlag{
+ Name: "txpool-journal",
+ Aliases: []string{"txpool.journal"},
+ Usage: "Disk journal for local transaction to survive node restarts",
+ Value: txpool.DefaultConfig.Journal,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolRejournalFlag = &cli.DurationFlag{
+ Name: "txpool-rejournal",
+ Aliases: []string{"txpool.rejournal"},
+ Usage: "Time interval to regenerate the local transaction journal",
+ Value: txpool.DefaultConfig.Rejournal,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolPriceLimitFlag = &cli.Uint64Flag{
+ Name: "txpool-pricelimit",
+ Aliases: []string{"txpool.pricelimit"},
+ Usage: "Minimum gas price limit to enforce for acceptance into the pool",
+ Value: ethconfig.Defaults.TxPool.PriceLimit,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolPriceBumpFlag = &cli.Uint64Flag{
+ Name: "txpool-pricebump",
+ Aliases: []string{"txpool.pricebump"},
+ Usage: "Price bump percentage to replace an already existing transaction",
+ Value: ethconfig.Defaults.TxPool.PriceBump,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolAccountSlotsFlag = &cli.Uint64Flag{
+ Name: "txpool-accountslots",
+ Aliases: []string{"txpool.accountslots"},
+ Usage: "Minimum number of executable transaction slots guaranteed per account",
+ Value: ethconfig.Defaults.TxPool.AccountSlots,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolGlobalSlotsFlag = &cli.Uint64Flag{
+ Name: "txpool-globalslots",
+ Aliases: []string{"txpool.globalslots"},
+ Usage: "Maximum number of executable transaction slots for all accounts",
+ Value: ethconfig.Defaults.TxPool.GlobalSlots,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolAccountQueueFlag = &cli.Uint64Flag{
+ Name: "txpool-accountqueue",
+ Aliases: []string{"txpool.accountqueue"},
+ Usage: "Maximum number of non-executable transaction slots permitted per account",
+ Value: ethconfig.Defaults.TxPool.AccountQueue,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolGlobalQueueFlag = &cli.Uint64Flag{
+ Name: "txpool-globalqueue",
+ Aliases: []string{"txpool.globalqueue"},
+ Usage: "Maximum number of non-executable transaction slots for all accounts",
+ Value: ethconfig.Defaults.TxPool.GlobalQueue,
+ Category: flags.TxPoolCategory,
+ }
+ TxPoolLifetimeFlag = &cli.DurationFlag{
+ Name: "txpool-lifetime",
+ Aliases: []string{"txpool.lifetime"},
+ Usage: "Maximum amount of time non-executable transaction are queued",
+ Value: ethconfig.Defaults.TxPool.Lifetime,
+ Category: flags.TxPoolCategory,
+ }
+
+ // Performance tuning settings
+ CacheFlag = &cli.IntFlag{
+ Name: "cache",
+ Usage: "Megabytes of memory allocated to internal caching",
+ Value: 1024,
+ Category: flags.PerfCategory,
+ }
+ CacheDatabaseFlag = &cli.IntFlag{
+ Name: "cache-database",
+ Aliases: []string{"cache.database"},
+ Usage: "Percentage of cache memory allowance to use for database io",
+ Value: 50,
+ Category: flags.PerfCategory,
+ }
+ CacheGCFlag = &cli.IntFlag{
+ Name: "cache-gc",
+ Aliases: []string{"cache.gc"},
+ Usage: "Percentage of cache memory allowance to use for trie pruning",
+ Value: 25,
+ Category: flags.PerfCategory,
+ }
+ CacheLogSizeFlag = &cli.IntFlag{
+ Name: "cache-blocklogs",
+ Aliases: []string{"cache.blocklogs"},
+ Usage: "Size (in number of blocks) of the log cache for filtering",
+ Value: ethconfig.Defaults.FilterLogCacheSize,
+ Category: flags.PerfCategory,
+ }
+ FDLimitFlag = &cli.IntFlag{
+ Name: "fdlimit",
+ Usage: "Raise the open file descriptor resource limit (default = system fd limit)",
+ Category: flags.PerfCategory,
+ }
+ CryptoKZGFlag = &cli.StringFlag{
+ Name: "crypto-kzg",
+ Usage: "KZG library implementation to use; gokzg (recommended) or ckzg",
+ Value: "gokzg",
+ Category: flags.PerfCategory,
+ }
+
+ // Miner settings
+ MiningEnabledFlag = &cli.BoolFlag{
+ Name: "mine",
+ Usage: "Enable mining",
+ Category: flags.MinerCategory,
+ }
+ MinerThreadsFlag = &cli.IntFlag{
+ Name: "miner-threads",
+ Aliases: []string{"minerthreads"},
+ Usage: "Number of CPU threads to use for mining",
+ Value: runtime.NumCPU(),
+ Category: flags.MinerCategory,
+ }
+ MinerGasLimitFlag = &cli.Uint64Flag{
+ Name: "miner-gaslimit",
+ Aliases: []string{"targetgaslimit"},
+ Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
+ Value: params.XDCGenesisGasLimit,
+ Category: flags.MinerCategory,
+ }
+ MinerGasPriceFlag = &flags.BigFlag{
+ Name: "miner-gasprice",
+ Aliases: []string{"gasprice"},
+ Usage: "Minimal gas price to accept for mining a transactions",
+ Value: ethconfig.Defaults.GasPrice,
+ Category: flags.MinerCategory,
+ }
+ MinerEtherbaseFlag = &cli.StringFlag{
+ Name: "miner-etherbase",
+ Aliases: []string{"etherbase"},
+ Usage: "Public address for block mining rewards (default = first account created)",
+ Value: "0",
+ Category: flags.MinerCategory,
+ }
+ MinerExtraDataFlag = &cli.StringFlag{
+ Name: "miner-extradata",
+ Aliases: []string{"extradata"},
+ Usage: "Block extra data set by the miner (default = client version)",
+ Category: flags.MinerCategory,
+ }
+
+ // Account settings
+ UnlockedAccountFlag = &cli.StringFlag{
+ Name: "unlock",
+ Usage: "Comma separated list of accounts to unlock",
+ Value: "",
+ Category: flags.AccountCategory,
+ }
+ PasswordFileFlag = &cli.StringFlag{
+ Name: "password",
+ Usage: "Password file to use for non-interactive password input",
+ Value: "",
+ Category: flags.AccountCategory,
+ }
+
+ // EVM settings
+ VMEnableDebugFlag = &cli.BoolFlag{
+ Name: "vmdebug",
+ Usage: "Record information useful for VM and contract debugging",
+ Category: flags.VMCategory,
+ }
+
+ // API options
+ RPCGlobalGasCapFlag = &cli.Uint64Flag{
+ Name: "rpc-gascap",
+ Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
+ Value: ethconfig.Defaults.RPCGasCap,
+ Category: flags.APICategory,
+ }
+ RPCGlobalTxFeeCap = &cli.Float64Flag{
+ Name: "rpc-txfeecap",
+ Aliases: []string{"rpc.txfeecap"},
+ Usage: "Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)",
+ Value: ethconfig.Defaults.RPCTxFeeCap,
+ Category: flags.APICategory,
+ }
+
// Logging and debug settings
- EthStatsURLFlag = cli.StringFlag{
- Name: "ethstats",
- Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)",
+ EthStatsURLFlag = &cli.StringFlag{
+ Name: "ethstats",
+ Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)",
+ Category: flags.MetricsCategory,
+ }
+ FakePoWFlag = &cli.BoolFlag{
+ Name: "fakepow",
+ Usage: "Disables proof-of-work verification",
+ Category: flags.LoggingCategory,
+ }
+ NoCompactionFlag = &cli.BoolFlag{
+ Name: "nocompaction",
+ Usage: "Disables db compaction after import",
+ Category: flags.LoggingCategory,
+ }
+
+ // RPC settings
+ IPCDisabledFlag = &cli.BoolFlag{
+ Name: "ipcdisable",
+ Usage: "Disable the IPC-RPC server",
+ Category: flags.APICategory,
+ }
+ IPCPathFlag = &flags.DirectoryFlag{
+ Name: "ipcpath",
+ Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
+ Category: flags.APICategory,
+ }
+ HTTPEnabledFlag = &cli.BoolFlag{
+ Name: "http",
+ Aliases: []string{"rpc"},
+ Usage: "Enable the HTTP-RPC server",
+ Category: flags.APICategory,
+ }
+ HTTPListenAddrFlag = &cli.StringFlag{
+ Name: "http-addr",
+ Aliases: []string{"rpcaddr"},
+ Usage: "HTTP-RPC server listening interface",
+ Value: node.DefaultHTTPHost,
+ Category: flags.APICategory,
+ }
+ HTTPPortFlag = &cli.IntFlag{
+ Name: "http-port",
+ Aliases: []string{"rpcport"},
+ Usage: "HTTP-RPC server listening port",
+ Value: node.DefaultHTTPPort,
+ Category: flags.APICategory,
+ }
+ HTTPCORSDomainFlag = &cli.StringFlag{
+ Name: "http-corsdomain",
+ Aliases: []string{"rpccorsdomain"},
+ Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
+ Value: "",
+ Category: flags.APICategory,
+ }
+ HTTPVirtualHostsFlag = &cli.StringFlag{
+ Name: "http-vhosts",
+ Aliases: []string{"rpcvhosts"},
+ Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
+ Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
+ Category: flags.APICategory,
+ }
+ HTTPApiFlag = &cli.StringFlag{
+ Name: "http-api",
+ Aliases: []string{"rpcapi"},
+ Usage: "API's offered over the HTTP-RPC interface",
+ Value: "",
+ Category: flags.APICategory,
+ }
+ HTTPReadTimeoutFlag = &cli.DurationFlag{
+ Name: "http-readtimeout",
+ Aliases: []string{"rpcreadtimeout"},
+ Usage: "HTTP-RPC server read timeout",
+ Value: rpc.DefaultHTTPTimeouts.ReadTimeout,
+ Category: flags.APICategory,
+ }
+ HTTPWriteTimeoutFlag = &cli.DurationFlag{
+ Name: "http-writetimeout",
+ Aliases: []string{"rpcwritetimeout"},
+ Usage: "HTTP-RPC server write timeout",
+ Value: rpc.DefaultHTTPTimeouts.WriteTimeout,
+ Category: flags.APICategory,
+ }
+ HTTPIdleTimeoutFlag = &cli.DurationFlag{
+ Name: "http-idletimeout",
+ Aliases: []string{"rpcidletimeout"},
+ Usage: "HTTP-RPC server idle timeout",
+ Value: rpc.DefaultHTTPTimeouts.IdleTimeout,
+ Category: flags.APICategory,
+ }
+ WSEnabledFlag = &cli.BoolFlag{
+ Name: "ws",
+ Usage: "Enable the WS-RPC server",
+ Category: flags.APICategory,
+ }
+ WSListenAddrFlag = &cli.StringFlag{
+ Name: "ws-addr",
+ Aliases: []string{"wsaddr"},
+ Usage: "WS-RPC server listening interface",
+ Value: node.DefaultWSHost,
+ Category: flags.APICategory,
+ }
+ WSPortFlag = &cli.IntFlag{
+ Name: "ws-port",
+ Aliases: []string{"wsport"},
+ Usage: "WS-RPC server listening port",
+ Value: node.DefaultWSPort,
+ Category: flags.APICategory,
+ }
+ WSApiFlag = &cli.StringFlag{
+ Name: "ws-api",
+ Aliases: []string{"wsapi"},
+ Usage: "API's offered over the WS-RPC interface",
+ Value: "",
+ Category: flags.APICategory,
+ }
+ WSAllowedOriginsFlag = &cli.StringFlag{
+ Name: "ws-origins",
+ Aliases: []string{"wsorigins"},
+ Usage: "Origins from which to accept websockets requests",
+ Value: "",
+ Category: flags.APICategory,
+ }
+ ExecFlag = &cli.StringFlag{
+ Name: "exec",
+ Usage: "Execute JavaScript statement",
+ Category: flags.APICategory,
+ }
+ PreloadJSFlag = &cli.StringFlag{
+ Name: "preload",
+ Usage: "Comma separated list of JavaScript files to preload into the console",
+ Category: flags.APICategory,
+ }
+
+ // Network Settings
+ MaxPeersFlag = &cli.IntFlag{
+ Name: "maxpeers",
+ Usage: "Maximum number of network peers (network disabled if set to 0)",
+ Value: node.DefaultConfig.P2P.MaxPeers,
+ Category: flags.NetworkingCategory,
+ }
+ MaxPendingPeersFlag = &cli.IntFlag{
+ Name: "maxpendpeers",
+ Usage: "Maximum number of pending connection attempts (defaults used if set to 0)",
+ Value: node.DefaultConfig.P2P.MaxPendingPeers,
+ Category: flags.NetworkingCategory,
+ }
+ ListenPortFlag = &cli.IntFlag{
+ Name: "port",
+ Usage: "Network listening port",
+ Value: 30303,
+ Category: flags.NetworkingCategory,
+ }
+ BootnodesFlag = &cli.StringFlag{
+ Name: "bootnodes",
+ Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)",
+ Value: "",
+ Category: flags.NetworkingCategory,
+ }
+ BootnodesV4Flag = &cli.StringFlag{
+ Name: "bootnodesv4",
+ Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)",
+ Value: "",
+ Category: flags.NetworkingCategory,
+ }
+ BootnodesV5Flag = &cli.StringFlag{
+ Name: "bootnodesv5",
+ Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)",
+ Value: "",
+ Category: flags.NetworkingCategory,
+ }
+ NodeKeyFileFlag = &cli.StringFlag{
+ Name: "nodekey",
+ Usage: "P2P node key file",
+ Category: flags.NetworkingCategory,
+ }
+ NodeKeyHexFlag = &cli.StringFlag{
+ Name: "nodekeyhex",
+ Usage: "P2P node key as hex (for testing)",
+ Category: flags.NetworkingCategory,
+ }
+ NATFlag = &cli.StringFlag{
+ Name: "nat",
+ Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)",
+ Value: "any",
+ Category: flags.NetworkingCategory,
+ }
+ NoDiscoverFlag = &cli.BoolFlag{
+ Name: "nodiscover",
+ Usage: "Disables the peer discovery mechanism (manual peer addition)",
+ Category: flags.NetworkingCategory,
+ }
+ DiscoveryV5Flag = &cli.BoolFlag{
+ Name: "v5disc",
+ Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
+ Category: flags.NetworkingCategory,
+ }
+ NetrestrictFlag = &cli.StringFlag{
+ Name: "netrestrict",
+ Usage: "Restricts network communication to the given IP networks (CIDR masks)",
+ Category: flags.NetworkingCategory,
+ }
+
+ // Console
+ JSpathFlag = &cli.StringFlag{
+ Name: "jspath",
+ Usage: "JavaScript root path for `loadScript`",
+ Value: ".",
+ Category: flags.APICategory,
+ }
+
+ // Gas price oracle settings
+ GpoBlocksFlag = &cli.IntFlag{
+ Name: "gpo-blocks",
+ Aliases: []string{"gpoblocks"},
+ Usage: "Number of recent blocks to check for gas prices",
+ Value: ethconfig.Defaults.GPO.Blocks,
+ Category: flags.GasPriceCategory,
+ }
+ GpoPercentileFlag = &cli.IntFlag{
+ Name: "gpo-percentile",
+ Aliases: []string{"gpopercentile"},
+ Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
+ Value: ethconfig.Defaults.GPO.Percentile,
+ Category: flags.GasPriceCategory,
+ }
+ GpoMaxGasPriceFlag = &cli.Int64Flag{
+ Name: "gpo-maxprice",
+ Aliases: []string{"gpo.maxprice"},
+ Usage: "Maximum gas price will be recommended by gpo",
+ Value: ethconfig.Defaults.GPO.MaxPrice.Int64(),
+ Category: flags.GasPriceCategory,
+ }
+ GpoIgnoreGasPriceFlag = &cli.Int64Flag{
+ Name: "gpo-ignoreprice",
+ Aliases: []string{"gpo.ignoreprice"},
+ Usage: "Gas price below which gpo will ignore transactions",
+ Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(),
+ Category: flags.GasPriceCategory,
+ }
+
+ // Metrics flags
+ MetricsEnabledFlag = &cli.BoolFlag{
+ Name: "metrics",
+ Usage: "Enable metrics collection and reporting",
+ Category: flags.MetricsCategory,
}
// MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint.
// Since the pprof service enables sensitive/vulnerable behavior, this allows a user
// to enable a public-OK metrics endpoint without having to worry about ALSO exposing
// other profiling behavior or information.
- MetricsHTTPFlag = cli.StringFlag{
- Name: "metrics.addr",
- Usage: "Enable stand-alone metrics HTTP server listening interface",
- Value: metrics.DefaultConfig.HTTP,
+ MetricsHTTPFlag = &cli.StringFlag{
+ Name: "metrics-addr",
+ Aliases: []string{"metrics.addr"},
+ Usage: "Enable stand-alone metrics HTTP server listening interface",
+ Value: metrics.DefaultConfig.HTTP,
+ Category: flags.MetricsCategory,
}
- MetricsPortFlag = cli.IntFlag{
- Name: "metrics.port",
- Usage: "Metrics HTTP server listening port",
- Value: metrics.DefaultConfig.Port,
+ MetricsPortFlag = &cli.IntFlag{
+ Name: "metrics-port",
+ Aliases: []string{"metrics.port"},
+ Usage: "Metrics HTTP server listening port",
+ Value: metrics.DefaultConfig.Port,
+ Category: flags.MetricsCategory,
}
- MetricsEnabledFlag = cli.BoolFlag{
- Name: metrics.MetricsEnabledFlag,
- Usage: "Enable metrics collection and reporting",
+ MetricsEnableInfluxDBFlag = &cli.BoolFlag{
+ Name: "metrics-influxdb",
+ Usage: "Enable metrics export/push to an external InfluxDB database",
+ Category: flags.MetricsCategory,
}
- FakePoWFlag = cli.BoolFlag{
- Name: "fakepow",
- Usage: "Disables proof-of-work verification",
+ MetricsInfluxDBEndpointFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.endpoint",
+ Usage: "InfluxDB API endpoint to report metrics to",
+ Value: metrics.DefaultConfig.InfluxDBEndpoint,
+ Category: flags.MetricsCategory,
}
- NoCompactionFlag = cli.BoolFlag{
- Name: "nocompaction",
- Usage: "Disables db compaction after import",
+ MetricsInfluxDBDatabaseFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.database",
+ Usage: "InfluxDB database name to push reported metrics to",
+ Value: metrics.DefaultConfig.InfluxDBDatabase,
+ Category: flags.MetricsCategory,
}
- // RPC settings
- RPCEnabledFlag = cli.BoolFlag{
- Name: "rpc",
- Usage: "Enable the HTTP-RPC server",
+ MetricsInfluxDBUsernameFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.username",
+ Usage: "Username to authorize access to the database",
+ Value: metrics.DefaultConfig.InfluxDBUsername,
+ Category: flags.MetricsCategory,
}
- RPCListenAddrFlag = cli.StringFlag{
- Name: "rpcaddr",
- Usage: "HTTP-RPC server listening interface",
- Value: node.DefaultHTTPHost,
+ MetricsInfluxDBPasswordFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.password",
+ Usage: "Password to authorize access to the database",
+ Value: metrics.DefaultConfig.InfluxDBPassword,
+ Category: flags.MetricsCategory,
}
- RewoundFlag = cli.IntFlag{
- Name: "rewound",
- Usage: "Rewound blocks",
- Value: 0,
- }
- RPCPortFlag = cli.IntFlag{
- Name: "rpcport",
- Usage: "HTTP-RPC server listening port",
- Value: node.DefaultHTTPPort,
- }
- RPCHttpWriteTimeoutFlag = cli.DurationFlag{
- Name: "rpcwritetimeout",
- Usage: "HTTP-RPC server write timeout (default = 10s)",
- Value: node.DefaultHTTPWriteTimeOut,
- }
- RPCCORSDomainFlag = cli.StringFlag{
- Name: "rpccorsdomain",
- Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
- Value: "",
- }
- RPCVirtualHostsFlag = cli.StringFlag{
- Name: "rpcvhosts",
- Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
- Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
- }
- RPCApiFlag = cli.StringFlag{
- Name: "rpcapi",
- Usage: "API's offered over the HTTP-RPC interface",
- Value: "",
- }
- IPCDisabledFlag = cli.BoolFlag{
- Name: "ipcdisable",
- Usage: "Disable the IPC-RPC server",
- }
- IPCPathFlag = DirectoryFlag{
- Name: "ipcpath",
- Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
- }
- WSEnabledFlag = cli.BoolFlag{
- Name: "ws",
- Usage: "Enable the WS-RPC server",
- }
- WSListenAddrFlag = cli.StringFlag{
- Name: "wsaddr",
- Usage: "WS-RPC server listening interface",
- Value: node.DefaultWSHost,
- }
- WSPortFlag = cli.IntFlag{
- Name: "wsport",
- Usage: "WS-RPC server listening port",
- Value: node.DefaultWSPort,
- }
- WSApiFlag = cli.StringFlag{
- Name: "wsapi",
- Usage: "API's offered over the WS-RPC interface",
- Value: "",
- }
- WSAllowedOriginsFlag = cli.StringFlag{
- Name: "wsorigins",
- Usage: "Origins from which to accept websockets requests",
- Value: "",
- }
- ExecFlag = cli.StringFlag{
- Name: "exec",
- Usage: "Execute JavaScript statement",
- }
- PreloadJSFlag = cli.StringFlag{
- Name: "preload",
- Usage: "Comma separated list of JavaScript files to preload into the console",
+ // Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
+ // For example `host` tag could be used so that we can group all nodes and average a measurement
+ // across all of them, but also so that we can select a specific node and inspect its measurements.
+ // https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
+ MetricsInfluxDBTagsFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.tags",
+ Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
+ Value: metrics.DefaultConfig.InfluxDBTags,
+ Category: flags.MetricsCategory,
}
- // Network Settings
- MaxPeersFlag = cli.IntFlag{
- Name: "maxpeers",
- Usage: "Maximum number of network peers (network disabled if set to 0)",
- Value: 25,
+ MetricsEnableInfluxDBV2Flag = &cli.BoolFlag{
+ Name: "metrics-influxdbv2",
+ Usage: "Enable metrics export/push to an external InfluxDB v2 database",
+ Category: flags.MetricsCategory,
}
- MaxPendingPeersFlag = cli.IntFlag{
- Name: "maxpendpeers",
- Usage: "Maximum number of pending connection attempts (defaults used if set to 0)",
- Value: 0,
+ MetricsInfluxDBTokenFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.token",
+ Usage: "Token to authorize access to the database (v2 only)",
+ Value: metrics.DefaultConfig.InfluxDBToken,
+ Category: flags.MetricsCategory,
}
- ListenPortFlag = cli.IntFlag{
- Name: "port",
- Usage: "Network listening port",
- Value: 30303,
+ MetricsInfluxDBBucketFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.bucket",
+ Usage: "InfluxDB bucket name to push reported metrics to (v2 only)",
+ Value: metrics.DefaultConfig.InfluxDBBucket,
+ Category: flags.MetricsCategory,
}
- BootnodesFlag = cli.StringFlag{
- Name: "bootnodes",
- Usage: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)",
- Value: "",
- }
- BootnodesV4Flag = cli.StringFlag{
- Name: "bootnodesv4",
- Usage: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)",
- Value: "",
- }
- BootnodesV5Flag = cli.StringFlag{
- Name: "bootnodesv5",
- Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)",
- Value: "",
- }
- NodeKeyFileFlag = cli.StringFlag{
- Name: "nodekey",
- Usage: "P2P node key file",
- }
- NodeKeyHexFlag = cli.StringFlag{
- Name: "nodekeyhex",
- Usage: "P2P node key as hex (for testing)",
- }
- NATFlag = cli.StringFlag{
- Name: "nat",
- Usage: "NAT port mapping mechanism (any|none|upnp|pmp|extip:)",
- Value: "any",
- }
- NoDiscoverFlag = cli.BoolFlag{
- Name: "nodiscover",
- Usage: "Disables the peer discovery mechanism (manual peer addition)",
- }
- DiscoveryV5Flag = cli.BoolFlag{
- Name: "v5disc",
- Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
- }
- NetrestrictFlag = cli.StringFlag{
- Name: "netrestrict",
- Usage: "Restricts network communication to the given IP networks (CIDR masks)",
+ MetricsInfluxDBOrganizationFlag = &cli.StringFlag{
+ Name: "metrics-influxdb.organization",
+ Usage: "InfluxDB organization name (v2 only)",
+ Value: metrics.DefaultConfig.InfluxDBOrganization,
+ Category: flags.MetricsCategory,
}
- // ATM the url is left to the user and deployment to
- JSpathFlag = cli.StringFlag{
- Name: "jspath",
- Usage: "JavaScript root path for `loadScript`",
- Value: ".",
+ // MISC settings
+ RollbackFlag = &cli.StringFlag{
+ Name: "rollback",
+ Usage: "Rollback chain at hash",
+ Value: "",
+ Category: flags.MiscCategory,
+ }
+ AnnounceTxsFlag = &cli.BoolFlag{
+ Name: "announce-txs",
+ Usage: "Always commit transactions",
+ Value: false,
+ Category: flags.MiscCategory,
+ }
+ StoreRewardFlag = &cli.BoolFlag{
+ Name: "store-reward",
+ Usage: "Store reward to file",
+ Value: false,
+ Category: flags.MiscCategory,
+ }
+ RewoundFlag = &cli.IntFlag{
+ Name: "rewound",
+ Usage: "Rewound blocks",
+ Value: 0,
+ Category: flags.MiscCategory,
}
- // Gas price oracle settings
- GpoBlocksFlag = cli.IntFlag{
- Name: "gpoblocks",
- Usage: "Number of recent blocks to check for gas prices",
- Value: ethconfig.Defaults.GPO.Blocks,
+ // XDC settings
+ Enable0xPrefixFlag = &cli.BoolFlag{
+ Name: "enable-0x-prefix",
+ Usage: "Addres use 0x-prefix (Deprecated: this is on by default, to use xdc prefix use --enable-xdc-prefix)",
+ Value: true,
+ Category: flags.XdcCategory,
}
- GpoPercentileFlag = cli.IntFlag{
- Name: "gpopercentile",
- Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
- Value: ethconfig.Defaults.GPO.Percentile,
+ EnableXDCPrefixFlag = &cli.BoolFlag{
+ Name: "enable-xdc-prefix",
+ Usage: "Addres use xdc-prefix (default = false)",
+ Value: false,
+ Category: flags.XdcCategory,
}
- GpoMaxGasPriceFlag = cli.Int64Flag{
- Name: "gpo.maxprice",
- Usage: "Maximum gas price will be recommended by gpo",
- Value: ethconfig.Defaults.GPO.MaxPrice.Int64(),
+ XDCSlaveModeFlag = &cli.BoolFlag{
+ Name: "slave",
+ Usage: "Enable slave mode",
+ Category: flags.XdcCategory,
}
- GpoIgnoreGasPriceFlag = cli.Int64Flag{
- Name: "gpo.ignoreprice",
- Usage: "Gas price below which gpo will ignore transactions",
- Value: ethconfig.Defaults.GPO.IgnorePrice.Int64(),
+
+ // XDCX settings
+ XDCXEnabledFlag = &cli.BoolFlag{
+ Name: "XDCx",
+ Usage: "Enable the XDCX protocol",
+ Category: flags.XdcxCategory,
}
- WhisperEnabledFlag = cli.BoolFlag{
- Name: "shh",
- Usage: "Enable Whisper",
+ XDCXDataDirFlag = &flags.DirectoryFlag{
+ Name: "XDCx-datadir",
+ Aliases: []string{"XDCx.datadir"},
+ Usage: "Data directory for the XDCX databases",
+ Value: flags.DirectoryString(filepath.Join(DataDirFlag.Value.String(), "XDCx")),
+ Category: flags.XdcxCategory,
}
- WhisperMaxMessageSizeFlag = cli.IntFlag{
- Name: "shh.maxmessagesize",
- Usage: "Max message size accepted",
- Value: int(whisper.DefaultMaxMessageSize),
+ XDCXDBEngineFlag = &cli.StringFlag{
+ Name: "XDCx-dbengine",
+ Aliases: []string{"XDCx.dbengine"},
+ Usage: "Database engine for XDCX (leveldb, mongodb)",
+ Value: "leveldb",
+ Category: flags.XdcxCategory,
}
- WhisperMinPOWFlag = cli.Float64Flag{
- Name: "shh.pow",
- Usage: "Minimum POW accepted",
- Value: whisper.DefaultMinimumPoW,
+ XDCXDBNameFlag = &cli.StringFlag{
+ Name: "XDCx-dbName",
+ Aliases: []string{"XDCx.dbName"},
+ Usage: "Database name for XDCX",
+ Value: "XDCdex",
+ Category: flags.XdcxCategory,
}
- XDCXDataDirFlag = DirectoryFlag{
- Name: "XDCx.datadir",
- Usage: "Data directory for the XDCX databases",
- Value: DirectoryString{filepath.Join(DataDirFlag.Value.String(), "XDCx")},
+ XDCXDBConnectionUrlFlag = &cli.StringFlag{
+ Name: "XDCx-dbConnectionUrl",
+ Aliases: []string{"XDCx.dbConnectionUrl"},
+ Usage: "ConnectionUrl to database if dbEngine is mongodb. Host:port. If there are multiple instances, separated by comma. Eg: localhost:27017,localhost:27018",
+ Value: "localhost:27017",
+ Category: flags.XdcxCategory,
}
- XDCXDBEngineFlag = cli.StringFlag{
- Name: "XDCx.dbengine",
- Usage: "Database engine for XDCX (leveldb, mongodb)",
- Value: "leveldb",
- }
- XDCXDBNameFlag = cli.StringFlag{
- Name: "XDCx.dbName",
- Usage: "Database name for XDCX",
- Value: "XDCdex",
- }
- XDCXDBConnectionUrlFlag = cli.StringFlag{
- Name: "XDCx.dbConnectionUrl",
- Usage: "ConnectionUrl to database if dbEngine is mongodb. Host:port. If there are multiple instances, separated by comma. Eg: localhost:27017,localhost:27018",
- Value: "localhost:27017",
- }
- XDCXDBReplicaSetNameFlag = cli.StringFlag{
- Name: "XDCx.dbReplicaSetName",
- Usage: "ReplicaSetName if Master-Slave is setup",
- }
- XDCSlaveModeFlag = cli.BoolFlag{
- Name: "slave",
- Usage: "Enable slave mode",
+ XDCXDBReplicaSetNameFlag = &cli.StringFlag{
+ Name: "XDCx-dbReplicaSetName",
+ Aliases: []string{"XDCx.dbReplicaSetName"},
+ Usage: "ReplicaSetName if Master-Slave is setup",
+ Category: flags.XdcxCategory,
}
)
+// GroupFlags combines the given flag slices together and returns the merged one.
+func GroupFlags(groups ...[]cli.Flag) []cli.Flag {
+ var ret []cli.Flag
+ for _, group := range groups {
+ ret = append(ret, group...)
+ }
+ return ret
+}
+
// MakeDataDir retrieves the currently requested data directory, terminating
// if none (or the empty string) is specified. If the node is starting a testnet,
// the a subdirectory of the specified datadir will be used.
func MakeDataDir(ctx *cli.Context) string {
- if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
- if ctx.GlobalBool(TestnetFlag.Name) {
+ if path := ctx.String(DataDirFlag.Name); path != "" {
+ if ctx.Bool(TestnetFlag.Name) {
return filepath.Join(path, "testnet")
}
- if ctx.GlobalBool(RinkebyFlag.Name) {
+ if ctx.Bool(RinkebyFlag.Name) {
return filepath.Join(path, "rinkeby")
}
return path
@@ -646,8 +845,8 @@ func MakeDataDir(ctx *cli.Context) string {
// method returns nil and an emphemeral key is to be generated.
func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
var (
- hex = ctx.GlobalString(NodeKeyHexFlag.Name)
- file = ctx.GlobalString(NodeKeyFileFlag.Name)
+ hex = ctx.String(NodeKeyHexFlag.Name)
+ file = ctx.String(NodeKeyFileFlag.Name)
key *ecdsa.PrivateKey
err error
)
@@ -669,7 +868,7 @@ func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
// setNodeUserIdent creates the user identifier from CLI flags.
func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
- if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
+ if identity := ctx.String(IdentityFlag.Name); len(identity) > 0 {
cfg.UserIdent = identity
}
}
@@ -679,21 +878,21 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls := []string{}
switch {
- case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name):
- if ctx.GlobalIsSet(BootnodesV4Flag.Name) {
- urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",")
+ case ctx.IsSet(BootnodesFlag.Name) || ctx.IsSet(BootnodesV4Flag.Name):
+ if ctx.IsSet(BootnodesV4Flag.Name) {
+ urls = strings.Split(ctx.String(BootnodesV4Flag.Name), ",")
} else {
- urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
+ urls = strings.Split(ctx.String(BootnodesFlag.Name), ",")
}
- // case ctx.GlobalBool(TestnetFlag.Name):
+ // case ctx.Bool(TestnetFlag.Name):
// urls = params.TestnetBootnodes
- // case ctx.GlobalBool(RinkebyFlag.Name):
+ // case ctx.Bool(RinkebyFlag.Name):
// urls = params.RinkebyBootnodes
case cfg.BootstrapNodes != nil:
return // already set, don't apply defaults.
- case !ctx.GlobalIsSet(BootnodesFlag.Name):
+ case !ctx.IsSet(BootnodesFlag.Name):
urls = params.MainnetBootnodes
- case ctx.GlobalBool(XDCTestnetFlag.Name):
+ case ctx.Bool(XDCTestnetFlag.Name):
urls = params.TestnetBootnodes
}
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
@@ -712,13 +911,13 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls := params.DiscoveryV5Bootnodes
switch {
- case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV5Flag.Name):
- if ctx.GlobalIsSet(BootnodesV5Flag.Name) {
- urls = strings.Split(ctx.GlobalString(BootnodesV5Flag.Name), ",")
+ case ctx.IsSet(BootnodesFlag.Name) || ctx.IsSet(BootnodesV5Flag.Name):
+ if ctx.IsSet(BootnodesV5Flag.Name) {
+ urls = strings.Split(ctx.String(BootnodesV5Flag.Name), ",")
} else {
- urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
+ urls = strings.Split(ctx.String(BootnodesFlag.Name), ",")
}
- case ctx.GlobalBool(RinkebyFlag.Name):
+ case ctx.Bool(RinkebyFlag.Name):
urls = params.RinkebyBootnodes
case cfg.BootstrapNodesV5 != nil:
return // already set, don't apply defaults.
@@ -738,16 +937,16 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
// setListenAddress creates a TCP listening address string from set command
// line flags.
func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
- if ctx.GlobalIsSet(ListenPortFlag.Name) {
- cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
+ if ctx.IsSet(ListenPortFlag.Name) {
+ cfg.ListenAddr = fmt.Sprintf(":%d", ctx.Int(ListenPortFlag.Name))
}
}
// setNAT creates a port mapper from command line flags.
func setNAT(ctx *cli.Context, cfg *p2p.Config) {
- if ctx.GlobalIsSet(NATFlag.Name) {
- log.Info("NAT is setted", "value", ctx.GlobalString(NATFlag.Name))
- natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
+ if ctx.IsSet(NATFlag.Name) {
+ log.Info("NAT is setted", "value", ctx.String(NATFlag.Name))
+ natif, err := nat.Parse(ctx.String(NATFlag.Name))
if err != nil {
Fatalf("Option %s: %v", NATFlag.Name, err)
}
@@ -757,59 +956,68 @@ func setNAT(ctx *cli.Context, cfg *p2p.Config) {
// splitAndTrim splits input separated by a comma
// and trims excessive white space from the substrings.
-func splitAndTrim(input string) []string {
- result := strings.Split(input, ",")
- for i, r := range result {
- result[i] = strings.TrimSpace(r)
+func splitAndTrim(input string) (ret []string) {
+ l := strings.Split(input, ",")
+ for _, r := range l {
+ r = strings.TrimSpace(r)
+ if len(r) > 0 {
+ ret = append(ret, r)
+ }
}
- return result
+ return ret
}
// setHTTP creates the HTTP RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
func setHTTP(ctx *cli.Context, cfg *node.Config) {
- if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" {
+ if ctx.Bool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" {
cfg.HTTPHost = "127.0.0.1"
- if ctx.GlobalIsSet(RPCListenAddrFlag.Name) {
- cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
+ if ctx.IsSet(HTTPListenAddrFlag.Name) {
+ cfg.HTTPHost = ctx.String(HTTPListenAddrFlag.Name)
}
}
- if ctx.GlobalIsSet(RPCPortFlag.Name) {
- cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
+ if ctx.IsSet(HTTPPortFlag.Name) {
+ cfg.HTTPPort = ctx.Int(HTTPPortFlag.Name)
}
- if ctx.GlobalIsSet(RPCHttpWriteTimeoutFlag.Name) {
- cfg.HTTPWriteTimeout = ctx.GlobalDuration(RPCHttpWriteTimeoutFlag.Name)
+ if ctx.IsSet(HTTPReadTimeoutFlag.Name) {
+ cfg.HTTPTimeouts.ReadTimeout = ctx.Duration(HTTPReadTimeoutFlag.Name)
}
- if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
- cfg.HTTPCors = splitAndTrim(ctx.GlobalString(RPCCORSDomainFlag.Name))
+ if ctx.IsSet(HTTPWriteTimeoutFlag.Name) {
+ cfg.HTTPTimeouts.WriteTimeout = ctx.Duration(HTTPWriteTimeoutFlag.Name)
}
- if ctx.GlobalIsSet(RPCApiFlag.Name) {
- cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
+ if ctx.IsSet(HTTPIdleTimeoutFlag.Name) {
+ cfg.HTTPTimeouts.IdleTimeout = ctx.Duration(HTTPIdleTimeoutFlag.Name)
}
- if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) {
- cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
+ if ctx.IsSet(HTTPCORSDomainFlag.Name) {
+ cfg.HTTPCors = splitAndTrim(ctx.String(HTTPCORSDomainFlag.Name))
+ }
+ if ctx.IsSet(HTTPApiFlag.Name) {
+ cfg.HTTPModules = splitAndTrim(ctx.String(HTTPApiFlag.Name))
+ }
+ if ctx.IsSet(HTTPVirtualHostsFlag.Name) {
+ cfg.HTTPVirtualHosts = splitAndTrim(ctx.String(HTTPVirtualHostsFlag.Name))
}
}
// setWS creates the WebSocket RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled.
func setWS(ctx *cli.Context, cfg *node.Config) {
- if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" {
+ if ctx.Bool(WSEnabledFlag.Name) && cfg.WSHost == "" {
cfg.WSHost = "127.0.0.1"
- if ctx.GlobalIsSet(WSListenAddrFlag.Name) {
- cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
+ if ctx.IsSet(WSListenAddrFlag.Name) {
+ cfg.WSHost = ctx.String(WSListenAddrFlag.Name)
}
}
- if ctx.GlobalIsSet(WSPortFlag.Name) {
- cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
+ if ctx.IsSet(WSPortFlag.Name) {
+ cfg.WSPort = ctx.Int(WSPortFlag.Name)
}
- if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) {
- cfg.WSOrigins = splitAndTrim(ctx.GlobalString(WSAllowedOriginsFlag.Name))
+ if ctx.IsSet(WSAllowedOriginsFlag.Name) {
+ cfg.WSOrigins = splitAndTrim(ctx.String(WSAllowedOriginsFlag.Name))
}
- if ctx.GlobalIsSet(WSApiFlag.Name) {
- cfg.WSModules = splitAndTrim(ctx.GlobalString(WSApiFlag.Name))
+ if ctx.IsSet(WSApiFlag.Name) {
+ cfg.WSModules = splitAndTrim(ctx.String(WSApiFlag.Name))
}
}
@@ -818,10 +1026,10 @@ func setWS(ctx *cli.Context, cfg *node.Config) {
func setIPC(ctx *cli.Context, cfg *node.Config) {
checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
switch {
- case ctx.GlobalBool(IPCDisabledFlag.Name):
+ case ctx.Bool(IPCDisabledFlag.Name):
cfg.IPCPath = ""
- case ctx.GlobalIsSet(IPCPathFlag.Name):
- cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name)
+ case ctx.IsSet(IPCPathFlag.Name):
+ cfg.IPCPath = ctx.String(IPCPathFlag.Name)
}
}
@@ -884,10 +1092,10 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
// setEtherbase retrieves the etherbase either from the directly specified
// command line flags or from the keystore if CLI indexed.
func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config) {
- if ctx.GlobalIsSet(EtherbaseFlag.Name) {
- account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name))
+ if ctx.IsSet(MinerEtherbaseFlag.Name) {
+ account, err := MakeAddress(ks, ctx.String(MinerEtherbaseFlag.Name))
if err != nil {
- Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
+ Fatalf("Option %q: %v", MinerEtherbaseFlag.Name, err)
}
cfg.Etherbase = account.Address
}
@@ -895,7 +1103,7 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *ethconfig.Config
// MakePasswordList reads password lines from the file specified by the global --password flag.
func MakePasswordList(ctx *cli.Context) []string {
- path := ctx.GlobalString(PasswordFileFlag.Name)
+ path := ctx.String(PasswordFileFlag.Name)
if path == "" {
return nil
}
@@ -918,20 +1126,20 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setBootstrapNodes(ctx, cfg)
// setBootstrapNodesV5(ctx, cfg)
- lightClient := ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalString(SyncModeFlag.Name) == "light"
- lightServer := ctx.GlobalInt(LightServFlag.Name) != 0
- lightPeers := ctx.GlobalInt(LightPeersFlag.Name)
+ lightClient := ctx.Bool(LightModeFlag.Name) || ctx.String(SyncModeFlag.Name) == "light"
+ lightServer := ctx.Int(LightServFlag.Name) != 0
+ lightPeers := ctx.Int(LightPeersFlag.Name)
- if ctx.GlobalIsSet(MaxPeersFlag.Name) {
- cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
- if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) {
+ if ctx.IsSet(MaxPeersFlag.Name) {
+ cfg.MaxPeers = ctx.Int(MaxPeersFlag.Name)
+ if lightServer && !ctx.IsSet(LightPeersFlag.Name) {
cfg.MaxPeers += lightPeers
}
} else {
if lightServer {
cfg.MaxPeers += lightPeers
}
- if lightClient && ctx.GlobalIsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers {
+ if lightClient && ctx.IsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers {
cfg.MaxPeers = lightPeers
}
}
@@ -944,24 +1152,24 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
}
log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)
- if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) {
- cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name)
+ if ctx.IsSet(MaxPendingPeersFlag.Name) {
+ cfg.MaxPendingPeers = ctx.Int(MaxPendingPeersFlag.Name)
}
- if ctx.GlobalIsSet(NoDiscoverFlag.Name) || lightClient {
+ if ctx.IsSet(NoDiscoverFlag.Name) || lightClient {
cfg.NoDiscovery = true
}
// if we're running a light client or server, force enable the v5 peer discovery
// unless it is explicitly disabled with --nodiscover note that explicitly specifying
// --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
- forceV5Discovery := (lightClient || lightServer) && !ctx.GlobalBool(NoDiscoverFlag.Name)
- if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
- cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
+ forceV5Discovery := (lightClient || lightServer) && !ctx.Bool(NoDiscoverFlag.Name)
+ if ctx.IsSet(DiscoveryV5Flag.Name) {
+ cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name)
} else if forceV5Discovery {
cfg.DiscoveryV5 = true
}
- if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
+ if netrestrict := ctx.String(NetrestrictFlag.Name); netrestrict != "" {
list, err := netutil.ParseNetlist(netrestrict)
if err != nil {
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
@@ -969,7 +1177,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
cfg.NetRestrict = list
}
- if ctx.GlobalBool(DeveloperFlag.Name) {
+ if ctx.Bool(DeveloperFlag.Name) {
// --dev mode can't use p2p networking.
cfg.MaxPeers = 0
cfg.ListenAddr = ":0"
@@ -988,27 +1196,34 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
setPrefix(ctx, cfg)
switch {
- case ctx.GlobalIsSet(DataDirFlag.Name):
- cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
- case ctx.GlobalBool(DeveloperFlag.Name):
+ case ctx.IsSet(DataDirFlag.Name):
+ cfg.DataDir = ctx.String(DataDirFlag.Name)
+ case ctx.Bool(DeveloperFlag.Name):
cfg.DataDir = "" // unless explicitly requested, use memory databases
- case ctx.GlobalBool(TestnetFlag.Name):
+ case ctx.Bool(TestnetFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
- case ctx.GlobalBool(RinkebyFlag.Name):
+ case ctx.Bool(RinkebyFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
}
- if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
- cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name)
+ if ctx.IsSet(KeyStoreDirFlag.Name) {
+ cfg.KeyStoreDir = ctx.String(KeyStoreDirFlag.Name)
}
- if ctx.GlobalIsSet(LightKDFFlag.Name) {
- cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name)
+ if ctx.IsSet(LightKDFFlag.Name) {
+ cfg.UseLightweightKDF = ctx.Bool(LightKDFFlag.Name)
}
- if ctx.GlobalIsSet(NoUSBFlag.Name) {
- cfg.NoUSB = ctx.GlobalBool(NoUSBFlag.Name)
+ if ctx.IsSet(NoUSBFlag.Name) {
+ cfg.NoUSB = ctx.Bool(NoUSBFlag.Name)
}
- if ctx.GlobalIsSet(AnnounceTxsFlag.Name) {
- cfg.AnnounceTxs = ctx.GlobalBool(AnnounceTxsFlag.Name)
+ if ctx.IsSet(AnnounceTxsFlag.Name) {
+ cfg.AnnounceTxs = ctx.Bool(AnnounceTxsFlag.Name)
+ }
+ // deprecation notice for log debug flags (TODO: find a more appropriate place to put these?)
+ if ctx.IsSet(LogBacktraceAtFlag.Name) {
+ log.Warn("log.backtrace flag is deprecated")
+ }
+ if ctx.IsSet(LogDebugFlag.Name) {
+ log.Warn("log.debug flag is deprecated")
}
}
@@ -1016,74 +1231,73 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
// If we are running the light client, apply another group
// settings for gas oracle.
if light {
- cfg.Blocks = ethconfig.LightClientGPO.Blocks
- cfg.Percentile = ethconfig.LightClientGPO.Percentile
+ *cfg = ethconfig.LightClientGPO
}
- if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
- cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
+ if ctx.IsSet(GpoBlocksFlag.Name) {
+ cfg.Blocks = ctx.Int(GpoBlocksFlag.Name)
}
- if ctx.GlobalIsSet(GpoPercentileFlag.Name) {
- cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name)
+ if ctx.IsSet(GpoPercentileFlag.Name) {
+ cfg.Percentile = ctx.Int(GpoPercentileFlag.Name)
}
- if ctx.GlobalIsSet(GpoMaxGasPriceFlag.Name) {
- cfg.MaxPrice = big.NewInt(ctx.GlobalInt64(GpoMaxGasPriceFlag.Name))
+ if ctx.IsSet(GpoMaxGasPriceFlag.Name) {
+ cfg.MaxPrice = big.NewInt(ctx.Int64(GpoMaxGasPriceFlag.Name))
}
- if ctx.GlobalIsSet(GpoIgnoreGasPriceFlag.Name) {
- cfg.IgnorePrice = big.NewInt(ctx.GlobalInt64(GpoIgnoreGasPriceFlag.Name))
+ if ctx.IsSet(GpoIgnoreGasPriceFlag.Name) {
+ cfg.IgnorePrice = big.NewInt(ctx.Int64(GpoIgnoreGasPriceFlag.Name))
}
}
-func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
- if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
- cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
+func setTxPool(ctx *cli.Context, cfg *txpool.Config) {
+ if ctx.IsSet(TxPoolNoLocalsFlag.Name) {
+ cfg.NoLocals = ctx.Bool(TxPoolNoLocalsFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolJournalFlag.Name) {
- cfg.Journal = ctx.GlobalString(TxPoolJournalFlag.Name)
+ if ctx.IsSet(TxPoolJournalFlag.Name) {
+ cfg.Journal = ctx.String(TxPoolJournalFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolRejournalFlag.Name) {
- cfg.Rejournal = ctx.GlobalDuration(TxPoolRejournalFlag.Name)
+ if ctx.IsSet(TxPoolRejournalFlag.Name) {
+ cfg.Rejournal = ctx.Duration(TxPoolRejournalFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolPriceLimitFlag.Name) {
- cfg.PriceLimit = ctx.GlobalUint64(TxPoolPriceLimitFlag.Name)
+ if ctx.IsSet(TxPoolPriceLimitFlag.Name) {
+ cfg.PriceLimit = ctx.Uint64(TxPoolPriceLimitFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolPriceBumpFlag.Name) {
- cfg.PriceBump = ctx.GlobalUint64(TxPoolPriceBumpFlag.Name)
+ if ctx.IsSet(TxPoolPriceBumpFlag.Name) {
+ cfg.PriceBump = ctx.Uint64(TxPoolPriceBumpFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolAccountSlotsFlag.Name) {
- cfg.AccountSlots = ctx.GlobalUint64(TxPoolAccountSlotsFlag.Name)
+ if ctx.IsSet(TxPoolAccountSlotsFlag.Name) {
+ cfg.AccountSlots = ctx.Uint64(TxPoolAccountSlotsFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolGlobalSlotsFlag.Name) {
- cfg.GlobalSlots = ctx.GlobalUint64(TxPoolGlobalSlotsFlag.Name)
+ if ctx.IsSet(TxPoolGlobalSlotsFlag.Name) {
+ cfg.GlobalSlots = ctx.Uint64(TxPoolGlobalSlotsFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolAccountQueueFlag.Name) {
- cfg.AccountQueue = ctx.GlobalUint64(TxPoolAccountQueueFlag.Name)
+ if ctx.IsSet(TxPoolAccountQueueFlag.Name) {
+ cfg.AccountQueue = ctx.Uint64(TxPoolAccountQueueFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolGlobalQueueFlag.Name) {
- cfg.GlobalQueue = ctx.GlobalUint64(TxPoolGlobalQueueFlag.Name)
+ if ctx.IsSet(TxPoolGlobalQueueFlag.Name) {
+ cfg.GlobalQueue = ctx.Uint64(TxPoolGlobalQueueFlag.Name)
}
- if ctx.GlobalIsSet(TxPoolLifetimeFlag.Name) {
- cfg.Lifetime = ctx.GlobalDuration(TxPoolLifetimeFlag.Name)
+ if ctx.IsSet(TxPoolLifetimeFlag.Name) {
+ cfg.Lifetime = ctx.Duration(TxPoolLifetimeFlag.Name)
}
}
func setEthash(ctx *cli.Context, cfg *ethconfig.Config) {
- if ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
- cfg.Ethash.CacheDir = ctx.GlobalString(EthashCacheDirFlag.Name)
+ if ctx.IsSet(EthashCacheDirFlag.Name) {
+ cfg.Ethash.CacheDir = ctx.String(EthashCacheDirFlag.Name)
}
- if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
- cfg.Ethash.DatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name)
+ if ctx.IsSet(EthashDatasetDirFlag.Name) {
+ cfg.Ethash.DatasetDir = ctx.String(EthashDatasetDirFlag.Name)
}
- if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) {
- cfg.Ethash.CachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name)
+ if ctx.IsSet(EthashCachesInMemoryFlag.Name) {
+ cfg.Ethash.CachesInMem = ctx.Int(EthashCachesInMemoryFlag.Name)
}
- if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) {
- cfg.Ethash.CachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name)
+ if ctx.IsSet(EthashCachesOnDiskFlag.Name) {
+ cfg.Ethash.CachesOnDisk = ctx.Int(EthashCachesOnDiskFlag.Name)
}
- if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) {
- cfg.Ethash.DatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name)
+ if ctx.IsSet(EthashDatasetsInMemoryFlag.Name) {
+ cfg.Ethash.DatasetsInMem = ctx.Int(EthashDatasetsInMemoryFlag.Name)
}
- if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) {
- cfg.Ethash.DatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name)
+ if ctx.IsSet(EthashDatasetsOnDiskFlag.Name) {
+ cfg.Ethash.DatasetsOnDisk = ctx.Int(EthashDatasetsOnDiskFlag.Name)
}
}
@@ -1099,13 +1313,13 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) {
panic(fmt.Sprintf("invalid argument, not cli.Flag type: %T", args[i]))
}
// Check if next arg extends current and expand its name if so
- name := flag.GetName()
+ name := flag.Names()[0]
if i+1 < len(args) {
switch option := args[i+1].(type) {
case string:
// Extended flag, expand the name and shift the arguments
- if ctx.GlobalString(flag.GetName()) == option {
+ if ctx.String(flag.Names()[0]) == option {
name += "=" + option
}
i++
@@ -1116,7 +1330,7 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) {
}
}
// Mark the flag if it's set
- if ctx.GlobalIsSet(flag.GetName()) {
+ if ctx.IsSet(flag.Names()[0]) {
set = append(set, "--"+name)
}
}
@@ -1125,19 +1339,9 @@ func checkExclusive(ctx *cli.Context, args ...interface{}) {
}
}
-// SetShhConfig applies shh-related command line flags to the config.
-func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
- if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) {
- cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name))
- }
- if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
- cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
- }
-}
-
func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) {
- if ctx.GlobalIsSet(XDCXDataDirFlag.Name) {
- cfg.DataDir = ctx.GlobalString(XDCXDataDirFlag.Name)
+ if ctx.IsSet(XDCXDataDirFlag.Name) {
+ cfg.DataDir = ctx.String(XDCXDataDirFlag.Name)
} else {
// default XDCx datadir: DATADIR/XDCx
defaultXDCXDataDir := filepath.Join(XDCDataDir, "XDCx")
@@ -1151,23 +1355,23 @@ func SetXDCXConfig(ctx *cli.Context, cfg *XDCx.Config, XDCDataDir string) {
}
}
log.Info("XDCX datadir", "path", cfg.DataDir)
- if ctx.GlobalIsSet(XDCXDBEngineFlag.Name) {
- cfg.DBEngine = ctx.GlobalString(XDCXDBEngineFlag.Name)
+ if ctx.IsSet(XDCXDBEngineFlag.Name) {
+ cfg.DBEngine = ctx.String(XDCXDBEngineFlag.Name)
} else {
cfg.DBEngine = XDCXDBEngineFlag.Value
}
- if ctx.GlobalIsSet(XDCXDBNameFlag.Name) {
- cfg.DBName = ctx.GlobalString(XDCXDBNameFlag.Name)
+ if ctx.IsSet(XDCXDBNameFlag.Name) {
+ cfg.DBName = ctx.String(XDCXDBNameFlag.Name)
} else {
cfg.DBName = XDCXDBNameFlag.Value
}
- if ctx.GlobalIsSet(XDCXDBConnectionUrlFlag.Name) {
- cfg.ConnectionUrl = ctx.GlobalString(XDCXDBConnectionUrlFlag.Name)
+ if ctx.IsSet(XDCXDBConnectionUrlFlag.Name) {
+ cfg.ConnectionUrl = ctx.String(XDCXDBConnectionUrlFlag.Name)
} else {
cfg.ConnectionUrl = XDCXDBConnectionUrlFlag.Value
}
- if ctx.GlobalIsSet(XDCXDBReplicaSetNameFlag.Name) {
- cfg.ReplicaSetName = ctx.GlobalString(XDCXDBReplicaSetNameFlag.Name)
+ if ctx.IsSet(XDCXDBReplicaSetNameFlag.Name) {
+ cfg.ReplicaSetName = ctx.String(XDCXDBReplicaSetNameFlag.Name)
}
}
@@ -1181,7 +1385,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
setEtherbase(ctx, ks, cfg)
- setGPO(ctx, &cfg.GPO, ctx.GlobalString(SyncModeFlag.Name) == "light")
+ setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light")
setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg)
@@ -1206,67 +1410,72 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
godebug.SetGCPercent(int(gogc))
switch {
- case ctx.GlobalIsSet(SyncModeFlag.Name):
- cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
- case ctx.GlobalBool(FastSyncFlag.Name):
+ case ctx.IsSet(SyncModeFlag.Name):
+ if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil {
+ Fatalf("invalid --syncmode flag: %v", err)
+ }
+ case ctx.Bool(FastSyncFlag.Name):
cfg.SyncMode = downloader.FastSync
- case ctx.GlobalBool(LightModeFlag.Name):
+ case ctx.Bool(LightModeFlag.Name):
cfg.SyncMode = downloader.LightSync
}
- if ctx.GlobalIsSet(LightServFlag.Name) {
- cfg.LightServ = ctx.GlobalInt(LightServFlag.Name)
+ if ctx.IsSet(LightServFlag.Name) {
+ cfg.LightServ = ctx.Int(LightServFlag.Name)
}
- if ctx.GlobalIsSet(LightPeersFlag.Name) {
- cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
+ if ctx.IsSet(LightPeersFlag.Name) {
+ cfg.LightPeers = ctx.Int(LightPeersFlag.Name)
}
- if ctx.GlobalIsSet(NetworkIdFlag.Name) {
- cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
+ if ctx.IsSet(NetworkIdFlag.Name) {
+ cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name)
}
- if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
- cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
+ if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheDatabaseFlag.Name) {
+ cfg.DatabaseCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
}
- cfg.DatabaseHandles = MakeDatabaseHandles(ctx.GlobalInt(FDLimitFlag.Name))
+ cfg.DatabaseHandles = MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name))
- if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
+ if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
- cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
+ cfg.NoPruning = ctx.String(GCModeFlag.Name) == "archive"
- if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
- cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
+ if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) {
+ cfg.TrieCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
}
- if ctx.GlobalIsSet(StakerThreadsFlag.Name) {
- cfg.MinerThreads = ctx.GlobalInt(StakerThreadsFlag.Name)
+ if ctx.IsSet(MinerThreadsFlag.Name) {
+ cfg.MinerThreads = ctx.Int(MinerThreadsFlag.Name)
}
- if ctx.GlobalIsSet(DocRootFlag.Name) {
- cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name)
+ if ctx.IsSet(DocRootFlag.Name) {
+ cfg.DocRoot = ctx.String(DocRootFlag.Name)
}
- if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) {
- cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name)
+ if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
+ cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
}
- if ctx.GlobalIsSet(RPCGlobalTxFeeCap.Name) {
- cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCap.Name)
+ if ctx.IsSet(RPCGlobalTxFeeCap.Name) {
+ cfg.RPCTxFeeCap = ctx.Float64(RPCGlobalTxFeeCap.Name)
}
- if ctx.GlobalIsSet(ExtraDataFlag.Name) {
- cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name))
+ if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
+ cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
}
- if ctx.GlobalIsSet(GasPriceFlag.Name) {
- cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name)
+ if ctx.IsSet(MinerExtraDataFlag.Name) {
+ cfg.ExtraData = []byte(ctx.String(MinerExtraDataFlag.Name))
+ }
+ if ctx.IsSet(MinerGasPriceFlag.Name) {
+ cfg.GasPrice = flags.GlobalBig(ctx, MinerGasPriceFlag.Name)
}
if ctx.IsSet(CacheLogSizeFlag.Name) {
cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name)
}
- if ctx.GlobalIsSet(VMEnableDebugFlag.Name) {
+ if ctx.IsSet(VMEnableDebugFlag.Name) {
// TODO(fjl): force-enable this in --dev mode
- cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
+ cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
}
if cfg.RPCGasCap != 0 {
log.Info("Set global gas cap", "cap", cfg.RPCGasCap)
} else {
log.Info("Global gas cap disabled")
}
- if ctx.GlobalIsSet(StoreRewardFlag.Name) {
+ if ctx.IsSet(StoreRewardFlag.Name) {
common.StoreRewardFolder = filepath.Join(stack.DataDir(), "XDC", "rewards")
if _, err := os.Stat(common.StoreRewardFolder); os.IsNotExist(err) {
os.Mkdir(common.StoreRewardFolder, os.ModePerm)
@@ -1274,17 +1483,17 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
}
// Override any default configs for hard coded networks.
switch {
- case ctx.GlobalBool(TestnetFlag.Name):
- if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
+ case ctx.Bool(TestnetFlag.Name):
+ if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 3
}
cfg.Genesis = core.DefaultTestnetGenesisBlock()
- case ctx.GlobalBool(RinkebyFlag.Name):
- if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
+ case ctx.Bool(RinkebyFlag.Name):
+ if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 4
}
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
- case ctx.GlobalBool(DeveloperFlag.Name):
+ case ctx.Bool(DeveloperFlag.Name):
// Create new developer account or reuse existing one
var (
developer accounts.Account
@@ -1303,28 +1512,102 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
}
log.Info("Using developer account", "address", developer.Address)
- cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
- if !ctx.GlobalIsSet(GasPriceFlag.Name) {
+ cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), developer.Address)
+ if !ctx.IsSet(MinerGasPriceFlag.Name) {
cfg.GasPrice = big.NewInt(1)
}
}
- // TODO(fjl): move trie cache generations into config
+ // Set any dangling config values
+ if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
+ Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
+ }
+ log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
+ if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
+ Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
+ }
}
// SetupNetwork configures the system for either the main net or some test network.
func SetupNetwork(ctx *cli.Context) {
// TODO(fjl): move target gas limit into config
- params.TargetGasLimit = ctx.GlobalUint64(TargetGasLimitFlag.Name)
+ params.TargetGasLimit = ctx.Uint64(MinerGasLimitFlag.Name)
+}
+
+// SetupMetrics configures the metrics system.
+func SetupMetrics(cfg *metrics.Config) {
+ if !cfg.Enabled {
+ return
+ }
+ log.Info("Enabling metrics collection")
+ metrics.Enable()
+
+ // InfluxDB exporter.
+ var (
+ enableExport = cfg.EnableInfluxDB
+ enableExportV2 = cfg.EnableInfluxDBV2
+ )
+ if cfg.EnableInfluxDB && cfg.EnableInfluxDBV2 {
+ Fatalf("Flags %v can't be used at the same time", strings.Join([]string{MetricsEnableInfluxDBFlag.Name, MetricsEnableInfluxDBV2Flag.Name}, ", "))
+ }
+ var (
+ endpoint = cfg.InfluxDBEndpoint
+ database = cfg.InfluxDBDatabase
+ username = cfg.InfluxDBUsername
+ password = cfg.InfluxDBPassword
+
+ token = cfg.InfluxDBToken
+ bucket = cfg.InfluxDBBucket
+ organization = cfg.InfluxDBOrganization
+ tagsMap = SplitTagsFlag(cfg.InfluxDBTags)
+ )
+ if enableExport {
+ log.Info("Enabling metrics export to InfluxDB")
+ go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
+ } else if enableExportV2 {
+ tagsMap := SplitTagsFlag(cfg.InfluxDBTags)
+ log.Info("Enabling metrics export to InfluxDB (v2)")
+ go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap)
+ }
+
+ // Expvar exporter.
+ if cfg.HTTP != "" {
+ address := net.JoinHostPort(cfg.HTTP, fmt.Sprintf("%d", cfg.Port))
+ log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address)
+ exp.Setup(address)
+ } else if cfg.HTTP == "" && cfg.Port != 0 {
+ log.Warn(fmt.Sprintf("--%s specified without --%s, metrics server will not start.", MetricsPortFlag.Name, MetricsHTTPFlag.Name))
+ }
+
+ // Enable system metrics collection.
+ go metrics.CollectProcessMetrics(3 * time.Second)
+}
+
+// SplitTagsFlag parses a comma-separated list of k=v metrics tags.
+func SplitTagsFlag(tagsFlag string) map[string]string {
+ tags := strings.Split(tagsFlag, ",")
+ tagsMap := map[string]string{}
+
+ for _, t := range tags {
+ if t != "" {
+ kv := strings.Split(t, "=")
+
+ if len(kv) == 2 {
+ tagsMap[kv[0]] = kv[1]
+ }
+ }
+ }
+
+ return tagsMap
}
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var (
- cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
- handles = MakeDatabaseHandles(ctx.GlobalInt(FDLimitFlag.Name))
+ cache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
+ handles = MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name))
)
name := "chaindata"
- if ctx.GlobalBool(LightModeFlag.Name) {
+ if ctx.Bool(LightModeFlag.Name) {
name = "lightchaindata"
}
chainDb, err := stack.OpenDatabase(name, cache, handles, "")
@@ -1337,11 +1620,11 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
func MakeGenesis(ctx *cli.Context) *core.Genesis {
var genesis *core.Genesis
switch {
- case ctx.GlobalBool(TestnetFlag.Name):
+ case ctx.Bool(TestnetFlag.Name):
genesis = core.DefaultTestnetGenesisBlock()
- case ctx.GlobalBool(RinkebyFlag.Name):
+ case ctx.Bool(RinkebyFlag.Name):
genesis = core.DefaultRinkebyGenesisBlock()
- case ctx.GlobalBool(DeveloperFlag.Name):
+ case ctx.Bool(DeveloperFlag.Name):
Fatalf("Developer chains are ephemeral")
}
return genesis
@@ -1361,7 +1644,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
engine = XDPoS.New(config, chainDb)
} else {
engine = ethash.NewFaker()
- if !ctx.GlobalBool(FakePoWFlag.Name) {
+ if !ctx.Bool(FakePoWFlag.Name) {
engine = ethash.New(ethash.Config{
CacheDir: stack.ResolvePath(ethconfig.Defaults.Ethash.CacheDir),
CachesInMem: ethconfig.Defaults.Ethash.CachesInMem,
@@ -1373,18 +1656,18 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
}
Fatalf("Only support XDPoS consensus")
}
- if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
+ if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
cache := &core.CacheConfig{
- Disabled: ctx.GlobalString(GCModeFlag.Name) == "archive",
+ Disabled: ctx.String(GCModeFlag.Name) == "archive",
TrieNodeLimit: ethconfig.Defaults.TrieCache,
TrieTimeLimit: ethconfig.Defaults.TrieTimeout,
}
- if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
- cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
+ if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) {
+ cache.TrieNodeLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
}
- vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
+ vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
if err != nil {
Fatalf("Can't create BlockChain: %v", err)
@@ -1396,43 +1679,19 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
// scripts to preload before starting.
func MakeConsolePreloads(ctx *cli.Context) []string {
// Skip preloading if there's nothing to preload
- if ctx.GlobalString(PreloadJSFlag.Name) == "" {
+ if ctx.String(PreloadJSFlag.Name) == "" {
return nil
}
// Otherwise resolve absolute paths and return them
preloads := []string{}
- assets := ctx.GlobalString(JSpathFlag.Name)
- for _, file := range strings.Split(ctx.GlobalString(PreloadJSFlag.Name), ",") {
+ assets := ctx.String(JSpathFlag.Name)
+ for _, file := range strings.Split(ctx.String(PreloadJSFlag.Name), ",") {
preloads = append(preloads, common.AbsolutePath(assets, strings.TrimSpace(file)))
}
return preloads
}
-// MigrateFlags sets the global flag from a local flag when it's set.
-// This is a temporary function used for migrating old command/flags to the
-// new format.
-//
-// e.g. XDC account new --keystore /tmp/mykeystore --lightkdf
-//
-// is equivalent after calling this method with:
-//
-// XDC --keystore /tmp/mykeystore --lightkdf account new
-//
-// This allows the use of the existing configuration functionality.
-// When all flags are migrated this function can be removed and the existing
-// configuration functionality must be changed that is uses local flags
-func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error {
- return func(ctx *cli.Context) error {
- for _, name := range ctx.FlagNames() {
- if ctx.IsSet(name) {
- ctx.GlobalSet(name, ctx.String(name))
- }
- }
- return action(ctx)
- }
-}
-
// find all filenames match the given pattern in the given root directory
func WalkMatch(root, pattern string) ([]string, error) {
matches := []string{}
@@ -1468,15 +1727,3 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
}})
return filterSystem
}
-
-func SetupMetrics(ctx *cli.Context) {
- if metrics.Enabled {
- log.Info("Enabling metrics collection")
-
- if ctx.GlobalIsSet(MetricsHTTPFlag.Name) {
- address := fmt.Sprintf("%s:%d", ctx.GlobalString(MetricsHTTPFlag.Name), ctx.GlobalInt(MetricsPortFlag.Name))
- log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address)
- exp.Setup(address)
- }
- }
-}
diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go
new file mode 100644
index 0000000000..afd1e0e8bb
--- /dev/null
+++ b/cmd/utils/flags_legacy.go
@@ -0,0 +1,90 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package utils
+
+import (
+ "fmt"
+
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
+ "github.com/urfave/cli/v2"
+)
+
+var ShowDeprecated = &cli.Command{
+ Action: showDeprecated,
+ Name: "show-deprecated-flags",
+ Usage: "Show flags that have been deprecated",
+ ArgsUsage: " ",
+ Description: "Show flags that have been deprecated and will soon be removed",
+}
+
+var DeprecatedFlags = []cli.Flag{
+ FastSyncFlag,
+ LightModeFlag,
+ NoUSBFlag,
+ LogBacktraceAtFlag,
+ LogDebugFlag,
+}
+
+var (
+ FastSyncFlag = &cli.BoolFlag{
+ Name: "fast",
+ Usage: "Enable fast syncing through state downloads",
+ Category: flags.DeprecatedCategory,
+ }
+ LightModeFlag = &cli.BoolFlag{
+ Name: "light",
+ Usage: "Enable light client mode",
+ Category: flags.DeprecatedCategory,
+ }
+ // Deprecated May 2020, shown in aliased flags section
+ NoUSBFlag = &cli.BoolFlag{
+ Name: "nousb",
+ Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)",
+ Category: flags.DeprecatedCategory,
+ }
+ // Deprecated November 2023
+ LogBacktraceAtFlag = &cli.StringFlag{
+ Name: "log-backtrace",
+ Usage: "Request a stack trace at a specific logging statement (deprecated)",
+ Value: "",
+ Category: flags.DeprecatedCategory,
+ }
+ LogDebugFlag = &cli.BoolFlag{
+ Name: "log-debug",
+ Usage: "Prepends log messages with call-site location (deprecated)",
+ Category: flags.DeprecatedCategory,
+ }
+ // Deprecated February 2024
+ MetricsEnabledExpensiveFlag = &cli.BoolFlag{
+ Name: "metrics-expensive",
+ Usage: "Enable expensive metrics collection and reporting (deprecated)",
+ Category: flags.DeprecatedCategory,
+ }
+)
+
+// showDeprecated displays deprecated flags that will be soon removed from the codebase.
+func showDeprecated(*cli.Context) error {
+ fmt.Println("--------------------------------------------------------------------")
+ fmt.Println("The following flags are deprecated and will be removed in the future!")
+ fmt.Println("--------------------------------------------------------------------")
+ fmt.Println()
+ for _, flag := range DeprecatedFlags {
+ fmt.Println(flag.String())
+ }
+ fmt.Println()
+ return nil
+}
diff --git a/cmd/utils/flags_test.go b/cmd/utils/flags_test.go
index adbf25b45b..728cbb7070 100644
--- a/cmd/utils/flags_test.go
+++ b/cmd/utils/flags_test.go
@@ -1,3 +1,20 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+// Package utils contains internal helper functions for go-ethereum commands.
package utils
import (
@@ -8,6 +25,49 @@ import (
"testing"
)
+func Test_SplitTagsFlag(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ args string
+ want map[string]string
+ }{
+ {
+ "2 tags case",
+ "host=localhost,bzzkey=123",
+ map[string]string{
+ "host": "localhost",
+ "bzzkey": "123",
+ },
+ },
+ {
+ "1 tag case",
+ "host=localhost123",
+ map[string]string{
+ "host": "localhost123",
+ },
+ },
+ {
+ "empty case",
+ "",
+ map[string]string{},
+ },
+ {
+ "garbage",
+ "smth=smthelse=123",
+ map[string]string{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ if got := SplitTagsFlag(tt.args); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("splitTagsFlag() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
func TestWalkMatch(t *testing.T) {
type args struct {
root string
diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go
index a329fc8c4d..5afa50c32b 100644
--- a/cmd/utils/utils.go
+++ b/cmd/utils/utils.go
@@ -1,6 +1,10 @@
package utils
import (
+ "fmt"
+ "runtime"
+ "strings"
+
"github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
"github.com/XinFinOrg/XDPoSChain/eth"
@@ -8,12 +12,12 @@ import (
"github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
"github.com/XinFinOrg/XDPoSChain/ethstats"
"github.com/XinFinOrg/XDPoSChain/les"
+ "github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/node"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
)
// RegisterEthService adds an Ethereum client to the stack.
-func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) {
+func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, version string) {
var err error
if cfg.SyncMode == downloader.LightSync {
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
@@ -30,6 +34,21 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) {
ls, _ := les.NewLesServer(fullNode, cfg)
fullNode.AddLesServer(ls)
}
+
+ // TODO: move the following code to function makeFullNode
+ // Ref: #21105, #22641, #23761, #24877
+ // Create gauge with geth system and build information
+ var protos []string
+ for _, p := range fullNode.Protocols() {
+ protos = append(protos, fmt.Sprintf("%v/%d", p.Name, p.Version))
+ }
+ metrics.NewRegisteredGaugeInfo("xdc/info", nil).Update(metrics.GaugeInfoValue{
+ "arch": runtime.GOARCH,
+ "os": runtime.GOOS,
+ "version": version, // cfg.Node.Version
+ "eth_protocols": strings.Join(protos, ","),
+ })
+
return fullNode, err
})
}
@@ -38,15 +57,6 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) {
}
}
-// RegisterShhService configures Whisper and adds it to the given node.
-func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
- if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {
- return whisper.New(cfg), nil
- }); err != nil {
- Fatalf("Failed to register the Whisper service: %v", err)
- }
-}
-
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to the node.
func RegisterEthStatsService(stack *node.Node, url string) {
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go
deleted file mode 100644
index 5fa29ab96c..0000000000
--- a/cmd/wnode/main.go
+++ /dev/null
@@ -1,773 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
-
-// This is a simple Whisper node. It could be used as a stand-alone bootstrap node.
-// Also, could be used for different test and diagnostics purposes.
-
-package main
-
-import (
- "bufio"
- "crypto/ecdsa"
- crand "crypto/rand"
- "crypto/sha512"
- "encoding/binary"
- "encoding/hex"
- "flag"
- "fmt"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/console"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/p2p/discover"
- "github.com/XinFinOrg/XDPoSChain/p2p/nat"
- "github.com/XinFinOrg/XDPoSChain/whisper/mailserver"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
- "golang.org/x/crypto/pbkdf2"
-)
-
-const quitCommand = "~Q"
-const entropySize = 32
-
-// singletons
-var (
- server *p2p.Server
- shh *whisper.Whisper
- done chan struct{}
- mailServer mailserver.WMailServer
- entropy [entropySize]byte
-
- input = bufio.NewReader(os.Stdin)
-)
-
-// encryption
-var (
- symKey []byte
- pub *ecdsa.PublicKey
- asymKey *ecdsa.PrivateKey
- nodeid *ecdsa.PrivateKey
- topic whisper.TopicType
-
- asymKeyID string
- asymFilterID string
- symFilterID string
- symPass string
- msPassword string
-)
-
-// cmd arguments
-var (
- bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections")
- forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages")
- mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
- requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
- asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
- generateKey = flag.Bool("generatekey", false, "generate and show the private key")
- fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
- fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text")
- testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)")
- echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
-
- argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
- argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
- argWorkTime = flag.Uint("work", 5, "work time in seconds")
- argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message")
- argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
- argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
-
- argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
- argPub = flag.String("pub", "", "public key for asymmetric encryption")
- argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
- argIDFile = flag.String("idfile", "", "file name with node id (private key)")
- argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
- argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
- argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files")
-)
-
-func main() {
- processArgs()
- initialize()
- run()
- shutdown()
-}
-
-func processArgs() {
- flag.Parse()
-
- if len(*argIDFile) > 0 {
- var err error
- nodeid, err = crypto.LoadECDSA(*argIDFile)
- if err != nil {
- utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err)
- }
- }
-
- const enodePrefix = "enode://"
- if len(*argEnode) > 0 {
- if (*argEnode)[:len(enodePrefix)] != enodePrefix {
- *argEnode = enodePrefix + *argEnode
- }
- }
-
- if len(*argTopic) > 0 {
- x, err := hex.DecodeString(*argTopic)
- if err != nil {
- utils.Fatalf("Failed to parse the topic: %s", err)
- }
- topic = whisper.BytesToTopic(x)
- }
-
- if *asymmetricMode && len(*argPub) > 0 {
- var err error
- if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
- utils.Fatalf("invalid public key")
- }
- }
-
- if len(*argSaveDir) > 0 {
- if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {
- utils.Fatalf("Download directory '%s' does not exist", *argSaveDir)
- }
- } else if *fileExMode {
- utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode")
- }
-
- if *echoMode {
- echo()
- }
-}
-
-func echo() {
- fmt.Printf("ttl = %d \n", *argTTL)
- fmt.Printf("workTime = %d \n", *argWorkTime)
- fmt.Printf("pow = %f \n", *argPoW)
- fmt.Printf("mspow = %f \n", *argServerPoW)
- fmt.Printf("ip = %s \n", *argIP)
- fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
- fmt.Printf("idfile = %s \n", *argIDFile)
- fmt.Printf("dbpath = %s \n", *argDBPath)
- fmt.Printf("boot = %s \n", *argEnode)
-}
-
-func initialize() {
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
-
- done = make(chan struct{})
- var peers []*discover.Node
- var err error
-
- if *generateKey {
- key, err := crypto.GenerateKey()
- if err != nil {
- utils.Fatalf("Failed to generate private key: %s", err)
- }
- k := hex.EncodeToString(crypto.FromECDSA(key))
- fmt.Printf("Random private key: %s \n", k)
- os.Exit(0)
- }
-
- if *testMode {
- symPass = "wwww" // ascii code: 0x77777777
- msPassword = "wwww"
- }
-
- if *bootstrapMode {
- if len(*argIP) == 0 {
- argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
- }
- } else if *fileReader {
- *bootstrapMode = true
- } else {
- if len(*argEnode) == 0 {
- argEnode = scanLineA("Please enter the peer's enode: ")
- }
- peer := discover.MustParseNode(*argEnode)
- peers = append(peers, peer)
- }
-
- if *mailServerMode {
- if len(msPassword) == 0 {
- msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
- if err != nil {
- utils.Fatalf("Failed to read Mail Server password: %s", err)
- }
- }
- }
-
- cfg := &whisper.Config{
- MaxMessageSize: uint32(*argMaxSize),
- MinimumAcceptedPOW: *argPoW,
- }
-
- shh = whisper.New(cfg)
-
- if *argPoW != whisper.DefaultMinimumPoW {
- err := shh.SetMinimumPoW(*argPoW)
- if err != nil {
- utils.Fatalf("Failed to set PoW: %s", err)
- }
- }
-
- if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {
- err := shh.SetMaxMessageSize(uint32(*argMaxSize))
- if err != nil {
- utils.Fatalf("Failed to set max message size: %s", err)
- }
- }
-
- asymKeyID, err = shh.NewKeyPair()
- if err != nil {
- utils.Fatalf("Failed to generate a new key pair: %s", err)
- }
-
- asymKey, err = shh.GetPrivateKey(asymKeyID)
- if err != nil {
- utils.Fatalf("Failed to retrieve a new key pair: %s", err)
- }
-
- if nodeid == nil {
- tmpID, err := shh.NewKeyPair()
- if err != nil {
- utils.Fatalf("Failed to generate a new key pair: %s", err)
- }
-
- nodeid, err = shh.GetPrivateKey(tmpID)
- if err != nil {
- utils.Fatalf("Failed to retrieve a new key pair: %s", err)
- }
- }
-
- maxPeers := 80
- if *bootstrapMode {
- maxPeers = 800
- }
-
- _, err = crand.Read(entropy[:])
- if err != nil {
- utils.Fatalf("crypto/rand failed: %s", err)
- }
-
- if *mailServerMode {
- shh.RegisterServer(&mailServer)
- mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
- }
-
- server = &p2p.Server{
- Config: p2p.Config{
- PrivateKey: nodeid,
- MaxPeers: maxPeers,
- Name: common.MakeName("wnode", "6.0"),
- Protocols: shh.Protocols(),
- ListenAddr: *argIP,
- NAT: nat.Any(),
- BootstrapNodes: peers,
- StaticNodes: peers,
- TrustedNodes: peers,
- },
- }
-}
-
-func startServer() error {
- err := server.Start()
- if err != nil {
- fmt.Printf("Failed to start Whisper peer: %s.", err)
- return err
- }
-
- fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
- fmt.Println(server.NodeInfo().Enode)
-
- if *bootstrapMode {
- configureNode()
- fmt.Println("Bootstrap Whisper node started")
- } else {
- fmt.Println("Whisper node started")
- // first see if we can establish connection, then ask for user input
- waitForConnection(true)
- configureNode()
- }
-
- if *fileExMode {
- fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand)
- } else if *fileReader {
- fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand)
- } else if !*forwarderMode {
- fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
- }
- return nil
-}
-
-func isKeyValid(k *ecdsa.PublicKey) bool {
- return k.X != nil && k.Y != nil
-}
-
-func configureNode() {
- var err error
- var p2pAccept bool
-
- if *forwarderMode {
- return
- }
-
- if *asymmetricMode {
- if len(*argPub) == 0 {
- s := scanLine("Please enter the peer's public key: ")
- b := common.FromHex(s)
- if b == nil {
- utils.Fatalf("Error: can not convert hexadecimal string")
- }
- if pub, err = crypto.UnmarshalPubkey(b); err != nil {
- utils.Fatalf("Error: invalid peer public key")
- }
- }
- }
-
- if *requestMail {
- p2pAccept = true
- if len(msPassword) == 0 {
- msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
- if err != nil {
- utils.Fatalf("Failed to read Mail Server password: %s", err)
- }
- }
- }
-
- if !*asymmetricMode && !*forwarderMode {
- if len(symPass) == 0 {
- symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ")
- if err != nil {
- utils.Fatalf("Failed to read passphrase: %v", err)
- }
- }
-
- symKeyID, err := shh.AddSymKeyFromPassword(symPass)
- if err != nil {
- utils.Fatalf("Failed to create symmetric key: %s", err)
- }
- symKey, err = shh.GetSymKey(symKeyID)
- if err != nil {
- utils.Fatalf("Failed to save symmetric key: %s", err)
- }
- if len(*argTopic) == 0 {
- generateTopic([]byte(symPass))
- }
-
- fmt.Printf("Filter is configured for the topic: %x \n", topic)
- }
-
- if *mailServerMode {
- if len(*argDBPath) == 0 {
- argDBPath = scanLineA("Please enter the path to DB file: ")
- }
- }
-
- symFilter := whisper.Filter{
- KeySym: symKey,
- Topics: [][]byte{topic[:]},
- AllowP2P: p2pAccept,
- }
- symFilterID, err = shh.Subscribe(&symFilter)
- if err != nil {
- utils.Fatalf("Failed to install filter: %s", err)
- }
-
- asymFilter := whisper.Filter{
- KeyAsym: asymKey,
- Topics: [][]byte{topic[:]},
- AllowP2P: p2pAccept,
- }
- asymFilterID, err = shh.Subscribe(&asymFilter)
- if err != nil {
- utils.Fatalf("Failed to install filter: %s", err)
- }
-}
-
-func generateTopic(password []byte) {
- x := pbkdf2.Key(password, password, 4096, 128, sha512.New)
- for i := 0; i < len(x); i++ {
- topic[i%whisper.TopicLength] ^= x[i]
- }
-}
-
-func waitForConnection(timeout bool) {
- var cnt int
- var connected bool
- for !connected {
- time.Sleep(time.Millisecond * 50)
- connected = server.PeerCount() > 0
- if timeout {
- cnt++
- if cnt > 1000 {
- utils.Fatalf("Timeout expired, failed to connect")
- }
- }
- }
-
- fmt.Println("Connected to peer.")
-}
-
-func run() {
- err := startServer()
- if err != nil {
- return
- }
- defer server.Stop()
- shh.Start(nil)
- defer shh.Stop()
-
- if !*forwarderMode {
- go messageLoop()
- }
-
- if *requestMail {
- requestExpiredMessagesLoop()
- } else if *fileExMode {
- sendFilesLoop()
- } else if *fileReader {
- fileReaderLoop()
- } else {
- sendLoop()
- }
-}
-
-func shutdown() {
- close(done)
- mailServer.Close()
-}
-
-func sendLoop() {
- for {
- s := scanLine("")
- if s == quitCommand {
- fmt.Println("Quit command received")
- return
- }
- sendMsg([]byte(s))
- if *asymmetricMode {
- // print your own message for convenience,
- // because in asymmetric mode it is impossible to decrypt it
- timestamp := time.Now().Unix()
- from := crypto.PubkeyToAddress(asymKey.PublicKey)
- fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
- }
- }
-}
-
-func sendFilesLoop() {
- for {
- s := scanLine("")
- if s == quitCommand {
- fmt.Println("Quit command received")
- return
- }
- b, err := os.ReadFile(s)
- if err != nil {
- fmt.Printf(">>> Error: %s \n", err)
- } else {
- h := sendMsg(b)
- if (h == common.Hash{}) {
- fmt.Printf(">>> Error: message was not sent \n")
- } else {
- timestamp := time.Now().Unix()
- from := crypto.PubkeyToAddress(asymKey.PublicKey)
- fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h)
- }
- }
- }
-}
-
-func fileReaderLoop() {
- watcher1 := shh.GetFilter(symFilterID)
- watcher2 := shh.GetFilter(asymFilterID)
- if watcher1 == nil && watcher2 == nil {
- fmt.Println("Error: neither symmetric nor asymmetric filter is installed")
- return
- }
-
- for {
- s := scanLine("")
- if s == quitCommand {
- fmt.Println("Quit command received")
- return
- }
- raw, err := os.ReadFile(s)
- if err != nil {
- fmt.Printf(">>> Error: %s \n", err)
- } else {
- env := whisper.Envelope{Data: raw} // the topic is zero
- msg := env.Open(watcher1) // force-open envelope regardless of the topic
- if msg == nil {
- msg = env.Open(watcher2)
- }
- if msg == nil {
- fmt.Printf(">>> Error: failed to decrypt the message \n")
- } else {
- printMessageInfo(msg)
- }
- }
- }
-}
-
-func scanLine(prompt string) string {
- if len(prompt) > 0 {
- fmt.Print(prompt)
- }
- txt, err := input.ReadString('\n')
- if err != nil {
- utils.Fatalf("input error: %s", err)
- }
- txt = strings.TrimRight(txt, "\n\r")
- return txt
-}
-
-func scanLineA(prompt string) *string {
- s := scanLine(prompt)
- return &s
-}
-
-func scanUint(prompt string) uint32 {
- s := scanLine(prompt)
- i, err := strconv.Atoi(s)
- if err != nil {
- utils.Fatalf("Fail to parse the lower time limit: %s", err)
- }
- return uint32(i)
-}
-
-func sendMsg(payload []byte) common.Hash {
- params := whisper.MessageParams{
- Src: asymKey,
- Dst: pub,
- KeySym: symKey,
- Payload: payload,
- Topic: topic,
- TTL: uint32(*argTTL),
- PoW: *argPoW,
- WorkTime: uint32(*argWorkTime),
- }
-
- msg, err := whisper.NewSentMessage(¶ms)
- if err != nil {
- utils.Fatalf("failed to create new message: %s", err)
- }
-
- envelope, err := msg.Wrap(¶ms)
- if err != nil {
- fmt.Printf("failed to seal message: %v \n", err)
- return common.Hash{}
- }
-
- err = shh.Send(envelope)
- if err != nil {
- fmt.Printf("failed to send message: %v \n", err)
- return common.Hash{}
- }
-
- return envelope.Hash()
-}
-
-func messageLoop() {
- sf := shh.GetFilter(symFilterID)
- if sf == nil {
- utils.Fatalf("symmetric filter is not installed")
- }
-
- af := shh.GetFilter(asymFilterID)
- if af == nil {
- utils.Fatalf("asymmetric filter is not installed")
- }
-
- ticker := time.NewTicker(time.Millisecond * 50)
-
- for {
- select {
- case <-ticker.C:
- m1 := sf.Retrieve()
- m2 := af.Retrieve()
- messages := append(m1, m2...)
- for _, msg := range messages {
- reportedOnce := false
- if !*fileExMode && len(msg.Payload) <= 2048 {
- printMessageInfo(msg)
- reportedOnce = true
- }
-
- // All messages are saved upon specifying argSaveDir.
- // fileExMode only specifies how messages are displayed on the console after they are saved.
- // if fileExMode == true, only the hashes are displayed, since messages might be too big.
- if len(*argSaveDir) > 0 {
- writeMessageToFile(*argSaveDir, msg, !reportedOnce)
- }
- }
- case <-done:
- return
- }
- }
-}
-
-func printMessageInfo(msg *whisper.ReceivedMessage) {
- timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics
- text := string(msg.Payload)
-
- var address common.Address
- if msg.Src != nil {
- address = crypto.PubkeyToAddress(*msg.Src)
- }
-
- if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
- fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself
- } else {
- fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer
- }
-}
-
-func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {
- if len(dir) == 0 {
- return
- }
-
- timestamp := fmt.Sprintf("%d", msg.Sent)
- name := fmt.Sprintf("%x", msg.EnvelopeHash)
-
- var address common.Address
- if msg.Src != nil {
- address = crypto.PubkeyToAddress(*msg.Src)
- }
-
- env := shh.GetEnvelope(msg.EnvelopeHash)
- if env == nil {
- fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash)
- return
- }
-
- // this is a sample code; uncomment if you don't want to save your own messages.
- //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
- // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
- // return
- //}
-
- fullpath := filepath.Join(dir, name)
- err := os.WriteFile(fullpath, env.Data, 0644)
- if err != nil {
- fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
- } else if show {
- fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data))
- }
-}
-
-func requestExpiredMessagesLoop() {
- var key, peerID, bloom []byte
- var timeLow, timeUpp uint32
- var t string
- var xt whisper.TopicType
-
- keyID, err := shh.AddSymKeyFromPassword(msPassword)
- if err != nil {
- utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
- }
- key, err = shh.GetSymKey(keyID)
- if err != nil {
- utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
- }
- peerID = extractIDFromEnode(*argEnode)
- shh.AllowP2PMessagesFromPeer(peerID)
-
- for {
- timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
- timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
- t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ")
- if len(t) == whisper.TopicLength*2 {
- x, err := hex.DecodeString(t)
- if err != nil {
- fmt.Printf("Failed to parse the topic: %s \n", err)
- continue
- }
- xt = whisper.BytesToTopic(x)
- bloom = whisper.TopicToBloom(xt)
- obfuscateBloom(bloom)
- } else if len(t) == 0 {
- bloom = whisper.MakeFullNodeBloom()
- } else {
- fmt.Println("Error: topic is invalid, request aborted")
- continue
- }
-
- if timeUpp == 0 {
- timeUpp = 0xFFFFFFFF
- }
-
- data := make([]byte, 8, 8+whisper.BloomFilterSize)
- binary.BigEndian.PutUint32(data, timeLow)
- binary.BigEndian.PutUint32(data[4:], timeUpp)
- data = append(data, bloom...)
-
- var params whisper.MessageParams
- params.PoW = *argServerPoW
- params.Payload = data
- params.KeySym = key
- params.Src = asymKey
- params.WorkTime = 5
-
- msg, err := whisper.NewSentMessage(¶ms)
- if err != nil {
- utils.Fatalf("failed to create new message: %s", err)
- }
- env, err := msg.Wrap(¶ms)
- if err != nil {
- utils.Fatalf("Wrap failed: %s", err)
- }
-
- err = shh.RequestHistoricMessages(peerID, env)
- if err != nil {
- utils.Fatalf("Failed to send P2P message: %s", err)
- }
-
- time.Sleep(time.Second * 5)
- }
-}
-
-func extractIDFromEnode(s string) []byte {
- n, err := discover.ParseNode(s)
- if err != nil {
- utils.Fatalf("Failed to parse enode: %s", err)
- }
- return n.ID[:]
-}
-
-// obfuscateBloom adds 16 random bits to the the bloom
-// filter, in order to obfuscate the containing topics.
-// it does so deterministically within every session.
-// despite additional bits, it will match on average
-// 32000 times less messages than full node's bloom filter.
-func obfuscateBloom(bloom []byte) {
- const half = entropySize / 2
- for i := 0; i < half; i++ {
- x := int(entropy[i])
- if entropy[half+i] < 128 {
- x += 256
- }
-
- bloom[x/8] = 1 << uint(x%8) // set the bit number X
- }
-}
diff --git a/common/constants/constants.go.devnet b/common/constants/constants.go.devnet
index d0a585cc9a..81e8c0487c 100644
--- a/common/constants/constants.go.devnet
+++ b/common/constants/constants.go.devnet
@@ -51,7 +51,7 @@ var BerlinBlock = big.NewInt(16832700)
var LondonBlock = big.NewInt(16832700)
var MergeBlock = big.NewInt(16832700)
var ShanghaiBlock = big.NewInt(16832700)
-var Eip1559Block = big.NewInt(9999999999)
+var Eip1559Block = big.NewInt(23035500)
var TIPXDCXTestnet = big.NewInt(0)
var IsTestnet bool = false
diff --git a/common/gas.go b/common/gas.go
index a5b5690a76..1e40721b14 100644
--- a/common/gas.go
+++ b/common/gas.go
@@ -4,6 +4,7 @@ import "math/big"
var MinGasPrice50x = big.NewInt(12500000000)
var GasPrice50x = big.NewInt(12500000000)
+var BaseFee = big.NewInt(12500000000)
func GetGasFee(blockNumber, gas uint64) *big.Int {
fee := new(big.Int).SetUint64(gas)
diff --git a/common/types.go b/common/types.go
index c7abab289b..533aa562d9 100644
--- a/common/types.go
+++ b/common/types.go
@@ -17,6 +17,7 @@
package common
import (
+ "bytes"
"encoding/hex"
"fmt"
"math/big"
@@ -24,7 +25,7 @@ import (
"reflect"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -77,29 +78,50 @@ type Vote struct {
Voter Address
}
+// BytesToHash sets b to hash.
+// If b is larger than len(h), b will be cropped from the left.
func BytesToHash(b []byte) Hash {
var h Hash
h.SetBytes(b)
return h
}
+
func StringToHash(s string) Hash { return BytesToHash([]byte(s)) }
-func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
+
+// BigToHash sets byte representation of b to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
+
func Uint64ToHash(b uint64) Hash { return BytesToHash(new(big.Int).SetUint64(b).Bytes()) }
-func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
+
+// HexToHash sets byte representation of s to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
+
+// Cmp compares two hashes.
+func (h Hash) Cmp(other Hash) int {
+ return bytes.Compare(h[:], other[:])
+}
// IsZero returns if a Hash is empty
func (h Hash) IsZero() bool { return h == Hash{} }
// Get the string representation of the underlying hash
-func (h Hash) Str() string { return string(h[:]) }
+func (h Hash) Str() string { return string(h[:]) }
+
+// Bytes gets the byte representation of the underlying hash.
func (h Hash) Bytes() []byte { return h[:] }
+
+// Big converts a hash to a big integer.
func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
-func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
+
+// Hex converts a hash to a hex string.
+func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
// TerminalString implements log.TerminalStringer, formatting a string for console
// output during logging.
func (h Hash) TerminalString() string {
- return fmt.Sprintf("%x…%x", h[:3], h[29:])
+ return fmt.Sprintf("%x..%x", h[:3], h[29:])
}
// String implements the stringer interface and is used also by the logger when
@@ -129,7 +151,8 @@ func (h Hash) MarshalText() ([]byte, error) {
return hexutil.Bytes(h[:]).MarshalText()
}
-// Sets the hash to the value of b. If b is larger than len(h), 'b' will be cropped (from the left).
+// SetBytes sets the hash to the value of b.
+// If b is larger than len(h), b will be cropped from the left.
func (h *Hash) SetBytes(b []byte) {
if len(b) > len(h) {
b = b[len(b)-HashLength:]
@@ -143,9 +166,7 @@ func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) }
// Sets h to other
func (h *Hash) Set(other Hash) {
- for i, v := range other {
- h[i] = v
- }
+ copy(h[:], other[:])
}
// Generate implements testing/quick.Generator.
@@ -219,7 +240,7 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) }
// Hex returns an EIP55-compliant hex string representation of the address.
func (a Address) Hex() string {
unchecksummed := hex.EncodeToString(a[:])
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write([]byte(unchecksummed))
hash := sha.Sum(nil)
@@ -262,9 +283,7 @@ func (a *Address) SetString(s string) { a.SetBytes([]byte(s)) }
// Sets a to other
func (a *Address) Set(other Address) {
- for i, v := range other {
- a[i] = v
- }
+ copy(a[:], other[:])
}
// MarshalText returns the hex representation of a.
diff --git a/common/types_test.go b/common/types_test.go
index 7621a6e4b8..7c2822854a 100644
--- a/common/types_test.go
+++ b/common/types_test.go
@@ -21,6 +21,7 @@ import (
"math/big"
"strings"
"testing"
+ "time"
)
func TestBytesConversion(t *testing.T) {
@@ -190,7 +191,7 @@ func TestBinaryAddressToString(t *testing.T) {
have := tt.bin.String()
want := tt.str
if have != want {
- t.Errorf("fail to convert binary address to string address\nwant:%s\nhave:%s", have, want)
+ t.Errorf("fail to convert binary address to string address\nwant: %s\nhave: %s", have, want)
}
}
}
@@ -199,7 +200,18 @@ func TestStringToBinaryAddress(t *testing.T) {
want := tt.bin
have := HexToAddress(tt.str)
if have != want {
- t.Errorf("fail to convert string address to binary address\nwant:%s\nhave:%s", have, want)
+ t.Errorf("fail to convert string address to binary address\nwant: %s\nhave: %s", have, want)
}
}
}
+
+func BenchmarkPrettyDuration(b *testing.B) {
+ var x = PrettyDuration(time.Duration(int64(1203123912312)))
+ b.Logf("Pre %s", time.Duration(x).String())
+ var a string
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ a = x.String()
+ }
+ b.Logf("Post %s", a)
+}
diff --git a/compression/rle/read_write.go b/compression/rle/read_write.go
index d5aadf8c1d..93922981f8 100644
--- a/compression/rle/read_write.go
+++ b/compression/rle/read_write.go
@@ -26,9 +26,9 @@ import (
const (
token byte = 0xfe
- emptyShaToken = 0xfd
- emptyListShaToken = 0xfe
- tokenToken = 0xff
+ emptyShaToken byte = 0xfd
+ emptyListShaToken byte = 0xfe
+ tokenToken byte = 0xff
)
var empty = crypto.Keccak256([]byte(""))
diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go
index 36ad2b3a6a..411411d518 100644
--- a/consensus/XDPoS/XDPoS.go
+++ b/consensus/XDPoS/XDPoS.go
@@ -22,20 +22,19 @@ import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v1"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
- "github.com/XinFinOrg/XDPoSChain/event"
-
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
+ "github.com/XinFinOrg/XDPoSChain/event"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rpc"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -60,7 +59,7 @@ type XDPoS struct {
db ethdb.Database // Database to store and retrieve snapshot checkpoints
// Transaction cache, only make sense for adaptor level
- signingTxsCache *lru.Cache
+ signingTxsCache *lru.Cache[common.Hash, []*types.Transaction]
// Share Channel
MinePeriodCh chan int // Miner wait Period Channel
@@ -109,17 +108,12 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS {
minePeriodCh := make(chan int)
newRoundCh := make(chan types.Round, newRoundChanSize)
- // Allocate the snapshot caches and create the engine
- signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit)
-
return &XDPoS{
- config: config,
- db: db,
-
- MinePeriodCh: minePeriodCh,
- NewRoundCh: newRoundCh,
-
- signingTxsCache: signingTxsCache,
+ config: config,
+ db: db,
+ MinePeriodCh: minePeriodCh,
+ NewRoundCh: newRoundCh,
+ signingTxsCache: lru.NewCache[common.Hash, []*types.Transaction](utils.BlockSignersCacheLimit),
EngineV1: engine_v1.New(chainConfig, db),
EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh),
}
@@ -138,22 +132,16 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS {
minePeriodCh := make(chan int)
newRoundCh := make(chan types.Round, newRoundChanSize)
- // Allocate the snapshot caches and create the engine
- signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit)
-
fakeEngine = &XDPoS{
- config: conf,
- db: db,
-
- MinePeriodCh: minePeriodCh,
- NewRoundCh: newRoundCh,
-
+ config: conf,
+ db: db,
+ MinePeriodCh: minePeriodCh,
+ NewRoundCh: newRoundCh,
GetXDCXService: func() utils.TradingService { return nil },
GetLendingService: func() utils.LendingService { return nil },
-
- signingTxsCache: signingTxsCache,
- EngineV1: engine_v1.NewFaker(db, chainConfig),
- EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh),
+ signingTxsCache: lru.NewCache[common.Hash, []*types.Transaction](utils.BlockSignersCacheLimit),
+ EngineV1: engine_v1.NewFaker(db, chainConfig),
+ EngineV2: engine_v2.New(chainConfig, db, minePeriodCh, newRoundCh),
}
return fakeEngine
}
@@ -446,7 +434,7 @@ func (x *XDPoS) CalculateMissingRounds(chain consensus.ChainReader, header *type
case params.ConsensusEngineVersion2:
return x.EngineV2.CalculateMissingRounds(chain, header)
default: // Default "v1"
- return nil, errors.New("Not supported in the v1 consensus")
+ return nil, errors.New("not supported in the v1 consensus")
}
}
@@ -554,7 +542,7 @@ func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*t
return signTxs
}
-func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
+func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) ([]*types.Transaction, bool) {
return x.signingTxsCache.Get(hash)
}
diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go
index c67dda8744..54d4d48b87 100644
--- a/consensus/XDPoS/engines/engine_v1/engine.go
+++ b/consensus/XDPoS/engines/engine_v1/engine.go
@@ -14,18 +14,18 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
-
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
- lru "github.com/hashicorp/golang-lru"
)
const (
@@ -43,17 +43,17 @@ type XDPoS_v1 struct {
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
- recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
- signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
- validatorSignatures *lru.ARCCache // Signatures of recent blocks to speed up mining
- verifiedHeaders *lru.ARCCache
+ recents *lru.Cache[common.Hash, *SnapshotV1] // Snapshots for recent block to speed up reorgs
+ signatures *utils.SigLRU // Signatures of recent blocks to speed up mining
+ validatorSignatures *utils.SigLRU // Signatures of recent blocks to speed up mining
+ verifiedHeaders *lru.Cache[common.Hash, struct{}]
proposals map[common.Address]bool // Current list of proposals we are pushing
signer common.Address // Ethereum address of the signing key
signFn clique.SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields
- HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
+ HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error)
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error)
HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
@@ -91,20 +91,16 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS_v1 {
conf.Epoch = utils.EpochLength
}
- recents, _ := lru.NewARC(utils.InmemorySnapshots)
- signatures, _ := lru.NewARC(utils.InmemorySnapshots)
- validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
- verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
return &XDPoS_v1{
chainConfig: chainConfig,
config: &conf,
db: db,
- recents: recents,
- signatures: signatures,
- verifiedHeaders: verifiedHeaders,
- validatorSignatures: validatorSignatures,
+ recents: lru.NewCache[common.Hash, *SnapshotV1](utils.InmemorySnapshots),
+ signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots),
+ verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots),
+ validatorSignatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots),
proposals: make(map[common.Address]bool),
}
}
@@ -144,7 +140,7 @@ func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *ty
}
err := x.verifyHeader(chain, header, parents, fullVerify)
if err == nil {
- x.verifiedHeaders.Add(header.Hash(), true)
+ x.verifiedHeaders.Add(header.Hash(), struct{}{})
}
return err
}
@@ -241,6 +237,10 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty
if parent.Time.Uint64()+x.config.Period > header.Time.Uint64() {
return utils.ErrInvalidTimestamp
}
+ // Verify the header's EIP-1559 attributes.
+ if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
+ return err
+ }
if number%x.config.Epoch != 0 {
return x.verifySeal(chain, header, parents, fullVerify)
@@ -394,7 +394,7 @@ func (x *XDPoS_v1) GetPeriod() uint64 { return x.config.Period }
func (x *XDPoS_v1) whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) {
if header.Number.Uint64() == 0 {
- return common.Address{}, errors.New("Don't take block 0")
+ return common.Address{}, errors.New("don't take block 0")
}
m, err := ecrecover(header, snap.sigcache)
if err != nil {
@@ -444,7 +444,7 @@ func (x *XDPoS_v1) yourTurn(chain consensus.ChainReader, parent *types.Header, s
return 0, -1, -1, false, err
}
if len(masternodes) == 0 {
- return 0, -1, -1, false, errors.New("Masternodes not found")
+ return 0, -1, -1, false, errors.New("masternodes not found")
}
pre := common.Address{}
// masternode[0] has chance to create block 1
@@ -476,10 +476,10 @@ func (x *XDPoS_v1) snapshot(chain consensus.ChainReader, number uint64, hash com
headers []*types.Header
snap *SnapshotV1
)
- for snap == nil {
+ for {
// If an in-memory SnapshotV1 was found, use that
- if s, ok := x.recents.Get(hash); ok {
- snap = s.(*SnapshotV1)
+ if s, ok := x.recents.Get(hash); ok && s != nil {
+ snap = s
break
}
// If an on-disk checkpoint snapshot can be found, use that
@@ -834,7 +834,7 @@ func (x *XDPoS_v1) Finalize(chain consensus.ChainReader, header *types.Header, s
// _ = c.CacheData(header, txs, receipts)
if x.HookReward != nil && number%rCheckpoint == 0 {
- err, rewards := x.HookReward(chain, state, parentState, header)
+ rewards, err := x.HookReward(chain, state, parentState, header)
if err != nil {
return nil, err
}
@@ -974,7 +974,7 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error
// If the signature's already cached, return that
hash := header.Hash()
if address, known := x.validatorSignatures.Get(hash); known {
- return address.(common.Address), nil
+ return address, nil
}
// Retrieve the signature from the header.Validator
// len equals 65 bytes
@@ -1029,7 +1029,7 @@ func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpoin
}
signers, err := x.HookGetSignersFromContract(startGapBlockHeader.Hash())
if err != nil {
- return []common.Address{}, fmt.Errorf("Can't get signers from Smart Contract . Err: %v", err)
+ return []common.Address{}, fmt.Errorf("can't get signers from Smart Contract . Err: %v", err)
}
return signers, nil
}
@@ -1039,20 +1039,15 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS_v1 {
// Set any missing consensus parameters to their defaults
conf := chainConfig.XDPoS
- // Allocate the snapshot caches and create the engine
- recents, _ := lru.NewARC(utils.InmemorySnapshots)
- signatures, _ := lru.NewARC(utils.InmemorySnapshots)
- validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
- verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
fakeEngine = &XDPoS_v1{
chainConfig: chainConfig,
config: conf,
db: db,
- recents: recents,
- signatures: signatures,
- verifiedHeaders: verifiedHeaders,
- validatorSignatures: validatorSignatures,
+ recents: lru.NewCache[common.Hash, *SnapshotV1](utils.InmemorySnapshots),
+ signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots),
+ verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots),
+ validatorSignatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots),
proposals: make(map[common.Address]bool),
}
return fakeEngine
diff --git a/consensus/XDPoS/engines/engine_v1/snapshot.go b/consensus/XDPoS/engines/engine_v1/snapshot.go
index 3ad8b22189..012b29aabe 100644
--- a/consensus/XDPoS/engines/engine_v1/snapshot.go
+++ b/consensus/XDPoS/engines/engine_v1/snapshot.go
@@ -10,7 +10,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/params"
- lru "github.com/hashicorp/golang-lru"
)
// Vote represents a single vote that an authorized signer made to modify the
@@ -32,7 +31,7 @@ import (
// Snapshot is the state of the authorization voting at a given point in time.
type SnapshotV1 struct {
config *params.XDPoSConfig // Consensus engine parameters to fine tune behavior
- sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
+ sigcache *utils.SigLRU // Cache of recent block signatures to speed up ecrecover
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
@@ -45,7 +44,7 @@ type SnapshotV1 struct {
// newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for
// the genesis block.
-func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *SnapshotV1 {
+func newSnapshot(config *params.XDPoSConfig, sigcache *utils.SigLRU, number uint64, hash common.Hash, signers []common.Address) *SnapshotV1 {
snap := &SnapshotV1{
config: config,
sigcache: sigcache,
@@ -62,7 +61,7 @@ func newSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, number uint
}
// loadSnapshot loads an existing snapshot from the database.
-func loadSnapshot(config *params.XDPoSConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*SnapshotV1, error) {
+func loadSnapshot(config *params.XDPoSConfig, sigcache *utils.SigLRU, db ethdb.Database, hash common.Hash) (*SnapshotV1, error) {
blob, err := db.Get(append([]byte("XDPoS-"), hash[:]...))
if err != nil {
return nil, err
diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go
index 56f1716a83..fbd7814935 100644
--- a/consensus/XDPoS/engines/engine_v1/utils.go
+++ b/consensus/XDPoS/engines/engine_v1/utils.go
@@ -7,11 +7,9 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
- "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
- lru "github.com/hashicorp/golang-lru"
+ "golang.org/x/crypto/sha3"
)
// Get masternodes address from checkpoint Header.
@@ -26,7 +24,7 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A
// Get m2 list from checkpoint block.
func getM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) {
if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 {
- return nil, errors.New("This block is not checkpoint block epoc.")
+ return nil, errors.New("this block is not checkpoint block")
}
// Get signers from this block.
masternodes := decodeMasternodesFromHeaderExtra(checkpointHeader)
@@ -60,9 +58,9 @@ func getM1M2(masternodes []common.Address, validators []int64, currentHeader *ty
}
func sigHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
- err := rlp.Encode(hasher, []interface{}{
+ enc := []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
@@ -75,23 +73,24 @@ func sigHash(header *types.Header) (hash common.Hash) {
header.GasLimit,
header.GasUsed,
header.Time,
- header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
+ header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
- })
- if err != nil {
- log.Debug("Fail to encode", err)
}
+ if header.BaseFee != nil {
+ enc = append(enc, header.BaseFee)
+ }
+ rlp.Encode(hasher, enc)
hasher.Sum(hash[:0])
return hash
}
// ecrecover extracts the Ethereum account address from a signed header.
-func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
+func ecrecover(header *types.Header, sigcache *utils.SigLRU) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
- return address.(common.Address), nil
+ return address, nil
}
// Retrieve the signature from the header extra-data
if len(header.Extra) < utils.ExtraSeal {
diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go
index 20558e860a..bcbf4651cb 100644
--- a/consensus/XDPoS/engines/engine_v2/engine.go
+++ b/consensus/XDPoS/engines/engine_v2/engine.go
@@ -13,6 +13,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/countdown"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
@@ -21,7 +22,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
- lru "github.com/hashicorp/golang-lru"
)
type XDPoS_v2 struct {
@@ -32,14 +32,14 @@ type XDPoS_v2 struct {
isInitilised bool // status of v2 variables
whosTurn common.Address // Record waiting for who to mine
- snapshots *lru.ARCCache // Snapshots for gap block
- signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
- epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info
- verifiedHeaders *lru.ARCCache
+ snapshots *lru.Cache[common.Hash, *SnapshotV2] // Snapshots for gap block
+ signatures *utils.SigLRU // Signatures of recent blocks to speed up mining
+ epochSwitches *lru.Cache[common.Hash, *types.EpochSwitchInfo] // infos of epoch: master nodes, epoch switch block info, parent of that info
+ verifiedHeaders *lru.Cache[common.Hash, struct{}]
// only contains epoch switch block info
// input: round, output: infos of epoch switch block and next epoch switch block info
- round2epochBlockInfo *lru.ARCCache
+ round2epochBlockInfo *lru.Cache[types.Round, *types.BlockInfo]
signer common.Address // Ethereum address of the signing key
signFn clique.SignerFn // Signer function to authorize hashes with
@@ -78,12 +78,6 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i
duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second
timeoutTimer := countdown.NewCountDown(duration)
- snapshots, _ := lru.NewARC(utils.InmemorySnapshots)
- signatures, _ := lru.NewARC(utils.InmemorySnapshots)
- epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs))
- verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
- round2epochBlockInfo, _ := lru.NewARC(utils.InmemoryRound2Epochs)
-
timeoutPool := utils.NewPool()
votePool := utils.NewPool()
engine := &XDPoS_v2{
@@ -93,17 +87,17 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database, minePeriodCh chan i
db: db,
isInitilised: false,
- signatures: signatures,
+ signatures: lru.NewCache[common.Hash, common.Address](utils.InmemorySnapshots),
- verifiedHeaders: verifiedHeaders,
- snapshots: snapshots,
- epochSwitches: epochSwitches,
+ verifiedHeaders: lru.NewCache[common.Hash, struct{}](utils.InmemorySnapshots),
+ snapshots: lru.NewCache[common.Hash, *SnapshotV2](utils.InmemorySnapshots),
+ epochSwitches: lru.NewCache[common.Hash, *types.EpochSwitchInfo](int(utils.InmemoryEpochs)),
timeoutWorker: timeoutTimer,
BroadcastCh: make(chan interface{}),
minePeriodCh: minePeriodCh,
newRoundCh: newRoundCh,
- round2epochBlockInfo: round2epochBlockInfo,
+ round2epochBlockInfo: lru.NewCache[types.Round, *types.BlockInfo](utils.InmemoryRound2Epochs),
timeoutPool: timeoutPool,
votePool: votePool,
@@ -674,7 +668,7 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg
}
if len(snap.NextEpochCandidates) == 0 {
log.Error("[VerifyTimeoutMessage] cannot find NextEpochCandidates from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
- return false, errors.New("Empty master node lists from snapshot")
+ return false, errors.New("empty master node lists from snapshot")
}
verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{
@@ -802,7 +796,7 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
epochInfo, err := x.getEpochSwitchInfo(blockChainReader, parentHeader, quorumCert.ProposedBlockInfo.Hash)
if err != nil {
log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err)
- return errors.New("Fail to verify QC due to failure in getting epoch switch info")
+ return errors.New("fail to verify QC due to failure in getting epoch switch info")
}
signatures, duplicates := UniqueSignatures(quorumCert.Signatures)
@@ -834,12 +828,12 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *
}), sig, epochInfo.Masternodes)
if err != nil {
log.Error("[verifyQC] Error while verfying QC message signatures", "Error", err)
- haveError = errors.New("Error while verfying QC message signatures")
+ haveError = errors.New("error while verfying QC message signatures")
return
}
if !verified {
log.Warn("[verifyQC] Signature not verified doing QC verification", "QC", quorumCert)
- haveError = errors.New("Fail to verify QC due to signature mis-match")
+ haveError = errors.New("fail to verify QC due to signature mis-match")
return
}
}(signature)
diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go
index 907f107199..40a684e335 100644
--- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go
+++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go
@@ -30,9 +30,8 @@ func (x *XDPoS_v2) getPreviousEpochSwitchInfoByHash(chain consensus.ChainReader,
// Given header and its hash, get epoch switch info from the epoch switch block of that epoch,
// header is allow to be nil.
func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*types.EpochSwitchInfo, error) {
- e, ok := x.epochSwitches.Get(hash)
- if ok {
- epochSwitchInfo := e.(*types.EpochSwitchInfo)
+ epochSwitchInfo, ok := x.epochSwitches.Get(hash)
+ if ok && epochSwitchInfo != nil {
log.Debug("[getEpochSwitchInfo] cache hit", "number", epochSwitchInfo.EpochSwitchBlockInfo.Number, "hash", hash.Hex())
return epochSwitchInfo, nil
}
@@ -87,7 +86,7 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
x.epochSwitches.Add(hash, epochSwitchInfo)
return epochSwitchInfo, nil
}
- epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, h.ParentHash)
+ epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, h.ParentHash)
if err != nil {
log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64())
return nil, err
diff --git a/consensus/XDPoS/engines/engine_v2/forensics.go b/consensus/XDPoS/engines/engine_v2/forensics.go
index b3198bd71c..adec35cfd1 100644
--- a/consensus/XDPoS/engines/engine_v2/forensics.go
+++ b/consensus/XDPoS/engines/engine_v2/forensics.go
@@ -165,7 +165,7 @@ func (f *Forensics) SendForensicProof(chain consensus.ChainReader, engine *XDPoS
if ancestorBlock == nil {
log.Error("[SendForensicProof] Unable to find the ancestor block by its hash", "Hash", ancestorHash)
- return errors.New("Can't find ancestor block via hash")
+ return errors.New("can't find ancestor block via hash")
}
content, err := json.Marshal(&types.ForensicsContent{
@@ -210,7 +210,7 @@ func (f *Forensics) findAncestorQCs(chain consensus.ChainReader, currentQc types
parentHash := quorumCertificate.ProposedBlockInfo.Hash
parentHeader := chain.GetHeaderByHash(parentHash)
if parentHeader == nil {
- log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "BlockNum", parentHeader.Number.Int64(), "ParentHash", parentHash.Hex())
+ log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "ParentHash", parentHash.Hex())
return nil, errors.New("unable to find parent block header in forensics")
}
var decodedExtraField types.ExtraFields_v2
@@ -457,7 +457,7 @@ func (f *Forensics) isExtendingFromAncestor(blockChainReader consensus.ChainRead
for i := 0; i < blockNumDiff; i++ {
parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash)
if parentBlock == nil {
- return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
+ return false, fmt.Errorf("could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
} else {
nextBlockHash = parentBlock.ParentHash
}
diff --git a/consensus/XDPoS/engines/engine_v2/forensics_test.go b/consensus/XDPoS/engines/engine_v2/forensics_test.go
index 3597bfb936..3b6dc1b252 100644
--- a/consensus/XDPoS/engines/engine_v2/forensics_test.go
+++ b/consensus/XDPoS/engines/engine_v2/forensics_test.go
@@ -58,10 +58,10 @@ func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account acco
pass := "" // not used but required by API
a1, err := ks.ImportECDSA(pk, pass)
if err != nil {
- return common.Address{}, nil, fmt.Errorf(err.Error())
+ return common.Address{}, nil, err
}
if err := ks.Unlock(a1, ""); err != nil {
- return a1.Address, nil, fmt.Errorf(err.Error())
+ return a1.Address, nil, err
}
return a1.Address, ks.SignHash, nil
}
diff --git a/consensus/XDPoS/engines/engine_v2/snapshot.go b/consensus/XDPoS/engines/engine_v2/snapshot.go
index ccae598412..de4fb76d82 100644
--- a/consensus/XDPoS/engines/engine_v2/snapshot.go
+++ b/consensus/XDPoS/engines/engine_v2/snapshot.go
@@ -84,8 +84,7 @@ func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64, isGap
log.Debug("get snapshot from gap block", "number", gapBlockNum, "hash", gapBlockHash.Hex())
// If an in-memory SnapshotV2 was found, use that
- if s, ok := x.snapshots.Get(gapBlockHash); ok {
- snap := s.(*SnapshotV2)
+ if snap, ok := x.snapshots.Get(gapBlockHash); ok && snap != nil {
log.Trace("Loaded snapshot from memory", "number", gapBlockNum, "hash", gapBlockHash)
return snap, nil
}
diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go
index 0c4d6d2220..49d79177e8 100644
--- a/consensus/XDPoS/engines/engine_v2/utils.go
+++ b/consensus/XDPoS/engines/engine_v2/utils.go
@@ -11,16 +11,15 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
- lru "github.com/hashicorp/golang-lru"
+ "golang.org/x/crypto/sha3"
)
func sigHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
- err := rlp.Encode(hasher, []interface{}{
+ enc := []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
@@ -38,19 +37,20 @@ func sigHash(header *types.Header) (hash common.Hash) {
header.Nonce,
header.Validators,
header.Penalties,
- })
- if err != nil {
- log.Debug("Fail to encode", err)
}
+ if header.BaseFee != nil {
+ enc = append(enc, header.BaseFee)
+ }
+ rlp.Encode(hasher, enc)
hasher.Sum(hash[:0])
return hash
}
-func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
+func ecrecover(header *types.Header, sigcache *utils.SigLRU) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
- return address.(common.Address), nil
+ return address, nil
}
// Recover the public key and the Ethereum address
@@ -99,7 +99,7 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro
signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes())
if err != nil {
- return nil, fmt.Errorf("Error %v while signing hash", err)
+ return nil, fmt.Errorf("error %v while signing hash", err)
}
return signedHash, nil
}
@@ -107,12 +107,12 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro
func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature types.Signature, masternodes []common.Address) (bool, common.Address, error) {
var signerAddress common.Address
if len(masternodes) == 0 {
- return false, signerAddress, errors.New("Empty masternode list detected when verifying message signatures")
+ return false, signerAddress, errors.New("empty masternode list detected when verifying message signatures")
}
// Recover the public key and the Ethereum address
pubkey, err := crypto.Ecrecover(signedHashToBeVerified.Bytes(), signature)
if err != nil {
- return false, signerAddress, fmt.Errorf("Error while verifying message: %v", err)
+ return false, signerAddress, fmt.Errorf("error while verifying message: %v", err)
}
copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:])
@@ -223,9 +223,8 @@ func (x *XDPoS_v2) CalculateMissingRounds(chain consensus.ChainReader, header *t
func (x *XDPoS_v2) getBlockByEpochNumberInCache(chain consensus.ChainReader, estRound types.Round) *types.BlockInfo {
epochSwitchInCache := make([]*types.BlockInfo, 0)
for r := estRound; r < estRound+types.Round(x.config.Epoch); r++ {
- info, ok := x.round2epochBlockInfo.Get(r)
- if ok {
- blockInfo := info.(*types.BlockInfo)
+ blockInfo, ok := x.round2epochBlockInfo.Get(r)
+ if ok && blockInfo != nil {
epochSwitchInCache = append(epochSwitchInCache, blockInfo)
}
}
diff --git a/consensus/XDPoS/engines/engine_v2/verifyHeader.go b/consensus/XDPoS/engines/engine_v2/verifyHeader.go
index 7ba672bccb..8341ae1c6b 100644
--- a/consensus/XDPoS/engines/engine_v2/verifyHeader.go
+++ b/consensus/XDPoS/engines/engine_v2/verifyHeader.go
@@ -9,6 +9,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log"
)
@@ -94,7 +95,10 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
if header.UncleHash != utils.UncleHash {
return utils.ErrInvalidUncleHash
}
-
+ // Verify the header's EIP-1559 attributes.
+ if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
+ return err
+ }
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
return utils.ErrInvalidDifficulty
}
@@ -109,7 +113,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
if !bytes.Equal(header.Nonce[:], utils.NonceDropVote) {
return utils.ErrInvalidCheckpointVote
}
- if header.Validators == nil || len(header.Validators) == 0 {
+ if len(header.Validators) == 0 {
return utils.ErrEmptyEpochSwitchValidators
}
if len(header.Validators)%common.AddressLength != 0 {
@@ -186,6 +190,6 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return utils.ErrNotItsTurn
}
- x.verifiedHeaders.Add(header.Hash(), true)
+ x.verifiedHeaders.Add(header.Hash(), struct{}{})
return nil
}
diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go
index 4c1fb828d3..f31e95c862 100644
--- a/consensus/XDPoS/engines/engine_v2/vote.go
+++ b/consensus/XDPoS/engines/engine_v2/vote.go
@@ -182,7 +182,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole
epochInfo, err := x.getEpochSwitchInfo(chain, nil, currentVoteMsg.(*types.Vote).ProposedBlockInfo.Hash)
if err != nil {
log.Error("[voteHandler] Error when getting epoch switch Info", "error", err)
- return errors.New("Fail on voteHandler due to failure in getting epoch switch info")
+ return errors.New("fail on voteHandler due to failure in getting epoch switch info")
}
// Skip and wait for the next vote to process again if valid votes is less than what we required
@@ -253,7 +253,7 @@ func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReade
for i := 0; i < blockNumDiff; i++ {
parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash)
if parentBlock == nil {
- return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
+ return false, fmt.Errorf("could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number)
} else {
nextBlockHash = parentBlock.ParentHash
}
diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go
index c6d0706ed8..eec246b54b 100644
--- a/consensus/XDPoS/utils/errors.go
+++ b/consensus/XDPoS/utils/errors.go
@@ -87,20 +87,20 @@ var (
ErrEmptyEpochSwitchValidators = errors.New("empty validators list on epoch switch block")
- ErrInvalidV2Extra = errors.New("Invalid v2 extra in the block")
- ErrInvalidQC = errors.New("Invalid QC content")
- ErrInvalidQCSignatures = errors.New("Invalid QC Signatures")
- ErrInvalidTC = errors.New("Invalid TC content")
- ErrInvalidTCSignatures = errors.New("Invalid TC Signatures")
- ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
- ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block")
- ErrValidatorNotWithinMasternodes = errors.New("Validator address is not in the master node list")
- ErrCoinbaseAndValidatorMismatch = errors.New("Validator and coinbase address in header does not match")
- ErrNotItsTurn = errors.New("Not validator's turn to mine this block")
+ ErrInvalidV2Extra = errors.New("invalid v2 extra in the block")
+ ErrInvalidQC = errors.New("invalid QC content")
+ ErrInvalidQCSignatures = errors.New("invalid QC Signatures")
+ ErrInvalidTC = errors.New("invalid TC content")
+ ErrInvalidTCSignatures = errors.New("invalid TC Signatures")
+ ErrEmptyBlockInfoHash = errors.New("blockInfo hash is empty")
+ ErrInvalidFieldInNonEpochSwitch = errors.New("invalid field exist in a non-epoch swtich block")
+ ErrValidatorNotWithinMasternodes = errors.New("validator address is not in the master node list")
+ ErrCoinbaseAndValidatorMismatch = errors.New("validator and coinbase address in header does not match")
+ ErrNotItsTurn = errors.New("not validator's turn to mine this block")
- ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round")
+ ErrRoundInvalid = errors.New("invalid Round, it shall be bigger than QC round")
- ErrAlreadyMined = errors.New("Already mined")
+ ErrAlreadyMined = errors.New("already mined")
)
type ErrIncomingMessageRoundNotEqualCurrentRound struct {
diff --git a/consensus/XDPoS/utils/types.go b/consensus/XDPoS/utils/types.go
index b5d1168341..fc4aaf0463 100644
--- a/consensus/XDPoS/utils/types.go
+++ b/consensus/XDPoS/utils/types.go
@@ -7,6 +7,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
@@ -79,3 +80,5 @@ type EpochNumInfo struct {
EpochFirstBlockNumber *big.Int `json:"firstBlock"`
EpochLastBlockNumber *big.Int `json:"lastBlock"`
}
+
+type SigLRU = lru.Cache[common.Hash, common.Address]
diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go
index 62b394c836..28ab0f2f64 100644
--- a/consensus/XDPoS/utils/utils.go
+++ b/consensus/XDPoS/utils/utils.go
@@ -9,9 +9,9 @@ import (
"strconv"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/rlp"
+ "golang.org/x/crypto/sha3"
)
func Position(list []common.Address, x common.Address) int {
@@ -91,7 +91,7 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error {
}
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewKeccak256()
+ hw := sha3.NewLegacyKeccak256()
err := rlp.Encode(hw, x)
if err != nil {
log.Error("[rlpHash] Fail to hash item", "Error", err)
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 9bae8fd743..d6c7f42bde 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -29,18 +29,18 @@ import (
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/rpc"
- lru "github.com/hashicorp/golang-lru"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -56,8 +56,8 @@ var (
epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes
blockPeriod = uint64(15) // Default minimum difference between two consecutive block's timestamps
- extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
- extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
+ extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
+ extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal
nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
@@ -145,9 +145,9 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error)
// panics. This is done to avoid accidentally using both forms (signature present
// or not), which could be abused to produce different hashes for the same header.
func sigHash(header *types.Header) (hash common.Hash) {
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
- rlp.Encode(hasher, []interface{}{
+ enc := []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
@@ -160,20 +160,24 @@ func sigHash(header *types.Header) (hash common.Hash) {
header.GasLimit,
header.GasUsed,
header.Time,
- header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
+ header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short
header.MixDigest,
header.Nonce,
- })
+ }
+ if header.BaseFee != nil {
+ enc = append(enc, header.BaseFee)
+ }
+ rlp.Encode(hasher, enc)
hasher.Sum(hash[:0])
return hash
}
// ecrecover extracts the Ethereum account address from a signed header.
-func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
+func ecrecover(header *types.Header, sigcache *sigLRU) (common.Address, error) {
// If the signature's already cached, return that
hash := header.Hash()
if address, known := sigcache.Get(hash); known {
- return address.(common.Address), nil
+ return address, nil
}
// Retrieve the signature from the header extra-data
if len(header.Extra) < extraSeal {
@@ -199,8 +203,8 @@ type Clique struct {
config *params.CliqueConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
- recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
- signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
+ recents *lru.Cache[common.Hash, *Snapshot] // Snapshots for recent block to speed up reorgs
+ signatures *sigLRU // Signatures of recent blocks to speed up mining
proposals map[common.Address]bool // Current list of proposals we are pushing
@@ -217,15 +221,12 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique {
if conf.Epoch == 0 {
conf.Epoch = epochLength
}
- // Allocate the snapshot caches and create the engine
- recents, _ := lru.NewARC(inmemorySnapshots)
- signatures, _ := lru.NewARC(inmemorySignatures)
return &Clique{
config: &conf,
db: db,
- recents: recents,
- signatures: signatures,
+ recents: lru.NewCache[common.Hash, *Snapshot](inmemorySnapshots),
+ signatures: lru.NewCache[common.Hash, common.Address](inmemorySignatures),
proposals: make(map[common.Address]bool),
}
}
@@ -390,10 +391,10 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
headers []*types.Header
snap *Snapshot
)
- for snap == nil {
+ for {
// If an in-memory snapshot was found, use that
- if s, ok := c.recents.Get(hash); ok {
- snap = s.(*Snapshot)
+ if s, ok := c.recents.Get(hash); ok && s != nil {
+ snap = s
break
}
// If an on-disk checkpoint snapshot can be found, use that
@@ -644,7 +645,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
}
}
// Sweet, the protocol permits us to sign the block, wait for our time
- delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
+ delay := time.Until(time.Unix(header.Time.Int64(), 0))
if header.Difficulty.Cmp(diffNoTurn) == 0 {
// It's not our turn explicitly to sign, delay it a bit
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
diff --git a/consensus/clique/snapshot.go b/consensus/clique/snapshot.go
index bc89168aff..ff7c5a4c8e 100644
--- a/consensus/clique/snapshot.go
+++ b/consensus/clique/snapshot.go
@@ -21,10 +21,10 @@ import (
"encoding/json"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/params"
- lru "github.com/hashicorp/golang-lru"
)
// Vote represents a single vote that an authorized signer made to modify the
@@ -43,10 +43,12 @@ type Tally struct {
Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal
}
+type sigLRU = lru.Cache[common.Hash, common.Address]
+
// Snapshot is the state of the authorization voting at a given point in time.
type Snapshot struct {
config *params.CliqueConfig // Consensus engine parameters to fine tune behavior
- sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
+ sigcache *sigLRU // Cache of recent block signatures to speed up ecrecover
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
@@ -59,7 +61,7 @@ type Snapshot struct {
// newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for
// the genesis block.
-func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
+func newSnapshot(config *params.CliqueConfig, sigcache *sigLRU, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
snap := &Snapshot{
config: config,
sigcache: sigcache,
@@ -76,7 +78,7 @@ func newSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, number uin
}
// loadSnapshot loads an existing snapshot from the database.
-func loadSnapshot(config *params.CliqueConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
+func loadSnapshot(config *params.CliqueConfig, sigcache *sigLRU, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
blob, err := db.Get(append([]byte("clique-"), hash[:]...))
if err != nil {
return nil, err
diff --git a/consensus/errors.go b/consensus/errors.go
index c8ed578c2c..999916380d 100644
--- a/consensus/errors.go
+++ b/consensus/errors.go
@@ -43,7 +43,7 @@ var (
ErrNotReadyToPropose = errors.New("not ready to propose, QC is not ready")
- ErrNotReadyToMine = errors.New("Not ready to mine, it's not your turn")
+ ErrNotReadyToMine = errors.New("not ready to mine, it's not your turn")
- ErrCoinbaseMismatch = errors.New("Block Coinbase address does not match its wallte address")
+ ErrCoinbaseMismatch = errors.New("block Coinbase address does not match its wallte address")
)
diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go
index f8dca9c408..0b47abb9bb 100644
--- a/consensus/ethash/algorithm.go
+++ b/consensus/ethash/algorithm.go
@@ -30,8 +30,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/bitutil"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
+ "golang.org/x/crypto/sha3"
)
const (
@@ -112,7 +112,7 @@ func seedHash(block uint64) []byte {
if block < epochLength {
return seed
}
- keccak256 := makeHasher(sha3.NewKeccak256())
+ keccak256 := makeHasher(sha3.NewLegacyKeccak256())
for i := 0; i < int(block/epochLength); i++ {
keccak256(seed, seed)
}
@@ -170,7 +170,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) {
}
}()
// Create a hasher to reuse between invocations
- keccak512 := makeHasher(sha3.NewKeccak512())
+ keccak512 := makeHasher(sha3.NewLegacyKeccak512())
// Sequentially produce the initial dataset
keccak512(cache, seed)
@@ -303,7 +303,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
defer pend.Done()
// Create a hasher to reuse between invocations
- keccak512 := makeHasher(sha3.NewKeccak512())
+ keccak512 := makeHasher(sha3.NewLegacyKeccak512())
// Calculate the data segment this thread should generate
batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads)))
@@ -377,7 +377,7 @@ func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32)
// in-memory cache) in order to produce our final value for a particular header
// hash and nonce.
func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) {
- keccak512 := makeHasher(sha3.NewKeccak512())
+ keccak512 := makeHasher(sha3.NewLegacyKeccak512())
lookup := func(index uint32) []uint32 {
rawData := generateDatasetItem(cache, index, keccak512)
diff --git a/consensus/ethash/algorithm_test.go b/consensus/ethash/algorithm_test.go
index 4d05815de0..6a9d8809ad 100644
--- a/consensus/ethash/algorithm_test.go
+++ b/consensus/ethash/algorithm_test.go
@@ -697,11 +697,11 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
block := types.NewBlockWithHeader(&types.Header{
Number: big.NewInt(3311058),
ParentHash: common.HexToHash("0xd783efa4d392943503f28438ad5830b2d5964696ffc285f338585e9fe0a37a05"),
- UncleHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
+ UncleHash: types.EmptyUncleHash,
Coinbase: common.HexToAddress("0xc0ea08a2d404d3172d2add29a45be56da40e2949"),
Root: common.HexToHash("0x77d14e10470b5850332524f8cd6f69ad21f070ce92dca33ab2858300242ef2f1"),
- TxHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
- ReceiptHash: common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
Difficulty: big.NewInt(167925187834220),
GasLimit: 4015682,
GasUsed: 0,
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index faa4c52302..92806693ae 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -28,6 +28,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
@@ -253,6 +254,10 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
if header.GasUsed > header.GasLimit {
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
}
+ // Verify the header's EIP-1559 attributes.
+ if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
+ return err
+ }
// Verify that the gas limit remains within allowed bounds
diff := int64(parent.GasLimit) - int64(header.GasLimit)
diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go
index a65468f571..96dd5bb68d 100644
--- a/consensus/ethash/ethash.go
+++ b/consensus/ethash/ethash.go
@@ -32,12 +32,12 @@ import (
"time"
"unsafe"
+ lrupkg "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/rpc"
mmap "github.com/edsrzf/mmap-go"
- "github.com/hashicorp/golang-lru/simplelru"
)
var ErrInvalidDumpMagic = errors.New("invalid dump magic")
@@ -144,34 +144,45 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
return memoryMap(path)
}
+type cacheOrDataset interface {
+ *cache | *dataset
+}
+
// lru tracks caches or datasets by their last use time, keeping at most N of them.
-type lru struct {
+type lru[T cacheOrDataset] struct {
what string
- new func(epoch uint64) interface{}
+ new func(epoch uint64) T
mu sync.Mutex
// Items are kept in a LRU cache, but there is a special case:
// We always keep an item for (highest seen epoch) + 1 as the 'future item'.
- cache *simplelru.LRU
+ cache lrupkg.BasicLRU[uint64, T]
future uint64
- futureItem interface{}
+ futureItem T
}
-// newlru create a new least-recently-used cache for ither the verification caches
+// newlru create a new least-recently-used cache for either the verification caches
// or the mining datasets.
-func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru {
- if maxItems <= 0 {
- maxItems = 1
+func newlru[T cacheOrDataset](maxItems int, new func(epoch uint64) T) *lru[T] {
+ var what string
+ switch any(T(nil)).(type) {
+ case *cache:
+ what = "cache"
+ case *dataset:
+ what = "dataset"
+ default:
+ panic("unknown type")
+ }
+ return &lru[T]{
+ what: what,
+ new: new,
+ cache: lrupkg.NewBasicLRU[uint64, T](maxItems),
}
- cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) {
- log.Trace("Evicted ethash "+what, "epoch", key)
- })
- return &lru{what: what, new: new, cache: cache}
}
// get retrieves or creates an item for the given epoch. The first return value is always
// non-nil. The second return value is non-nil if lru thinks that an item will be useful in
// the near future.
-func (lru *lru) get(epoch uint64) (item, future interface{}) {
+func (lru *lru[T]) get(epoch uint64) (item, future T) {
lru.mu.Lock()
defer lru.mu.Unlock()
@@ -205,9 +216,8 @@ type cache struct {
once sync.Once // Ensures the cache is generated only once
}
-// newCache creates a new ethash verification cache and returns it as a plain Go
-// interface to be usable in an LRU cache.
-func newCache(epoch uint64) interface{} {
+// newCache creates a new ethash verification cache.
+func newCache(epoch uint64) *cache {
return &cache{epoch: epoch}
}
@@ -283,7 +293,7 @@ type dataset struct {
// newDataset creates a new ethash mining dataset and returns it as a plain Go
// interface to be usable in an LRU cache.
-func newDataset(epoch uint64) interface{} {
+func newDataset(epoch uint64) *dataset {
return &dataset{epoch: epoch}
}
@@ -394,14 +404,14 @@ type Config struct {
type Ethash struct {
config Config
- caches *lru // In memory caches to avoid regenerating too often
- datasets *lru // In memory datasets to avoid regenerating too often
+ caches *lru[*cache] // In memory caches to avoid regenerating too often
+ datasets *lru[*dataset] // In memory datasets to avoid regenerating too often
// Mining related fields
- rand *rand.Rand // Properly seeded random source for nonces
- threads int // Number of threads to mine on if mining
- update chan struct{} // Notification channel to update mining parameters
- hashrate metrics.Meter // Meter tracking the average hashrate
+ rand *rand.Rand // Properly seeded random source for nonces
+ threads int // Number of threads to mine on if mining
+ update chan struct{} // Notification channel to update mining parameters
+ hashrate *metrics.Meter // Meter tracking the average hashrate
// The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
@@ -425,8 +435,8 @@ func New(config Config) *Ethash {
}
return &Ethash{
config: config,
- caches: newlru("cache", config.CachesInMem, newCache),
- datasets: newlru("dataset", config.DatasetsInMem, newDataset),
+ caches: newlru(config.CachesInMem, newCache),
+ datasets: newlru(config.DatasetsInMem, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeter(),
}
@@ -494,15 +504,13 @@ func NewShared() *Ethash {
// stored on disk, and finally generating one if none can be found.
func (ethash *Ethash) cache(block uint64) *cache {
epoch := block / epochLength
- currentI, futureI := ethash.caches.get(epoch)
- current := currentI.(*cache)
+ current, future := ethash.caches.get(epoch)
// Wait for generation finish.
current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
// If we need a new future cache, now's a good time to regenerate it.
- if futureI != nil {
- future := futureI.(*cache)
+ if future != nil {
go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
}
return current
@@ -513,15 +521,13 @@ func (ethash *Ethash) cache(block uint64) *cache {
// stored on disk, and finally generating one if none can be found.
func (ethash *Ethash) dataset(block uint64) *dataset {
epoch := block / epochLength
- currentI, futureI := ethash.datasets.get(epoch)
- current := currentI.(*dataset)
+ current, future := ethash.datasets.get(epoch)
// Wait for generation finish.
current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
// If we need a new future dataset, now's a good time to regenerate it.
- if futureI != nil {
- future := futureI.(*dataset)
+ if future != nil {
go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
}
@@ -562,7 +568,7 @@ func (ethash *Ethash) SetThreads(threads int) {
// Hashrate implements PoW, returning the measured rate of the search invocations
// per second over the last minute.
func (ethash *Ethash) Hashrate() float64 {
- return ethash.hashrate.Rate1()
+ return ethash.hashrate.Snapshot().Rate1()
}
// APIs implements consensus.Engine, returning the user facing RPC APIs. Currently
diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go
new file mode 100644
index 0000000000..0763ebd9c9
--- /dev/null
+++ b/consensus/misc/eip1559/eip1559.go
@@ -0,0 +1,62 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package eip1559
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/params"
+)
+
+// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
+// - gas limit check
+// - basefee check
+func VerifyEip1559Header(config *params.ChainConfig, header *types.Header) error {
+ if !config.IsEIP1559(header.Number) {
+ if header.BaseFee != nil {
+ return fmt.Errorf("invalid baseFee: have %s, want ",
+ header.BaseFee)
+ }
+ return nil
+ }
+
+ // Verify the header is not malformed
+ if header.BaseFee == nil {
+ return fmt.Errorf("header is missing baseFee")
+ }
+
+ // Verify the baseFee is correct based on the current header.
+ expectedBaseFee := CalcBaseFee(config, header)
+ if header.BaseFee.Cmp(expectedBaseFee) != 0 {
+ return fmt.Errorf("invalid baseFee: have %s, want %s",
+ header.BaseFee, expectedBaseFee)
+ }
+ return nil
+}
+
+// CalcBaseFee calculates the basefee of the header.
+func CalcBaseFee(config *params.ChainConfig, header *types.Header) *big.Int {
+ // If the current block is the first EIP-1559 block, return the InitialBaseFee.
+ if config.IsEIP1559(header.Number) {
+ return new(big.Int).Set(common.BaseFee)
+ } else {
+ return nil
+ }
+}
diff --git a/consensus/misc/forks.go b/consensus/misc/forks.go
index 910ed620c5..b1e789f601 100644
--- a/consensus/misc/forks.go
+++ b/consensus/misc/forks.go
@@ -35,7 +35,7 @@ func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bo
// If the homestead reprice hash is set, validate it
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
- return fmt.Errorf("homestead gas reprice fork: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
+ return fmt.Errorf("homestead gas reprice fork: have %#x, want %#x", header.Hash(), config.EIP150Hash)
}
}
// All ok, return
diff --git a/consensus/tests/engine_v1_tests/helper.go b/consensus/tests/engine_v1_tests/helper.go
index 52baa648e0..afc1a22cf0 100644
--- a/consensus/tests/engine_v1_tests/helper.go
+++ b/consensus/tests/engine_v1_tests/helper.go
@@ -20,7 +20,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core"
- . "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
@@ -192,12 +191,12 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
return signedTX, nil
}
-func UpdateSigner(bc *BlockChain) error {
+func UpdateSigner(bc *core.BlockChain) error {
err := bc.UpdateM1()
return err
}
-func GetSnapshotSigner(bc *BlockChain, header *types.Header) (signersList, error) {
+func GetSnapshotSigner(bc *core.BlockChain, header *types.Header) (signersList, error) {
engine := bc.Engine().(*XDPoS.XDPoS)
snap, err := engine.GetSnapshot(bc, header)
if err != nil {
@@ -238,7 +237,7 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi
}
// V1 consensus engine
-func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
+func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
// Preparation
var err error
// Authorise
@@ -249,7 +248,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
blockchain.Client = backend
if err != nil {
- panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
+ panic(fmt.Errorf("error while creating simulated wallet for generating singer address and signer fn: %v", err))
}
blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn)
@@ -291,7 +290,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
return blockchain, backend, currentBlock, signer, signFn
}
-func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block {
+func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block {
currentBlock := startingBlock
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
@@ -322,18 +321,18 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
// Sign all the things for v1 block use v1 sigHash function
sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes())
if err != nil {
- panic(errors.New("Error when sign last v1 block hash during test block creation"))
+ panic(errors.New("error when sign last v1 block hash during test block creation"))
}
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
}
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
if err != nil {
- panic(fmt.Errorf("Fail to create block in test helper, %v", err))
+ panic(fmt.Errorf("fail to create block in test helper, %v", err))
}
return block
}
-func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) {
+func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) {
if customHeader.Extra == nil {
extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation
customHeader.Extra, _ = hex.DecodeString(extraSubstring)
@@ -377,13 +376,13 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
if err != nil {
return nil, fmt.Errorf("%v when get state", err)
}
- gp := new(GasPool).AddGas(header.GasLimit)
+ gp := new(core.GasPool).AddGas(header.GasLimit)
var gasUsed = new(uint64)
var receipts types.Receipts
for i, tx := range txs {
- statedb.Prepare(tx.Hash(), header.Hash(), i)
- receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
+ statedb.SetTxContext(tx.Hash(), i)
+ receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
if err != nil {
return nil, fmt.Errorf("%v when applying transaction", err)
}
diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go
index 0c006d59cb..0b1258d2a4 100644
--- a/consensus/tests/engine_v2_tests/api_test.go
+++ b/consensus/tests/engine_v2_tests/api_test.go
@@ -20,7 +20,7 @@ func TestGetMissedRoundsInEpochByBlockNumOnlyForV2Consensus(t *testing.T) {
data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
- assert.EqualError(t, err, "Not supported in the v1 consensus")
+ assert.EqualError(t, err, "not supported in the v1 consensus")
assert.Nil(t, data)
}
@@ -204,20 +204,14 @@ func TestGetBlockByEpochNumber(t *testing.T) {
assert.Nil(t, info)
info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(1)
- assert.Equal(t, info.EpochFirstBlockNumber.Int64(), int64(901))
- assert.Equal(t, info.EpochLastBlockNumber.Int64(), int64(1799))
assert.Equal(t, info.EpochRound, types.Round(1))
assert.Nil(t, err)
info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(2)
- assert.Equal(t, info.EpochFirstBlockNumber.Int64(), int64(1800))
- assert.Equal(t, info.EpochLastBlockNumber.Int64(), int64(1802))
assert.Equal(t, info.EpochRound, types.Round(900))
assert.Nil(t, err)
info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(3)
- assert.Equal(t, info.EpochFirstBlockNumber.Int64(), int64(1803))
- assert.Nil(t, info.EpochLastBlockNumber)
assert.Equal(t, info.EpochRound, types.Round(largeRound))
assert.Nil(t, err)
@@ -227,8 +221,6 @@ func TestGetBlockByEpochNumber(t *testing.T) {
info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(5)
assert.Equal(t, info.EpochRound, types.Round(largeRound2))
- assert.Equal(t, info.EpochFirstBlockNumber.Int64(), int64(1804))
- assert.Nil(t, info.EpochLastBlockNumber)
assert.Nil(t, err)
info, err = engine.APIs(blockchain)[0].Service.(*XDPoS.API).GetBlockInfoByEpochNum(6)
diff --git a/consensus/tests/engine_v2_tests/helper.go b/consensus/tests/engine_v2_tests/helper.go
index 454f482d71..1e9b6d14b9 100644
--- a/consensus/tests/engine_v2_tests/helper.go
+++ b/consensus/tests/engine_v2_tests/helper.go
@@ -23,7 +23,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/contracts"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core"
- . "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
@@ -88,10 +87,10 @@ func getSignerAndSignFn(pk *ecdsa.PrivateKey) (common.Address, func(account acco
pass := "" // not used but required by API
a1, err := ks.ImportECDSA(pk, pass)
if err != nil {
- return common.Address{}, nil, fmt.Errorf(err.Error())
+ return common.Address{}, nil, err
}
if err := ks.Unlock(a1, ""); err != nil {
- return a1.Address, nil, fmt.Errorf(err.Error())
+ return a1.Address, nil, err
}
return a1.Address, ks.SignHash, nil
}
@@ -314,12 +313,12 @@ func signingTxWithSignerFn(header *types.Header, nonce uint64, signer common.Add
return signedTx, nil
}
-func UpdateSigner(bc *BlockChain) error {
+func UpdateSigner(bc *core.BlockChain) error {
err := bc.UpdateM1()
return err
}
-func GetSnapshotSigner(bc *BlockChain, header *types.Header) (signersList, error) {
+func GetSnapshotSigner(bc *core.BlockChain, header *types.Header) (signersList, error) {
engine := bc.Engine().(*XDPoS.XDPoS)
snap, err := engine.GetSnapshot(bc, header)
if err != nil {
@@ -366,12 +365,12 @@ type ForkedBlockOptions struct {
}
// V2 concensus engine
-func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, forkedBlockOptions *ForkedBlockOptions) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) {
+func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, forkedBlockOptions *ForkedBlockOptions) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) {
// Preparation
var err error
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
if err != nil {
- panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
+ panic(fmt.Errorf("error while creating simulated wallet for generating singer address and signer fn: %v", err))
}
backend := getCommonBackend(t, chainConfig)
blockchain := backend.GetBlockChain()
@@ -456,7 +455,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
}
// V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) add penalty
-func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
+func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
// Preparation
var err error
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
@@ -507,7 +506,7 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in
}
// V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) 128 masternode candidates
-func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
+func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*core.BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
// Preparation
var err error
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
@@ -569,7 +568,7 @@ func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, ch
return blockchain, backend, currentBlock, signer, signFn
}
-func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block {
+func CreateBlock(blockchain *core.BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block {
currentBlock := startingBlock
if len(merkleRoot) == 0 {
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
@@ -634,19 +633,19 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
// Sign all the things for v1 block use v1 sigHash function
sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes())
if err != nil {
- panic(errors.New("Error when sign last v1 block hash during test block creation"))
+ panic(errors.New("error when sign last v1 block hash during test block creation"))
}
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
}
}
block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, chainConfig)
if err != nil {
- panic(fmt.Errorf("Fail to create block in test helper, %v", err))
+ panic(fmt.Errorf("fail to create block in test helper, %v", err))
}
return block
}
-func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) {
+func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs []*types.Transaction, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (*types.Block, error) {
if customHeader.Extra == nil {
extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation
customHeader.Extra, _ = hex.DecodeString(extraSubstring)
@@ -694,13 +693,13 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
if err != nil {
return nil, fmt.Errorf("%v when get state", err)
}
- gp := new(GasPool).AddGas(header.GasLimit)
+ gp := new(core.GasPool).AddGas(header.GasLimit)
var gasUsed = new(uint64)
var receipts types.Receipts
for i, tx := range txs {
- statedb.Prepare(tx.Hash(), header.Hash(), i)
- receipt, _, err, _ := ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
+ statedb.SetTxContext(tx.Hash(), i)
+ receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
if err != nil {
return nil, fmt.Errorf("%v when applying transaction", err)
}
@@ -729,7 +728,7 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A
return masternodes
}
-func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
+func findSignerAndSignFn(bc *core.BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), config *params.ChainConfig) (common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
addressToSign := signer
addressedSignFn := signFn
@@ -758,14 +757,14 @@ func findSignerAndSignFn(bc *BlockChain, header *types.Header, signer common.Add
}
addressedSignFn = signFn
if err != nil {
- panic(errors.New("Error trying to use one of the pre-defined private key to sign"))
+ panic(errors.New("error trying to use one of the pre-defined private key to sign"))
}
}
return addressToSign, addressedSignFn
}
-func sealHeader(bc *BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) {
+func sealHeader(bc *core.BlockChain, header *types.Header, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) {
// Sign all the things and seal it
signedBlockHeader := bc.Engine().(*XDPoS.XDPoS).SigHash(header)
@@ -806,7 +805,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common
signedHash, err := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes())
if err != nil {
- panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
+ panic(fmt.Errorf("error generate QC by creating signedHash: %v", err))
}
var signatures []types.Signature
if len(accKeys) == 0 {
@@ -831,7 +830,7 @@ func generateV2Extra(roundNumber int64, currentBlock *types.Block, signer common
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
- panic(fmt.Errorf("Error encode extra into bytes: %v", err))
+ panic(fmt.Errorf("error encode extra into bytes: %v", err))
}
return extraInBytes
}
diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go
index 3518c31008..1237c49914 100644
--- a/consensus/tests/engine_v2_tests/verify_header_test.go
+++ b/consensus/tests/engine_v2_tests/verify_header_test.go
@@ -119,7 +119,7 @@ func TestShouldVerifyBlock(t *testing.T) {
// Genrate QC
signedHash, err := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes())
if err != nil {
- panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
+ panic(fmt.Errorf("error generate QC by creating signedHash: %v", err))
}
// Sign from acc 1, 2, 3
acc1SignedHash := SignHashByPK(acc1Key, types.VoteSigHash(voteForSign).Bytes())
@@ -139,7 +139,7 @@ func TestShouldVerifyBlock(t *testing.T) {
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
- panic(fmt.Errorf("Error encode extra into bytes: %v", err))
+ panic(fmt.Errorf("error encode extra into bytes: %v", err))
}
invalidRoundBlock := blockchain.GetBlockByNumber(902).Header()
@@ -398,7 +398,7 @@ func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
- panic(fmt.Errorf("Error encode extra into bytes: %v", err))
+ panic(fmt.Errorf("error encode extra into bytes: %v", err))
}
headerWithDuplicatedSignatures := currentBlock.Header()
headerWithDuplicatedSignatures.Extra = extraInBytes
diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go
index 35e9028f9c..afaf5e818d 100644
--- a/consensus/tests/engine_v2_tests/vote_test.go
+++ b/consensus/tests/engine_v2_tests/vote_test.go
@@ -538,7 +538,7 @@ func TestVerifyVoteMsg(t *testing.T) {
engineV2.SetNewRoundFaker(blockchain, types.Round(14), false)
verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg)
assert.False(t, verified)
- assert.Equal(t, "Error while verifying message: invalid signature length", err.Error())
+ assert.Equal(t, "error while verifying message: invalid signature length", err.Error())
// Valid vote message from a master node
signHash, _ := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes())
diff --git a/console/bridge.go b/console/bridge.go
index 80f8095336..34f77dcbfb 100644
--- a/console/bridge.go
+++ b/console/bridge.go
@@ -76,7 +76,7 @@ func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) {
return nil, err
}
if password != confirm {
- return nil, errors.New("passwords don't match!")
+ return nil, errors.New("passwords don't match")
}
// A single string password was specified, use that
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
diff --git a/console/console_test.go b/console/console_test.go
index 9433026db8..0d5c94a754 100644
--- a/console/console_test.go
+++ b/console/console_test.go
@@ -153,8 +153,8 @@ func (env *tester) Close(t *testing.T) {
if err := env.console.Stop(false); err != nil {
t.Errorf("failed to stop embedded console: %v", err)
}
- if err := env.stack.Stop(); err != nil {
- t.Errorf("failed to stop embedded node: %v", err)
+ if err := env.stack.Close(); err != nil {
+ t.Errorf("failed to tear down embedded node: %v", err)
}
os.RemoveAll(env.workspace)
}
diff --git a/contracts/ens/ens.go b/contracts/ens/ens.go
index d12efa690c..7c47865097 100644
--- a/contracts/ens/ens.go
+++ b/contracts/ens/ens.go
@@ -100,45 +100,45 @@ func ensNode(name string) common.Hash {
return crypto.Keccak256Hash(parentNode[:], parentLabel[:])
}
-func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) {
- resolverAddr, err := self.Resolver(node)
+func (e *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) {
+ resolverAddr, err := e.Resolver(node)
if err != nil {
return nil, err
}
- resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend)
+ resolver, err := contract.NewPublicResolver(resolverAddr, e.contractBackend)
if err != nil {
return nil, err
}
return &contract.PublicResolverSession{
Contract: resolver,
- TransactOpts: self.TransactOpts,
+ TransactOpts: e.TransactOpts,
}, nil
}
-func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) {
- registrarAddr, err := self.Owner(node)
+func (e *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) {
+ registrarAddr, err := e.Owner(node)
if err != nil {
return nil, err
}
- registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend)
+ registrar, err := contract.NewFIFSRegistrar(registrarAddr, e.contractBackend)
if err != nil {
return nil, err
}
return &contract.FIFSRegistrarSession{
Contract: registrar,
- TransactOpts: self.TransactOpts,
+ TransactOpts: e.TransactOpts,
}, nil
}
// Resolve is a non-transactional call that returns the content hash associated with a name.
-func (self *ENS) Resolve(name string) (common.Hash, error) {
+func (e *ENS) Resolve(name string) (common.Hash, error) {
node := ensNode(name)
- resolver, err := self.getResolver(node)
+ resolver, err := e.getResolver(node)
if err != nil {
return common.Hash{}, err
}
@@ -153,26 +153,26 @@ func (self *ENS) Resolve(name string) (common.Hash, error) {
// Register registers a new domain name for the caller, making them the owner of the new name.
// Only works if the registrar for the parent domain implements the FIFS registrar protocol.
-func (self *ENS) Register(name string) (*types.Transaction, error) {
+func (e *ENS) Register(name string) (*types.Transaction, error) {
parentNode, label := ensParentNode(name)
- registrar, err := self.getRegistrar(parentNode)
+ registrar, err := e.getRegistrar(parentNode)
if err != nil {
return nil, err
}
- return registrar.Contract.Register(&self.TransactOpts, label, self.TransactOpts.From)
+ return registrar.Contract.Register(&e.TransactOpts, label, e.TransactOpts.From)
}
// SetContentHash sets the content hash associated with a name. Only works if the caller
// owns the name, and the associated resolver implements a `setContent` function.
-func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
+func (e *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
node := ensNode(name)
- resolver, err := self.getResolver(node)
+ resolver, err := e.getResolver(node)
if err != nil {
return nil, err
}
- opts := self.TransactOpts
+ opts := e.TransactOpts
opts.GasLimit = 200000
return resolver.Contract.SetContent(&opts, node, hash)
}
diff --git a/contracts/tests/Inherited_test.go b/contracts/tests/Inherited_test.go
index a1e4a47203..94298fc0e3 100644
--- a/contracts/tests/Inherited_test.go
+++ b/contracts/tests/Inherited_test.go
@@ -19,9 +19,10 @@ var (
)
func TestPriceFeed(t *testing.T) {
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
- glogger.Verbosity(log.LvlTrace)
- log.Root().SetHandler(glogger)
+ glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
+ glogger.Verbosity(log.LevelTrace)
+ log.SetDefault(log.NewLogger(glogger))
+
common.TIPXDCXCancellationFee = big.NewInt(0)
// init genesis
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{
diff --git a/contracts/trc21issuer/simulation/test/main.go b/contracts/trc21issuer/simulation/test/main.go
index 1ee2aff60e..489a7e24ef 100644
--- a/contracts/trc21issuer/simulation/test/main.go
+++ b/contracts/trc21issuer/simulation/test/main.go
@@ -43,7 +43,7 @@ func airDropTokenToAccountNoXDC() {
fmt.Println("wait 10s to airdrop success ", tx.Hash().Hex())
time.Sleep(10 * time.Second)
- _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
+ _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
receipt := map[string]interface{}{}
err = json.Unmarshal(receiptRpc, &receipt)
if err != nil {
@@ -80,8 +80,8 @@ func testTransferTRC21TokenWithAccountNoXDC() {
trc21IssuerInstance, _ := trc21issuer.NewTRC21Issuer(airDropAccount, common.TRC21IssuerSMC, client)
remainFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr)
- airDropBalanceBefore, err := trc21Instance.BalanceOf(simulation.AirdropAddr)
- receiverBalanceBefore, err := trc21Instance.BalanceOf(simulation.ReceiverAddr)
+ airDropBalanceBefore, _ := trc21Instance.BalanceOf(simulation.AirdropAddr)
+ receiverBalanceBefore, _ := trc21Instance.BalanceOf(simulation.ReceiverAddr)
// execute transferAmount trc to other address
tx, err := trc21Instance.Transfer(simulation.ReceiverAddr, simulation.TransferAmount)
if err != nil {
@@ -105,7 +105,7 @@ func testTransferTRC21TokenWithAccountNoXDC() {
if err != nil || balance.Cmp(remainAirDrop) != 0 {
log.Fatal("check balance after fail transferAmount in tr21: ", err, "get", balance, "wanted", remainAirDrop)
}
- _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
+ _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
receipt := map[string]interface{}{}
err = json.Unmarshal(receiptRpc, &receipt)
if err != nil {
@@ -140,15 +140,15 @@ func testTransferTrc21Fail() {
airDropAccount.GasPrice = big.NewInt(0).Mul(common.TRC21GasPrice, big.NewInt(2))
trc21Instance, _ := trc21issuer.NewTRC21(airDropAccount, trc21TokenAddr, client)
trc21IssuerInstance, _ := trc21issuer.NewTRC21Issuer(airDropAccount, common.TRC21IssuerSMC, client)
- balanceIssuerFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr)
+ balanceIssuerFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr)
minFee, err := trc21Instance.MinFee()
if err != nil {
log.Fatal("can't get minFee of trc21 smart contract:", err)
}
- ownerBalance, err := trc21Instance.BalanceOf(simulation.MainAddr)
- remainFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr)
- airDropBalanceBefore, err := trc21Instance.BalanceOf(simulation.AirdropAddr)
+ ownerBalance, _ := trc21Instance.BalanceOf(simulation.MainAddr)
+ remainFee, _ := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr)
+ airDropBalanceBefore, _ := trc21Instance.BalanceOf(simulation.AirdropAddr)
tx, err := trc21Instance.Transfer(common.Address{}, big.NewInt(1))
if err != nil {
@@ -171,7 +171,7 @@ func testTransferTrc21Fail() {
if err != nil || balance.Cmp(ownerBalance) != 0 {
log.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
}
- _, receiptRpc, err := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
+ _, receiptRpc, _ := client.GetTransactionReceiptResult(context.Background(), tx.Hash())
receipt := map[string]interface{}{}
err = json.Unmarshal(receiptRpc, &receipt)
if err != nil {
diff --git a/contracts/trc21issuer/trc21issuer_test.go b/contracts/trc21issuer/trc21issuer_test.go
index 698aa3f97d..a8658b105b 100644
--- a/contracts/trc21issuer/trc21issuer_test.go
+++ b/contracts/trc21issuer/trc21issuer_test.go
@@ -1,6 +1,7 @@
package trc21issuer
import (
+ "context"
"math/big"
"testing"
@@ -60,7 +61,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
contractBackend.Commit()
//check trc21 SMC balance
- balance, err := contractBackend.BalanceAt(nil, trc21IssuerAddr, nil)
+ balance, err := contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil)
if err != nil || balance.Cmp(minApply) != 0 {
t.Fatal("can't get balance in trc21Issuer SMC: ", err, "got", balance, "wanted", minApply)
}
@@ -78,7 +79,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
t.Fatal("can't execute transfer in tr20: ", err)
}
contractBackend.Commit()
- receipt, err := contractBackend.TransactionReceipt(nil, tx.Hash())
+ receipt, err := contractBackend.TransactionReceipt(context.TODO(), tx.Hash())
if err != nil {
t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash())
}
@@ -100,7 +101,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
t.Fatal("check balance token fee in smart contract: got", balanceIssuerFee, "wanted", remainFee)
}
//check trc21 SMC balance
- balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil)
+ balance, err = contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil)
if err != nil || balance.Cmp(remainFee) != 0 {
t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
}
@@ -130,7 +131,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
t.Fatal("check balance after fail transfer in tr20: ", err, "get", balance, "wanted", remainAirDrop)
}
- receipt, err = contractBackend.TransactionReceipt(nil, tx.Hash())
+ receipt, err = contractBackend.TransactionReceipt(context.TODO(), tx.Hash())
if err != nil {
t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash())
}
@@ -142,7 +143,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) {
t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
}
//check trc21 SMC balance
- balance, err = contractBackend.BalanceAt(nil, trc21IssuerAddr, nil)
+ balance, err = contractBackend.BalanceAt(context.TODO(), trc21IssuerAddr, nil)
if err != nil || balance.Cmp(remainFee) != 0 {
t.Fatal("can't get balance token fee in smart contract: ", err, "got", balanceIssuerFee, "wanted", remainFee)
}
diff --git a/contracts/utils.go b/contracts/utils.go
index 9af44e052a..b309e5bdd2 100644
--- a/contracts/utils.go
+++ b/contracts/utils.go
@@ -35,14 +35,13 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/consensus"
-
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract"
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
- stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
@@ -62,7 +61,7 @@ type RewardLog struct {
var TxSignMu sync.RWMutex
// Send tx sign for block number to smart contract blockSigner.
-func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error {
+func CreateTransactionSign(chainConfig *params.ChainConfig, pool *txpool.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error {
TxSignMu.Lock()
defer TxSignMu.Unlock()
if chainConfig.XDPoS != nil {
@@ -210,8 +209,8 @@ func BuildTxOpeningRandomize(nonce uint64, randomizeAddr common.Address, randomi
}
// Get signers signed for blockNumber from blockSigner contract.
-func GetSignersFromContract(state *stateDatabase.StateDB, block *types.Block) ([]common.Address, error) {
- return stateDatabase.GetSigners(state, block), nil
+func GetSignersFromContract(statedb *state.StateDB, block *types.Block) ([]common.Address, error) {
+ return state.GetSigners(statedb, block), nil
}
// Get signers signed for blockNumber from blockSigner contract.
@@ -332,20 +331,19 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- {
header = chain.GetHeader(header.ParentHash, i)
mapBlkHash[i] = header.Hash()
- signData, ok := c.GetCachedSigningTxs(header.Hash())
+ signingTxs, ok := c.GetCachedSigningTxs(header.Hash())
if !ok {
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
block := chain.GetBlock(header.Hash(), i)
txs := block.Transactions()
if !chain.Config().IsTIPSigning(header.Number) {
receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i)
- signData = c.CacheNoneTIPSigningTxs(header, txs, receipts)
+ signingTxs = c.CacheNoneTIPSigningTxs(header, txs, receipts)
} else {
- signData = c.CacheSigningTxs(header.Hash(), txs)
+ signingTxs = c.CacheSigningTxs(header.Hash(), txs)
}
}
- txs := signData.([]*types.Transaction)
- for _, tx := range txs {
+ for _, tx := range signingTxs {
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
from := *tx.From()
data[blkHash] = append(data[blkHash], from)
@@ -414,27 +412,27 @@ func CalculateRewardForSigner(chainReward *big.Int, signers map[common.Address]*
}
// Get candidate owner by address.
-func GetCandidatesOwnerBySigner(state *state.StateDB, signerAddr common.Address) common.Address {
- owner := stateDatabase.GetCandidateOwner(state, signerAddr)
+func GetCandidatesOwnerBySigner(statedb *state.StateDB, signerAddr common.Address) common.Address {
+ owner := state.GetCandidateOwner(statedb, signerAddr)
return owner
}
-func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state.StateDB, signer common.Address, calcReward *big.Int, blockNumber uint64) (error, map[common.Address]*big.Int) {
+func CalculateRewardForHolders(foundationWalletAddr common.Address, state *state.StateDB, signer common.Address, calcReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) {
rewards, err := GetRewardBalancesRate(foundationWalletAddr, state, signer, calcReward, blockNumber)
if err != nil {
- return err, nil
+ return nil, err
}
- return nil, rewards
+ return rewards, nil
}
-func GetRewardBalancesRate(foundationWalletAddr common.Address, state *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) {
- owner := GetCandidatesOwnerBySigner(state, masterAddr)
+func GetRewardBalancesRate(foundationWalletAddr common.Address, statedb *state.StateDB, masterAddr common.Address, totalReward *big.Int, blockNumber uint64) (map[common.Address]*big.Int, error) {
+ owner := GetCandidatesOwnerBySigner(statedb, masterAddr)
balances := make(map[common.Address]*big.Int)
rewardMaster := new(big.Int).Mul(totalReward, new(big.Int).SetInt64(common.RewardMasterPercent))
rewardMaster = new(big.Int).Div(rewardMaster, new(big.Int).SetInt64(100))
balances[owner] = rewardMaster
// Get voters for masternode.
- voters := stateDatabase.GetVoters(state, masterAddr)
+ voters := state.GetVoters(statedb, masterAddr)
if len(voters) > 0 {
totalVoterReward := new(big.Int).Mul(totalReward, new(big.Int).SetUint64(common.RewardVoterPercent))
@@ -446,7 +444,7 @@ func GetRewardBalancesRate(foundationWalletAddr common.Address, state *state.Sta
if _, ok := voterCaps[voteAddr]; ok && common.TIP2019Block.Uint64() <= blockNumber {
continue
}
- voterCap := stateDatabase.GetVoterCap(state, masterAddr, voteAddr)
+ voterCap := state.GetVoterCap(statedb, masterAddr, voteAddr)
totalCap.Add(totalCap, voterCap)
voterCaps[voteAddr] = voterCap
}
@@ -558,7 +556,7 @@ func Decrypt(key []byte, cryptoText string) string {
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
- return fmt.Sprintf("%s", ciphertext)
+ return string(ciphertext[:])
}
// Generate random string.
diff --git a/core/asm/asm.go b/core/asm/asm.go
index 79406aaf60..33aa034e71 100644
--- a/core/asm/asm.go
+++ b/core/asm/asm.go
@@ -109,7 +109,7 @@ func PrintDisassembled(code string) error {
it := NewInstructionIterator(script)
for it.Next() {
if it.Arg() != nil && 0 < len(it.Arg()) {
- fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
+ fmt.Printf("%06v: %v %#x\n", it.PC(), it.Op(), it.Arg())
} else {
fmt.Printf("%06v: %v\n", it.PC(), it.Op())
}
@@ -124,7 +124,7 @@ func Disassemble(script []byte) ([]string, error) {
it := NewInstructionIterator(script)
for it.Next() {
if it.Arg() != nil && 0 < len(it.Arg()) {
- instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
+ instrs = append(instrs, fmt.Sprintf("%06v: %v %#x\n", it.PC(), it.Op(), it.Arg()))
} else {
instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op()))
}
diff --git a/core/bench_test.go b/core/bench_test.go
index 1129142b1a..0d797dbea5 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
- gas, _ := IntrinsicGas(data, nil, false, false)
+ gas, _ := IntrinsicGas(data, nil, false, false, false)
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
@@ -231,13 +231,15 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
ParentHash: hash,
Difficulty: big.NewInt(1),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
}
hash = header.Hash()
+
rawdb.WriteHeader(db, header)
rawdb.WriteCanonicalHash(db, hash, n)
WriteTd(db, hash, n, big.NewInt(int64(n+1)))
+
if full || n == 0 {
block := types.NewBlockWithHeader(header)
rawdb.WriteBody(db, hash, n, block.Body())
diff --git a/core/blockchain.go b/core/blockchain.go
index 006e94fd0e..217e3be9e0 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -56,19 +56,38 @@ import (
)
var (
- blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
- CheckpointCh = make(chan int)
- ErrNoGenesis = errors.New("Genesis not found in chain")
+ headBlockGauge = metrics.NewRegisteredGauge("chain/head/block", nil)
+ headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
+ headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
- blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
- blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
- blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
- blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil)
+ chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
+
+ accountReadTimer = metrics.NewRegisteredResettingTimer("chain/account/reads", nil)
+ accountHashTimer = metrics.NewRegisteredResettingTimer("chain/account/hashes", nil)
+ accountUpdateTimer = metrics.NewRegisteredResettingTimer("chain/account/updates", nil)
+ accountCommitTimer = metrics.NewRegisteredResettingTimer("chain/account/commits", nil)
+
+ storageReadTimer = metrics.NewRegisteredResettingTimer("chain/storage/reads", nil)
+ storageHashTimer = metrics.NewRegisteredResettingTimer("chain/storage/hashes", nil)
+ storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
+ storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
+
+ blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
+ blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
+ blockExecutionTimer = metrics.NewRegisteredResettingTimer("chain/execution", nil)
+ blockWriteTimer = metrics.NewRegisteredResettingTimer("chain/write", nil)
+
+ blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
+ blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
+ blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
+
+ CheckpointCh = make(chan int)
)
const (
bodyCacheLimit = 256
blockCacheLimit = 256
+ receiptsCacheLimit = 32
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
badBlockLimit = 10
@@ -134,7 +153,6 @@ type BlockChain struct {
chainmu sync.RWMutex // blockchain insertion lock
procmu sync.RWMutex // block processor lock
- checkpoint int // checkpoint counts towards the new checkpoint
currentBlock atomic.Value // Current head of the block chain
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
@@ -142,6 +160,7 @@ type BlockChain struct {
bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] // Cache for the most recent block bodies in RLP encoded format
+ receiptsCache *lru.Cache[common.Hash, types.Receipts] // Cache for the most recent block receipts
blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks
resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks
calculatingBlock *lru.Cache[common.Hash, *CalculatedBlock] // Cache for processing blocks
@@ -161,9 +180,8 @@ type BlockChain struct {
validator Validator // block and state validator interface
vmConfig vm.Config
- shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
- IPCEndpoint string
- Client bind.ContractBackend // Global ipc client instance.
+ IPCEndpoint string
+ Client bind.ContractBackend // Global ipc client instance.
// Blocks hash array by block number
// cache field for tracking finality purpose, can't use for tracking block vs block relationship
@@ -196,6 +214,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
quit: make(chan struct{}),
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
+ receiptsCache: lru.NewCache[common.Hash, types.Receipts](receiptsCacheLimit),
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
resultProcess: lru.NewCache[common.Hash, *ResultProcessBlock](blockCacheLimit),
@@ -223,6 +242,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if bc.genesisBlock == nil {
return nil, ErrNoGenesis
}
+
+ // Update chain info data metrics
+ chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainId.String()})
+
if err := bc.loadLastState(); err != nil {
return nil, err
}
@@ -338,6 +361,7 @@ func (bc *BlockChain) loadLastState() error {
}
// Everything seems to be fine, set as the head block
bc.currentBlock.Store(currentBlock)
+ headBlockGauge.Update(int64(currentBlock.NumberU64()))
// Restore the last known head header
currentHeader := currentBlock.Header()
@@ -357,9 +381,12 @@ func (bc *BlockChain) loadLastState() error {
// Restore the last known head fast block
bc.currentFastBlock.Store(currentBlock)
+ headFastBlockGauge.Update(int64(currentBlock.NumberU64()))
+
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock.Store(block)
+ headFastBlockGauge.Update(int64(block.NumberU64()))
}
}
@@ -397,6 +424,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Clear out any stale content from the caches
bc.bodyCache.Purge()
bc.bodyRLPCache.Purge()
+ bc.receiptsCache.Purge()
bc.blockCache.Purge()
bc.futureBlocks.Purge()
bc.blocksHashCache.Purge()
@@ -404,23 +432,28 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Rewind the block chain, ensuring we don't end up with a stateless head block
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
+ headBlockGauge.Update(int64(currentHeader.Number.Uint64()))
}
if currentBlock := bc.CurrentBlock(); currentBlock != nil {
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
bc.currentBlock.Store(bc.genesisBlock)
+ headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
}
}
// Rewind the fast block in a simpleton way to the target head
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
+ headFastBlockGauge.Update(int64(currentHeader.Number.Uint64()))
}
// If either blocks reached nil, reset to the genesis state
if currentBlock := bc.CurrentBlock(); currentBlock == nil {
bc.currentBlock.Store(bc.genesisBlock)
+ headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
}
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
bc.currentFastBlock.Store(bc.genesisBlock)
+ headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
}
currentBlock := bc.CurrentBlock()
currentFastBlock := bc.CurrentFastBlock()
@@ -437,7 +470,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// Make sure that both the block as well at its state trie exists
block := bc.GetBlockByHash(hash)
if block == nil {
- return fmt.Errorf("non existent block [%x…]", hash[:4])
+ return fmt.Errorf("non existent block [%x..]", hash[:4])
}
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
return err
@@ -445,6 +478,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
// If all checks out, manually set the head block
bc.mu.Lock()
bc.currentBlock.Store(block)
+ headBlockGauge.Update(int64(block.NumberU64()))
bc.mu.Unlock()
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
@@ -575,9 +609,12 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
bc.genesisBlock = genesis
bc.insert(bc.genesisBlock, false)
bc.currentBlock.Store(bc.genesisBlock)
+ headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
+
bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock.Store(bc.genesisBlock)
+ headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
return nil
}
@@ -677,7 +714,9 @@ func (bc *BlockChain) insert(block *types.Block, writeBlock bool) {
if writeBlock {
rawdb.WriteBlock(bc.db, block)
}
+
bc.currentBlock.Store(block)
+ headBlockGauge.Update(int64(block.NumberU64()))
// save cache BlockSigners
if bc.chainConfig.XDPoS != nil && !bc.chainConfig.IsTIPSigning(block.Number()) {
@@ -695,6 +734,7 @@ func (bc *BlockChain) insert(block *types.Block, writeBlock bool) {
log.Crit("Failed to insert head fast block hash", "err", err)
}
bc.currentFastBlock.Store(block)
+ headFastBlockGauge.Update(int64(block.NumberU64()))
}
}
@@ -809,7 +849,19 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
- return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
+ if receipts, ok := bc.receiptsCache.Get(hash); ok {
+ return receipts
+ }
+ number := rawdb.ReadHeaderNumber(bc.db, hash)
+ if number == nil {
+ return nil
+ }
+ receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
+ if receipts == nil {
+ return nil
+ }
+ bc.receiptsCache.Add(hash, receipts)
+ return receipts
}
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
@@ -1020,58 +1072,17 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
bc.currentFastBlock.Store(newFastBlock)
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
+ headFastBlockGauge.Update(int64(newFastBlock.NumberU64()))
}
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
bc.currentBlock.Store(newBlock)
+ headBlockGauge.Update(int64(newBlock.NumberU64()))
rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash())
}
}
}
-// SetReceiptsData computes all the non-consensus fields of the receipts
-func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
- signer := types.MakeSigner(config, block.Number())
-
- transactions, logIndex := block.Transactions(), uint(0)
- if len(transactions) != len(receipts) {
- return errors.New("transaction and receipt count mismatch")
- }
-
- for j := 0; j < len(receipts); j++ {
- // The transaction hash can be retrieved from the transaction itself
- receipts[j].TxHash = transactions[j].Hash()
-
- // block location fields
- receipts[j].BlockHash = block.Hash()
- receipts[j].BlockNumber = block.Number()
- receipts[j].TransactionIndex = uint(j)
-
- // The contract address can be derived from the transaction itself
- if transactions[j].To() == nil {
- // Deriving the signer is expensive, only do if it's actually needed
- from, _ := types.Sender(signer, transactions[j])
- receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
- }
- // The used gas can be calculated based on previous receipts
- if j == 0 {
- receipts[j].GasUsed = receipts[j].CumulativeGasUsed
- } else {
- receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed
- }
- // The derived log fields can simply be set from the block and transaction
- for k := 0; k < len(receipts[j].Logs); k++ {
- receipts[j].Logs[k].BlockNumber = block.NumberU64()
- receipts[j].Logs[k].BlockHash = block.Hash()
- receipts[j].Logs[k].TxHash = receipts[j].TxHash
- receipts[j].Logs[k].TxIndex = uint(j)
- receipts[j].Logs[k].Index = logIndex
- logIndex++
- }
- }
- return nil
-}
-
// InsertReceiptChain attempts to complete an already existing header chain with
// transaction and receipt data.
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
@@ -1083,7 +1094,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
- return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
+ return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, blockChain[i-1].NumberU64(),
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
}
}
@@ -1100,22 +1111,23 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
return 0, nil
}
+ blockHash, blockNumber := block.Hash(), block.NumberU64()
// Short circuit if the owner header is unknown
- if !bc.HasHeader(block.Hash(), block.NumberU64()) {
- return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
+ if !bc.HasHeader(blockHash, blockNumber) {
+ return i, fmt.Errorf("containing header #%d [%x..] unknown", blockNumber, blockHash.Bytes()[:4])
}
// Skip if the entire data is already known
- if bc.HasBlock(block.Hash(), block.NumberU64()) {
+ if bc.HasBlock(blockHash, blockNumber) {
stats.ignored++
continue
}
// Compute all the non-consensus fields of the receipts
- if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
+ if err := receipts.DeriveFields(bc.chainConfig, blockHash, blockNumber, block.BaseFee(), block.Transactions()); err != nil {
return i, fmt.Errorf("failed to set receipts data: %v", err)
}
// Write all the data out into the database
- rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
- if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
+ rawdb.WriteBody(batch, blockHash, blockNumber, block.Body())
+ if err := WriteBlockReceipts(batch, blockHash, blockNumber, receipts); err != nil {
return i, fmt.Errorf("failed to write block receipts: %v", err)
}
if err := WriteTxLookupEntries(batch, block); err != nil {
@@ -1148,6 +1160,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
log.Crit("Failed to update head fast block hash", "err", err)
}
bc.currentFastBlock.Store(head)
+ headFastBlockGauge.Update(int64(head.NumberU64()))
}
}
bc.mu.Unlock()
@@ -1271,8 +1284,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
if current := block.NumberU64(); current > triesInMemory {
// Find the next state trie we need to commit
chosen := current - triesInMemory
- oldTradingRoot := common.Hash{}
- oldLendingRoot := common.Hash{}
// Only write to disk if we exceeded our memory allowance *and* also have at
// least a given number of tries gapped.
//
@@ -1308,8 +1319,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
if tradingTrieDb != nil && lendingTrieDb != nil {
b := bc.GetBlock(header.Hash(), current-triesInMemory)
author, _ := bc.Engine().Author(b.Header())
- oldTradingRoot, _ = tradingService.GetTradingStateRoot(b, author)
- oldLendingRoot, _ = lendingService.GetLendingStateRoot(b, author)
+ oldTradingRoot, _ := tradingService.GetTradingStateRoot(b, author)
+ oldLendingRoot, _ := lendingService.GetLendingStateRoot(b, author)
tradingTrieDb.Commit(oldTradingRoot, true)
lendingTrieDb.Commit(oldLendingRoot, true)
}
@@ -1451,7 +1462,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
- return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
+ return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].NumberU64(),
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
}
}
@@ -1484,7 +1495,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
defer close(abort)
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
- senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
+ SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
// Iterate over the blocks and insert when the verifier permits
for i, block := range chain {
@@ -1647,10 +1658,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}
// liquidate / finalize open lendingTrades
if block.Number().Uint64()%bc.chainConfig.XDPoS.Epoch == common.LiquidateLendingTradeBlock {
- finalizedTrades := map[common.Hash]*lendingstate.LendingTrade{}
- finalizedTrades, _, _, _, _, err = lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState)
+ finalizedTrades, _, _, _, _, err := lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState)
if err != nil {
- return i, events, coalescedLogs, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err)
+ return i, events, coalescedLogs, fmt.Errorf("failed to ProcessLiquidationData. Err: %v", err)
}
if isSDKNode {
finalizedTx := lendingstate.FinalizedResult{}
@@ -1662,7 +1672,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}
}
//check
- if tradingState != nil && tradingService != nil {
+ if tradingState != nil {
gotRoot := tradingState.IntermediateRoot()
expectRoot, _ := tradingService.GetTradingStateRoot(block, author)
parentRoot, _ := tradingService.GetTradingStateRoot(parent, parentAuthor)
@@ -1678,7 +1688,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
expectRoot, _ := lendingService.GetLendingStateRoot(block, author)
parentRoot, _ := lendingService.GetLendingStateRoot(parent, parentAuthor)
if gotRoot != expectRoot {
- err = fmt.Errorf("invalid lending state merke trie got : %s , expect : %s , parent :%s ", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex())
+ err = fmt.Errorf("invalid lending state merke trie got: %s, expect: %s, parent: %s", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex())
bc.reportBlock(block, nil, err)
return i, events, coalescedLogs, err
}
@@ -1688,7 +1698,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}
feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), statedb)
// Process block using the parent state as reference point.
+ t0 := time.Now()
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, tradingState, bc.vmConfig, feeCapacity)
+ t1 := time.Now()
if err != nil {
bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err
@@ -1699,19 +1711,41 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err
}
+ t2 := time.Now()
proctime := time.Since(bstart)
+
// Write the block to the chain and get the status.
status, err := bc.WriteBlockWithState(block, receipts, statedb, tradingState, lendingState)
+ t3 := time.Now()
if err != nil {
return i, events, coalescedLogs, err
}
+
+ // Update the metrics subsystem with all the measurements
+ accountReadTimer.Update(statedb.AccountReads)
+ accountHashTimer.Update(statedb.AccountHashes)
+ accountUpdateTimer.Update(statedb.AccountUpdates)
+ accountCommitTimer.Update(statedb.AccountCommits)
+
+ storageReadTimer.Update(statedb.StorageReads)
+ storageHashTimer.Update(statedb.StorageHashes)
+ storageUpdateTimer.Update(statedb.StorageUpdates)
+ storageCommitTimer.Update(statedb.StorageCommits)
+
+ trieAccess := statedb.AccountReads + statedb.AccountHashes + statedb.AccountUpdates + statedb.AccountCommits
+ trieAccess += statedb.StorageReads + statedb.StorageHashes + statedb.StorageUpdates + statedb.StorageCommits
+
+ blockInsertTimer.UpdateSince(bstart)
+ blockExecutionTimer.Update(t1.Sub(t0) - trieAccess)
+ blockValidationTimer.Update(t2.Sub(t1))
+ blockWriteTimer.Update(t3.Sub(t2))
+
switch status {
case CanonStatTy:
log.Debug("Inserted new block from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)))
coalescedLogs = append(coalescedLogs, logs...)
- blockInsertTimer.UpdateSince(bstart)
events = append(events, ChainEvent{block, block.Hash(), logs})
lastCanon = block
@@ -1725,8 +1759,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
case SideStatTy:
log.Debug("Inserted forked block from downloader", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
-
- blockInsertTimer.UpdateSince(bstart)
events = append(events, ChainSideEvent{block})
bc.UpdateBlocksHashCache(block)
}
@@ -1930,7 +1962,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
if block.Number().Uint64()%bc.chainConfig.XDPoS.Epoch == common.LiquidateLendingTradeBlock {
finalizedTrades, _, _, _, _, err := lendingService.ProcessLiquidationData(block.Header(), bc, statedb, tradingState, lendingState)
if err != nil {
- return nil, fmt.Errorf("failed to ProcessLiquidationData. Err: %v ", err)
+ return nil, fmt.Errorf("failed to ProcessLiquidationData. Err: %v", err)
}
if isSDKNode {
finalizedTx := lendingstate.FinalizedResult{}
@@ -1941,7 +1973,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
}
}
}
- if tradingState != nil && tradingService != nil {
+ if tradingState != nil {
gotRoot := tradingState.IntermediateRoot()
expectRoot, _ := tradingService.GetTradingStateRoot(block, author)
parentRoot, _ := tradingService.GetTradingStateRoot(parent, parentAuthor)
@@ -1957,7 +1989,7 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu
expectRoot, _ := lendingService.GetLendingStateRoot(block, author)
parentRoot, _ := lendingService.GetLendingStateRoot(parent, parentAuthor)
if gotRoot != expectRoot {
- err = fmt.Errorf("invalid lending state merke trie got : %s , expect : %s , parent : %s ", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex())
+ err = fmt.Errorf("invalid lending state merke trie got: %s , expect : %s , parent : %s", gotRoot.Hex(), expectRoot.Hex(), parentRoot.Hex())
bc.reportBlock(block, nil, err)
return nil, err
}
@@ -2045,7 +2077,6 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(block.ReceivedAt)))
coalescedLogs = append(coalescedLogs, result.logs...)
events = append(events, ChainEvent{block, block.Hash(), result.logs})
-
// Only count canonical blocks for GC processing time
bc.gcproc += result.proctime
bc.UpdateBlocksHashCache(block)
@@ -2056,10 +2087,8 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
case SideStatTy:
log.Debug("Inserted forked block from fetcher", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(block.ReceivedAt)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
-
blockInsertTimer.Update(result.proctime)
events = append(events, ChainSideEvent{block})
-
bc.UpdateBlocksHashCache(block)
}
stats.processed++
@@ -2134,6 +2163,25 @@ func countTransactions(chain []*types.Block) (c int) {
return c
}
+// collectLogs collects the logs that were generated or removed during
+// the processing of a block. These logs are later announced as deleted or reborn.
+func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
+ receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
+ if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.BaseFee(), b.Transactions()); err != nil {
+ log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err)
+ }
+
+ var logs []*types.Log
+ for _, receipt := range receipts {
+ for _, log := range receipt.Logs {
+ l := *log
+ l.Removed = removed
+ logs = append(logs, &l)
+ }
+ }
+ return logs
+}
+
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
// to be part of the new canonical chain and accumulates potential missing transactions and post an
// event about them
@@ -2144,20 +2192,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
commonBlock *types.Block
deletedTxs types.Transactions
deletedLogs []*types.Log
- // collectLogs collects the logs that were generated during the
- // processing of the block that corresponds with the given hash.
- // These logs are later announced as deleted.
- collectLogs = func(h common.Hash) {
- // Coalesce logs and set 'Removed'.
- receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
- for _, receipt := range receipts {
- for _, log := range receipt.Logs {
- del := *log
- del.Removed = true
- deletedLogs = append(deletedLogs, &del)
- }
- }
- }
)
log.Warn("Reorg", "oldBlock hash", oldBlock.Hash().Hex(), "number", oldBlock.NumberU64(), "newBlock hash", newBlock.Hash().Hex(), "number", newBlock.NumberU64())
@@ -2167,8 +2201,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
oldChain = append(oldChain, oldBlock)
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
-
- collectLogs(oldBlock.Hash())
+ if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 {
+ deletedLogs = append(deletedLogs, logs...)
+ }
}
} else {
// reduce new chain and append new chain blocks for inserting later on
@@ -2177,10 +2212,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
}
}
if oldBlock == nil {
- return errors.New("Invalid old chain")
+ return errors.New("invalid old chain")
}
if newBlock == nil {
- return errors.New("Invalid new chain")
+ return errors.New("invalid new chain")
}
for {
@@ -2192,14 +2227,16 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
oldChain = append(oldChain, oldBlock)
newChain = append(newChain, newBlock)
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
- collectLogs(oldBlock.Hash())
+ if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 {
+ deletedLogs = append(deletedLogs, logs...)
+ }
oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
if oldBlock == nil {
- return errors.New("Invalid old chain")
+ return errors.New("invalid old chain")
}
if newBlock == nil {
- return errors.New("Invalid new chain")
+ return errors.New("invalid new chain")
}
}
// Ensure XDPoS engine committed block will be not reverted
@@ -2276,9 +2313,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
}
}()
}
- if bc.chainConfig.IsTIPXDCXReceiver(commonBlock.Number()) && bc.chainConfig.XDPoS != nil && commonBlock.NumberU64() > bc.chainConfig.XDPoS.Epoch {
- bc.reorgTxMatches(deletedTxs, newChain)
- }
return nil
}
@@ -2365,7 +2399,7 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e
Chain config: %v
Number: %v
-Hash: 0x%x
+Hash: %#x
%v
Round: %v
@@ -2406,26 +2440,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
return bc.hc.InsertHeaderChain(chain, whFunc, start)
}
-// writeHeader writes a header into the local chain, given that its parent is
-// already known. If the total difficulty of the newly inserted header becomes
-// greater than the current known TD, the canonical chain is re-routed.
-//
-// Note: This method is not concurrent-safe with inserting blocks simultaneously
-// into the chain, as side effects caused by reorganisations cannot be emulated
-// without the real blocks. Hence, writing headers directly should only be done
-// in two scenarios: pure-header mode of operation (light clients), or properly
-// separated header/block phases (non-archive clients).
-func (bc *BlockChain) writeHeader(header *types.Header) error {
- bc.wg.Add(1)
- defer bc.wg.Done()
-
- bc.mu.Lock()
- defer bc.mu.Unlock()
-
- _, err := bc.hc.WriteHeader(header)
- return err
-}
-
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
func (bc *BlockChain) CurrentHeader() *types.Header {
@@ -2663,44 +2677,6 @@ func (bc *BlockChain) logExchangeData(block *types.Block) {
}
}
-func (bc *BlockChain) reorgTxMatches(deletedTxs types.Transactions, newChain types.Blocks) {
- engine, ok := bc.Engine().(*XDPoS.XDPoS)
- if !ok || engine == nil {
- return
- }
- XDCXService := engine.GetXDCXService()
- lendingService := engine.GetLendingService()
- if XDCXService == nil || !XDCXService.IsSDKNode() {
- return
- }
- start := time.Now()
- defer func() {
- //The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns
- // That's why we should put this log statement in an anonymous function
- log.Debug("reorgTxMatches takes", "time", common.PrettyDuration(time.Since(start)))
- }()
- for _, deletedTx := range deletedTxs {
- if deletedTx.IsTradingTransaction() {
- log.Debug("Rollback reorg txMatch", "txhash", deletedTx.Hash())
- if err := XDCXService.RollbackReorgTxMatch(deletedTx.Hash()); err != nil {
- log.Crit("Reorg trading failed", "err", err, "hash", deletedTx.Hash())
- }
- }
- if lendingService != nil && (deletedTx.IsLendingTransaction() || deletedTx.IsLendingFinalizedTradeTransaction()) {
- log.Debug("Rollback reorg lendingItem", "txhash", deletedTx.Hash())
- if err := lendingService.RollbackLendingData(deletedTx.Hash()); err != nil {
- log.Crit("Reorg lending failed", "err", err, "hash", deletedTx.Hash())
- }
- }
- }
-
- // apply new chain
- for i := len(newChain) - 1; i >= 0; i-- {
- bc.logExchangeData(newChain[i])
- bc.logLendingData(newChain[i])
- }
-}
-
func (bc *BlockChain) logLendingData(block *types.Block) {
engine, ok := bc.Engine().(*XDPoS.XDPoS)
if !ok || engine == nil {
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 87b58bf3ad..de15d9ca6c 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "errors"
"fmt"
"math/big"
"math/rand"
@@ -527,7 +528,7 @@ func testInsertNonceError(t *testing.T, full bool) {
blockchain.engine = ethash.NewFakeFailer(failNum)
blockchain.hc.engine = blockchain.engine
- failRes, err = blockchain.InsertHeaderChain(headers, 1)
+ failRes, _ = blockchain.InsertHeaderChain(headers, 1)
// Check that the returned error indicates the failure.
if failRes != failAt {
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
@@ -555,10 +556,11 @@ func TestFastVsFullChains(t *testing.T) {
gendb = rawdb.NewMemoryDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
+ funds = big.NewInt(1000000000000000)
gspec = &Genesis{
- Config: params.TestChainConfig,
- Alloc: GenesisAlloc{address: {Balance: funds}},
+ Config: params.TestChainConfig,
+ Alloc: GenesisAlloc{address: {Balance: funds}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
}
genesis = gspec.MustCommit(gendb)
signer = types.LatestSigner(gspec.Config)
@@ -569,7 +571,7 @@ func TestFastVsFullChains(t *testing.T) {
// If the block number is multiple of 3, send a few bonus transactions to the miner
if i%3 == 2 {
for j := 0; j < i%4+1; j++ {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
if err != nil {
panic(err)
}
@@ -643,8 +645,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
gendb = rawdb.NewMemoryDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
+ funds = big.NewInt(1000000000000000)
+ gspec = &Genesis{
+ Config: params.TestChainConfig,
+ Alloc: GenesisAlloc{address: {Balance: funds}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
genesis = gspec.MustCommit(gendb)
)
height := uint64(1024)
@@ -730,9 +736,9 @@ func TestChainTxReorgs(t *testing.T) {
Config: params.TestChainConfig,
GasLimit: 3141592,
Alloc: GenesisAlloc{
- addr1: {Balance: big.NewInt(1000000)},
- addr2: {Balance: big.NewInt(1000000)},
- addr3: {Balance: big.NewInt(1000000)},
+ addr1: {Balance: big.NewInt(1000000000000000)},
+ addr2: {Balance: big.NewInt(1000000000000000)},
+ addr3: {Balance: big.NewInt(1000000000000000)},
},
}
genesis = gspec.MustCommit(db)
@@ -742,8 +748,8 @@ func TestChainTxReorgs(t *testing.T) {
// Create two transactions shared between the chains:
// - postponed: transaction included at a later block in the forked chain
// - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
- swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
+ postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
+ swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
// Create two transactions that will be dropped by the forked chain:
// - pastDrop: transaction dropped retroactively from a past block
@@ -759,13 +765,13 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
case 2:
- freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
+ freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
@@ -784,18 +790,18 @@ func TestChainTxReorgs(t *testing.T) {
chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
- pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
case 2:
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
case 3:
- futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
+ futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
@@ -840,7 +846,7 @@ func TestLogReorgs(t *testing.T) {
db = rawdb.NewMemoryDatabase()
// this code generates a log
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
+ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
genesis = gspec.MustCommit(db)
signer = types.LatestSigner(gspec.Config)
)
@@ -852,7 +858,7 @@ func TestLogReorgs(t *testing.T) {
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
if i == 1 {
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
@@ -886,7 +892,7 @@ func TestLogReorgs(t *testing.T) {
// addr1 = crypto.PubkeyToAddress(key1.PublicKey)
// gspec = &Genesis{
// Config: params.TestChainConfig,
-// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
+// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
// }
// genesis = gspec.MustCommit(db)
// signer = types.LatestSigner(gspec.Config)
@@ -901,7 +907,7 @@ func TestLogReorgs(t *testing.T) {
// }
//
// replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
-// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1)
+// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1)
// if i == 2 {
// gen.OffsetTime(-9)
// }
@@ -986,14 +992,17 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
continue // busy wait for canonical hash to be written
}
if ch != block.Hash() {
- t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
+ t.Errorf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
+ return
}
fb := GetBlock(blockchain.db, ch, block.NumberU64())
if fb == nil {
- t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
+ t.Errorf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
+ return
}
if fb.Hash() != block.Hash() {
- t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
+ t.Errorf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
+ return
}
return
}
@@ -1104,8 +1113,8 @@ func TestEIP155Transition(t *testing.T) {
}
})
_, err := blockchain.InsertChain(blocks)
- if err != types.ErrInvalidChainId {
- t.Error("expected error:", types.ErrInvalidChainId)
+ if have, want := err, types.ErrInvalidChainId; !errors.Is(have, want) {
+ t.Errorf("have %v, want %v", have, want)
}
}
@@ -1185,7 +1194,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
+ genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain
@@ -1201,7 +1210,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
// Import the canonical and fork chain side by side, verifying the current block
// and current header consistency
diskdb := rawdb.NewMemoryDatabase()
- new(Genesis).MustCommit(diskdb)
+ (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
@@ -1212,13 +1221,13 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
}
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
- t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
+ t.Errorf("block %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
}
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
}
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
- t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
+ t.Errorf(" fork %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
}
}
}
@@ -1230,7 +1239,7 @@ func TestTrieForkGC(t *testing.T) {
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
+ genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain
@@ -1245,7 +1254,7 @@ func TestTrieForkGC(t *testing.T) {
}
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
diskdb := rawdb.NewMemoryDatabase()
- new(Genesis).MustCommit(diskdb)
+ (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
@@ -1276,7 +1285,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
- genesis := new(Genesis).MustCommit(db)
+ genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
@@ -1284,7 +1293,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
// Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase()
- new(Genesis).MustCommit(diskdb)
+ (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
@@ -1428,8 +1437,9 @@ func TestEIP2718Transition(t *testing.T) {
// A sender who makes transactions, has some funds
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000)
- gspec = &Genesis{
+ // funds = big.NewInt(math.MaxInt64)
+ funds = big.NewInt(1000000000000000)
+ gspec = &Genesis{
Config: ¶ms.ChainConfig{
ChainId: new(big.Int).SetBytes([]byte("eip1559")),
HomesteadBlock: big.NewInt(0),
@@ -1455,7 +1465,7 @@ func TestEIP2718Transition(t *testing.T) {
byte(vm.SLOAD),
},
Nonce: 0,
- Balance: big.NewInt(0),
+ Balance: big.NewInt(50000000000),
},
},
}
@@ -1472,7 +1482,7 @@ func TestEIP2718Transition(t *testing.T) {
Nonce: 0,
To: &aa,
Gas: 30000,
- GasPrice: big.NewInt(1),
+ GasPrice: b.header.BaseFee,
AccessList: types.AccessList{{
Address: aa,
StorageKeys: []common.Hash{{0}},
@@ -1496,8 +1506,218 @@ func TestEIP2718Transition(t *testing.T) {
block := chain.GetBlockByNumber(1)
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
- expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
+ expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
+ vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
if block.GasUsed() != expected {
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
}
}
+
+// TestTransientStorageReset ensures the transient storage is wiped correctly
+// between transactions.
+func TestTransientStorageReset(t *testing.T) {
+ var (
+ engine = ethash.NewFaker()
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ address = crypto.PubkeyToAddress(key.PublicKey)
+ destAddress = crypto.CreateAddress(address, 0)
+ funds = big.NewInt(1000000000000000)
+ vmConfig = vm.Config{
+ ExtraEips: []int{1153}, // Enable transient storage EIP
+ }
+ )
+ code := append([]byte{
+ // TLoad value with location 1
+ byte(vm.PUSH1), 0x1,
+ byte(vm.TLOAD),
+
+ // PUSH location
+ byte(vm.PUSH1), 0x1,
+
+ // SStore location:value
+ byte(vm.SSTORE),
+ }, make([]byte, 32-6)...)
+ initCode := []byte{
+ // TSTORE 1:1
+ byte(vm.PUSH1), 0x1,
+ byte(vm.PUSH1), 0x1,
+ byte(vm.TSTORE),
+
+ // Get the runtime-code on the stack
+ byte(vm.PUSH32)}
+ initCode = append(initCode, code...)
+ initCode = append(initCode, []byte{
+ byte(vm.PUSH1), 0x0, // offset
+ byte(vm.MSTORE),
+ byte(vm.PUSH1), 0x6, // size
+ byte(vm.PUSH1), 0x0, // offset
+ byte(vm.RETURN), // return 6 bytes of zero-code
+ }...)
+ gspec := &Genesis{
+ Config: params.TestChainConfig,
+ Alloc: GenesisAlloc{
+ address: {Balance: funds},
+ },
+ }
+ nonce := uint64(0)
+ signer := types.HomesteadSigner{}
+ _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
+ fee := big.NewInt(1)
+ if b.header.BaseFee != nil {
+ fee = b.header.BaseFee
+ }
+ b.SetCoinbase(common.Address{1})
+ tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
+ Nonce: nonce,
+ GasPrice: new(big.Int).Set(fee),
+ Gas: 100000,
+ Data: initCode,
+ })
+ nonce++
+ b.AddTxWithVMConfig(tx, vmConfig)
+
+ tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
+ Nonce: nonce,
+ GasPrice: new(big.Int).Set(fee),
+ Gas: 100000,
+ To: &destAddress,
+ })
+ b.AddTxWithVMConfig(tx, vmConfig)
+ nonce++
+ })
+
+ diskdb := rawdb.NewMemoryDatabase()
+ gspec.MustCommit(diskdb)
+
+ // Initialize the blockchain with 1153 enabled.
+ chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vmConfig)
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ // Import the blocks
+ if _, err := chain.InsertChain(blocks); err != nil {
+ t.Fatalf("failed to insert into chain: %v", err)
+ }
+ // Check the storage
+ state, err := chain.StateAt(chain.CurrentHeader().Root)
+ if err != nil {
+ t.Fatalf("Failed to load state %v", err)
+ }
+ loc := common.BytesToHash([]byte{1})
+ slot := state.GetState(destAddress, loc)
+ if slot != (common.Hash{}) {
+ t.Fatalf("Unexpected dirty storage slot")
+ }
+}
+
+func TestEIP3651(t *testing.T) {
+ var (
+ ConstantinopleBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Constantinople
+
+ aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
+ bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
+ engine = ethash.NewFaker()
+
+ // A sender who makes transactions, has some funds
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
+ addr2 = crypto.PubkeyToAddress(key2.PublicKey)
+ funds = big.NewInt(8000000000000000)
+ gspec = &Genesis{
+ Config: params.TestChainConfig,
+ Alloc: GenesisAlloc{
+ addr1: {Balance: funds},
+ addr2: {Balance: funds},
+ // The address 0xAAAA sloads 0x00 and 0x01
+ aa: {
+ Code: []byte{
+ byte(vm.PC),
+ byte(vm.PC),
+ byte(vm.SLOAD),
+ byte(vm.SLOAD),
+ },
+ Nonce: 0,
+ Balance: big.NewInt(0),
+ },
+ // The address 0xBBBB calls 0xAAAA
+ bb: {
+ Code: []byte{
+ byte(vm.PUSH1), 0, // out size
+ byte(vm.DUP1), // out offset
+ byte(vm.DUP1), // out insize
+ byte(vm.DUP1), // in offset
+ byte(vm.PUSH2), // address
+ byte(0xaa),
+ byte(0xaa),
+ byte(vm.GAS), // gas
+ byte(vm.DELEGATECALL),
+ },
+ Nonce: 0,
+ Balance: big.NewInt(0),
+ },
+ },
+ }
+ )
+
+ gspec.Config.Eip1559Block = common.Big0
+ signer := types.LatestSigner(gspec.Config)
+
+ _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
+ b.SetCoinbase(aa)
+ // One transaction to Coinbase
+ txdata := &types.DynamicFeeTx{
+ ChainID: gspec.Config.ChainId,
+ Nonce: 0,
+ To: &bb,
+ Gas: 500000,
+ GasFeeCap: big.NewInt(12500000000),
+ GasTipCap: big.NewInt(0),
+ AccessList: nil,
+ Data: []byte{},
+ }
+ tx := types.NewTx(txdata)
+ tx, _ = types.SignTx(tx, signer, key1)
+
+ b.AddTx(tx)
+ })
+
+ diskdb := rawdb.NewMemoryDatabase()
+ gspec.MustCommit(diskdb)
+
+ chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("failed to create tester chain: %v", err)
+ }
+ if n, err := chain.InsertChain(blocks); err != nil {
+ t.Fatalf("block %d: failed to insert into chain: %v", n, err)
+ }
+
+ block := chain.GetBlockByNumber(1)
+
+ // 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
+ innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2
+ expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list
+ if block.GasUsed() != expectedGas {
+ t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
+ }
+
+ state, _ := chain.State()
+
+ // 3: Ensure that miner received only the tx's tip.
+ actual := state.GetBalance(block.Coinbase())
+ expected := new(big.Int).Add(
+ new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
+ ConstantinopleBlockReward,
+ )
+ if actual.Cmp(expected) != 0 {
+ t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
+ }
+
+ // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
+ actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
+ expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
+ if actual.Cmp(expected) != 0 {
+ t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
+ }
+}
diff --git a/core/chain_indexer.go b/core/chain_indexer.go
index dd6466ab37..77cfac232b 100644
--- a/core/chain_indexer.go
+++ b/core/chain_indexer.go
@@ -356,7 +356,7 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com
}
header := GetHeader(c.chainDb, hash, number)
if header == nil {
- return common.Hash{}, fmt.Errorf("block #%d [%x…] not found", number, hash[:4])
+ return common.Hash{}, fmt.Errorf("block #%d [%x..] not found", number, hash[:4])
} else if header.ParentHash != lastHead {
return common.Hash{}, errors.New("chain reorged during section processing")
}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 7a4012945f..1b6633e535 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -20,11 +20,11 @@ import (
"fmt"
"math/big"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
@@ -75,6 +75,31 @@ func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data
}
+// addTx adds a transaction to the generated block. If no coinbase has
+// been set, the block's coinbase is set to the zero address.
+//
+// There are a few options can be passed as well in order to run some
+// customized rules.
+// - bc: enables the ability to query historical block hashes for BLOCKHASH
+// - vmConfig: extends the flexibility for customizing evm rules, e.g. enable extra EIPs
+func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transaction) {
+ if b.gasPool == nil {
+ b.SetCoinbase(common.Address{})
+ }
+ feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
+ b.statedb.SetTxContext(tx.Hash(), len(b.txs))
+ receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vmConfig)
+ if err != nil {
+ panic(err)
+ }
+ b.txs = append(b.txs, tx)
+ b.receipts = append(b.receipts, receipt)
+ if tokenFeeUsed {
+ fee := common.GetGasFee(b.header.Number.Uint64(), gas)
+ state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
+ }
+}
+
// AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
@@ -84,7 +109,7 @@ func (b *BlockGen) SetExtra(data []byte) {
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
func (b *BlockGen) AddTx(tx *types.Transaction) {
- b.AddTxWithChain(nil, tx)
+ b.addTx(nil, vm.Config{}, tx)
}
// AddTxWithChain adds a transaction to the generated block. If no coinbase has
@@ -96,21 +121,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
// added. If contract code relies on the BLOCKHASH instruction,
// the block in chain will be returned.
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
- if b.gasPool == nil {
- b.SetCoinbase(common.Address{})
- }
- feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
- b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
- receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vm.Config{})
- if err != nil {
- panic(err)
- }
- b.txs = append(b.txs, tx)
- b.receipts = append(b.receipts, receipt)
- if tokenFeeUsed {
- fee := common.GetGasFee(b.header.Number.Uint64(), gas)
- state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
- }
+ b.addTx(bc, vm.Config{}, tx)
+}
+
+// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has
+// been set, the block's coinbase is set to the zero address.
+// The evm interpreter can be customized with the provided vm config.
+func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
+ b.addTx(nil, config, tx)
}
// AddUncheckedTx forcefully adds a transaction to the block without any
@@ -127,6 +145,11 @@ func (b *BlockGen) Number() *big.Int {
return new(big.Int).Set(b.header.Number)
}
+// BaseFee returns the EIP-1559 base fee of the block being generated.
+func (b *BlockGen) BaseFee() *big.Int {
+ return new(big.Int).Set(b.header.BaseFee)
+}
+
// AddUncheckedReceipt forcefully adds a receipts to the block without a
// backing transaction.
//
@@ -265,7 +288,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
}
- return &types.Header{
+ header := &types.Header{
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
@@ -279,6 +302,10 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
Number: new(big.Int).Add(parent.Number(), common.Big1),
Time: time,
}
+
+ header.BaseFee = eip1559.CalcBaseFee(chain.Config(), header)
+
+ return header
}
// newCanonical creates a chain database, and injects a deterministic canonical
@@ -324,3 +351,18 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
})
return blocks
}
+
+type fakeChainReader struct {
+ config *params.ChainConfig
+}
+
+// Config returns the chain configuration.
+func (cr *fakeChainReader) Config() *params.ChainConfig {
+ return cr.config
+}
+
+func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil }
+func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil }
+func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
+func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
+func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
diff --git a/core/dao_test.go b/core/dao_test.go
index cfd636e7c0..f1d60bc0ca 100644
--- a/core/dao_test.go
+++ b/core/dao_test.go
@@ -17,11 +17,11 @@
package core
import (
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params"
)
@@ -33,7 +33,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Generate a common prefix for both pro-forkers and non-forkers
db := rawdb.NewMemoryDatabase()
- gspec := new(Genesis)
+ gspec := &Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}
genesis := gspec.MustCommit(db)
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
diff --git a/core/database_util.go b/core/database_util.go
index 9d48ffec6c..e65ac0ff8e 100644
--- a/core/database_util.go
+++ b/core/database_util.go
@@ -262,6 +262,10 @@ func GetBlockReceipts(db DatabaseReader, hash common.Hash, number uint64) types.
receipts[i].BlockHash = hash
receipts[i].BlockNumber = big.NewInt(0).SetUint64(number)
receipts[i].TransactionIndex = uint(i)
+ for _, log := range receipts[i].Logs {
+ // set BlockHash to fix #650
+ log.BlockHash = hash
+ }
}
return receipts
}
diff --git a/core/database_util_test.go b/core/database_util_test.go
index a0d5a9ec83..1f29908b96 100644
--- a/core/database_util_test.go
+++ b/core/database_util_test.go
@@ -24,7 +24,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
+ "golang.org/x/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
@@ -47,7 +47,7 @@ func TestHeaderStorage(t *testing.T) {
if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
t.Fatalf("Stored header RLP not found")
} else {
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
hasher.Write(entry)
if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
@@ -68,7 +68,7 @@ func TestBodyStorage(t *testing.T) {
// Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil))
@@ -85,7 +85,7 @@ func TestBodyStorage(t *testing.T) {
if entry := GetBodyRLP(db, hash, 0); entry == nil {
t.Fatalf("Stored body RLP not found")
} else {
- hasher := sha3.NewKeccak256()
+ hasher := sha3.NewLegacyKeccak256()
hasher.Write(entry)
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
diff --git a/core/error.go b/core/error.go
index 5503a6e6f2..67bb75730e 100644
--- a/core/error.go
+++ b/core/error.go
@@ -26,13 +26,25 @@ var (
// ErrKnownBlock is returned when a block to import is already known locally.
ErrKnownBlock = errors.New("block already known")
- // ErrGasLimitReached is returned by the gas pool if the amount of gas required
- // by a transaction is higher than what's left in the block.
- ErrGasLimitReached = errors.New("gas limit reached")
-
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
ErrBlacklistedHash = errors.New("blacklisted hash")
+ // ErrNoGenesis is returned when there is no Genesis Block.
+ ErrNoGenesis = errors.New("genesis not found in chain")
+)
+
+// List of evm-call-message pre-checking errors. All state transtion messages will
+// be pre-checked before execution. If any invalidation detected, the corresponding
+// error should be returned which is defined here.
+//
+// - If the pre-checking happens in the miner, then the transaction won't be packed.
+// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk"
+// error should be emitted.
+var (
+ // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
+ // one present in the local chain.
+ ErrNonceTooLow = errors.New("nonce too low")
+
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
// next one expected based on the local chain.
ErrNonceTooHigh = errors.New("nonce too high")
@@ -41,16 +53,55 @@ var (
// maximum allowed value and would become invalid if incremented.
ErrNonceMax = errors.New("nonce has max value")
- ErrNotXDPoS = errors.New("XDPoS not found in config")
+ // ErrGasLimitReached is returned by the gas pool if the amount of gas required
+ // by a transaction is higher than what's left in the block.
+ ErrGasLimitReached = errors.New("gas limit reached")
- ErrNotFoundM1 = errors.New("list M1 not found ")
+ // ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
+ // have enough funds for transfer(topmost call only).
+ ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
- ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
+ // ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
+ // than init code size limit.
+ ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
+
+ // ErrInsufficientFunds is returned if the total cost of executing a transaction
+ // is higher than the balance of the user's account.
+ ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
+
+ // ErrGasUintOverflow is returned when calculating gas usage.
+ ErrGasUintOverflow = errors.New("gas uint64 overflow")
+
+ // ErrIntrinsicGas is returned if the transaction is specified to use less gas
+ // than required to start the invocation.
+ ErrIntrinsicGas = errors.New("intrinsic gas too low")
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
// current network configuration.
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
- // ErrGasUintOverflow is returned when calculating gas usage.
- ErrGasUintOverflow = errors.New("gas uint64 overflow")
+ // ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
+ // transaction with a tip higher than the total fee cap.
+ ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas")
+
+ // ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
+ // in the tip field.
+ ErrTipVeryHigh = errors.New("max priority fee per gas higher than 2^256-1")
+
+ // ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified
+ // in the fee cap field.
+ ErrFeeCapVeryHigh = errors.New("max fee per gas higher than 2^256-1")
+
+ // ErrFeeCapTooLow is returned if the transaction fee cap is less than the
+ // the base fee of the block.
+ ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
+
+ // ErrSenderNoEOA is returned if the sender of a transaction is a contract.
+ ErrSenderNoEOA = errors.New("sender not an eoa")
+
+ ErrNotXDPoS = errors.New("XDPoS not found in config")
+
+ ErrNotFoundM1 = errors.New("list M1 not found ")
+
+ ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
)
diff --git a/core/evm.go b/core/evm.go
index bc1c72ce5b..9847a8d790 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -26,11 +26,12 @@ import (
"github.com/XinFinOrg/XDPoSChain/crypto"
)
-// NewEVMContext creates a new context for use in the EVM.
-func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainContext, author *common.Address) vm.Context {
+// NewEVMBlockContext creates a new context for use in the EVM.
+func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, author *common.Address) vm.BlockContext {
// If we don't have an explicit author (i.e. not mining), extract from the header
var (
beneficiary common.Address
+ baseFee *big.Int
random common.Hash
)
if author == nil {
@@ -38,23 +39,33 @@ func NewEVMContext(msg Message, header *types.Header, chain consensus.ChainConte
} else {
beneficiary = *author
}
+ if header.BaseFee != nil {
+ baseFee = new(big.Int).Set(header.BaseFee)
+ }
// since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random
random = crypto.Keccak256Hash(header.Number.Bytes())
- return vm.Context{
+ return vm.BlockContext{
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
- Origin: msg.From(),
Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
Time: new(big.Int).Set(header.Time),
Difficulty: new(big.Int).Set(header.Difficulty),
+ BaseFee: baseFee,
GasLimit: header.GasLimit,
- GasPrice: new(big.Int).Set(msg.GasPrice()),
Random: &random,
}
}
+// NewEVMTxContext creates a new transaction context for a single transaction.
+func NewEVMTxContext(msg Message) vm.TxContext {
+ return vm.TxContext{
+ Origin: msg.From(),
+ GasPrice: new(big.Int).Set(msg.GasPrice()),
+ }
+}
+
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, chain consensus.ChainContext) func(n uint64) common.Hash {
// Cache will initially contain [refHash.parent],
diff --git a/core/gen_genesis.go b/core/gen_genesis.go
index 82fe48663c..27dcb1f7cc 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -15,6 +15,7 @@ import (
var _ = (*genesisSpecMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct {
Config *params.ChainConfig `json:"config"`
@@ -29,6 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
Number math.HexOrDecimal64 `json:"number"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
}
var enc Genesis
enc.Config = g.Config
@@ -48,9 +50,11 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
enc.Number = math.HexOrDecimal64(g.Number)
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
enc.ParentHash = g.ParentHash
+ enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (g *Genesis) UnmarshalJSON(input []byte) error {
type Genesis struct {
Config *params.ChainConfig `json:"config"`
@@ -65,6 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
Number *math.HexOrDecimal64 `json:"number"`
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
ParentHash *common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
}
var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil {
@@ -112,5 +117,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
if dec.ParentHash != nil {
g.ParentHash = *dec.ParentHash
}
+ if dec.BaseFee != nil {
+ g.BaseFee = (*big.Int)(dec.BaseFee)
+ }
return nil
}
diff --git a/core/gen_genesis_account.go b/core/gen_genesis_account.go
index 9d3ea26d68..dd0a6f6719 100644
--- a/core/gen_genesis_account.go
+++ b/core/gen_genesis_account.go
@@ -14,6 +14,7 @@ import (
var _ = (*genesisAccountMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
type GenesisAccount struct {
Code hexutil.Bytes `json:"code,omitempty"`
@@ -36,6 +37,7 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) {
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
type GenesisAccount struct {
Code *hexutil.Bytes `json:"code,omitempty"`
diff --git a/core/genesis.go b/core/genesis.go
index 2156ddc725..d14b1c73d1 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -26,6 +26,7 @@ import (
"strings"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
@@ -61,6 +62,7 @@ type Genesis struct {
Number uint64 `json:"number"`
GasUsed uint64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
+ BaseFee *big.Int `json:"baseFeePerGas"`
}
// GenesisAlloc specifies the initial state that is part of the genesis block.
@@ -96,6 +98,7 @@ type genesisSpecMarshaling struct {
GasUsed math.HexOrDecimal64
Number math.HexOrDecimal64
Difficulty *math.HexOrDecimal256
+ BaseFee *math.HexOrDecimal256
Alloc map[common.UnprefixedAddress]GenesisAccount
}
@@ -253,6 +256,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
Extra: g.ExtraData,
GasLimit: g.GasLimit,
GasUsed: g.GasUsed,
+ BaseFee: g.BaseFee,
Difficulty: g.Difficulty,
MixDigest: g.Mixhash,
Coinbase: g.Coinbase,
@@ -264,6 +268,13 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
if g.Difficulty == nil {
head.Difficulty = params.GenesisDifficulty
}
+ if g.Config != nil && g.Config.IsEIP1559(common.Big0) {
+ if g.BaseFee != nil {
+ head.BaseFee = g.BaseFee
+ } else {
+ head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
+ }
+ }
statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true)
@@ -308,7 +319,10 @@ func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
- g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}}
+ g := Genesis{
+ Alloc: GenesisAlloc{addr: {Balance: balance}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
return g.MustCommit(db)
}
@@ -363,8 +377,9 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// Assemble and return the genesis with the precompiles and faucet pre-funded
return &Genesis{
Config: &config,
- ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
+ ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
GasLimit: 6283185,
+ BaseFee: big.NewInt(params.InitialBaseFee),
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
diff --git a/core/headerchain.go b/core/headerchain.go
index 424bfb687d..51a876d2ec 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -101,6 +101,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
}
}
hc.currentHeaderHash = hc.CurrentHeader().Hash()
+ headHeaderGauge.Update(hc.CurrentHeader().Number.Int64())
return hc, nil
}
@@ -176,8 +177,10 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
log.Crit("Failed to insert head header hash", "err", err)
}
+
hc.currentHeaderHash = hash
hc.currentHeader.Store(types.CopyHeader(header))
+ headHeaderGauge.Update(header.Number.Int64())
status = CanonStatTy
} else {
@@ -205,7 +208,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(),
"parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash())
- return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number,
+ return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, chain[i-1].Number,
chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4])
}
}
@@ -392,8 +395,10 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
log.Crit("Failed to insert head header hash", "err", err)
}
+
hc.currentHeader.Store(head)
hc.currentHeaderHash = head.Hash()
+ headHeaderGauge.Update(head.Number.Int64())
}
// DeleteCallback is a callback function that is called by SetHead before
@@ -432,6 +437,7 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
hc.currentHeader.Store(hc.genesisHeader)
}
hc.currentHeaderHash = hc.CurrentHeader().Hash()
+ headHeaderGauge.Update(hc.CurrentHeader().Number.Int64())
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
log.Crit("Failed to reset head header hash", "err", err)
diff --git a/core/helper_test.go b/core/helper_test.go
deleted file mode 100644
index c499d15db5..0000000000
--- a/core/helper_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2014 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "container/list"
-
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/event"
-)
-
-// Implement our EthTest Manager
-type TestManager struct {
- // stateManager *StateManager
- eventMux *event.TypeMux
-
- db ethdb.Database
- txPool *TxPool
- blockChain *BlockChain
- Blocks []*types.Block
-}
-
-func (tm *TestManager) IsListening() bool {
- return false
-}
-
-func (tm *TestManager) IsMining() bool {
- return false
-}
-
-func (tm *TestManager) PeerCount() int {
- return 0
-}
-
-func (tm *TestManager) Peers() *list.List {
- return list.New()
-}
-
-func (tm *TestManager) BlockChain() *BlockChain {
- return tm.blockChain
-}
-
-func (tm *TestManager) TxPool() *TxPool {
- return tm.txPool
-}
-
-// func (tm *TestManager) StateManager() *StateManager {
-// return tm.stateManager
-// }
-
-func (tm *TestManager) EventMux() *event.TypeMux {
- return tm.eventMux
-}
-
-// func (tm *TestManager) KeyManager() *crypto.KeyManager {
-// return nil
-// }
-
-func (tm *TestManager) Db() ethdb.Database {
- return tm.db
-}
-
-func NewTestManager() *TestManager {
- db := rawdb.NewMemoryDatabase()
-
- testManager := &TestManager{}
- testManager.eventMux = new(event.TypeMux)
- testManager.db = db
- // testManager.txPool = NewTxPool(testManager)
- // testManager.blockChain = NewBlockChain(testManager)
- // testManager.stateManager = NewStateManager(testManager)
-
- return testManager
-}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 6790fe141c..7073e8431a 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -20,6 +20,7 @@ import (
"bytes"
"encoding/binary"
"errors"
+ "math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -36,15 +37,6 @@ func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64
}
}
-// WriteHeaderNumber stores the hash->number mapping.
-func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
- key := headerNumberKey(hash)
- enc := encodeBlockNumber(number)
- if err := db.Put(key, enc); err != nil {
- log.Crit("Failed to store hash to number mapping", "err", err)
- }
-}
-
// ReadHeaderNumber returns the header number assigned to a hash.
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
data, _ := db.Get(headerNumberKey(hash))
@@ -55,6 +47,15 @@ func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
return &number
}
+// WriteHeaderNumber stores the hash->number mapping.
+func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
+ key := headerNumberKey(hash)
+ enc := encodeBlockNumber(number)
+ if err := db.Put(key, enc); err != nil {
+ log.Crit("Failed to store hash to number mapping", "err", err)
+ }
+}
+
// WriteHeadBlockHash stores the head block's hash.
func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
@@ -62,6 +63,26 @@ func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
}
}
+// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
+func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(headerKey(number, hash))
+ return data
+}
+
+// ReadHeader retrieves the block header corresponding to the hash.
+func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header {
+ data := ReadHeaderRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ header := new(types.Header)
+ if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
+ log.Error("Invalid block header RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return header
+}
+
// WriteHeader stores a block header into the database and also stores the hash-
// to-number mapping.
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
@@ -193,6 +214,9 @@ func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Rec
receipts := make(types.Receipts, len(storageReceipts))
for i, storageReceipt := range storageReceipts {
receipts[i] = (*types.Receipt)(storageReceipt)
+ receipts[i].BlockHash = hash
+ receipts[i].BlockNumber = new(big.Int).SetUint64(number)
+ receipts[i].TransactionIndex = uint(i)
}
return receipts
}
@@ -215,7 +239,14 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para
log.Error("Missing body but have receipt", "hash", hash, "number", number)
return nil
}
- if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil {
+ header := ReadHeader(db, hash, number)
+ var baseFee *big.Int
+ if header == nil {
+ baseFee = big.NewInt(0)
+ } else {
+ baseFee = header.BaseFee
+ }
+ if err := receipts.DeriveFields(config, hash, number, baseFee, body.Transactions); err != nil {
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
return nil
}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index a514c5857f..5ecd5ed6c7 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -19,8 +19,8 @@ package rawdb
import (
"bytes"
"encoding/hex"
- "io/ioutil"
"math/big"
+ "os"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
@@ -214,7 +214,7 @@ func TestDeriveLogFields(t *testing.T) {
func BenchmarkDecodeRLPLogs(b *testing.B) {
// Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269
- buf, err := ioutil.ReadFile("testdata/stored_receipts.bin")
+ buf, err := os.ReadFile("testdata/stored_receipts.bin")
if err != nil {
b.Fatal(err)
}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
index 0a08eeed69..26d098347e 100644
--- a/core/rawdb/schema.go
+++ b/core/rawdb/schema.go
@@ -25,7 +25,9 @@ import (
// The fields below define the low level database schema prefixing.
var (
+ // headBlockKey tracks the latest known full block's hash.
headBlockKey = []byte("LastBlock")
+
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
diff --git a/core/tx_cacher.go b/core/sender_cacher.go
similarity index 88%
rename from core/tx_cacher.go
rename to core/sender_cacher.go
index ea4ab6cc07..e556ebd4e4 100644
--- a/core/tx_cacher.go
+++ b/core/sender_cacher.go
@@ -22,8 +22,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
)
-// senderCacher is a concurrent tranaction sender recoverer anc cacher.
-var senderCacher = newTxSenderCacher(runtime.NumCPU())
+// SenderCacher is a concurrent transaction sender recoverer and cacher.
+var SenderCacher = newTxSenderCacher(runtime.NumCPU())
// txSenderCacherRequest is a request for recovering transaction senders with a
// specific signature scheme and caching it into the transactions themselves.
@@ -67,10 +67,10 @@ func (cacher *txSenderCacher) cache() {
}
}
-// recover recovers the senders from a batch of transactions and caches them
+// Recover recovers the senders from a batch of transactions and caches them
// back into the same data structures. There is no validation being done, nor
// any reaction to invalid signatures. That is up to calling code later.
-func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) {
+func (cacher *txSenderCacher) Recover(signer types.Signer, txs []*types.Transaction) {
// If there's nothing to recover, abort
if len(txs) == 0 {
return
@@ -89,10 +89,10 @@ func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transact
}
}
-// recoverFromBlocks recovers the senders from a batch of blocks and caches them
+// RecoverFromBlocks recovers the senders from a batch of blocks and caches them
// back into the same data structures. There is no validation being done, nor
// any reaction to invalid signatures. That is up to calling code later.
-func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) {
+func (cacher *txSenderCacher) RecoverFromBlocks(signer types.Signer, blocks []*types.Block) {
count := 0
for _, block := range blocks {
count += len(block.Transactions())
@@ -101,5 +101,5 @@ func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*t
for _, block := range blocks {
txs = append(txs, block.Transactions()...)
}
- cacher.recover(signer, txs)
+ cacher.Recover(signer, txs)
}
diff --git a/core/state/dump.go b/core/state/dump.go
index d99b580bc4..ba4bfee0e1 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -39,15 +39,15 @@ type Dump struct {
Accounts map[string]DumpAccount `json:"accounts"`
}
-func (self *StateDB) RawDump() Dump {
+func (s *StateDB) RawDump() Dump {
dump := Dump{
- Root: fmt.Sprintf("%x", self.trie.Hash()),
+ Root: fmt.Sprintf("%x", s.trie.Hash()),
Accounts: make(map[string]DumpAccount),
}
- it := trie.NewIterator(self.trie.NodeIterator(nil))
+ it := trie.NewIterator(s.trie.NodeIterator(nil))
for it.Next() {
- addr := self.trie.GetKey(it.Key)
+ addr := s.trie.GetKey(it.Key)
var data Account
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err)
@@ -59,20 +59,20 @@ func (self *StateDB) RawDump() Dump {
Nonce: data.Nonce,
Root: common.Bytes2Hex(data.Root[:]),
CodeHash: common.Bytes2Hex(data.CodeHash),
- Code: common.Bytes2Hex(obj.Code(self.db)),
+ Code: common.Bytes2Hex(obj.Code(s.db)),
Storage: make(map[string]string),
}
- storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil))
+ storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
for storageIt.Next() {
- account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
+ account.Storage[common.Bytes2Hex(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
}
dump.Accounts[common.Bytes2Hex(addr)] = account
}
return dump
}
-func (self *StateDB) Dump() []byte {
- json, err := json.MarshalIndent(self.RawDump(), "", " ")
+func (s *StateDB) Dump() []byte {
+ json, err := json.MarshalIndent(s.RawDump(), "", " ")
if err != nil {
fmt.Println("dump err", err)
}
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 63ae2b08c4..1fc639abb7 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -21,6 +21,7 @@ import (
"fmt"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie"
)
@@ -116,7 +117,7 @@ func (it *NodeIterator) step() error {
if !it.dataIt.Next(true) {
it.dataIt = nil
}
- if !bytes.Equal(account.CodeHash, emptyCodeHash) {
+ if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
it.codeHash = common.BytesToHash(account.CodeHash)
addrHash := common.BytesToHash(it.stateIt.LeafKey())
it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash))
diff --git a/core/state/journal.go b/core/state/journal.go
index b9348f7bf2..d8f748fa64 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -88,6 +88,11 @@ type (
address *common.Address
slot *common.Hash
}
+
+ transientStorageChange struct {
+ account *common.Address
+ key, prevalue common.Hash
+ }
)
func (ch createObjectChange) undo(s *StateDB) {
@@ -134,6 +139,10 @@ func (ch storageChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
+func (ch transientStorageChange) undo(s *StateDB) {
+ s.setTransientState(*ch.account, ch.key, ch.prevalue)
+}
+
func (ch refundChange) undo(s *StateDB) {
s.refund = ch.prev
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 99763db267..bb19597cc6 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -21,14 +21,14 @@ import (
"fmt"
"io"
"math/big"
+ "time"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
-var emptyCodeHash = crypto.Keccak256(nil)
-
type Code []byte
func (c Code) String() string {
@@ -93,7 +93,7 @@ type stateObject struct {
// empty returns whether the account is considered empty.
func (s *stateObject) empty() bool {
- return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
+ return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
}
// Account is the Ethereum consensus representation of accounts.
@@ -111,7 +111,7 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
- data.CodeHash = emptyCodeHash
+ data.CodeHash = types.EmptyCodeHash.Bytes()
}
return &stateObject{
db: db,
@@ -174,6 +174,8 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if s.fakeStorage != nil {
return s.fakeStorage[key]
}
+ // Track the amount of time wasted on reading the storge trie
+ defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now())
value := common.Hash{}
// Load from DB in case it is missing.
enc, err := s.getTrie(db).TryGet(key[:])
@@ -270,6 +272,8 @@ func (s *stateObject) setState(key, value common.Hash) {
// updateTrie writes cached storage modifications into the object's storage trie.
func (s *stateObject) updateTrie(db Database) Trie {
+ // Track the amount of time wasted on updating the storge trie
+ defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
tr := s.getTrie(db)
for key, value := range s.dirtyStorage {
delete(s.dirtyStorage, key)
@@ -287,6 +291,10 @@ func (s *stateObject) updateTrie(db Database) Trie {
// UpdateRoot sets the trie root to the current root hash of
func (s *stateObject) updateRoot(db Database) {
s.updateTrie(db)
+
+ // Track the amount of time wasted on hashing the storge trie
+ defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
+
s.data.Root = s.trie.Hash()
}
@@ -297,6 +305,9 @@ func (s *stateObject) CommitTrie(db Database) error {
if s.dbErr != nil {
return s.dbErr
}
+ // Track the amount of time wasted on committing the storge trie
+ defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
+
root, err := s.trie.Commit(nil)
if err == nil {
s.data.Root = root
@@ -372,7 +383,7 @@ func (s *stateObject) Code(db Database) []byte {
if s.code != nil {
return s.code
}
- if bytes.Equal(s.CodeHash(), emptyCodeHash) {
+ if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return nil
}
code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
diff --git a/core/state/statedb.go b/core/state/statedb.go
index bac5106f5b..d357e6e1bf 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -22,11 +22,13 @@ import (
"math/big"
"sort"
"sync"
+ "time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/trie"
)
@@ -36,14 +38,6 @@ type revision struct {
journalIndex int
}
-var (
- // emptyState is the known hash of an empty state trie entry.
- emptyState = crypto.Keccak256Hash(nil)
-
- // emptyCode is the known hash of the empty EVM bytecode.
- emptyCode = crypto.Keccak256Hash(nil)
-)
-
// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
@@ -67,16 +61,19 @@ type StateDB struct {
// The refund counter, also used by state transitioning.
refund uint64
- thash, bhash common.Hash
- txIndex int
- logs map[common.Hash][]*types.Log
- logSize uint
+ thash common.Hash
+ txIndex int
+ logs map[common.Hash][]*types.Log
+ logSize uint
preimages map[common.Hash][]byte
// Per-transaction access list
accessList *accessList
+ // Transient storage
+ transientStorage transientStorage
+
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal journal
@@ -84,6 +81,16 @@ type StateDB struct {
nextRevisionId int
lock sync.Mutex
+
+ // Measurements gathered during execution for debugging purposes
+ AccountReads time.Duration
+ AccountHashes time.Duration
+ AccountUpdates time.Duration
+ AccountCommits time.Duration
+ StorageReads time.Duration
+ StorageHashes time.Duration
+ StorageUpdates time.Duration
+ StorageCommits time.Duration
}
type AccountInfo struct {
@@ -94,19 +101,19 @@ type AccountInfo struct {
StorageHash common.Hash
}
-func (self *StateDB) SubRefund(gas uint64) {
- self.journal = append(self.journal, refundChange{
- prev: self.refund})
- if gas > self.refund {
- panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, self.refund))
+func (s *StateDB) SubRefund(gas uint64) {
+ s.journal = append(s.journal, refundChange{
+ prev: s.refund})
+ if gas > s.refund {
+ panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
}
- self.refund -= gas
+ s.refund -= gas
}
-func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
- return stateObject.GetCommittedState(self.db, hash)
+ return stateObject.GetCommittedState(s.db, hash)
}
return common.Hash{}
}
@@ -125,108 +132,111 @@ func New(root common.Hash, db Database) (*StateDB, error) {
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
accessList: newAccessList(),
+ transientStorage: newTransientStorage(),
}, nil
}
// setError remembers the first non-nil error it is called with.
-func (self *StateDB) setError(err error) {
- if self.dbErr == nil {
- self.dbErr = err
+func (s *StateDB) setError(err error) {
+ if s.dbErr == nil {
+ s.dbErr = err
}
}
-func (self *StateDB) Error() error {
- return self.dbErr
+func (s *StateDB) Error() error {
+ return s.dbErr
}
// Reset clears out all ephemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
-func (self *StateDB) Reset(root common.Hash) error {
- tr, err := self.db.OpenTrie(root)
+func (s *StateDB) Reset(root common.Hash) error {
+ tr, err := s.db.OpenTrie(root)
if err != nil {
return err
}
- self.trie = tr
- self.stateObjects = make(map[common.Address]*stateObject)
- self.stateObjectsDirty = make(map[common.Address]struct{})
- self.thash = common.Hash{}
- self.bhash = common.Hash{}
- self.txIndex = 0
- self.logs = make(map[common.Hash][]*types.Log)
- self.logSize = 0
- self.preimages = make(map[common.Hash][]byte)
- self.clearJournalAndRefund()
- self.accessList = newAccessList()
+ s.trie = tr
+ s.stateObjects = make(map[common.Address]*stateObject)
+ s.stateObjectsDirty = make(map[common.Address]struct{})
+ s.thash = common.Hash{}
+ s.txIndex = 0
+ s.logs = make(map[common.Hash][]*types.Log)
+ s.logSize = 0
+ s.preimages = make(map[common.Hash][]byte)
+ s.clearJournalAndRefund()
+ s.accessList = newAccessList()
return nil
}
-func (self *StateDB) AddLog(log *types.Log) {
- self.journal = append(self.journal, addLogChange{txhash: self.thash})
+func (s *StateDB) AddLog(log *types.Log) {
+ s.journal = append(s.journal, addLogChange{txhash: s.thash})
- log.TxHash = self.thash
- log.BlockHash = self.bhash
- log.TxIndex = uint(self.txIndex)
- log.Index = self.logSize
- self.logs[self.thash] = append(self.logs[self.thash], log)
- self.logSize++
+ log.TxHash = s.thash
+ log.TxIndex = uint(s.txIndex)
+ log.Index = s.logSize
+ s.logs[s.thash] = append(s.logs[s.thash], log)
+ s.logSize++
}
-func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
- return self.logs[hash]
+func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log {
+ logs := s.logs[hash]
+ for _, l := range logs {
+ l.BlockHash = blockHash
+ }
+ return logs
}
-func (self *StateDB) Logs() []*types.Log {
+func (s *StateDB) Logs() []*types.Log {
var logs []*types.Log
- for _, lgs := range self.logs {
+ for _, lgs := range s.logs {
logs = append(logs, lgs...)
}
return logs
}
// AddPreimage records a SHA3 preimage seen by the VM.
-func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
- if _, ok := self.preimages[hash]; !ok {
- self.journal = append(self.journal, addPreimageChange{hash: hash})
+func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
+ if _, ok := s.preimages[hash]; !ok {
+ s.journal = append(s.journal, addPreimageChange{hash: hash})
pi := make([]byte, len(preimage))
copy(pi, preimage)
- self.preimages[hash] = pi
+ s.preimages[hash] = pi
}
}
// Preimages returns a list of SHA3 preimages that have been submitted.
-func (self *StateDB) Preimages() map[common.Hash][]byte {
- return self.preimages
+func (s *StateDB) Preimages() map[common.Hash][]byte {
+ return s.preimages
}
-func (self *StateDB) AddRefund(gas uint64) {
- self.journal = append(self.journal, refundChange{prev: self.refund})
- self.refund += gas
+func (s *StateDB) AddRefund(gas uint64) {
+ s.journal = append(s.journal, refundChange{prev: s.refund})
+ s.refund += gas
}
// Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts.
-func (self *StateDB) Exist(addr common.Address) bool {
- return self.getStateObject(addr) != nil
+func (s *StateDB) Exist(addr common.Address) bool {
+ return s.getStateObject(addr) != nil
}
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
-func (self *StateDB) Empty(addr common.Address) bool {
- so := self.getStateObject(addr)
+func (s *StateDB) Empty(addr common.Address) bool {
+ so := s.getStateObject(addr)
return so == nil || so.empty()
}
// Retrieve the balance from the given address or 0 if object not found
-func (self *StateDB) GetBalance(addr common.Address) *big.Int {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetBalance(addr common.Address) *big.Int {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Balance()
}
return common.Big0
}
-func (self *StateDB) GetNonce(addr common.Address) uint64 {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetNonce(addr common.Address) uint64 {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Nonce()
}
@@ -236,8 +246,8 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
// GetStorageRoot retrieves the storage root from the given address or empty
// if object not found.
-func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Root()
}
@@ -245,50 +255,45 @@ func (self *StateDB) GetStorageRoot(addr common.Address) common.Hash {
}
// TxIndex returns the current transaction index set by Prepare.
-func (self *StateDB) TxIndex() int {
- return self.txIndex
+func (s *StateDB) TxIndex() int {
+ return s.txIndex
}
-// BlockHash returns the current block hash set by Prepare.
-func (self *StateDB) BlockHash() common.Hash {
- return self.bhash
-}
-
-func (self *StateDB) GetCode(addr common.Address) []byte {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetCode(addr common.Address) []byte {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
- return stateObject.Code(self.db)
+ return stateObject.Code(s.db)
}
return nil
}
-func (self *StateDB) GetCodeSize(addr common.Address) int {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetCodeSize(addr common.Address) int {
+ stateObject := s.getStateObject(addr)
if stateObject == nil {
return 0
}
if stateObject.code != nil {
return len(stateObject.code)
}
- size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+ size, err := s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
if err != nil {
- self.setError(err)
+ s.setError(err)
}
return size
}
-func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
+ stateObject := s.getStateObject(addr)
if stateObject == nil {
return common.Hash{}
}
return common.BytesToHash(stateObject.CodeHash())
}
-func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo {
+func (s *StateDB) GetAccountInfo(addr common.Address) *AccountInfo {
result := AccountInfo{}
- stateObject := self.getStateObject(addr)
+ stateObject := s.getStateObject(addr)
if stateObject == nil {
result.Balance = common.Big0
return &result
@@ -297,7 +302,7 @@ func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo {
if stateObject.code != nil {
result.CodeSize = len(stateObject.code)
} else {
- result.CodeSize, _ = self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+ result.CodeSize, _ = s.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
}
result.Nonce = stateObject.Nonce()
result.Balance = stateObject.Balance()
@@ -307,32 +312,32 @@ func (self *StateDB) GetAccountInfo(addr common.Address) *AccountInfo {
return &result
}
-func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
- return stateObject.GetState(self.db, bhash)
+ return stateObject.GetState(s.db, bhash)
}
return common.Hash{}
}
// Database retrieves the low level database supporting the lower level trie ops.
-func (self *StateDB) Database() Database {
- return self.db
+func (s *StateDB) Database() Database {
+ return s.db
}
// StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts.
-func (self *StateDB) StorageTrie(addr common.Address) Trie {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) StorageTrie(addr common.Address) Trie {
+ stateObject := s.getStateObject(addr)
if stateObject == nil {
return nil
}
- cpy := stateObject.deepCopy(self, nil)
- return cpy.updateTrie(self.db)
+ cpy := stateObject.deepCopy(s, nil)
+ return cpy.updateTrie(s.db)
}
-func (self *StateDB) HasSuicided(addr common.Address) bool {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) HasSuicided(addr common.Address) bool {
+ stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.suicided
}
@@ -344,46 +349,46 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
*/
// AddBalance adds amount to the account associated with addr.
-func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
// SubBalance subtracts amount from the account associated with addr.
-func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalance(amount)
}
}
-func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount)
}
}
-func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
-func (self *StateDB) SetCode(addr common.Address, code []byte) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) SetCode(addr common.Address, code []byte) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
}
-func (self *StateDB) SetState(addr common.Address, key, value common.Hash) {
- stateObject := self.GetOrNewStateObject(addr)
+func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
+ stateObject := s.GetOrNewStateObject(addr)
if stateObject != nil {
- stateObject.SetState(self.db, key, value)
+ stateObject.SetState(s.db, key, value)
}
}
@@ -401,12 +406,12 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
//
// The account's state object is still available until the state is committed,
// getStateObject will return a non-nil account after Suicide.
-func (self *StateDB) Suicide(addr common.Address) bool {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) Suicide(addr common.Address) bool {
+ stateObject := s.getStateObject(addr)
if stateObject == nil {
return false
}
- self.journal = append(self.journal, suicideChange{
+ s.journal = append(s.journal, suicideChange{
account: &addr,
prev: stateObject.suicided,
prevbalance: new(big.Int).Set(stateObject.Balance()),
@@ -417,49 +422,90 @@ func (self *StateDB) Suicide(addr common.Address) bool {
return true
}
+// SetTransientState sets transient storage for a given account. It
+// adds the change to the journal so that it can be rolled back
+// to its previous value if there is a revert.
+func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
+ prev := s.GetTransientState(addr, key)
+ if prev == value {
+ return
+ }
+
+ s.journal = append(s.journal, transientStorageChange{
+ account: &addr,
+ key: key,
+ prevalue: prev,
+ })
+
+ s.setTransientState(addr, key, value)
+}
+
+// setTransientState is a lower level setter for transient storage. It
+// is called during a revert to prevent modifications to the journal.
+func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) {
+ s.transientStorage.Set(addr, key, value)
+}
+
+// GetTransientState gets transient storage for a given account.
+func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
+ return s.transientStorage.Get(addr, key)
+}
+
//
// Setting, updating & deleting state object methods.
//
// updateStateObject writes the given object to the trie.
-func (self *StateDB) updateStateObject(stateObject *stateObject) {
+func (s *StateDB) updateStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on updating the account from the trie
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+
+ // Encode the account and update the account trie
addr := stateObject.Address()
+
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
- self.setError(self.trie.TryUpdate(addr[:], data))
+ s.setError(s.trie.TryUpdate(addr[:], data))
}
// deleteStateObject removes the given object from the state trie.
-func (self *StateDB) deleteStateObject(stateObject *stateObject) {
+func (s *StateDB) deleteStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on deleting the account from the trie
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+
+ // Delete the account from the trie
stateObject.deleted = true
+
addr := stateObject.Address()
- self.setError(self.trie.TryDelete(addr[:]))
+ s.setError(s.trie.TryDelete(addr[:]))
}
// DeleteAddress removes the address from the state trie.
-func (self *StateDB) DeleteAddress(addr common.Address) {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) DeleteAddress(addr common.Address) {
+ stateObject := s.getStateObject(addr)
if stateObject != nil && !stateObject.deleted {
- self.deleteStateObject(stateObject)
+ s.deleteStateObject(stateObject)
}
}
// Retrieve a state object given my the address. Returns nil if not found.
-func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
- // Prefer 'live' objects.
- if obj := self.stateObjects[addr]; obj != nil {
+func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
+ // Prefer live objects if any is available
+ if obj := s.stateObjects[addr]; obj != nil {
if obj.deleted {
return nil
}
return obj
}
+ // Track the amount of time wasted on loading the object from the database
+ defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
- // Load the object from the database.
- enc, err := self.trie.TryGet(addr[:])
+ // Load the object from the database
+ enc, err := s.trie.TryGet(addr[:])
if len(enc) == 0 {
- self.setError(err)
+ s.setError(err)
return nil
}
var data Account
@@ -467,43 +513,43 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
}
- // Insert into the live set.
- obj := newObject(self, addr, data, self.MarkStateObjectDirty)
- self.setStateObject(obj)
+ // Insert into the live set
+ obj := newObject(s, addr, data, s.MarkStateObjectDirty)
+ s.setStateObject(obj)
return obj
}
-func (self *StateDB) setStateObject(object *stateObject) {
- self.stateObjects[object.Address()] = object
+func (s *StateDB) setStateObject(object *stateObject) {
+ s.stateObjects[object.Address()] = object
}
// Retrieve a state object or create a new state object if nil.
-func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
- stateObject := self.getStateObject(addr)
+func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
+ stateObject := s.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
- stateObject, _ = self.createObject(addr)
+ stateObject, _ = s.createObject(addr)
}
return stateObject
}
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
-func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
- self.stateObjectsDirty[addr] = struct{}{}
+func (s *StateDB) MarkStateObjectDirty(addr common.Address) {
+ s.stateObjectsDirty[addr] = struct{}{}
}
// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.
-func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
- prev = self.getStateObject(addr)
- newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
+func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
+ prev = s.getStateObject(addr)
+ newobj = newObject(s, addr, Account{}, s.MarkStateObjectDirty)
newobj.setNonce(0) // sets the object to dirty
if prev == nil {
- self.journal = append(self.journal, createObjectChange{account: &addr})
+ s.journal = append(s.journal, createObjectChange{account: &addr})
} else {
- self.journal = append(self.journal, resetObjectChange{prev: prev})
+ s.journal = append(s.journal, resetObjectChange{prev: prev})
}
- self.setStateObject(newobj)
+ s.setStateObject(newobj)
return newobj, prev
}
@@ -517,8 +563,8 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
// Carrying over the balance ensures that Ether doesn't disappear.
-func (self *StateDB) CreateAccount(addr common.Address) {
- new, prev := self.createObject(addr)
+func (s *StateDB) CreateAccount(addr common.Address) {
+ new, prev := s.createObject(addr)
if prev != nil {
new.setBalance(prev.data.Balance)
}
@@ -548,29 +594,29 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
-func (self *StateDB) Copy() *StateDB {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (s *StateDB) Copy() *StateDB {
+ s.lock.Lock()
+ defer s.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &StateDB{
- db: self.db,
- trie: self.db.CopyTrie(self.trie),
- stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
- stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
- refund: self.refund,
- logs: make(map[common.Hash][]*types.Log, len(self.logs)),
- logSize: self.logSize,
+ db: s.db,
+ trie: s.db.CopyTrie(s.trie),
+ stateObjects: make(map[common.Address]*stateObject, len(s.stateObjectsDirty)),
+ stateObjectsDirty: make(map[common.Address]struct{}, len(s.stateObjectsDirty)),
+ refund: s.refund,
+ logs: make(map[common.Hash][]*types.Log, len(s.logs)),
+ logSize: s.logSize,
preimages: make(map[common.Hash][]byte),
}
// Copy the dirty states, logs, and preimages
- for addr := range self.stateObjectsDirty {
- state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
+ for addr := range s.stateObjectsDirty {
+ state.stateObjects[addr] = s.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
state.stateObjectsDirty[addr] = struct{}{}
}
// Deep copy the logs occurred in the scope of block
- for hash, logs := range self.logs {
+ for hash, logs := range s.logs {
cpy := make([]*types.Log, len(logs))
for i, l := range logs {
cpy[i] = new(types.Log)
@@ -579,7 +625,7 @@ func (self *StateDB) Copy() *StateDB {
state.logs[hash] = cpy
}
- for hash, preimage := range self.preimages {
+ for hash, preimage := range s.preimages {
state.preimages[hash] = preimage
}
// Do we need to copy the access list? In practice: No. At the start of a
@@ -587,42 +633,45 @@ func (self *StateDB) Copy() *StateDB {
// _between_ transactions/blocks, never in the middle of a transaction.
// However, it doesn't cost us much to copy an empty list, so we do it anyway
// to not blow up if we ever decide copy it in the middle of a transaction
- state.accessList = self.accessList.Copy()
+ state.accessList = s.accessList.Copy()
+
+ state.transientStorage = s.transientStorage.Copy()
+
return state
}
// Snapshot returns an identifier for the current revision of the state.
-func (self *StateDB) Snapshot() int {
- id := self.nextRevisionId
- self.nextRevisionId++
- self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
+func (s *StateDB) Snapshot() int {
+ id := s.nextRevisionId
+ s.nextRevisionId++
+ s.validRevisions = append(s.validRevisions, revision{id, len(s.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
-func (self *StateDB) RevertToSnapshot(revid int) {
+func (s *StateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
- idx := sort.Search(len(self.validRevisions), func(i int) bool {
- return self.validRevisions[i].id >= revid
+ idx := sort.Search(len(s.validRevisions), func(i int) bool {
+ return s.validRevisions[i].id >= revid
})
- if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
+ if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
- snapshot := self.validRevisions[idx].journalIndex
+ snapshot := s.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
- for i := len(self.journal) - 1; i >= snapshot; i-- {
- self.journal[i].undo(self)
+ for i := len(s.journal) - 1; i >= snapshot; i-- {
+ s.journal[i].undo(s)
}
- self.journal = self.journal[:snapshot]
+ s.journal = s.journal[:snapshot]
// Remove invalidated snapshots from the stack.
- self.validRevisions = self.validRevisions[:idx]
+ s.validRevisions = s.validRevisions[:idx]
}
// GetRefund returns the current value of the refund counter.
-func (self *StateDB) GetRefund() uint64 {
- return self.refund
+func (s *StateDB) GetRefund() uint64 {
+ return s.refund
}
// Finalise finalises the state by removing the self destructed objects
@@ -646,16 +695,19 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
// goes into transaction receipts.
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
s.Finalise(deleteEmptyObjects)
+
+ // Track the amount of time wasted on hashing the account trie
+ defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
+
return s.trie.Hash()
}
-// Prepare sets the current transaction hash and index and block hash which is
-// used when the EVM emits new state logs.
-func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
- self.thash = thash
- self.bhash = bhash
- self.txIndex = ti
- self.accessList = newAccessList()
+// SetTxContext sets the current transaction hash and index which are
+// used when the EVM emits new state logs. It should be invoked before
+// transaction execution.
+func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
+ s.thash = thash
+ s.txIndex = ti
}
// DeleteSuicides flags the suicided objects for deletion so that it
@@ -689,7 +741,7 @@ func (s *StateDB) clearJournalAndRefund() {
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
- // Commit objects to the trie.
+ // Commit objects to the trie, measuring the elapsed time
for addr, stateObject := range s.stateObjects {
_, isDirty := s.stateObjectsDirty[addr]
switch {
@@ -712,17 +764,19 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
}
delete(s.stateObjectsDirty, addr)
}
- // Write trie changes.
+ // Write the account trie changes, measuing the amount of wasted time
+ defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now())
+
root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
- if account.Root != emptyState {
+ if account.Root != types.EmptyRootHash {
s.db.TrieDB().Reference(account.Root, parent)
}
code := common.BytesToHash(account.CodeHash)
- if code != emptyCode {
+ if code != types.EmptyCodeHash {
s.db.TrieDB().Reference(code, parent)
}
return nil
@@ -730,30 +784,44 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
return root, err
}
-// PrepareAccessList handles the preparatory steps for executing a state transition with
-// regards to both EIP-2929 and EIP-2930:
+// Prepare handles the preparatory steps for executing a state transition with.
+// This method must be invoked before state transition.
//
+// Berlin fork:
// - Add sender to access list (2929)
// - Add destination to access list (2929)
// - Add precompiles to access list (2929)
// - Add the contents of the optional tx access list (2930)
//
-// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
-func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
- s.AddAddressToAccessList(sender)
- if dst != nil {
- s.AddAddressToAccessList(*dst)
- // If it's a create-tx, the destination will be added inside evm.create
- }
- for _, addr := range precompiles {
- s.AddAddressToAccessList(addr)
- }
- for _, el := range list {
- s.AddAddressToAccessList(el.Address)
- for _, key := range el.StorageKeys {
- s.AddSlotToAccessList(el.Address, key)
+// Potential EIPs:
+// - Reset access list (Berlin)
+// - Add coinbase to access list (EIP-3651)
+// - Reset transient storage (EIP-1153)
+func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
+ if rules.IsEIP1559 {
+ // Clear out any leftover from previous executions
+ al := newAccessList()
+ s.accessList = al
+
+ al.AddAddress(sender)
+ if dst != nil {
+ al.AddAddress(*dst)
+ // If it's a create-tx, the destination will be added inside evm.create
}
+ for _, addr := range precompiles {
+ al.AddAddress(addr)
+ }
+ for _, el := range list {
+ al.AddAddress(el.Address)
+ for _, key := range el.StorageKeys {
+ al.AddSlot(el.Address, key)
+ }
+ }
+ // EIP-3651: warm coinbase
+ al.AddAddress(coinbase)
}
+ // Reset transient storage at the beginning of transaction execution
+ s.transientStorage = newTransientStorage()
}
// AddAddressToAccessList adds the given address to the access list
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index 15533ec5a0..5166224b35 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -297,6 +297,16 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
},
args: make([]int64, 1),
},
+ {
+ name: "SetTransientState",
+ fn: func(a testAction, s *StateDB) {
+ var key, val common.Hash
+ binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
+ binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
+ s.SetTransientState(addr, key, val)
+ },
+ args: make([]int64, 2),
+ },
}
action := actions[r.Intn(len(actions))]
var nameargs []string
@@ -419,9 +429,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())
}
- if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
+ if !reflect.DeepEqual(state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
- state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
+ state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{}))
}
return nil
}
@@ -615,3 +625,37 @@ func TestStateDBAccessList(t *testing.T) {
t.Fatalf("expected empty, got %d", got)
}
}
+
+func TestStateDBTransientStorage(t *testing.T) {
+ memDb := rawdb.NewMemoryDatabase()
+ db := NewDatabase(memDb)
+ state, _ := New(common.Hash{}, db)
+
+ key := common.Hash{0x01}
+ value := common.Hash{0x02}
+ addr := common.Address{}
+
+ state.SetTransientState(addr, key, value)
+ if exp, got := 1, state.journal.length(); exp != got {
+ t.Fatalf("journal length mismatch: have %d, want %d", got, exp)
+ }
+ // the retrieved value should equal what was set
+ if got := state.GetTransientState(addr, key); got != value {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
+ }
+
+ // revert the transient state being set and then check that the
+ // value is now the empty hash
+ state.journal[0].undo(state)
+ if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
+ }
+
+ // set transient state and then copy the statedb and ensure that
+ // the transient state is copied
+ state.SetTransientState(addr, key, value)
+ cpy := state.Copy()
+ if got := cpy.GetTransientState(addr, key); got != value {
+ t.Fatalf("transient storage mismatch: have %x, want %x", got, value)
+ }
+}
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index e38943ab58..57fa80e773 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -23,6 +23,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
@@ -125,8 +126,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
// Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) {
- empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
- if req := NewStateSync(empty, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 {
+ if req := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 {
t.Errorf("content requested for empty state: %v", req)
}
}
diff --git a/core/state/transient_storage.go b/core/state/transient_storage.go
new file mode 100644
index 0000000000..1cc9548e89
--- /dev/null
+++ b/core/state/transient_storage.go
@@ -0,0 +1,55 @@
+// Copyright 2022 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package state
+
+import (
+ "github.com/XinFinOrg/XDPoSChain/common"
+)
+
+// transientStorage is a representation of EIP-1153 "Transient Storage".
+type transientStorage map[common.Address]Storage
+
+// newTransientStorage creates a new instance of a transientStorage.
+func newTransientStorage() transientStorage {
+ return make(transientStorage)
+}
+
+// Set sets the transient-storage `value` for `key` at the given `addr`.
+func (t transientStorage) Set(addr common.Address, key, value common.Hash) {
+ if _, ok := t[addr]; !ok {
+ t[addr] = make(Storage)
+ }
+ t[addr][key] = value
+}
+
+// Get gets the transient storage for `key` at the given `addr`.
+func (t transientStorage) Get(addr common.Address, key common.Hash) common.Hash {
+ val, ok := t[addr]
+ if !ok {
+ return common.Hash{}
+ }
+ return val[key]
+}
+
+// Copy does a deep copy of the transientStorage
+func (t transientStorage) Copy() transientStorage {
+ storage := make(transientStorage)
+ for key, value := range t {
+ storage[key] = value.Copy()
+ }
+ return storage
+}
diff --git a/core/state/trc21_reader.go b/core/state/trc21_reader.go
index af5fd25b13..561cebb5c7 100644
--- a/core/state/trc21_reader.go
+++ b/core/state/trc21_reader.go
@@ -5,8 +5,7 @@ import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
- lru "github.com/hashicorp/golang-lru"
-
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
)
var (
@@ -22,21 +21,18 @@ var (
}
transferFuncHex = common.Hex2Bytes("0xa9059cbb")
transferFromFuncHex = common.Hex2Bytes("0x23b872dd")
- cache, _ = lru.NewARC(128)
+ cache = lru.NewCache[common.Hash, map[common.Address]*big.Int](128)
)
func GetTRC21FeeCapacityFromStateWithCache(trieRoot common.Hash, statedb *StateDB) map[common.Address]*big.Int {
if statedb == nil {
return map[common.Address]*big.Int{}
}
- data, _ := cache.Get(trieRoot)
- var info map[common.Address]*big.Int
- if data != nil {
- info = data.(map[common.Address]*big.Int)
- } else {
+ info, ok := cache.Get(trieRoot)
+ if !ok || info == nil {
info = GetTRC21FeeCapacityFromState(statedb)
+ cache.Add(trieRoot, info)
}
- cache.Add(trieRoot, info)
tokensFee := map[common.Address]*big.Int{}
for key, value := range info {
tokensFee[key] = big.NewInt(0).SetBytes(value.Bytes())
diff --git a/core/state_processor.go b/core/state_processor.go
index e4bab6797b..5fc975c7c7 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -25,8 +25,6 @@ import (
"sync"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
- "github.com/XinFinOrg/XDPoSChain/log"
-
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
@@ -34,6 +32,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
)
@@ -69,33 +68,39 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
var (
- receipts types.Receipts
- usedGas = new(uint64)
- header = block.Header()
- allLogs []*types.Log
- gp = new(GasPool).AddGas(block.GasLimit())
+ receipts types.Receipts
+ usedGas = new(uint64)
+ header = block.Header()
+ blockHash = block.Hash()
+ blockNumber = block.Number()
+ allLogs []*types.Log
+ gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
- if common.TIPSigning.Cmp(header.Number) == 0 {
+ if common.TIPSigning.Cmp(blockNumber) == 0 {
statedb.DeleteAddress(common.BlockSignersBinary)
}
parentState := statedb.Copy()
InitSignerInTransactions(p.config, header, block.Transactions())
balanceUpdated := map[common.Address]*big.Int{}
totalFeeUsed := big.NewInt(0)
+ blockContext := NewEVMBlockContext(header, p.bc, nil)
+ vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
+ coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
+ // Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
// check black-list txs after hf
if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return nil, nil, 0, fmt.Errorf("Block contains transaction with sender in black-list: %v", tx.From().Hex())
+ return nil, nil, 0, fmt.Errorf("block contains transaction with sender in black-list: %v", tx.From().Hex())
}
// check if receiver is in black list
if tx.To() != nil && common.Blacklist[*tx.To()] {
- return nil, nil, 0, fmt.Errorf("Block contains transaction with receiver in black-list: %v", tx.To().Hex())
+ return nil, nil, 0, fmt.Errorf("block contains transaction with receiver in black-list: %v", tx.To().Hex())
}
}
// validate minFee slot for XDCZ
@@ -112,8 +117,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
return nil, nil, 0, err
}
}
- statedb.Prepare(tx.Hash(), block.Hash(), i)
- receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg)
+ statedb.SetTxContext(tx.Hash(), i)
+ receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
if err != nil {
return nil, nil, 0, err
}
@@ -135,17 +140,19 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
block := cBlock.block
var (
- receipts types.Receipts
- usedGas = new(uint64)
- header = block.Header()
- allLogs []*types.Log
- gp = new(GasPool).AddGas(block.GasLimit())
+ receipts types.Receipts
+ usedGas = new(uint64)
+ header = block.Header()
+ blockHash = block.Hash()
+ blockNumber = block.Number()
+ allLogs []*types.Log
+ gp = new(GasPool).AddGas(block.GasLimit())
)
// Mutate the the block and state according to any hard-fork specs
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
- if common.TIPSigning.Cmp(header.Number) == 0 {
+ if common.TIPSigning.Cmp(blockNumber) == 0 {
statedb.DeleteAddress(common.BlockSignersBinary)
}
if cBlock.stop {
@@ -159,6 +166,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
if cBlock.stop {
return nil, nil, 0, ErrStopPreparingBlock
}
+ blockContext := NewEVMBlockContext(header, p.bc, nil)
+ vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
+ coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
// Iterate over and process the individual transactions
receipts = make([]*types.Receipt, block.Transactions().Len())
for i, tx := range block.Transactions() {
@@ -166,11 +176,11 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
if (block.Number().Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return nil, nil, 0, fmt.Errorf("Block contains transaction with sender in black-list: %v", tx.From().Hex())
+ return nil, nil, 0, fmt.Errorf("block contains transaction with sender in black-list: %v", tx.From().Hex())
}
// check if receiver is in black list
if tx.To() != nil && common.Blacklist[*tx.To()] {
- return nil, nil, 0, fmt.Errorf("Block contains transaction with receiver in black-list: %v", tx.To().Hex())
+ return nil, nil, 0, fmt.Errorf("block contains transaction with receiver in black-list: %v", tx.To().Hex())
}
}
// validate minFee slot for XDCZ
@@ -187,8 +197,8 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
return nil, nil, 0, err
}
}
- statedb.Prepare(tx.Hash(), block.Hash(), i)
- receipt, gas, err, tokenFeeUsed := ApplyTransaction(p.config, balanceFee, p.bc, nil, gp, statedb, tradingState, header, tx, usedGas, cfg)
+ statedb.SetTxContext(tx.Hash(), i)
+ receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
if err != nil {
return nil, nil, 0, err
}
@@ -210,27 +220,24 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
return receipts, allLogs, *usedGas, nil
}
-// ApplyTransaction attempts to apply a transaction to the given state database
-// and uses the input parameters for its environment. It returns the receipt
-// for the transaction, gas used and an error if the transaction failed,
-// indicating the block was invalid.
-func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) {
+func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber, baseFee *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) {
to := tx.To()
- if to != nil && *to == common.BlockSignersBinary && config.IsTIPSigning(header.Number) {
- return ApplySignTransaction(config, statedb, header, tx, usedGas)
+ if to != nil {
+ if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
+ return ApplySignTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
+ }
+ if *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(blockNumber) {
+ return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
+ }
+ if *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(blockNumber) {
+ return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
+ }
}
- if to != nil && *to == common.TradingStateAddrBinary && config.IsTIPXDCXReceiver(header.Number) {
- return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
+ if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(blockNumber) {
+ return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
}
- if to != nil && *to == common.XDCXLendingAddressBinary && config.IsTIPXDCXReceiver(header.Number) {
- return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
- }
- if tx.IsTradingTransaction() && config.IsTIPXDCXReceiver(header.Number) {
- return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
- }
-
- if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(header.Number) {
- return ApplyEmptyTransaction(config, statedb, header, tx, usedGas)
+ if tx.IsLendingFinalizedTradeTransaction() && config.IsTIPXDCXReceiver(blockNumber) {
+ return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
}
var balanceFee *big.Int
@@ -239,29 +246,21 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
balanceFee = value
}
}
- msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), balanceFee, header.Number)
+ // msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber)
+ msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber, baseFee)
if err != nil {
return nil, 0, err, false
}
- // Create a new context to be used in the EVM environment.
- context := NewEVMContext(msg, header, bc, author)
- // Create a new environment which holds all relevant information
- // about the transaction and calling mechanisms.
- vmenv := vm.NewEVM(context, statedb, XDCxState, config, cfg)
- // If we don't have an explicit author (i.e. not mining), extract from the header
- var beneficiary common.Address
- if author == nil {
- beneficiary, _ = bc.Engine().Author(header) // Ignore error, we're past header validation
- } else {
- beneficiary = *author
- }
+ // Create a new context to be used in the EVM environment
+ txContext := NewEVMTxContext(msg)
- coinbaseOwner := statedb.GetOwner(beneficiary)
+ // Update the evm with the new transaction context.
+ evm.Reset(txContext, statedb)
// Bypass blacklist address
maxBlockNumber := new(big.Int).SetInt64(9147459)
- if header.Number.Cmp(maxBlockNumber) <= 0 {
+ if blockNumber.Cmp(maxBlockNumber) <= 0 {
addrMap := make(map[string]string)
addrMap["0x5248bfb72fd4f234e062d3e9bb76f08643004fcd"] = "29410"
addrMap["0x5ac26105b35ea8935be382863a70281ec7a985e9"] = "23551"
@@ -389,9 +388,9 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
addrFrom := msg.From().Hex()
- currentBlockNumber := header.Number.Int64()
+ currentBlockNumber := blockNumber.Int64()
if addr, ok := blockMap[currentBlockNumber]; ok {
- if strings.ToLower(addr) == strings.ToLower(addrFrom) {
+ if strings.EqualFold(addr, addrFrom) { // case insensitive
bal := addrMap[addr]
hBalance := new(big.Int)
hBalance.SetString(bal+"000000000000000000", 10)
@@ -404,7 +403,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
// End Bypass blacklist address
// Apply the transaction to the current state (included in the env)
- _, gas, failed, err, _ := ApplyMessage(vmenv, msg, gp, coinbaseOwner)
+ result, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner)
if err != nil {
return nil, 0, err, false
@@ -412,50 +411,74 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
// Update the state with pending changes.
var root []byte
- if config.IsByzantium(header.Number) {
+ if config.IsByzantium(blockNumber) {
statedb.Finalise(true)
} else {
- root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
}
- *usedGas += gas
+ *usedGas += result.UsedGas
// Create a new receipt for the transaction, storing the intermediate root and gas used
// by the tx.
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
- if failed {
+ if result.Failed() {
receipt.Status = types.ReceiptStatusFailed
} else {
receipt.Status = types.ReceiptStatusSuccessful
}
receipt.TxHash = tx.Hash()
- receipt.GasUsed = gas
+ receipt.GasUsed = result.UsedGas
// If the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
- receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
+ receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
}
// Set the receipt logs and create the bloom filter.
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
- receipt.BlockHash = statedb.BlockHash()
- receipt.BlockNumber = header.Number
+ receipt.BlockHash = blockHash
+ receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
- if balanceFee != nil && failed {
+ if balanceFee != nil && result.Failed() {
state.PayFeeWithTRC21TxFail(statedb, msg.From(), *to)
}
- return receipt, gas, err, balanceFee != nil
+ return receipt, result.UsedGas, err, balanceFee != nil
}
-func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
+func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address {
+ // If we don't have an explicit author (i.e. not mining), extract from the header
+ var beneficiary common.Address
+ if author == nil {
+ beneficiary, _ = bc.Engine().Author(header) // Ignore error, we're past header validation
+ } else {
+ beneficiary = *author
+ }
+ return statedb.GetOwner(beneficiary)
+}
+
+// ApplyTransaction attempts to apply a transaction to the given state database
+// and uses the input parameters for its environment. It returns the receipt
+// for the transaction, gas used and an error if the transaction failed,
+// indicating the block was invalid.
+func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) {
+ // Create a new context to be used in the EVM environment
+ blockContext := NewEVMBlockContext(header, bc, author)
+ vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
+ coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
+ // return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
+ return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
+}
+
+func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
// Update the state with pending changes
var root []byte
- if config.IsByzantium(header.Number) {
+ if config.IsByzantium(blockNumber) {
statedb.Finalise(true)
} else {
- root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
}
- from, err := types.Sender(types.MakeSigner(config, header.Number), tx)
+ from, err := types.Sender(types.MakeSigner(config, blockNumber), tx)
if err != nil {
return nil, 0, err, false
}
@@ -475,23 +498,23 @@ func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, he
// Set the receipt logs and create a bloom for filtering
log := &types.Log{}
log.Address = common.BlockSignersBinary
- log.BlockNumber = header.Number.Uint64()
+ log.BlockNumber = blockNumber.Uint64()
statedb.AddLog(log)
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
- receipt.BlockHash = statedb.BlockHash()
- receipt.BlockNumber = header.Number
+ receipt.BlockHash = blockHash
+ receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, 0, nil, false
}
-func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
+func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
// Update the state with pending changes
var root []byte
- if config.IsByzantium(header.Number) {
+ if config.IsByzantium(blockNumber) {
statedb.Finalise(true)
} else {
- root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
+ root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
}
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts.
@@ -502,12 +525,12 @@ func ApplyEmptyTransaction(config *params.ChainConfig, statedb *state.StateDB, h
// Set the receipt logs and create a bloom for filtering
log := &types.Log{}
log.Address = *tx.To()
- log.BlockNumber = header.Number.Uint64()
+ log.BlockNumber = blockNumber.Uint64()
statedb.AddLog(log)
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
- receipt.BlockHash = statedb.BlockHash()
- receipt.BlockNumber = header.Number
+ receipt.BlockHash = blockHash
+ receipt.BlockNumber = blockNumber
receipt.TransactionIndex = uint(statedb.TxIndex())
return receipt, 0, nil, false
}
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
new file mode 100644
index 0000000000..cce4016752
--- /dev/null
+++ b/core/state_processor_test.go
@@ -0,0 +1,290 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package core
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/core/vm"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "golang.org/x/crypto/sha3"
+)
+
+// TestStateProcessorErrors tests the output from the 'core' errors
+// as defined in core/error.go. These errors are generated when the
+// blockchain imports bad blocks, meaning blocks which have valid headers but
+// contain invalid transactions
+func TestStateProcessorErrors(t *testing.T) {
+ var (
+ config = ¶ms.ChainConfig{
+ ChainId: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ BerlinBlock: big.NewInt(0),
+ LondonBlock: big.NewInt(0),
+ Eip1559Block: big.NewInt(0),
+ Ethash: new(params.EthashConfig),
+ }
+ signer = types.LatestSigner(config)
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ )
+ var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction {
+ tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
+ signedTx, err := types.SignTx(tx, signer, testKey)
+ if err != nil {
+ t.Fatalf("fail to sign tx: %v, err: %v", tx, err)
+ }
+ return signedTx
+ }
+ var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction {
+ tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
+ Nonce: nonce,
+ GasTipCap: gasTipCap,
+ GasFeeCap: gasFeeCap,
+ Gas: gasLimit,
+ To: &to,
+ Value: big.NewInt(0),
+ }), signer, testKey)
+ return tx
+ }
+ { // Tests against a 'recent' chain definition
+ var (
+ db = rawdb.NewMemoryDatabase()
+ gspec = &Genesis{
+ Config: config,
+ Alloc: GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Balance: big.NewInt(1000000000000000000), // 1 ether
+ Nonce: 0,
+ },
+ },
+ }
+ genesis = gspec.MustCommit(db)
+ blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
+ )
+ defer blockchain.Stop()
+ bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
+ tooBigNumber := new(big.Int).Set(bigNumber)
+ tooBigNumber.Add(tooBigNumber, common.Big1)
+ for i, tt := range []struct {
+ txs []*types.Transaction
+ want string
+ }{
+ { // ErrNonceTooLow
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ },
+ want: "nonce too low: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1",
+ },
+ { // ErrNonceTooHigh
+ txs: []*types.Transaction{
+ makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
+ },
+ want: "nonce too high: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0",
+ },
+ { // ErrGasLimitReached
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil),
+ },
+ want: "gas limit reached",
+ },
+ { // ErrInsufficientFundsForTransfer
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil),
+ },
+ want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7",
+ },
+ { // ErrInsufficientFunds
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil),
+ },
+ want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000",
+ },
+ // ErrGasUintOverflow
+ // One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow",
+ // In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the
+ // multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment
+ { // ErrIntrinsicGas
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil),
+ },
+ want: "intrinsic gas too low: have 20000, want 21000",
+ },
+ { // ErrGasLimitReached
+ txs: []*types.Transaction{
+ makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil),
+ },
+ want: "gas limit reached",
+ },
+ { // ErrFeeCapTooLow
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)),
+ },
+ want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000",
+ },
+ { // ErrTipVeryHigh
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)),
+ },
+ want: "max priority fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257",
+ },
+ { // ErrFeeCapVeryHigh
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber),
+ },
+ want: "max fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257",
+ },
+ { // ErrTipAboveFeeCap
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)),
+ },
+ want: "max priority fee per gas higher than max fee per gas: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1",
+ },
+ { // ErrInsufficientFunds
+ // Available balance: 1000000000000000000
+ // Effective cost: 18375000021000
+ // FeeCap * gas: 1050000000000000000
+ // This test is designed to have the effective cost be covered by the balance, but
+ // the extended requirement on FeeCap*gas < balance to fail
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)),
+ },
+ want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000",
+ },
+ { // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
+ },
+ want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
+ },
+ }[8:] {
+ block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
+ _, err := blockchain.InsertChain(types.Blocks{block})
+ if err == nil {
+ t.Fatal("block imported without errors")
+ }
+ if have, want := err.Error(), tt.want; have != want {
+ t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
+ }
+ }
+ }
+
+ // One final error is ErrTxTypeNotSupported. For this, we need an older chain
+ {
+ var (
+ db = rawdb.NewMemoryDatabase()
+ gspec = &Genesis{
+ Config: ¶ms.ChainConfig{
+ ChainId: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ },
+ Alloc: GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Balance: big.NewInt(1000000000000000000), // 1 ether
+ Nonce: 0,
+ },
+ },
+ }
+ genesis = gspec.MustCommit(db)
+ blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
+ )
+ defer blockchain.Stop()
+ for i, tt := range []struct {
+ txs []*types.Transaction
+ want string
+ }{
+ { // ErrTxTypeNotSupported
+ txs: []*types.Transaction{
+ mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
+ },
+ want: "transaction type not supported",
+ },
+ } {
+ block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
+ _, err := blockchain.InsertChain(types.Blocks{block})
+ if err == nil {
+ t.Fatal("block imported without errors")
+ }
+ if have, want := err.Error(), tt.want; have != want {
+ t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
+ }
+ }
+ }
+}
+
+// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
+// valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently
+// valid to be considered for import:
+// - valid pow (fake), ancestry, difficulty, gaslimit etc
+func GenerateBadBlock(t *testing.T, parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
+ header := &types.Header{
+ ParentHash: parent.Hash(),
+ Coinbase: parent.Coinbase(),
+ Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time().Uint64()+10, &types.Header{
+ Number: parent.Number(),
+ Time: parent.Time(),
+ Difficulty: parent.Difficulty(),
+ UncleHash: parent.UncleHash(),
+ }),
+ GasLimit: parent.GasLimit(),
+ Number: new(big.Int).Add(parent.Number(), common.Big1),
+ Time: new(big.Int).SetUint64(parent.Time().Uint64() + 10),
+ UncleHash: types.EmptyUncleHash,
+ }
+ if config.IsEIP1559(header.Number) {
+ header.BaseFee = common.BaseFee
+ }
+ var receipts []*types.Receipt
+ // The post-state result doesn't need to be correct (this is a bad block), but we do need something there
+ // Preferably something unique. So let's use a combo of blocknum + txhash
+ hasher := sha3.NewLegacyKeccak256()
+ hasher.Write(header.Number.Bytes())
+ var cumulativeGas uint64
+ for _, tx := range txs {
+ txh := tx.Hash()
+ hasher.Write(txh[:])
+ receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas())
+ receipt.TxHash = tx.Hash()
+ receipt.GasUsed = tx.Gas()
+ receipts = append(receipts, receipt)
+ cumulativeGas += tx.Gas()
+ }
+ header.Root = common.BytesToHash(hasher.Sum(nil))
+ // Assemble and return the final block for sealing
+ return types.NewBlock(header, txs, nil, receipts)
+}
diff --git a/core/state_transition.go b/core/state_transition.go
index 3e1120ceb9..42e044a8de 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -23,12 +23,16 @@ import (
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
+ cmath "github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
- "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/holiman/uint256"
)
+var emptyCodeHash = crypto.Keccak256Hash(nil)
+
var (
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
)
@@ -57,6 +61,8 @@ type StateTransition struct {
msg Message
gas uint64
gasPrice *big.Int
+ gasFeeCap *big.Int
+ gasTipCap *big.Int
initialGas uint64
value *big.Int
data []byte
@@ -67,22 +73,52 @@ type StateTransition struct {
// Message represents a message sent to a contract.
type Message interface {
From() common.Address
- //FromFrontier() (common.Address, error)
To() *common.Address
GasPrice() *big.Int
+ GasFeeCap() *big.Int
+ GasTipCap() *big.Int
Gas() uint64
Value() *big.Int
Nonce() uint64
- CheckNonce() bool
+ IsFake() bool
Data() []byte
BalanceTokenFee() *big.Int
AccessList() types.AccessList
}
+// ExecutionResult includes all output after executing given evm
+// message no matter the execution itself is successful or not.
+type ExecutionResult struct {
+ UsedGas uint64 // Total used gas but include the refunded gas
+ Err error // Any error encountered during the execution(listed in core/vm/errors.go)
+ ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
+}
+
+// Failed returns the indicator whether the execution is successful or not
+func (result *ExecutionResult) Failed() bool { return result.Err != nil }
+
+// Return is a helper function to help caller distinguish between revert reason
+// and function return. Return returns the data after execution if no error occurs.
+func (result *ExecutionResult) Return() []byte {
+ if result.Err != nil {
+ return nil
+ }
+ return common.CopyBytes(result.ReturnData)
+}
+
+// Revert returns the concrete revert reason if the execution is aborted by `REVERT`
+// opcode. Note the reason can be nil if no data supplied with revert opcode.
+func (result *ExecutionResult) Revert() []byte {
+ if result.Err != vm.ErrExecutionReverted {
+ return nil
+ }
+ return common.CopyBytes(result.ReturnData)
+}
+
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
-func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool) (uint64, error) {
+func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) {
// Set the starting gas for the raw transaction
var gas uint64
if isContractCreation && isHomestead {
@@ -90,8 +126,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
} else {
gas = params.TxGas
}
+ dataLen := uint64(len(data))
// Bump the required gas by the amount of transactional data
- if len(data) > 0 {
+ if dataLen > 0 {
// Zero and non-zero bytes are priced differently
var nz uint64
for _, byt := range data {
@@ -105,11 +142,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
}
gas += nz * params.TxDataNonZeroGas
- z := uint64(len(data)) - nz
+ z := dataLen - nz
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
return 0, ErrGasUintOverflow
}
gas += z * params.TxDataZeroGas
+
+ if isContractCreation && isEIP3860 {
+ lenWords := toWordSize(dataLen)
+ if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
+ return 0, ErrGasUintOverflow
+ }
+ gas += lenWords * params.InitCodeWordGas
+ }
}
if accessList != nil {
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
@@ -118,16 +163,27 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
return gas, nil
}
+// toWordSize returns the ceiled word size required for init code payment calculation.
+func toWordSize(size uint64) uint64 {
+ if size > math.MaxUint64-31 {
+ return math.MaxUint64/32 + 1
+ }
+
+ return (size + 31) / 32
+}
+
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
- gp: gp,
- evm: evm,
- msg: msg,
- gasPrice: msg.GasPrice(),
- value: msg.Value(),
- data: msg.Data(),
- state: evm.StateDB,
+ gp: gp,
+ evm: evm,
+ msg: msg,
+ gasPrice: msg.GasPrice(),
+ gasFeeCap: msg.GasFeeCap(),
+ gasTipCap: msg.GasTipCap(),
+ value: msg.Value(),
+ data: msg.Data(),
+ state: evm.StateDB,
}
}
@@ -138,7 +194,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
-func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) ([]byte, uint64, bool, error, error) {
+func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) (*ExecutionResult, error, error) {
return NewStateTransition(evm, msg, gp).TransitionDb(owner)
}
@@ -171,18 +227,21 @@ func (st *StateTransition) to() vm.AccountRef {
}
func (st *StateTransition) buyGas() error {
- var (
- state = st.state
- balanceTokenFee = st.balanceTokenFee()
- from = st.from()
- )
- mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
+ mgval := new(big.Int).SetUint64(st.msg.Gas())
+ mgval = mgval.Mul(mgval, st.gasPrice)
+ balanceTokenFee := st.balanceTokenFee()
if balanceTokenFee == nil {
- if state.GetBalance(from.Address()).Cmp(mgval) < 0 {
- return errInsufficientBalanceForGas
+ balanceCheck := mgval
+ if st.gasFeeCap != nil {
+ balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
+ balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
+ balanceCheck.Add(balanceCheck, st.value)
+ }
+ if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
+ return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
}
} else if balanceTokenFee.Cmp(mgval) < 0 {
- return errInsufficientBalanceForGas
+ return ErrInsufficientFunds
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
return err
@@ -191,103 +250,168 @@ func (st *StateTransition) buyGas() error {
st.initialGas = st.msg.Gas()
if balanceTokenFee == nil {
- state.SubBalance(from.Address(), mgval)
+ st.state.SubBalance(st.msg.From(), mgval)
}
return nil
}
func (st *StateTransition) preCheck() error {
- // Make sure this transaction's nonce is correct
- if st.msg.CheckNonce() {
+ // Only check transactions that are not fake
+ msg := st.msg
+ if !msg.IsFake() {
// Make sure this transaction's nonce is correct.
- stNonce := st.state.GetNonce(st.from().Address())
- if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
+ stNonce := st.state.GetNonce(msg.From())
+ if msgNonce := msg.Nonce(); stNonce < msgNonce {
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
- st.msg.From().Hex(), msgNonce, stNonce)
+ msg.From().Hex(), msgNonce, stNonce)
} else if stNonce > msgNonce {
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
- st.msg.From().Hex(), msgNonce, stNonce)
+ msg.From().Hex(), msgNonce, stNonce)
} else if stNonce+1 < stNonce {
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
- st.msg.From().Hex(), stNonce)
+ msg.From().Hex(), stNonce)
+ }
+ // Make sure the sender is an EOA
+ if codeHash := st.state.GetCodeHash(msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
+ return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
+ msg.From().Hex(), codeHash)
+ }
+ }
+ // Make sure that transaction gasFeeCap is greater than the baseFee (post london)
+ if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
+ // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
+ if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 {
+ if l := st.gasFeeCap.BitLen(); l > 256 {
+ return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
+ msg.From().Hex(), l)
+ }
+ if l := st.gasTipCap.BitLen(); l > 256 {
+ return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
+ msg.From().Hex(), l)
+ }
+ if st.gasFeeCap.Cmp(st.gasTipCap) < 0 {
+ return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
+ msg.From().Hex(), st.gasTipCap, st.gasFeeCap)
+ }
+ // This will panic if baseFee is nil, but basefee presence is verified
+ // as part of header validation.
+ if (msg.To() == nil || *msg.To() != common.RandomizeSMCBinary) && st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
+ return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow,
+ msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee)
+ }
}
}
return st.buyGas()
}
// TransitionDb will transition the state by applying the current message and
-// returning the result including the the used gas. It returns an error if it
-// failed. An error indicates a consensus issue.
-func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) {
- if err = st.preCheck(); err != nil {
- return
- }
- msg := st.msg
- sender := st.from() // err checked in preCheck
+// returning the evm execution result with following fields.
+//
+// - used gas:
+// total gas used (including gas being refunded)
+// - returndata:
+// the returned data from evm
+// - concrete execution error:
+// various **EVM** error which aborts the execution,
+// e.g. ErrOutOfGas, ErrExecutionReverted
+//
+// However if any consensus issue encountered, return the error directly with
+// nil evm execution result.
+func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error, error) {
+ // First check this message satisfies all consensus rules before
+ // applying the message. The rules include these clauses
+ //
+ // 1. the nonce of the message caller is correct
+ // 2. caller has enough balance to cover transaction fee(gaslimit * gasprice)
+ // 3. the amount of gas required is available in the block
+ // 4. the purchased gas is enough to cover intrinsic usage
+ // 5. there is no overflow when calculating intrinsic gas
+ // 6. caller has enough balance to cover asset transfer for **topmost** call
- homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
- contractCreation := msg.To() == nil
-
- // Pay intrinsic gas
- gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead)
- if err != nil {
- return nil, 0, false, err, nil
- }
- if st.gas < gas {
- return nil, 0, false, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil
- }
- st.gas -= gas
-
- if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsEIP1559 {
- st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
+ // Check clauses 1-3, buy gas if everything is correct
+ if err := st.preCheck(); err != nil {
+ return nil, err, nil
}
var (
- evm = st.evm
- // vm errors do not effect consensus and are therefor
- // not assigned to err, except for insufficient balance
- // error.
- vmerr error
+ msg = st.msg
+ sender = st.from() // err checked in preCheck
+ rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber)
+ homestead = rules.IsHomestead
+ eip3529 = rules.IsEIP1559
+ contractCreation = msg.To() == nil
+ )
+
+ // Check clauses 4-5, subtract intrinsic gas if everything is correct
+ gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559)
+ if err != nil {
+ return nil, err, nil
+ }
+ if st.gas < gas {
+ return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil
+ }
+ st.gas -= gas
+
+ // Check whether the init code size has been exceeded.
+ if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize {
+ return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil
+ }
+
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
+
+ // Check clause 6
+ value, overflow := uint256.FromBig(msg.Value())
+ if overflow {
+ return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil
+ }
+ if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From(), value.ToBig()) {
+ return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil
+ }
+
+ var (
+ ret []byte
+ vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
- // for debugging purpose
- // TODO: clean it after fixing the issue https://github.com/XinFinOrg/XDPoSChain/issues/401
- var contractAction string
- nonce := uint64(1)
if contractCreation {
- ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
- contractAction = "contract creation"
+ ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
- nonce = st.state.GetNonce(sender.Address()) + 1
- st.state.SetNonce(sender.Address(), nonce)
- ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
- contractAction = "contract call"
+ st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)
+ ret, st.gas, vmerr = st.evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
}
- if vmerr != nil {
- log.Debug("VM returned with error", "action", contractAction, "contract address", st.to().Address(), "gas", st.gas, "gasPrice", st.gasPrice, "nonce", nonce, "err", vmerr)
- // The only possible consensus-error would be if there wasn't
- // sufficient balance to make the transfer happen. The first
- // balance transfer may never fail.
- if vmerr == vm.ErrInsufficientBalance {
- return nil, 0, false, vmerr, nil
- }
+ if !eip3529 {
+ // Before EIP-3529: refunds were capped to gasUsed / 2
+ st.refundGas(params.RefundQuotient)
+ } else {
+ // After EIP-3529: refunds are capped to gasUsed / 5
+ st.refundGas(params.RefundQuotientEIP3529)
}
- st.refundGas()
- if st.evm.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
+ if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
if (owner != common.Address{}) {
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
}
} else {
- st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
+ effectiveTip := st.gasPrice
+ if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
+ effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
+ }
+ st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
}
- return ret, st.gasUsed(), vmerr != nil, nil, vmerr
+ return &ExecutionResult{
+ UsedGas: st.gasUsed(),
+ Err: vmerr,
+ ReturnData: ret,
+ }, nil, vmerr
}
-func (st *StateTransition) refundGas() {
- // Apply refund counter, capped to half of the used gas.
- refund := st.gasUsed() / 2
+func (st *StateTransition) refundGas(refundQuotient uint64) {
+ // Apply refund counter, capped to a refund quotient
+ refund := st.gasUsed() / refundQuotient
if refund > st.state.GetRefund() {
refund = st.state.GetRefund()
}
diff --git a/core/token_validator.go b/core/token_validator.go
index f604be504f..e888f37b10 100644
--- a/core/token_validator.go
+++ b/core/token_validator.go
@@ -45,9 +45,11 @@ type callMsg struct {
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
-func (m callMsg) CheckNonce() bool { return false }
+func (m callMsg) IsFake() bool { return true }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
+func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
@@ -108,17 +110,18 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext,
msg.CallMsg.BalanceTokenFee = value
}
}
- evmContext := NewEVMContext(msg, chain.CurrentHeader(), chain, nil)
+ txContext := NewEVMTxContext(msg)
+ evmContext := NewEVMBlockContext(chain.CurrentHeader(), chain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
- vmenv := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{})
+ vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{})
gaspool := new(GasPool).AddGas(1000000)
owner := common.Address{}
- rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
+ result, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
if err != nil {
return nil, err
}
- return rval, err
+ return result.Return(), err
}
// make sure that balance of token is at slot 0
diff --git a/core/tx_journal.go b/core/txpool/journal.go
similarity index 91%
rename from core/tx_journal.go
rename to core/txpool/journal.go
index 4fe5fdca36..871807729c 100644
--- a/core/tx_journal.go
+++ b/core/txpool/journal.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
@@ -40,23 +40,23 @@ type devNull struct{}
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
func (*devNull) Close() error { return nil }
-// txJournal is a rotating log of transactions with the aim of storing locally
+// journal is a rotating log of transactions with the aim of storing locally
// created transactions to allow non-executed ones to survive node restarts.
-type txJournal struct {
+type journal struct {
path string // Filesystem path to store the transactions at
writer io.WriteCloser // Output stream to write new transactions into
}
// newTxJournal creates a new transaction journal to
-func newTxJournal(path string) *txJournal {
- return &txJournal{
+func newTxJournal(path string) *journal {
+ return &journal{
path: path,
}
}
// load parses a transaction journal dump from disk, loading its contents into
// the specified pool.
-func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
+func (journal *journal) load(add func([]*types.Transaction) []error) error {
// Skip the parsing if the journal file doens't exist at all
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
return nil
@@ -116,7 +116,7 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
}
// insert adds the specified transaction to the local disk journal.
-func (journal *txJournal) insert(tx *types.Transaction) error {
+func (journal *journal) insert(tx *types.Transaction) error {
if journal.writer == nil {
return errNoActiveJournal
}
@@ -128,7 +128,7 @@ func (journal *txJournal) insert(tx *types.Transaction) error {
// rotate regenerates the transaction journal based on the current contents of
// the transaction pool.
-func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
+func (journal *journal) rotate(all map[common.Address]types.Transactions) error {
// Close the current journal (if any is open)
if journal.writer != nil {
if err := journal.writer.Close(); err != nil {
@@ -168,7 +168,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
}
// close flushes the transaction journal contents to disk and closes the file.
-func (journal *txJournal) close() error {
+func (journal *journal) close() error {
var err error
if journal.writer != nil {
diff --git a/core/lending_pool.go b/core/txpool/lending_pool.go
similarity index 98%
rename from core/lending_pool.go
rename to core/txpool/lending_pool.go
index 89aac0665f..4a199384cf 100644
--- a/core/lending_pool.go
+++ b/core/txpool/lending_pool.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
@@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/event"
@@ -75,7 +76,7 @@ type blockChainLending interface {
GetBlock(hash common.Hash, number uint64) *types.Block
LendingStateAt(block *types.Block) (*lendingstate.LendingStateDB, error)
StateAt(root common.Hash) (*state.StateDB, error)
- SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
+ SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
Engine() consensus.Engine
// GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *types.Header
@@ -124,7 +125,7 @@ type LendingPool struct {
txFeed event.Feed
scope event.SubscriptionScope
- chainHeadCh chan ChainHeadEvent
+ chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription
signer types.LendingSigner
mu sync.RWMutex
@@ -161,7 +162,7 @@ func NewLendingPool(chainconfig *params.ChainConfig, chain blockChainLending) *L
queue: make(map[common.Address]*lendingtxList),
beats: make(map[common.Address]time.Time),
all: make(map[common.Hash]*types.LendingTransaction),
- chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
}
pool.locals = newLendingAccountSet(pool.signer)
pool.reset(nil, chain.CurrentBlock())
@@ -334,7 +335,7 @@ func (pool *LendingPool) Stop() {
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
// starts sending event to the given channel.
-func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- LendingTxPreEvent) event.Subscription {
+func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- core.LendingTxPreEvent) event.Subscription {
return pool.scope.Track(pool.txFeed.Subscribe(ch))
}
@@ -514,7 +515,7 @@ func (pool *LendingPool) validateTopupLending(cloneStateDb *state.StateDB, clone
func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendingStateDb *lendingstate.LendingStateDB, tx *types.LendingTransaction, collateralToken common.Address) error {
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
if !ok {
- return ErrNotXDPoS
+ return core.ErrNotXDPoS
}
XDCXServ := XDPoSEngine.GetXDCXService()
lendingServ := XDPoSEngine.GetLendingService()
@@ -598,7 +599,7 @@ func (pool *LendingPool) validateLending(tx *types.LendingTransaction) error {
if !lendingstate.IsValidRelayer(cloneStateDb, tx.RelayerAddress()) {
return fmt.Errorf("invalid lending relayer. ExchangeAddress: %s", tx.RelayerAddress().Hex())
}
- if valid, _ := lendingstate.IsValidPair(cloneStateDb, tx.RelayerAddress(), tx.LendingToken(), tx.Term()); valid == false {
+ if valid, _ := lendingstate.IsValidPair(cloneStateDb, tx.RelayerAddress(), tx.LendingToken(), tx.Term()); !valid {
return fmt.Errorf("invalid pair. Relayer: %s. LendingToken: %s. Term: %d", tx.RelayerAddress().Hex(), tx.LendingToken().Hex(), tx.Term())
}
if tx.IsCreatedLending() {
@@ -623,7 +624,7 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex())
+ return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
}
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
if tx.Size() > 32*1024 {
@@ -641,10 +642,10 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er
}
// Ensure the transaction adheres to nonce lending
if pool.currentLendingState.GetNonce(from.Hash()) > tx.Nonce() {
- return ErrNonceTooLow
+ return core.ErrNonceTooLow
}
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
- return ErrNonceTooHigh
+ return core.ErrNonceTooHigh
}
return nil
@@ -778,7 +779,7 @@ func (pool *LendingPool) promoteTx(addr common.Address, hash common.Hash, tx *ty
pool.beats[addr] = time.Now()
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
- go pool.txFeed.Send(LendingTxPreEvent{tx})
+ go pool.txFeed.Send(core.LendingTxPreEvent{Tx: tx})
}
// AddLocal enqueues a single transaction into the pool if it is valid, marking
diff --git a/core/lending_pool_test.go b/core/txpool/lending_pool_test.go
similarity index 98%
rename from core/lending_pool_test.go
rename to core/txpool/lending_pool_test.go
index 5ffe82d58c..ceb6970b7a 100644
--- a/core/lending_pool_test.go
+++ b/core/txpool/lending_pool_test.go
@@ -1,4 +1,4 @@
-package core
+package txpool
import (
"context"
@@ -14,9 +14,9 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/ethclient"
"github.com/XinFinOrg/XDPoSChain/rpc"
+ "golang.org/x/crypto/sha3"
)
type LendingMsg struct {
@@ -46,10 +46,10 @@ type LendingMsg struct {
func getLendingNonce(userAddress common.Address) (uint64, error) {
rpcClient, err := rpc.DialHTTP("http://127.0.0.1:8501")
- defer rpcClient.Close()
if err != nil {
return 0, err
}
+ defer rpcClient.Close()
var result interface{}
err = rpcClient.Call(&result, "XDCx_getLendingOrderCount", userAddress)
if err != nil {
@@ -58,12 +58,12 @@ func getLendingNonce(userAddress common.Address) (uint64, error) {
s := result.(string)
s = strings.TrimPrefix(s, "0x")
n, err := strconv.ParseUint(s, 16, 32)
- return uint64(n), nil
+ return uint64(n), err
}
func (l *LendingMsg) computeHash() common.Hash {
borrowing := l.Side == lendingstate.Borrowing
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
if l.Type == lendingstate.Repay {
sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes())
sha.Write([]byte(l.Status))
@@ -83,7 +83,7 @@ func (l *LendingMsg) computeHash() common.Hash {
sha.Write(common.BigToHash(l.Quantity).Bytes())
} else {
if l.Status == lendingstate.LendingStatusCancelled {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(l.Hash.Bytes())
sha.Write(common.BigToHash(big.NewInt(int64(l.AccountNonce))).Bytes())
sha.Write(l.UserAddress.Bytes())
@@ -181,7 +181,7 @@ func TestSendLending(t *testing.T) {
t.FailNow()
}
- for true {
+ for {
// 10%
interestRate := 10 * common.BaseLendingInterest.Uint64()
// lendToken: USD, collateral: BTC
diff --git a/core/lending_tx_journal.go b/core/txpool/lending_tx_journal.go
similarity index 99%
rename from core/lending_tx_journal.go
rename to core/txpool/lending_tx_journal.go
index 4bf835e9ce..fb9b487ac5 100644
--- a/core/lending_tx_journal.go
+++ b/core/txpool/lending_tx_journal.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"io"
diff --git a/core/lending_tx_list.go b/core/txpool/lending_tx_list.go
similarity index 99%
rename from core/lending_tx_list.go
rename to core/txpool/lending_tx_list.go
index 5d25ac47ef..d7c30cea59 100644
--- a/core/lending_tx_list.go
+++ b/core/txpool/lending_tx_list.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"container/heap"
diff --git a/core/tx_list.go b/core/txpool/list.go
similarity index 63%
rename from core/tx_list.go
rename to core/txpool/list.go
index 3e746ff351..70d2322f96 100644
--- a/core/tx_list.go
+++ b/core/txpool/list.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"container/heap"
@@ -23,6 +23,7 @@ import (
"sort"
"sync"
"sync/atomic"
+ "time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -48,30 +49,30 @@ func (h *nonceHeap) Pop() interface{} {
return x
}
-// txSortedMap is a nonce->transaction hash map with a heap based index to allow
+// sortedMap is a nonce->transaction hash map with a heap based index to allow
// iterating over the contents in a nonce-incrementing way.
-type txSortedMap struct {
+type sortedMap struct {
items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted
}
-// newTxSortedMap creates a new nonce-sorted transaction map.
-func newTxSortedMap() *txSortedMap {
- return &txSortedMap{
+// newSortedMap creates a new nonce-sorted transaction map.
+func newSortedMap() *sortedMap {
+ return &sortedMap{
items: make(map[uint64]*types.Transaction),
index: new(nonceHeap),
}
}
// Get retrieves the current transactions associated with the given nonce.
-func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
+func (m *sortedMap) Get(nonce uint64) *types.Transaction {
return m.items[nonce]
}
// Put inserts a new transaction into the map, also updating the map's nonce
// index. If a transaction already exists with the same nonce, it's overwritten.
-func (m *txSortedMap) Put(tx *types.Transaction) {
+func (m *sortedMap) Put(tx *types.Transaction) {
nonce := tx.Nonce()
if m.items[nonce] == nil {
heap.Push(m.index, nonce)
@@ -82,7 +83,7 @@ func (m *txSortedMap) Put(tx *types.Transaction) {
// Forward removes all transactions from the map with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
-func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
+func (m *sortedMap) Forward(threshold uint64) types.Transactions {
var removed types.Transactions
// Pop off heap items until the threshold is reached
@@ -103,7 +104,7 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
// If you want to do several consecutive filterings, it's therefore better to first
// do a .filter(func1) followed by .Filter(func2) or reheap()
-func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
+func (m *sortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
removed := m.filter(filter)
// If transactions were removed, the heap and cache are ruined
if len(removed) > 0 {
@@ -112,7 +113,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
return removed
}
-func (m *txSortedMap) reheap() {
+func (m *sortedMap) reheap() {
*m.index = make([]uint64, 0, len(m.items))
for nonce := range m.items {
*m.index = append(*m.index, nonce)
@@ -123,7 +124,7 @@ func (m *txSortedMap) reheap() {
// filter is identical to Filter, but **does not** regenerate the heap. This method
// should only be used if followed immediately by a call to Filter or reheap()
-func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
+func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
var removed types.Transactions
// Collect all the transactions to filter out
@@ -141,7 +142,7 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
-func (m *txSortedMap) Cap(threshold int) types.Transactions {
+func (m *sortedMap) Cap(threshold int) types.Transactions {
// Short circuit if the number of items is under the limit
if len(m.items) <= threshold {
return nil
@@ -166,7 +167,7 @@ func (m *txSortedMap) Cap(threshold int) types.Transactions {
// Remove deletes a transaction from the maintained map, returning whether the
// transaction was found.
-func (m *txSortedMap) Remove(nonce uint64) bool {
+func (m *sortedMap) Remove(nonce uint64) bool {
// Short circuit if no transaction is present
_, ok := m.items[nonce]
if !ok {
@@ -192,7 +193,7 @@ func (m *txSortedMap) Remove(nonce uint64) bool {
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
-func (m *txSortedMap) Ready(start uint64) types.Transactions {
+func (m *sortedMap) Ready(start uint64) types.Transactions {
// Short circuit if no transactions are available
if m.index.Len() == 0 || (*m.index)[0] > start {
return nil
@@ -210,11 +211,11 @@ func (m *txSortedMap) Ready(start uint64) types.Transactions {
}
// Len returns the length of the transaction map.
-func (m *txSortedMap) Len() int {
+func (m *sortedMap) Len() int {
return len(m.items)
}
-func (m *txSortedMap) flatten() types.Transactions {
+func (m *sortedMap) flatten() types.Transactions {
// If the sorting was not cached yet, create and cache it
if m.cache == nil {
m.cache = make(types.Transactions, 0, len(m.items))
@@ -229,7 +230,7 @@ func (m *txSortedMap) flatten() types.Transactions {
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
-func (m *txSortedMap) Flatten() types.Transactions {
+func (m *sortedMap) Flatten() types.Transactions {
// Copy the cache to prevent accidental modifications
cache := m.flatten()
txs := make(types.Transactions, len(cache))
@@ -239,36 +240,36 @@ func (m *txSortedMap) Flatten() types.Transactions {
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
-func (m *txSortedMap) LastElement() *types.Transaction {
+func (m *sortedMap) LastElement() *types.Transaction {
cache := m.flatten()
return cache[len(cache)-1]
}
-// txList is a "list" of transactions belonging to an account, sorted by account
+// list is a "list" of transactions belonging to an account, sorted by account
// nonce. The same type can be used both for storing contiguous transactions for
// the executable/pending queue; and for storing gapped transactions for the non-
// executable/future queue, with minor behavioral changes.
-type txList struct {
- strict bool // Whether nonces are strictly continuous or not
- txs *txSortedMap // Heap indexed sorted hash map of the transactions
+type list struct {
+ strict bool // Whether nonces are strictly continuous or not
+ txs *sortedMap // Heap indexed sorted hash map of the transactions
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
}
-// newTxList create a new transaction list for maintaining nonce-indexable fast,
+// newList create a new transaction list for maintaining nonce-indexable fast,
// gapped, sortable transaction lists.
-func newTxList(strict bool) *txList {
- return &txList{
+func newList(strict bool) *list {
+ return &list{
strict: strict,
- txs: newTxSortedMap(),
+ txs: newSortedMap(),
costcap: new(big.Int),
}
}
// Overlaps returns whether the transaction specified has the same nonce as one
// already contained within the list.
-func (l *txList) Overlaps(tx *types.Transaction) bool {
+func (l *list) Overlaps(tx *types.Transaction) bool {
return l.txs.Get(tx.Nonce()) != nil
}
@@ -277,22 +278,30 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
//
// If the new transaction is accepted into the list, the lists' cost and gas
// thresholds are also potentially updated.
-func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
+func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
// If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce())
if old != nil && old.IsSpecialTransaction() {
return false, nil
}
if old != nil {
- // threshold = oldGP * (100 + priceBump) / 100
+ if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 {
+ return false, nil
+ }
+ // thresholdFeeCap = oldFC * (100 + priceBump) / 100
a := big.NewInt(100 + int64(priceBump))
- a = a.Mul(a, old.GasPrice())
+ aFeeCap := new(big.Int).Mul(a, old.GasFeeCap())
+ aTip := a.Mul(a, old.GasTipCap())
+
+ // thresholdTip = oldTip * (100 + priceBump) / 100
b := big.NewInt(100)
- threshold := a.Div(a, b)
- // Have to ensure that the new gas price is higher than the old gas
- // price as well as checking the percentage threshold to ensure that
+ thresholdFeeCap := aFeeCap.Div(aFeeCap, b)
+ thresholdTip := aTip.Div(aTip, b)
+
+ // Have to ensure that either the new fee cap or tip is higher than the
+ // old ones as well as checking the percentage threshold to ensure that
// this is accurate for low (Wei-level) gas price replacements
- if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
+ if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 {
return false, nil
}
}
@@ -310,7 +319,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// Forward removes all transactions from the list with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
-func (l *txList) Forward(threshold uint64) types.Transactions {
+func (l *list) Forward(threshold uint64) types.Transactions {
return l.txs.Forward(threshold)
}
@@ -323,7 +332,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
// a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing
// the newly invalidated transactions.
-func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) {
+func (l *list) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
return nil, nil
@@ -362,14 +371,14 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[co
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
-func (l *txList) Cap(threshold int) types.Transactions {
+func (l *list) Cap(threshold int) types.Transactions {
return l.txs.Cap(threshold)
}
// Remove deletes a transaction from the maintained list, returning whether the
// transaction was found, and also returning any transaction invalidated due to
// the deletion (strict mode only).
-func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
+func (l *list) Remove(tx *types.Transaction) (bool, types.Transactions) {
// Remove the transaction from the set
nonce := tx.Nonce()
if removed := l.txs.Remove(nonce); !removed {
@@ -389,172 +398,206 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
-func (l *txList) Ready(start uint64) types.Transactions {
+func (l *list) Ready(start uint64) types.Transactions {
return l.txs.Ready(start)
}
// Len returns the length of the transaction list.
-func (l *txList) Len() int {
+func (l *list) Len() int {
return l.txs.Len()
}
// Empty returns whether the list of transactions is empty or not.
-func (l *txList) Empty() bool {
+func (l *list) Empty() bool {
return l.Len() == 0
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
-func (l *txList) Flatten() types.Transactions {
+func (l *list) Flatten() types.Transactions {
return l.txs.Flatten()
}
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
-func (l *txList) LastElement() *types.Transaction {
+func (l *list) LastElement() *types.Transaction {
return l.txs.LastElement()
}
// priceHeap is a heap.Interface implementation over transactions for retrieving
-// price-sorted transactions to discard when the pool fills up.
-type priceHeap []*types.Transaction
+// price-sorted transactions to discard when the pool fills up. If baseFee is set
+// then the heap is sorted based on the effective tip based on the given base fee.
+// If baseFee is nil then the sorting is based on gasFeeCap.
+type priceHeap struct {
+ baseFee *big.Int // heap should always be re-sorted after baseFee is changed
+ list []*types.Transaction
+}
-func (h priceHeap) Len() int { return len(h) }
-func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+func (h *priceHeap) Len() int { return len(h.list) }
+func (h *priceHeap) Swap(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }
-func (h priceHeap) Less(i, j int) bool {
- // Sort primarily by price, returning the cheaper one
- switch h[i].GasPriceCmp(h[j]) {
+func (h *priceHeap) Less(i, j int) bool {
+ switch h.cmp(h.list[i], h.list[j]) {
case -1:
return true
case 1:
return false
+ default:
+ return h.list[i].Nonce() > h.list[j].Nonce()
}
- // If the prices match, stabilize via nonces (high nonce is worse)
- return h[i].Nonce() > h[j].Nonce()
+}
+
+func (h *priceHeap) cmp(a, b *types.Transaction) int {
+ if h.baseFee != nil {
+ // Compare effective tips if baseFee is specified
+ if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 {
+ return c
+ }
+ }
+ // Compare fee caps if baseFee is not specified or effective tips are equal
+ if c := a.GasFeeCapCmp(b); c != 0 {
+ return c
+ }
+ // Compare tips if effective tips and fee caps are equal
+ return a.GasTipCapCmp(b)
}
func (h *priceHeap) Push(x interface{}) {
- *h = append(*h, x.(*types.Transaction))
+ tx := x.(*types.Transaction)
+ h.list = append(h.list, tx)
}
func (h *priceHeap) Pop() interface{} {
- old := *h
+ old := h.list
n := len(old)
x := old[n-1]
- *h = old[0 : n-1]
+ old[n-1] = nil
+ h.list = old[0 : n-1]
return x
}
-// txPricedList is a price-sorted heap to allow operating on transactions pool
+// pricedList is a price-sorted heap to allow operating on transactions pool
// contents in a price-incrementing way. It's built opon the all transactions
// in txpool but only interested in the remote part. It means only remote transactions
// will be considered for tracking, sorting, eviction, etc.
-type txPricedList struct {
- all *txLookup // Pointer to the map of all transactions
- remotes *priceHeap // Heap of prices of all the stored **remote** transactions
- stales int64 // Number of stale price points to (re-heap trigger)
- reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
+//
+// Two heaps are used for sorting: the urgent heap (based on effective tip in the next
+// block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for
+// eviction. Transactions evicted from the urgent heap are first demoted into the floating heap.
+// In some cases (during a congestion, when blocks are full) the urgent heap can provide
+// better candidates for inclusion while in other cases (at the top of the baseFee peak)
+// the floating heap is better. When baseFee is decreasing they behave similarly.
+type pricedList struct {
+ all *lookup // Pointer to the map of all transactions
+ urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions
+ stales int64 // Number of stale price points to (re-heap trigger)
+ reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
}
-// newTxPricedList creates a new price-sorted transaction heap.
-func newTxPricedList(all *txLookup) *txPricedList {
- return &txPricedList{
- all: all,
- remotes: new(priceHeap),
+const (
+ // urgentRatio : floatingRatio is the capacity ratio of the two queues
+ urgentRatio = 4
+ floatingRatio = 1
+)
+
+// newPricedList creates a new price-sorted transaction heap.
+func newPricedList(all *lookup) *pricedList {
+ return &pricedList{
+ all: all,
}
}
// Put inserts a new transaction into the heap.
-func (l *txPricedList) Put(tx *types.Transaction, local bool) {
+func (l *pricedList) Put(tx *types.Transaction, local bool) {
if local {
return
}
- heap.Push(l.remotes, tx)
+ // Insert every new transaction to the urgent heap first; Discard will balance the heaps
+ heap.Push(&l.urgent, tx)
}
// Removed notifies the prices transaction list that an old transaction dropped
// from the pool. The list will just keep a counter of stale objects and update
// the heap if a large enough ratio of transactions go stale.
-func (l *txPricedList) Removed(count int) {
+func (l *pricedList) Removed(count int) {
// Bump the stale counter, but exit if still too low (< 25%)
stales := atomic.AddInt64(&l.stales, int64(count))
- if int(stales) <= len(*l.remotes)/4 {
+ if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 {
return
}
// Seems we've reached a critical number of stale transactions, reheap
l.Reheap()
}
-// Cap finds all the transactions below the given price threshold, drops them
-// from the priced list and returns them for further removal from the entire pool.
-//
-// Note: only remote transactions will be considered for eviction.
-func (l *txPricedList) Cap(threshold *big.Int) types.Transactions {
- drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
- for len(*l.remotes) > 0 {
- // Discard stale transactions if found during cleanup
- cheapest := (*l.remotes)[0]
- if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated
- heap.Pop(l.remotes)
- l.stales--
- continue
- }
- // Stop the discards if we've reached the threshold
- if cheapest.GasPriceIntCmp(threshold) >= 0 {
- break
- }
- heap.Pop(l.remotes)
- drop = append(drop, cheapest)
- }
- return drop
-}
-
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced (remote) transaction currently being tracked.
-func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
+func (l *pricedList) Underpriced(tx *types.Transaction) bool {
+ // Note: with two queues, being underpriced is defined as being worse than the worst item
+ // in all non-empty queues if there is any. If both queues are empty then nothing is underpriced.
+ return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) &&
+ (l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) &&
+ (len(l.urgent.list) != 0 || len(l.floating.list) != 0)
+}
+
+// underpricedFor checks whether a transaction is cheaper than (or as cheap as) the
+// lowest priced (remote) transaction in the given heap.
+func (l *pricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool {
// Discard stale price points if found at the heap start
- for len(*l.remotes) > 0 {
- head := []*types.Transaction(*l.remotes)[0]
+ for len(h.list) > 0 {
+ head := h.list[0]
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
atomic.AddInt64(&l.stales, -1)
- heap.Pop(l.remotes)
+ heap.Pop(h)
continue
}
break
}
// Check if the transaction is underpriced or not
- if len(*l.remotes) == 0 {
+ if len(h.list) == 0 {
return false // There is no remote transaction at all.
}
// If the remote transaction is even cheaper than the
// cheapest one tracked locally, reject it.
- cheapest := []*types.Transaction(*l.remotes)[0]
- return cheapest.GasPriceCmp(tx) >= 0
+ return h.cmp(h.list[0], tx) >= 0
}
// Discard finds a number of most underpriced transactions, removes them from the
// priced list and returns them for further removal from the entire pool.
//
// Note local transaction won't be considered for eviction.
-func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) {
+func (l *pricedList) Discard(slots int, force bool) (types.Transactions, bool) {
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
- for len(*l.remotes) > 0 && slots > 0 {
- // Discard stale transactions if found during cleanup
- tx := heap.Pop(l.remotes).(*types.Transaction)
- if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
- atomic.AddInt64(&l.stales, -1)
- continue
+ for slots > 0 {
+ if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 {
+ // Discard stale transactions if found during cleanup
+ tx := heap.Pop(&l.urgent).(*types.Transaction)
+ if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
+ atomic.AddInt64(&l.stales, -1)
+ continue
+ }
+ // Non stale transaction found, move to floating heap
+ heap.Push(&l.floating, tx)
+ } else {
+ if len(l.floating.list) == 0 {
+ // Stop if both heaps are empty
+ break
+ }
+ // Discard stale transactions if found during cleanup
+ tx := heap.Pop(&l.floating).(*types.Transaction)
+ if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
+ atomic.AddInt64(&l.stales, -1)
+ continue
+ }
+ // Non stale transaction found, discard it
+ drop = append(drop, tx)
+ slots -= numSlots(tx)
}
- // Non stale transaction found, discard it
- drop = append(drop, tx)
- slots -= numSlots(tx)
}
// If we still can't make enough room for the new transaction
if slots > 0 && !force {
for _, tx := range drop {
- heap.Push(l.remotes, tx)
+ heap.Push(&l.urgent, tx)
}
return nil, false
}
@@ -562,16 +605,35 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool)
}
// Reheap forcibly rebuilds the heap based on the current remote transaction set.
-func (l *txPricedList) Reheap() {
+func (l *pricedList) Reheap() {
l.reheapMu.Lock()
defer l.reheapMu.Unlock()
- reheap := make(priceHeap, 0, l.all.RemoteCount())
-
+ start := time.Now()
atomic.StoreInt64(&l.stales, 0)
- l.remotes = &reheap
+ l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount())
l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
- *l.remotes = append(*l.remotes, tx)
+ l.urgent.list = append(l.urgent.list, tx)
return true
}, false, true) // Only iterate remotes
- heap.Init(l.remotes)
+ heap.Init(&l.urgent)
+
+ // balance out the two heaps by moving the worse half of transactions into the
+ // floating heap
+ // Note: Discard would also do this before the first eviction but Reheap can do
+ // is more efficiently. Also, Underpriced would work suboptimally the first time
+ // if the floating queue was empty.
+ floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio)
+ l.floating.list = make([]*types.Transaction, floatingCount)
+ for i := 0; i < floatingCount; i++ {
+ l.floating.list[i] = heap.Pop(&l.urgent).(*types.Transaction)
+ }
+ heap.Init(&l.floating)
+ reheapTimer.Update(time.Since(start))
+}
+
+// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
+// necessary to call right before SetBaseFee when processing a new block.
+func (l *pricedList) SetBaseFee(baseFee *big.Int) {
+ l.urgent.baseFee = baseFee
+ l.Reheap()
}
diff --git a/core/tx_list_test.go b/core/txpool/list_test.go
similarity index 84%
rename from core/tx_list_test.go
rename to core/txpool/list_test.go
index 36a0196f1e..10fe2c9405 100644
--- a/core/tx_list_test.go
+++ b/core/txpool/list_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"math/big"
@@ -27,7 +27,7 @@ import (
// Tests that transactions can be added to strict lists and list contents and
// nonce boundaries are correctly maintained.
-func TestStrictTxListAdd(t *testing.T) {
+func TestStrictListAdd(t *testing.T) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -36,9 +36,9 @@ func TestStrictTxListAdd(t *testing.T) {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
- list := newTxList(true)
+ list := newList(true)
for _, v := range rand.Perm(len(txs)) {
- list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
+ list.Add(txs[v], DefaultConfig.PriceBump)
}
// Verify internal state
if len(list.txs.items) != len(txs) {
@@ -51,7 +51,7 @@ func TestStrictTxListAdd(t *testing.T) {
}
}
-func BenchmarkTxListAdd(t *testing.B) {
+func BenchmarkListAdd(t *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -60,11 +60,11 @@ func BenchmarkTxListAdd(t *testing.B) {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
- list := newTxList(true)
- priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
+ list := newList(true)
+ priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit))
t.ResetTimer()
for _, v := range rand.Perm(len(txs)) {
- list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
- list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
+ list.Add(txs[v], DefaultConfig.PriceBump)
+ list.Filter(priceLimit, DefaultConfig.PriceBump, nil, nil)
}
}
diff --git a/core/tx_noncer.go b/core/txpool/noncer.go
similarity index 71%
rename from core/tx_noncer.go
rename to core/txpool/noncer.go
index cbadc39354..c9854a4238 100644
--- a/core/tx_noncer.go
+++ b/core/txpool/noncer.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"sync"
@@ -23,18 +23,18 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/state"
)
-// txNoncer is a tiny virtual state database to manage the executable nonces of
+// noncer is a tiny virtual state database to manage the executable nonces of
// accounts in the pool, falling back to reading from a real state database if
// an account is unknown.
-type txNoncer struct {
+type noncer struct {
fallback *state.StateDB
nonces map[common.Address]uint64
lock sync.Mutex
}
-// newTxNoncer creates a new virtual state database to track the pool nonces.
-func newTxNoncer(statedb *state.StateDB) *txNoncer {
- return &txNoncer{
+// newNoncer creates a new virtual state database to track the pool nonces.
+func newNoncer(statedb *state.StateDB) *noncer {
+ return &noncer{
fallback: statedb.Copy(),
nonces: make(map[common.Address]uint64),
}
@@ -42,21 +42,23 @@ func newTxNoncer(statedb *state.StateDB) *txNoncer {
// get returns the current nonce of an account, falling back to a real state
// database if the account is unknown.
-func (txn *txNoncer) get(addr common.Address) uint64 {
+func (txn *noncer) get(addr common.Address) uint64 {
// We use mutex for get operation is the underlying
// state will mutate db even for read access.
txn.lock.Lock()
defer txn.lock.Unlock()
if _, ok := txn.nonces[addr]; !ok {
- txn.nonces[addr] = txn.fallback.GetNonce(addr)
+ if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
+ txn.nonces[addr] = nonce
+ }
}
return txn.nonces[addr]
}
// set inserts a new virtual nonce into the virtual state database to be returned
// whenever the pool requests it instead of reaching into the real state database.
-func (txn *txNoncer) set(addr common.Address, nonce uint64) {
+func (txn *noncer) set(addr common.Address, nonce uint64) {
txn.lock.Lock()
defer txn.lock.Unlock()
@@ -65,15 +67,25 @@ func (txn *txNoncer) set(addr common.Address, nonce uint64) {
// setIfLower updates a new virtual nonce into the virtual state database if the
// the new one is lower.
-func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) {
+func (txn *noncer) setIfLower(addr common.Address, nonce uint64) {
txn.lock.Lock()
defer txn.lock.Unlock()
if _, ok := txn.nonces[addr]; !ok {
- txn.nonces[addr] = txn.fallback.GetNonce(addr)
+ if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
+ txn.nonces[addr] = nonce
+ }
}
if txn.nonces[addr] <= nonce {
return
}
txn.nonces[addr] = nonce
}
+
+// setAll sets the nonces for all accounts to the given map.
+func (txn *noncer) setAll(all map[common.Address]uint64) {
+ txn.lock.Lock()
+ defer txn.lock.Unlock()
+
+ txn.nonces = all
+}
diff --git a/core/order_pool.go b/core/txpool/order_pool.go
similarity index 98%
rename from core/order_pool.go
rename to core/txpool/order_pool.go
index dfade0ecd4..87ef9e80dc 100644
--- a/core/order_pool.go
+++ b/core/txpool/order_pool.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
@@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/event"
@@ -61,7 +62,7 @@ var (
var (
ErrPendingNonceTooLow = errors.New("pending nonce too low")
- ErrPoolOverflow = errors.New("Exceed pool size")
+ ErrPoolOverflow = errors.New("exceed pool size")
)
// OrderPoolConfig are the configuration parameters of the order transaction pool.
@@ -84,7 +85,7 @@ type blockChainXDCx interface {
GetBlock(hash common.Hash, number uint64) *types.Block
OrderStateAt(block *types.Block) (*tradingstate.TradingStateDB, error)
StateAt(root common.Hash) (*state.StateDB, error)
- SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
+ SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
Engine() consensus.Engine
// GetHeader returns the hash corresponding to their hash.
GetHeader(common.Hash, uint64) *types.Header
@@ -133,7 +134,7 @@ type OrderPool struct {
txFeed event.Feed
scope event.SubscriptionScope
- chainHeadCh chan ChainHeadEvent
+ chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription
signer types.OrderSigner
mu sync.RWMutex
@@ -170,7 +171,7 @@ func NewOrderPool(chainconfig *params.ChainConfig, chain blockChainXDCx) *OrderP
queue: make(map[common.Address]*ordertxList),
beats: make(map[common.Address]time.Time),
all: make(map[common.Hash]*types.OrderTransaction),
- chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
}
pool.locals = newOrderAccountSet(pool.signer)
pool.reset(nil, chain.CurrentBlock())
@@ -340,7 +341,7 @@ func (pool *OrderPool) Stop() {
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
// starts sending event to the given channel.
-func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- OrderTxPreEvent) event.Subscription {
+func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- core.OrderTxPreEvent) event.Subscription {
return pool.scope.Track(pool.txFeed.Subscribe(ch))
}
@@ -464,7 +465,7 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error {
if orderType == OrderTypeLimit {
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
if !ok {
- return ErrNotXDPoS
+ return core.ErrNotXDPoS
}
XDCXServ := XDPoSEngine.GetXDCXService()
if XDCXServ == nil {
@@ -532,7 +533,7 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex())
+ return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
}
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
if tx.Size() > 32*1024 {
@@ -550,10 +551,10 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error
}
// Ensure the transaction adheres to nonce ordering
if pool.currentOrderState.GetNonce(from.Hash()) > tx.Nonce() {
- return ErrNonceTooLow
+ return core.ErrNonceTooLow
}
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
- return ErrNonceTooHigh
+ return core.ErrNonceTooHigh
}
return nil
@@ -603,7 +604,7 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error)
pool.journalTx(from, tx)
log.Debug("Pooled new executable transaction", "hash", hash, "useraddress", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "status", tx.Status(), "orderid", tx.OrderID())
- go pool.txFeed.Send(OrderTxPreEvent{tx})
+ go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx})
return old != nil, nil
}
@@ -690,7 +691,7 @@ func (pool *OrderPool) promoteTx(addr common.Address, hash common.Hash, tx *type
pool.beats[addr] = time.Now()
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
log.Debug("promoteTx txFeed.Send", "addr", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "ohash", tx.OrderHash().Hex(), "status", tx.Status(), "orderid", tx.OrderID())
- go pool.txFeed.Send(OrderTxPreEvent{tx})
+ go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx})
}
// AddLocal enqueues a single transaction into the pool if it is valid, marking
diff --git a/core/order_pool_test.go b/core/txpool/order_pool_test.go
similarity index 98%
rename from core/order_pool_test.go
rename to core/txpool/order_pool_test.go
index 1f03c692ee..c5e1618561 100644
--- a/core/order_pool_test.go
+++ b/core/txpool/order_pool_test.go
@@ -1,4 +1,4 @@
-package core
+package txpool
import (
"context"
@@ -49,22 +49,17 @@ var (
EOSAddress = common.HexToAddress("0xd9bb01454c85247B2ef35BB5BE57384cC275a8cf")
USDAddress = common.HexToAddress("0x45c25041b8e6CBD5c963E7943007187C3673C7c9")
_1E18 = new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(100))
- _1E17 = new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(10))
_1E8 = big.NewInt(100000000)
_1E7 = big.NewInt(10000000)
)
func getNonce(t *testing.T, userAddress common.Address) (uint64, error) {
rpcClient, err := rpc.DialHTTP("http://127.0.0.1:8501")
+ if err != nil {
+ return 0, err
+ }
defer rpcClient.Close()
- if err != nil {
- return 0, err
- }
var result interface{}
- if err != nil {
-
- return 0, err
- }
err = rpcClient.Call(&result, "XDCx_getOrderCount", userAddress)
if err != nil {
return 0, err
@@ -72,7 +67,7 @@ func getNonce(t *testing.T, userAddress common.Address) (uint64, error) {
s := result.(string)
s = strings.TrimPrefix(s, "0x")
n, err := strconv.ParseUint(s, 16, 32)
- return uint64(n), nil
+ return uint64(n), err
}
func testSendOrder(t *testing.T, amount, price *big.Int, side string, status string, orderID uint64) {
diff --git a/core/order_tx_journal.go b/core/txpool/order_tx_journal.go
similarity index 99%
rename from core/order_tx_journal.go
rename to core/txpool/order_tx_journal.go
index 471c2f34f9..cbcb49c7b7 100644
--- a/core/order_tx_journal.go
+++ b/core/txpool/order_tx_journal.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"io"
diff --git a/core/order_tx_list.go b/core/txpool/order_tx_list.go
similarity index 99%
rename from core/order_tx_list.go
rename to core/txpool/order_tx_list.go
index 5135bfe4fd..60df14e858 100644
--- a/core/order_tx_list.go
+++ b/core/txpool/order_tx_list.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"container/heap"
diff --git a/core/tx_pool.go b/core/txpool/txpool.go
similarity index 85%
rename from core/tx_pool.go
rename to core/txpool/txpool.go
index 7a3aa8cd1e..fc57d9355a 100644
--- a/core/tx_pool.go
+++ b/core/txpool/txpool.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"errors"
@@ -29,6 +29,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/event"
@@ -62,10 +64,6 @@ var (
// ErrInvalidSender is returned if the transaction contains an invalid signature.
ErrInvalidSender = errors.New("invalid sender")
- // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
- // one present in the local chain.
- ErrNonceTooLow = errors.New("nonce too low")
-
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
// configured for the transaction pool.
ErrUnderpriced = errors.New("transaction underpriced")
@@ -78,14 +76,6 @@ var (
// with a different one without the required price bump.
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
- // ErrInsufficientFunds is returned if the total cost of executing a transaction
- // is higher than the balance of the user's account.
- ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
-
- // ErrIntrinsicGas is returned if the transaction is specified to use less gas
- // than required to start the invocation.
- ErrIntrinsicGas = errors.New("intrinsic gas too low")
-
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
// maximum allowance of the current block.
ErrGasLimit = errors.New("exceeds block gas limit")
@@ -134,10 +124,21 @@ var (
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil)
+ // throttleTxMeter counts how many transactions are rejected due to too-many-changes between
+ // txpool reorgs.
+ throttleTxMeter = metrics.NewRegisteredMeter("txpool/throttle", nil)
+ // reorgDurationTimer measures how long time a txpool reorg takes.
+ reorgDurationTimer = metrics.NewRegisteredTimer("txpool/reorgtime", nil)
+ // dropBetweenReorgHistogram counts how many drops we experience between two reorg runs. It is expected
+ // that this number is pretty low, since txpool reorgs happen very frequently.
+ dropBetweenReorgHistogram = metrics.NewRegisteredHistogram("txpool/dropbetweenreorg", nil, metrics.NewExpDecaySample(1028, 0.015))
+
pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil)
queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil)
localGauge = metrics.NewRegisteredGauge("txpool/local", nil)
slotsGauge = metrics.NewRegisteredGauge("txpool/slots", nil)
+
+ reheapTimer = metrics.NewRegisteredTimer("txpool/reheap", nil)
)
// TxStatus is the current status of a transaction as seen by the pool.
@@ -156,7 +157,7 @@ type blockChain interface {
CurrentBlock() *types.Block
GetBlock(hash common.Hash, number uint64) *types.Block
StateAt(root common.Hash) (*state.StateDB, error)
- SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
+ SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
// Engine retrieves the chain's consensus engine.
Engine() consensus.Engine
@@ -171,8 +172,8 @@ type blockChain interface {
Config() *params.ChainConfig
}
-// TxPoolConfig are the configuration parameters of the transaction pool.
-type TxPoolConfig struct {
+// Config are the configuration parameters of the transaction pool.
+type Config struct {
Locals []common.Address // Addresses that should be treated by default as local
NoLocals bool // Whether local transaction handling should be disabled
Journal string // Journal of local transactions to survive node restarts
@@ -189,9 +190,9 @@ type TxPoolConfig struct {
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
}
-// DefaultTxPoolConfig contains the default configurations for the transaction
+// DefaultConfig contains the default configurations for the transaction
// pool.
-var DefaultTxPoolConfig = TxPoolConfig{
+var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
@@ -199,7 +200,7 @@ var DefaultTxPoolConfig = TxPoolConfig{
PriceBump: 10,
AccountSlots: 16,
- GlobalSlots: 4096,
+ GlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio
AccountQueue: 64,
GlobalQueue: 1024,
@@ -208,39 +209,39 @@ var DefaultTxPoolConfig = TxPoolConfig{
// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
-func (config *TxPoolConfig) sanitize() TxPoolConfig {
+func (config *Config) sanitize() Config {
conf := *config
if conf.Rejournal < time.Second {
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
conf.Rejournal = time.Second
}
if conf.PriceLimit < 1 {
- log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
- conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
+ log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
+ conf.PriceLimit = DefaultConfig.PriceLimit
}
if conf.PriceBump < 1 {
- log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
- conf.PriceBump = DefaultTxPoolConfig.PriceBump
+ log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
+ conf.PriceBump = DefaultConfig.PriceBump
}
if conf.AccountSlots < 1 {
- log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
- conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
+ log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultConfig.AccountSlots)
+ conf.AccountSlots = DefaultConfig.AccountSlots
}
if conf.GlobalSlots < 1 {
- log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
- conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
+ log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultConfig.GlobalSlots)
+ conf.GlobalSlots = DefaultConfig.GlobalSlots
}
if conf.AccountQueue < 1 {
- log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
- conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
+ log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultConfig.AccountQueue)
+ conf.AccountQueue = DefaultConfig.AccountQueue
}
if conf.GlobalQueue < 1 {
- log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
- conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
+ log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultConfig.GlobalQueue)
+ conf.GlobalQueue = DefaultConfig.GlobalQueue
}
if conf.Lifetime < 1 {
- log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
- conf.Lifetime = DefaultTxPoolConfig.Lifetime
+ log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime)
+ conf.Lifetime = DefaultConfig.Lifetime
}
return conf
}
@@ -253,7 +254,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
// current state) and future transactions. Transactions move between those
// two states over time as they are received and processed.
type TxPool struct {
- config TxPoolConfig
+ config Config
chainconfig *params.ChainConfig
chain blockChain
gasPrice *big.Int
@@ -262,20 +263,23 @@ type TxPool struct {
signer types.Signer
mu sync.RWMutex
+ eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
+ eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
+
currentState *state.StateDB // Current state in the blockchain head
- pendingNonces *txNoncer // Pending state tracking virtual nonces
+ pendingNonces *noncer // Pending state tracking virtual nonces
currentMaxGas uint64 // Current gas limit for transaction caps
locals *accountSet // Set of local transaction to exempt from eviction rules
- journal *txJournal // Journal of local transaction to back up to disk
+ journal *journal // Journal of local transaction to back up to disk
- pending map[common.Address]*txList // All currently processable transactions
- queue map[common.Address]*txList // Queued but non-processable transactions
+ pending map[common.Address]*list // All currently processable transactions
+ queue map[common.Address]*list // Queued but non-processable transactions
beats map[common.Address]time.Time // Last heartbeat from each known account
- all *txLookup // All transactions to allow lookups
- priced *txPricedList // All transactions sorted by price
+ all *lookup // All transactions to allow lookups
+ priced *pricedList // All transactions sorted by price
- chainHeadCh chan ChainHeadEvent
+ chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription
reqResetCh chan *txpoolResetRequest
reqPromoteCh chan *accountSet
@@ -285,7 +289,8 @@ type TxPool struct {
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
- eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
+ changesSinceReorg int // A counter for how many drops we've performed in-between reorg.
+
IsSigner func(address common.Address) bool
trc21FeeCapacity map[common.Address]*big.Int
}
@@ -296,7 +301,7 @@ type txpoolResetRequest struct {
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
// transactions from the network.
-func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
+func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
@@ -306,11 +311,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
chainconfig: chainconfig,
chain: chain,
signer: types.LatestSigner(chainconfig),
- pending: make(map[common.Address]*txList),
- queue: make(map[common.Address]*txList),
+ pending: make(map[common.Address]*list),
+ queue: make(map[common.Address]*list),
beats: make(map[common.Address]time.Time),
- all: newTxLookup(),
- chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
+ all: newLookup(),
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
reqResetCh: make(chan *txpoolResetRequest),
reqPromoteCh: make(chan *accountSet),
queueTxEventCh: make(chan *types.Transaction),
@@ -325,7 +330,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
log.Info("Setting new local account", "address", addr)
pool.locals.add(addr)
}
- pool.priced = newTxPricedList(pool.all)
+ pool.priced = newPricedList(pool.all)
pool.reset(nil, chain.CurrentBlock().Header())
// Start the reorg loop early so it can handle requests generated during journal loading.
@@ -448,7 +453,7 @@ func (pool *TxPool) Stop() {
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
// starts sending event to the given channel.
-func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription {
+func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return pool.scope.Track(pool.txFeed.Subscribe(ch))
}
@@ -466,10 +471,18 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
pool.mu.Lock()
defer pool.mu.Unlock()
+ old := pool.gasPrice
pool.gasPrice = price
- for _, tx := range pool.priced.Cap(price) {
- pool.removeTx(tx.Hash(), false)
+ // if the min miner fee increased, remove transactions below the new threshold
+ if price.Cmp(old) > 0 {
+ // pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead
+ drop := pool.all.RemotesBelowTip(price)
+ for _, tx := range drop {
+ pool.removeTx(tx.Hash(), false)
+ }
+ pool.priced.Removed(len(drop))
}
+
log.Info("Transaction pool price threshold updated", "price", price)
}
@@ -511,29 +524,63 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
pool.mu.Lock()
defer pool.mu.Unlock()
- pending := make(map[common.Address]types.Transactions)
+ pending := make(map[common.Address]types.Transactions, len(pool.pending))
for addr, list := range pool.pending {
pending[addr] = list.Flatten()
}
- queued := make(map[common.Address]types.Transactions)
+ queued := make(map[common.Address]types.Transactions, len(pool.queue))
for addr, list := range pool.queue {
queued[addr] = list.Flatten()
}
return pending, queued
}
+// ContentFrom retrieves the data content of the transaction pool, returning the
+// pending as well as queued transactions of this address, grouped by nonce.
+func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ var pending types.Transactions
+ if list, ok := pool.pending[addr]; ok {
+ pending = list.Flatten()
+ }
+ var queued types.Transactions
+ if list, ok := pool.queue[addr]; ok {
+ queued = list.Flatten()
+ }
+ return pending, queued
+}
+
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
-func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
+//
+// The enforceTips parameter can be used to do an extra filtering on the pending
+// transactions and only return those whose **effective** tip is large enough in
+// the next pending execution environment.
+func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
pool.mu.Lock()
defer pool.mu.Unlock()
pending := make(map[common.Address]types.Transactions)
for addr, list := range pool.pending {
- pending[addr] = list.Flatten()
+ txs := list.Flatten()
+
+ // If the miner requests tip enforcement, cap the lists now
+ if enforceTips && !pool.locals.contains(addr) {
+ for i, tx := range txs {
+ if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 {
+ txs = txs[:i]
+ break
+ }
+ }
+ }
+ if len(txs) > 0 {
+ pending[addr] = txs
+ }
}
- return pending, nil
+ return pending
}
// Locals retrieves the accounts currently considered local by the pool.
@@ -573,7 +620,11 @@ func (pool *TxPool) GetSender(tx *types.Transaction) (common.Address, error) {
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// Accept only legacy transactions until EIP-2718/2930 activates.
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
- return ErrTxTypeNotSupported
+ return core.ErrTxTypeNotSupported
+ }
+ // Reject dynamic fee transactions until EIP-1559 activates.
+ if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
+ return core.ErrTxTypeNotSupported
}
// Reject transactions over defined size to prevent DOS attacks
if uint64(tx.Size()) > txMaxSize {
@@ -581,11 +632,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex())
+ return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
}
// check if receiver is in black list
if tx.To() != nil && common.Blacklist[*tx.To()] {
- return fmt.Errorf("Reject transaction with receiver in black-list: %v", tx.To().Hex())
+ return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex())
}
// Transactions can't be negative. This may never happen using RLP decoded
// transactions but may occur if you create a transaction using the RPC.
@@ -596,23 +647,34 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
+ // Sanity check for extremely large numbers
+ if tx.GasFeeCap().BitLen() > 256 {
+ return core.ErrFeeCapVeryHigh
+ }
+ if tx.GasTipCap().BitLen() > 256 {
+ return core.ErrTipVeryHigh
+ }
+ // Ensure gasFeeCap is greater than or equal to gasTipCap.
+ if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 {
+ return core.ErrTipAboveFeeCap
+ }
// Make sure the transaction is signed properly.
from, err := types.Sender(pool.signer, tx)
if err != nil {
return ErrInvalidSender
}
- // Drop non-local transactions under our own minimal accepted gas price
- if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
+ // Drop non-local transactions under our own minimal accepted gas price or tip
+ if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
return ErrUnderpriced
}
}
// Ensure the transaction adheres to nonce ordering
if pool.currentState.GetNonce(from) > tx.Nonce() {
- return ErrNonceTooLow
+ return core.ErrNonceTooLow
}
if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() {
- return ErrNonceTooHigh
+ return core.ErrNonceTooHigh
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
@@ -629,24 +691,24 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if value, ok := pool.trc21FeeCapacity[*tx.To()]; ok {
feeCapacity = value
if !state.ValidateTRC21Tx(pool.currentState, from, *tx.To(), tx.Data()) {
- return ErrInsufficientFunds
+ return core.ErrInsufficientFunds
}
cost = tx.TxCost(number)
}
}
if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 {
- return ErrInsufficientFunds
+ return core.ErrInsufficientFunds
}
if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) {
// Ensure the transaction has more gas than the basic tx fee.
- intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true)
+ intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.eip1559)
if err != nil {
return err
}
// Exclude check smart contract sign address.
if tx.Gas() < intrGas {
- return ErrIntrinsicGas
+ return core.ErrIntrinsicGas
}
// Check zero gas price.
@@ -670,13 +732,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// validate minFee slot for XDCZ
if tx.IsXDCZApplyTransaction() {
copyState := pool.currentState.Copy()
- return ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
+ return core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
}
// validate balance slot, token decimal for XDCX
if tx.IsXDCXApplyTransaction() {
copyState := pool.currentState.Copy()
- return ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
+ return core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
}
return nil
}
@@ -686,8 +748,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// pending or queued one, it overwrites the previous transaction if its price is higher.
//
// If a newly added transaction is marked as local, its sending account will be
-// whitelisted, preventing any associated transaction from being dropped out of the pool
-// due to pricing constraints.
+// be added to the allowlist, preventing any associated transaction from being dropped
+// out of the pool due to pricing constraints.
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
// If the transaction is already known, discard it
hash := tx.Hash()
@@ -711,14 +773,22 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
return pool.promoteSpecialTx(from, tx, isLocal)
}
// If the transaction pool is full, discard underpriced transactions
- if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
- log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce())
+ if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue {
// If the new transaction is underpriced, don't accept it
if !isLocal && pool.priced.Underpriced(tx) {
- log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
+ log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
underpricedTxMeter.Mark(1)
return false, ErrUnderpriced
}
+ // We're about to replace a transaction. The reorg does a more thorough
+ // analysis of what to remove and how, but it runs async. We don't want to
+ // do too many replacements between reorg-runs, so we cap the number of
+ // replacements to 25% of the slots
+ if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) {
+ throttleTxMeter.Mark(1)
+ return false, ErrTxPoolOverflow
+ }
+
// New transaction is better than our worse ones, make room for it.
// If it's a local transaction, forcibly discard all available transactions.
// Otherwise if we can't make enough room for new one, abort the operation.
@@ -730,9 +800,11 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
overflowedTxMeter.Mark(1)
return false, ErrTxPoolOverflow
}
+ // Bump the counter of rejections-since-reorg
+ pool.changesSinceReorg += len(drop)
// Kick out the underpriced remote transactions.
for _, tx := range drop {
- log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
+ log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
underpricedTxMeter.Mark(1)
pool.removeTx(tx.Hash(), false)
}
@@ -788,7 +860,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
// Try to insert the transaction into the future queue
from, _ := types.Sender(pool.signer, tx) // already validated
if pool.queue[from] == nil {
- pool.queue[from] = newTxList(false)
+ pool.queue[from] = newList(false)
}
inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
if !inserted {
@@ -840,7 +912,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
// Try to insert the transaction into the pending queue
if pool.pending[addr] == nil {
- pool.pending[addr] = newTxList(true)
+ pool.pending[addr] = newList(true)
}
list := pool.pending[addr]
@@ -873,7 +945,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction, isLocal bool) (bool, error) {
// Try to insert the transaction into the pending queue
if pool.pending[addr] == nil {
- pool.pending[addr] = newTxList(true)
+ pool.pending[addr] = newList(true)
}
list := pool.pending[addr]
@@ -904,7 +976,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction,
// Set the potentially new pending nonce and notify any subsystems of the new tx
pool.beats[addr] = time.Now()
pool.pendingNonces.set(addr, tx.Nonce()+1)
- go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
+ go pool.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
return true, nil
}
@@ -1146,7 +1218,7 @@ func (pool *TxPool) scheduleReorgLoop() {
launchNextRun bool
reset *txpoolResetRequest
dirtyAccounts *accountSet
- queuedEvents = make(map[common.Address]*txSortedMap)
+ queuedEvents = make(map[common.Address]*sortedMap)
)
for {
// Launch next background reorg if needed
@@ -1159,7 +1231,7 @@ func (pool *TxPool) scheduleReorgLoop() {
launchNextRun = false
reset, dirtyAccounts = nil, nil
- queuedEvents = make(map[common.Address]*txSortedMap)
+ queuedEvents = make(map[common.Address]*sortedMap)
}
select {
@@ -1188,7 +1260,7 @@ func (pool *TxPool) scheduleReorgLoop() {
// request one later if they want the events sent.
addr, _ := types.Sender(pool.signer, tx)
if _, ok := queuedEvents[addr]; !ok {
- queuedEvents[addr] = newTxSortedMap()
+ queuedEvents[addr] = newSortedMap()
}
queuedEvents[addr].Put(tx)
@@ -1207,7 +1279,10 @@ func (pool *TxPool) scheduleReorgLoop() {
}
// runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop.
-func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*txSortedMap) {
+func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*sortedMap) {
+ defer func(t0 time.Time) {
+ reorgDurationTimer.Update(time.Since(t0))
+ }(time.Now())
defer close(done)
var promoteAddrs []common.Address
@@ -1243,23 +1318,31 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
// because of another transaction (e.g. higher gas price).
if reset != nil {
pool.demoteUnexecutables()
+ if reset.newHead != nil && pool.chainconfig.IsEIP1559(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) {
+ pendingBaseFee := eip1559.CalcBaseFee(pool.chainconfig, reset.newHead)
+ pool.priced.SetBaseFee(pendingBaseFee)
+ }
+ // Update all accounts to the latest known pending nonce
+ nonces := make(map[common.Address]uint64, len(pool.pending))
+ for addr, list := range pool.pending {
+ highestPending := list.LastElement()
+ nonces[addr] = highestPending.Nonce() + 1
+ }
+ pool.pendingNonces.setAll(nonces)
}
// Ensure pool.queue and pool.pending sizes stay within the configured limits.
pool.truncatePending()
pool.truncateQueue()
- // Update all accounts to the latest known pending nonce
- for addr, list := range pool.pending {
- highestPending := list.LastElement()
- pool.pendingNonces.set(addr, highestPending.Nonce()+1)
- }
+ dropBetweenReorgHistogram.Update(int64(pool.changesSinceReorg))
+ pool.changesSinceReorg = 0 // Reset change counter
pool.mu.Unlock()
// Notify subsystems for newly added transactions
for _, tx := range promoted {
addr, _ := types.Sender(pool.signer, tx)
if _, ok := events[addr]; !ok {
- events[addr] = newTxSortedMap()
+ events[addr] = newSortedMap()
}
events[addr].Put(tx)
}
@@ -1268,7 +1351,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
for _, set := range events {
txs = append(txs, set.Flatten()...)
}
- pool.txFeed.Send(NewTxsEvent{txs})
+ pool.txFeed.Send(core.NewTxsEvent{Txs: txs})
}
}
@@ -1295,7 +1378,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
if rem == nil {
// This can happen if a setHead is performed, where we simply discard the old
// head from the chain.
- // If that is the case, we don't have the lost transactions any more, and
+ // If that is the case, we don't have the lost transactions anymore, and
// there's nothing to add
if newNum >= oldNum {
// If we reorged to a same or higher number, then it's not a case of setHead
@@ -1349,17 +1432,18 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
}
pool.currentState = statedb
pool.trc21FeeCapacity = state.GetTRC21FeeCapacityFromStateWithCache(newHead.Root, statedb)
- pool.pendingNonces = newTxNoncer(statedb)
+ pool.pendingNonces = newNoncer(statedb)
pool.currentMaxGas = newHead.GasLimit
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
- senderCacher.recover(pool.signer, reinject)
+ core.SenderCacher.Recover(pool.signer, reinject)
pool.addTxsLocked(reinject, false)
// Update all fork indicator by next pending block number.
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
pool.eip2718 = pool.chainconfig.IsEIP1559(next)
+ pool.eip1559 = pool.chainconfig.IsEIP1559(next)
}
// promoteExecutables moves transactions that have become processable from the
@@ -1524,7 +1608,7 @@ func (pool *TxPool) truncatePending() {
pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending))
}
-// truncateQueue drops the oldes transactions in the queue if the pool is above the global queue limit.
+// truncateQueue drops the oldest transactions in the queue if the pool is above the global queue limit.
func (pool *TxPool) truncateQueue() {
queued := uint64(0)
for _, list := range pool.queue {
@@ -1541,7 +1625,7 @@ func (pool *TxPool) truncateQueue() {
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
}
}
- sort.Sort(addresses)
+ sort.Sort(sort.Reverse(addresses))
// Drop transactions until the total is below the limit or only locals remain
for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
@@ -1572,6 +1656,10 @@ func (pool *TxPool) truncateQueue() {
// demoteUnexecutables removes invalid and processed transactions from the pools
// executable/pending queue and any subsequent transactions that become unexecutable
// are moved back into the future queue.
+//
+// Note: transactions are not marked as removed in the priced list because re-heaping
+// is always explicitly triggered by SetBaseFee and it would be unnecessary and wasteful
+// to trigger a re-heap is this function
func (pool *TxPool) demoteUnexecutables() {
// Iterate over all accounts and demote any non-executable transactions
for addr, list := range pool.pending {
@@ -1620,8 +1708,6 @@ func (pool *TxPool) demoteUnexecutables() {
pool.enqueueTx(hash, tx, false, false)
}
pendingGauge.Dec(int64(len(gapped)))
- // This might happen in a reorg, so log it to the metering
- blockReorgInvalidatedTx.Mark(int64(len(gapped)))
}
// Delete the entire pending entry if it became empty.
if list.Empty() {
@@ -1654,7 +1740,7 @@ type accountSet struct {
// derivations.
func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet {
as := &accountSet{
- accounts: make(map[common.Address]struct{}),
+ accounts: make(map[common.Address]struct{}, len(addrs)),
signer: signer,
}
for _, addr := range addrs {
@@ -1716,7 +1802,7 @@ func (as *accountSet) merge(other *accountSet) {
as.cache = nil
}
-// txLookup is used internally by TxPool to track transactions while allowing
+// lookup is used internally by TxPool to track transactions while allowing
// lookup without mutex contention.
//
// Note, although this type is properly protected against concurrent access, it
@@ -1728,16 +1814,16 @@ func (as *accountSet) merge(other *accountSet) {
//
// This lookup set combines the notion of "local transactions", which is useful
// to build upper-level structure.
-type txLookup struct {
+type lookup struct {
slots int
lock sync.RWMutex
locals map[common.Hash]*types.Transaction
remotes map[common.Hash]*types.Transaction
}
-// newTxLookup returns a new txLookup structure.
-func newTxLookup() *txLookup {
- return &txLookup{
+// newLookup returns a new lookup structure.
+func newLookup() *lookup {
+ return &lookup{
locals: make(map[common.Hash]*types.Transaction),
remotes: make(map[common.Hash]*types.Transaction),
}
@@ -1746,7 +1832,7 @@ func newTxLookup() *txLookup {
// Range calls f on each key and value present in the map. The callback passed
// should return the indicator whether the iteration needs to be continued.
// Callers need to specify which set (or both) to be iterated.
-func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
+func (t *lookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1767,7 +1853,7 @@ func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local b
}
// Get returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) Get(hash common.Hash) *types.Transaction {
+func (t *lookup) Get(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1778,7 +1864,7 @@ func (t *txLookup) Get(hash common.Hash) *types.Transaction {
}
// GetLocal returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
+func (t *lookup) GetLocal(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1786,7 +1872,7 @@ func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
}
// GetRemote returns a transaction if it exists in the lookup, or nil if not found.
-func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
+func (t *lookup) GetRemote(hash common.Hash) *types.Transaction {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1794,7 +1880,7 @@ func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
}
// Count returns the current number of transactions in the lookup.
-func (t *txLookup) Count() int {
+func (t *lookup) Count() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1802,7 +1888,7 @@ func (t *txLookup) Count() int {
}
// LocalCount returns the current number of local transactions in the lookup.
-func (t *txLookup) LocalCount() int {
+func (t *lookup) LocalCount() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1810,7 +1896,7 @@ func (t *txLookup) LocalCount() int {
}
// RemoteCount returns the current number of remote transactions in the lookup.
-func (t *txLookup) RemoteCount() int {
+func (t *lookup) RemoteCount() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1818,7 +1904,7 @@ func (t *txLookup) RemoteCount() int {
}
// Slots returns the current number of slots used in the lookup.
-func (t *txLookup) Slots() int {
+func (t *lookup) Slots() int {
t.lock.RLock()
defer t.lock.RUnlock()
@@ -1826,7 +1912,7 @@ func (t *txLookup) Slots() int {
}
// Add adds a transaction to the lookup.
-func (t *txLookup) Add(tx *types.Transaction, local bool) {
+func (t *lookup) Add(tx *types.Transaction, local bool) {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1841,7 +1927,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) {
}
// Remove removes a transaction from the lookup.
-func (t *txLookup) Remove(hash common.Hash) {
+func (t *lookup) Remove(hash common.Hash) {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1862,7 +1948,7 @@ func (t *txLookup) Remove(hash common.Hash) {
// RemoteToLocals migrates the transactions belongs to the given locals to locals
// set. The assumption is held the locals set is thread-safe to be used.
-func (t *txLookup) RemoteToLocals(locals *accountSet) int {
+func (t *lookup) RemoteToLocals(locals *accountSet) int {
t.lock.Lock()
defer t.lock.Unlock()
@@ -1877,6 +1963,18 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int {
return migrated
}
+// RemotesBelowTip finds all remote transactions below the given tip threshold.
+func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
+ found := make(types.Transactions, 0, 128)
+ t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
+ if tx.GasTipCapIntCmp(threshold) < 0 {
+ found = append(found, tx)
+ }
+ return true
+ }, false, true) // Only iterate remotes
+ return found
+}
+
// numSlots calculates the number of slots needed for a single transaction.
func numSlots(tx *types.Transaction) int {
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
diff --git a/core/tx_pool_test.go b/core/txpool/txpool_test.go
similarity index 70%
rename from core/tx_pool_test.go
rename to core/txpool/txpool_test.go
index 62e1d70a61..3b1ac21f62 100644
--- a/core/tx_pool_test.go
+++ b/core/txpool/txpool_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package core
+package txpool
import (
"crypto/ecdsa"
@@ -28,6 +28,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -36,13 +37,23 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
)
-// testTxPoolConfig is a transaction pool configuration without stateful disk
-// sideeffects used during testing.
-var testTxPoolConfig TxPoolConfig
+var (
+ // testTxPoolConfig is a transaction pool configuration without stateful disk
+ // sideeffects used during testing.
+ testTxPoolConfig Config
+
+ // eip1559Config is a chain config with EIP-1559 enabled at block 0.
+ eip1559Config *params.ChainConfig
+)
func init() {
- testTxPoolConfig = DefaultTxPoolConfig
+ testTxPoolConfig = DefaultConfig
testTxPoolConfig.Journal = ""
+
+ cpy := *params.TestChainConfig
+ eip1559Config = &cpy
+ eip1559Config.BerlinBlock = common.Big0
+ eip1559Config.Eip1559Block = common.Big0
}
type testBlockChain struct {
@@ -81,7 +92,7 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
return bc.statedb, nil
}
-func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
+func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
return bc.chainHeadFeed.Subscribe(ch)
}
@@ -102,21 +113,40 @@ func pricedDataTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key
return tx
}
-func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
+func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
+ tx, _ := types.SignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainId), &types.DynamicFeeTx{
+ ChainID: params.TestChainConfig.ChainId,
+ Nonce: nonce,
+ GasTipCap: tip,
+ GasFeeCap: gasFee,
+ Gas: gaslimit,
+ To: &common.Address{},
+ Value: big.NewInt(100),
+ Data: nil,
+ AccessList: nil,
+ })
+ return tx
+}
+
+func setupPool() (*TxPool, *ecdsa.PrivateKey) {
+ return setupPoolWithConfig(params.TestChainConfig)
+}
+
+func setupPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) {
diskdb := rawdb.NewMemoryDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb))
blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)}
key, _ := crypto.GenerateKey()
- pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ pool := NewTxPool(testTxPoolConfig, config, blockchain)
// wait for the pool to initialize
<-pool.initDoneCh
return pool, key
}
-// validateTxPoolInternals checks various consistency invariants within the pool.
-func validateTxPoolInternals(pool *TxPool) error {
+// validatePoolInternals checks various consistency invariants within the pool.
+func validatePoolInternals(pool *TxPool) error {
pool.mu.RLock()
defer pool.mu.RUnlock()
@@ -126,7 +156,7 @@ func validateTxPoolInternals(pool *TxPool) error {
return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
}
pool.priced.Reheap()
- priced, remote := pool.priced.remotes.Len(), pool.all.RemoteCount()
+ priced, remote := pool.priced.urgent.Len()+pool.priced.floating.Len(), pool.all.RemoteCount()
if priced != remote {
return fmt.Errorf("total priced transaction count %d != %d", priced, remote)
}
@@ -148,7 +178,7 @@ func validateTxPoolInternals(pool *TxPool) error {
// validateEvents checks that the correct number of transaction addition events
// were fired on the pool's event feed.
-func validateEvents(events chan NewTxsEvent, count int) error {
+func validateEvents(events chan core.NewTxsEvent, count int) error {
var received []*types.Transaction
for len(received) < count {
@@ -206,7 +236,7 @@ func (c *testChain) State() (*state.StateDB, error) {
// This test simulates a scenario where a new block is imported during a
// state reset and tests whether the pending state is in sync with the
// block head event that initiated the resetState().
-func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
+func TestStateChangeDuringReset(t *testing.T) {
t.Parallel()
var (
@@ -243,41 +273,49 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
trigger = true
<-pool.requestReset(nil, nil)
- _, err := pool.Pending()
- if err != nil {
- t.Fatalf("Could not fetch pending transactions: %v", err)
- }
nonce = pool.Nonce(address)
if nonce != 2 {
t.Fatalf("Invalid nonce, want 2, got %d", nonce)
}
}
+func testAddBalance(pool *TxPool, addr common.Address, amount *big.Int) {
+ pool.mu.Lock()
+ pool.currentState.AddBalance(addr, amount)
+ pool.mu.Unlock()
+}
+
+func testSetNonce(pool *TxPool, addr common.Address, nonce uint64) {
+ pool.mu.Lock()
+ pool.currentState.SetNonce(addr, nonce)
+ pool.mu.Unlock()
+}
+
func TestInvalidTransactions(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx := transaction(0, 100, key)
from, _ := deriveSender(tx)
- pool.currentState.AddBalance(from, big.NewInt(1))
- if err := pool.AddRemote(tx); err != ErrInsufficientFunds {
- t.Error("expected", ErrInsufficientFunds)
+ testAddBalance(pool, from, big.NewInt(1))
+ if err := pool.AddRemote(tx); err != core.ErrInsufficientFunds {
+ t.Error("expected", core.ErrInsufficientFunds)
}
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
- pool.currentState.AddBalance(from, balance)
- if err := pool.AddRemote(tx); err != ErrIntrinsicGas {
- t.Error("expected", ErrIntrinsicGas, "got", err)
+ testAddBalance(pool, from, balance)
+ if err := pool.AddRemote(tx); err != core.ErrIntrinsicGas {
+ t.Error("expected", core.ErrIntrinsicGas, "got", err)
}
- pool.currentState.SetNonce(from, 1)
- pool.currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
+ testSetNonce(pool, from, 1)
+ testAddBalance(pool, from, big.NewInt(0xffffffffffffff))
tx = transaction(0, 100000, key)
- if err := pool.AddRemote(tx); err != ErrNonceTooLow {
- t.Error("expected", ErrNonceTooLow)
+ if err := pool.AddRemote(tx); err != core.ErrNonceTooLow {
+ t.Error("expected", core.ErrNonceTooLow)
}
tx = transaction(1, 100000, key)
@@ -290,15 +328,15 @@ func TestInvalidTransactions(t *testing.T) {
}
}
-func TestTransactionQueue(t *testing.T) {
+func TestQueue(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx := transaction(0, 100, key)
from, _ := deriveSender(tx)
- pool.currentState.AddBalance(from, big.NewInt(1000))
+ testAddBalance(pool, from, big.NewInt(1000))
<-pool.requestReset(nil, nil)
pool.enqueueTx(tx.Hash(), tx, false, true)
@@ -309,7 +347,7 @@ func TestTransactionQueue(t *testing.T) {
tx = transaction(1, 100, key)
from, _ = deriveSender(tx)
- pool.currentState.SetNonce(from, 2)
+ testSetNonce(pool, from, 2)
pool.enqueueTx(tx.Hash(), tx, false, true)
<-pool.requestPromoteExecutables(newAccountSet(pool.signer, from))
@@ -322,17 +360,17 @@ func TestTransactionQueue(t *testing.T) {
}
}
-func TestTransactionQueue2(t *testing.T) {
+func TestQueue2(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx1 := transaction(0, 100, key)
tx2 := transaction(10, 100, key)
tx3 := transaction(11, 100, key)
from, _ := deriveSender(tx1)
- pool.currentState.AddBalance(from, big.NewInt(1000))
+ testAddBalance(pool, from, big.NewInt(1000))
pool.reset(nil, nil)
pool.enqueueTx(tx1.Hash(), tx1, false, true)
@@ -348,24 +386,57 @@ func TestTransactionQueue2(t *testing.T) {
}
}
-func TestTransactionNegativeValue(t *testing.T) {
+func TestNegativeValue(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
from, _ := deriveSender(tx)
- pool.currentState.AddBalance(from, big.NewInt(1))
+ testAddBalance(pool, from, big.NewInt(1))
if err := pool.AddRemote(tx); err != ErrNegativeValue {
t.Error("expected", ErrNegativeValue, "got", err)
}
}
-func TestTransactionChainFork(t *testing.T) {
+func TestTipAboveFeeCap(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+
+ tx := dynamicFeeTx(0, 100, big.NewInt(1), big.NewInt(2), key)
+
+ if err := pool.AddRemote(tx); err != core.ErrTipAboveFeeCap {
+ t.Error("expected", core.ErrTipAboveFeeCap, "got", err)
+ }
+}
+
+func TestVeryHighValues(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+
+ veryBigNumber := big.NewInt(1)
+ veryBigNumber.Lsh(veryBigNumber, 300)
+
+ tx := dynamicFeeTx(0, 100, big.NewInt(1), veryBigNumber, key)
+ if err := pool.AddRemote(tx); err != core.ErrTipVeryHigh {
+ t.Error("expected", core.ErrTipVeryHigh, "got", err)
+ }
+
+ tx2 := dynamicFeeTx(0, 100, veryBigNumber, big.NewInt(1), key)
+ if err := pool.AddRemote(tx2); err != core.ErrFeeCapVeryHigh {
+ t.Error("expected", core.ErrFeeCapVeryHigh, "got", err)
+ }
+}
+
+func TestChainFork(t *testing.T) {
+ t.Parallel()
+
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -392,10 +463,10 @@ func TestTransactionChainFork(t *testing.T) {
}
}
-func TestTransactionDoubleNonce(t *testing.T) {
+func TestDoubleNonce(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
@@ -444,14 +515,14 @@ func TestTransactionDoubleNonce(t *testing.T) {
}
}
-func TestTransactionMissingNonce(t *testing.T) {
+func TestMissingNonce(t *testing.T) {
t.Parallel()
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
+ testAddBalance(pool, addr, big.NewInt(100000000000000))
tx := transaction(1, 100000, key)
if _, err := pool.add(tx, false); err != nil {
t.Error("didn't expect error", err)
@@ -467,16 +538,16 @@ func TestTransactionMissingNonce(t *testing.T) {
}
}
-func TestTransactionNonceRecovery(t *testing.T) {
+func TestNonceRecovery(t *testing.T) {
t.Parallel()
const n = 10
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
addr := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.SetNonce(addr, n)
- pool.currentState.AddBalance(addr, big.NewInt(100000000000000))
+ testSetNonce(pool, addr, n)
+ testAddBalance(pool, addr, big.NewInt(100000000000000))
<-pool.requestReset(nil, nil)
tx := transaction(n, 100000, key)
@@ -484,7 +555,7 @@ func TestTransactionNonceRecovery(t *testing.T) {
t.Error(err)
}
// simulate some weird re-order of transactions and missing nonce(s)
- pool.currentState.SetNonce(addr, n-1)
+ testSetNonce(pool, addr, n-1)
<-pool.requestReset(nil, nil)
if fn := pool.Nonce(addr); fn != n-1 {
t.Errorf("expected nonce to be %d, got %d", n-1, fn)
@@ -493,15 +564,15 @@ func TestTransactionNonceRecovery(t *testing.T) {
// Tests that if an account runs out of funds, any pending and queued transactions
// are dropped.
-func TestTransactionDropping(t *testing.T) {
+func TestDropping(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000))
+ testAddBalance(pool, account, big.NewInt(1000))
// Add some pending and some queued transactions
var (
@@ -549,7 +620,7 @@ func TestTransactionDropping(t *testing.T) {
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 6)
}
// Reduce the balance of the account, and check that invalidated transactions are dropped
- pool.currentState.AddBalance(account, big.NewInt(-650))
+ testAddBalance(pool, account, big.NewInt(-650))
<-pool.requestReset(nil, nil)
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
@@ -597,7 +668,7 @@ func TestTransactionDropping(t *testing.T) {
// Tests that if a transaction is dropped from the current pending pool (e.g. out
// of fund), all consecutive (still valid, but not executable) transactions are
// postponed back into the future queue to prevent broadcasting them.
-func TestTransactionPostponing(t *testing.T) {
+func TestPostponing(t *testing.T) {
t.Parallel()
// Create the pool to test the postponing with
@@ -616,7 +687,7 @@ func TestTransactionPostponing(t *testing.T) {
keys[i], _ = crypto.GenerateKey()
accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
}
// Add a batch consecutive pending transactions for validation
txs := []*types.Transaction{}
@@ -659,7 +730,7 @@ func TestTransactionPostponing(t *testing.T) {
}
// Reduce the balance of the account, and check that transactions are reorganised
for _, addr := range accs {
- pool.currentState.AddBalance(addr, big.NewInt(-1))
+ testAddBalance(pool, addr, big.NewInt(-1))
}
<-pool.requestReset(nil, nil)
@@ -712,18 +783,18 @@ func TestTransactionPostponing(t *testing.T) {
// Tests that if the transaction pool has both executable and non-executable
// transactions from an origin account, filling the nonce gap moves all queued
// ones into the pending pool.
-func TestTransactionGapFilling(t *testing.T) {
+func TestGapFilling(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
+ events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue+5)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -742,7 +813,7 @@ func TestTransactionGapFilling(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Fill the nonce gap and ensure all transactions become pending
@@ -759,22 +830,22 @@ func TestTransactionGapFilling(t *testing.T) {
if err := validateEvents(events, 2); err != nil {
t.Fatalf("gap-filling event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that if the transaction count belonging to a single account goes above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
-func TestTransactionQueueAccountLimiting(t *testing.T) {
+func TestQueueAccountLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
testTxPoolConfig.AccountQueue = 10
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= testTxPoolConfig.AccountQueue; i++ {
@@ -804,14 +875,14 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
//
// This logic should not hold for local transactions, unless the local tracking
// mechanism is disabled.
-func TestTransactionQueueGlobalLimiting(t *testing.T) {
- testTransactionQueueGlobalLimiting(t, false)
+func TestQueueGlobalLimiting(t *testing.T) {
+ testQueueGlobalLimiting(t, false)
}
-func TestTransactionQueueGlobalLimitingNoLocals(t *testing.T) {
- testTransactionQueueGlobalLimiting(t, true)
+func TestQueueGlobalLimitingNoLocals(t *testing.T) {
+ testQueueGlobalLimiting(t, true)
}
-func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
+func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -831,7 +902,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
local := keys[len(keys)-1]
@@ -896,12 +967,12 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
//
// This logic should not hold for local transactions, unless the local tracking
// mechanism is disabled.
-func TestTransactionQueueTimeLimiting(t *testing.T) { testTransactionQueueTimeLimiting(t, false) }
-func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) {
- testTransactionQueueTimeLimiting(t, true)
+func TestQueueTimeLimiting(t *testing.T) { testQueueTimeLimiting(t, false) }
+func TestQueueTimeLimitingNoLocals(t *testing.T) {
+ testQueueTimeLimiting(t, true)
}
-func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
+func testQueueTimeLimiting(t *testing.T, nolocals bool) {
common.MinGasPrice = big.NewInt(0)
// Reduce the eviction interval to a testable amount
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
@@ -923,8 +994,8 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
- pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
// Add the two transactions and ensure they both are queued up
if err := pool.AddLocal(pricedTransaction(1, 100000, big.NewInt(1), local)); err != nil {
@@ -940,7 +1011,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -955,7 +1026,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -975,7 +1046,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -992,7 +1063,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1022,7 +1093,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
@@ -1041,7 +1112,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1049,18 +1120,18 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
// Tests that even if the transaction count belonging to a single account goes
// above some threshold, as long as the transactions are executable, they are
// accepted.
-func TestTransactionPendingLimiting(t *testing.T) {
+func TestPendingLimiting(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
testTxPoolConfig.AccountQueue = 10
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue)
+ events := make(chan core.NewTxsEvent, testTxPoolConfig.AccountQueue)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1082,7 +1153,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
if err := validateEvents(events, int(testTxPoolConfig.AccountQueue)); err != nil {
t.Fatalf("event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1090,7 +1161,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, the higher transactions are dropped to prevent DOS
// attacks.
-func TestTransactionPendingGlobalLimiting(t *testing.T) {
+func TestPendingGlobalLimiting(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1108,7 +1179,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64)
@@ -1131,7 +1202,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
if pending > int(config.GlobalSlots) {
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, config.GlobalSlots)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1139,15 +1210,15 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Test the limit on transaction size is enforced correctly.
// This test verifies every transaction having allowed size
// is added to the pool, and longer transactions are rejected.
-func TestTransactionAllowedTxSize(t *testing.T) {
+func TestAllowedTxSize(t *testing.T) {
t.Parallel()
// Create a test account and fund it
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000000))
+ testAddBalance(pool, account, big.NewInt(1000000000))
// Compute maximal data size for transactions (lower bound).
//
@@ -1187,13 +1258,13 @@ func TestTransactionAllowedTxSize(t *testing.T) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that if transactions start being capped, transactions are also removed from 'all'
-func TestTransactionCapClearsFromAll(t *testing.T) {
+func TestCapClearsFromAll(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1212,7 +1283,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
// Create a number of test accounts and fund them
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(addr, big.NewInt(1000000))
+ testAddBalance(pool, addr, big.NewInt(1000000))
txs := types.Transactions{}
for j := 0; j < int(config.GlobalSlots)*2; j++ {
@@ -1220,7 +1291,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
}
// Import the batch and verify that limits have been enforced
pool.AddRemotes(txs)
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1228,7 +1299,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, if they are under the minimum guaranteed slot count then
// the transactions are still kept.
-func TestTransactionPendingMinimumAllowance(t *testing.T) {
+func TestPendingMinimumAllowance(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
@@ -1246,7 +1317,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64)
@@ -1267,7 +1338,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1277,7 +1348,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
// from the pending pool to the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolRepricing(t *testing.T) {
+func TestRepricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1289,7 +1360,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1297,7 +1368,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
@@ -1330,7 +1401,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 7); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Reprice the pool and check that underpriced transactions get dropped
@@ -1346,7 +1417,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Check that we can't add the old transactions back
@@ -1362,7 +1433,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// However we can add local underpriced transactions
@@ -1376,7 +1447,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("post-reprice local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// And we can fill gaps with properly priced transactions
@@ -1392,14 +1463,141 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateEvents(events, 5); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that setting the transaction pool gas price to a higher value correctly
+// discards everything cheaper (legacy & dynamic fee) than that and moves any
+// gapped transactions back from the pending pool to the queue.
+//
+// Note, local transactions are never allowed to be dropped.
+func TestRepricingDynamicFee(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the pricing enforcement with
+ pool, _ := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+
+ // Keep track of transaction events to ensure all executables get announced
+ events := make(chan core.NewTxsEvent, 32)
+ sub := pool.txFeed.Subscribe(events)
+ defer sub.Unsubscribe()
+
+ // Create a number of test accounts and fund them
+ keys := make([]*ecdsa.PrivateKey, 4)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+ // Generate and queue a batch of transactions, both pending and queued
+ txs := types.Transactions{}
+
+ txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
+ txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
+
+ txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1]))
+ txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(3), big.NewInt(2), keys[1]))
+ txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(3), big.NewInt(2), keys[1]))
+
+ txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(2), keys[2]))
+ txs = append(txs, dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2]))
+ txs = append(txs, dynamicFeeTx(3, 100000, big.NewInt(2), big.NewInt(2), keys[2]))
+
+ ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[3])
+
+ // Import the batch and that both pending and queued transactions match up
+ pool.AddRemotesSync(txs)
+ pool.AddLocal(ltx)
+
+ pending, queued := pool.Stats()
+ if pending != 7 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
+ }
+ if queued != 3 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
+ }
+ if err := validateEvents(events, 7); err != nil {
+ t.Fatalf("original event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Reprice the pool and check that underpriced transactions get dropped
+ pool.SetGasPrice(big.NewInt(2))
+
+ pending, queued = pool.Stats()
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ if queued != 5 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5)
+ }
+ if err := validateEvents(events, 0); err != nil {
+ t.Fatalf("reprice event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Check that we can't add the old transactions back
+ tx := pricedTransaction(1, 100000, big.NewInt(1), keys[0])
+ if err := pool.AddRemote(tx); err != ErrUnderpriced {
+ t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
+ }
+ tx = dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])
+ if err := pool.AddRemote(tx); err != ErrUnderpriced {
+ t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
+ }
+ tx = dynamicFeeTx(2, 100000, big.NewInt(1), big.NewInt(1), keys[2])
+ if err := pool.AddRemote(tx); err != ErrUnderpriced {
+ t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
+ }
+ if err := validateEvents(events, 0); err != nil {
+ t.Fatalf("post-reprice event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // However we can add local underpriced transactions
+ tx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(1), keys[3])
+ if err := pool.AddLocal(tx); err != nil {
+ t.Fatalf("failed to add underpriced local transaction: %v", err)
+ }
+ if pending, _ = pool.Stats(); pending != 3 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
+ }
+ if err := validateEvents(events, 1); err != nil {
+ t.Fatalf("post-reprice local event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // And we can fill gaps with properly priced transactions
+ tx = pricedTransaction(1, 100000, big.NewInt(2), keys[0])
+ if err := pool.AddRemote(tx); err != nil {
+ t.Fatalf("failed to add pending transaction: %v", err)
+ }
+ tx = dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[1])
+ if err := pool.AddRemote(tx); err != nil {
+ t.Fatalf("failed to add pending transaction: %v", err)
+ }
+ tx = dynamicFeeTx(2, 100000, big.NewInt(2), big.NewInt(2), keys[2])
+ if err := pool.AddRemote(tx); err != nil {
+ t.Fatalf("failed to add queued transaction: %v", err)
+ }
+ if err := validateEvents(events, 5); err != nil {
+ t.Fatalf("post-reprice event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that setting the transaction pool gas price to a higher value does not
-// remove local transactions.
-func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
+// remove local transactions (legacy & dynamic fee).
+func TestRepricingKeepsLocals(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1407,30 +1605,42 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
- pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
+ pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
defer pool.Stop()
// Create a number of test accounts and fund them
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000*1000000))
}
// Create transaction (both pending and queued) with a linearly growing gasprice
+ // common.LimitThresholdNonceInQueue = 10
for i := uint64(0); i < 5; i++ {
- // Add pending
- p_tx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2])
- if err := pool.AddLocal(p_tx); err != nil {
+ // Add pending transaction.
+ pendingTx := pricedTransaction(i, 100000, big.NewInt(int64(i+1)), keys[2])
+ if err := pool.AddLocal(pendingTx); err != nil {
t.Fatal(err)
}
- // Add queued
- q_tx := pricedTransaction(i+6, 100000, big.NewInt(int64(i+1)), keys[2])
- if err := pool.AddLocal(q_tx); err != nil {
+ // Add queued transaction.
+ queuedTx := pricedTransaction(i+6, 100000, big.NewInt(int64(i+1)), keys[2])
+ if err := pool.AddLocal(queuedTx); err != nil {
+ t.Fatal(err)
+ }
+
+ // Add pending dynamic fee transaction.
+ pendingTx = dynamicFeeTx(i, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
+ if err := pool.AddLocal(pendingTx); err != nil {
+ t.Fatal(err)
+ }
+ // Add queued dynamic fee transaction.
+ queuedTx = dynamicFeeTx(i+6, 100000, big.NewInt(int64(i)+1), big.NewInt(int64(i)), keys[1])
+ if err := pool.AddLocal(queuedTx); err != nil {
t.Fatal(err)
}
}
pending, queued := pool.Stats()
- expPending, expQueued := 5, 5
+ expPending, expQueued := 10, 10
validate := func() {
pending, queued = pool.Stats()
if pending != expPending {
@@ -1440,7 +1650,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, expQueued)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1462,7 +1672,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
// pending transactions are moved into the queue.
//
// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolUnderpricing(t *testing.T) {
+func TestPoolUnderpricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1478,7 +1688,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1486,7 +1696,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
@@ -1512,7 +1722,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 3); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding an underpriced transaction on block limit fails
@@ -1539,7 +1749,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("additional event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding local transactions can push out even higher priced ones
@@ -1561,7 +1771,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if err := validateEvents(events, 2); err != nil {
t.Fatalf("local event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
@@ -1569,7 +1779,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
// Tests that more expensive transactions push out cheap ones from the pool, but
// without producing instability by creating gaps that start jumping transactions
// back and forth between queued/pending.
-func TestTransactionPoolStableUnderpricing(t *testing.T) {
+func TestPoolStableUnderpricing(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1586,7 +1796,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
@@ -1594,7 +1804,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 2)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Fill up the entire queue with the same transaction price points
txs := types.Transactions{}
@@ -1613,7 +1823,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
if err := validateEvents(events, int(config.GlobalSlots)); err != nil {
t.Fatalf("original event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap
@@ -1630,13 +1840,180 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
if err := validateEvents(events, 1); err != nil {
t.Fatalf("additional event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that when the pool reaches its global transaction limit, underpriced
+// transactions (legacy & dynamic fee) are gradually shifted out for more
+// expensive ones and any gapped pending transactions are moved into the queue.
+//
+// Note, local transactions are never allowed to be dropped.
+func TestPoolUnderpricingDynamicFee(t *testing.T) {
+ t.Parallel()
+
+ pool, _ := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+
+ pool.config.GlobalSlots = 2
+ pool.config.GlobalQueue = 2
+
+ // Keep track of transaction events to ensure all executables get announced
+ events := make(chan core.NewTxsEvent, 32)
+ sub := pool.txFeed.Subscribe(events)
+ defer sub.Unsubscribe()
+
+ // Create a number of test accounts and fund them
+ keys := make([]*ecdsa.PrivateKey, 4)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ }
+
+ // Generate and queue a batch of transactions, both pending and queued
+ txs := types.Transactions{}
+
+ txs = append(txs, dynamicFeeTx(0, 100000, big.NewInt(3), big.NewInt(2), keys[0]))
+ txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
+ txs = append(txs, dynamicFeeTx(1, 100000, big.NewInt(2), big.NewInt(1), keys[1]))
+
+ ltx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[2])
+
+ // Import the batch and that both pending and queued transactions match up
+ pool.AddRemotes(txs) // Pend K0:0, K0:1; Que K1:1
+ pool.AddLocal(ltx) // +K2:0 => Pend K0:0, K0:1, K2:0; Que K1:1
+
+ pending, queued := pool.Stats()
+ if pending != 3 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
+ }
+ if queued != 1 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
+ }
+ if err := validateEvents(events, 3); err != nil {
+ t.Fatalf("original event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+
+ // Ensure that adding an underpriced transaction fails
+ tx := dynamicFeeTx(0, 100000, big.NewInt(2), big.NewInt(1), keys[1])
+ if err := pool.AddRemote(tx); err != ErrUnderpriced { // Pend K0:0, K0:1, K2:0; Que K1:1
+ t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
+ }
+
+ // Ensure that adding high priced transactions drops cheap ones, but not own
+ tx = pricedTransaction(0, 100000, big.NewInt(2), keys[1])
+ if err := pool.AddRemote(tx); err != nil { // +K1:0, -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que -
+ t.Fatalf("failed to add well priced transaction: %v", err)
+ }
+
+ tx = pricedTransaction(2, 100000, big.NewInt(3), keys[1])
+ if err := pool.AddRemote(tx); err != nil { // +K1:2, -K0:1 => Pend K0:0 K1:0, K2:0; Que K1:2
+ t.Fatalf("failed to add well priced transaction: %v", err)
+ }
+ tx = dynamicFeeTx(3, 100000, big.NewInt(4), big.NewInt(1), keys[1])
+ if err := pool.AddRemote(tx); err != nil { // +K1:3, -K1:0 => Pend K0:0 K2:0; Que K1:2 K1:3
+ t.Fatalf("failed to add well priced transaction: %v", err)
+ }
+ pending, queued = pool.Stats()
+ if pending != 2 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
+ }
+ if queued != 2 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
+ }
+ if err := validateEvents(events, 1); err != nil {
+ t.Fatalf("additional event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+ // Ensure that adding local transactions can push out even higher priced ones
+ ltx = dynamicFeeTx(1, 100000, big.NewInt(1), big.NewInt(0), keys[2])
+ if err := pool.AddLocal(ltx); err != nil {
+ t.Fatalf("failed to append underpriced local transaction: %v", err)
+ }
+ ltx = dynamicFeeTx(0, 100000, big.NewInt(1), big.NewInt(0), keys[3])
+ if err := pool.AddLocal(ltx); err != nil {
+ t.Fatalf("failed to add new underpriced local transaction: %v", err)
+ }
+ pending, queued = pool.Stats()
+ if pending != 3 {
+ t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
+ }
+ if queued != 1 {
+ t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
+ }
+ if err := validateEvents(events, 2); err != nil {
+ t.Fatalf("local event firing failed: %v", err)
+ }
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests whether highest fee cap transaction is retained after a batch of high effective
+// tip transactions are added and vice versa
+func TestDualHeapEviction(t *testing.T) {
+ t.Parallel()
+
+ pool, _ := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+
+ pool.config.GlobalSlots = 10
+ pool.config.GlobalQueue = 10
+
+ var (
+ highTip, highCap *types.Transaction
+ baseFee int
+ )
+
+ check := func(tx *types.Transaction, name string) {
+ if pool.all.GetRemote(tx.Hash()) == nil {
+ t.Fatalf("highest %s transaction evicted from the pool", name)
+ }
+ }
+
+ add := func(urgent bool) {
+ for i := 0; i < 20; i++ {
+ var tx *types.Transaction
+ // Create a test accounts and fund it
+ key, _ := crypto.GenerateKey()
+ testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000000))
+ if urgent {
+ tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key)
+ highTip = tx
+ } else {
+ tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key)
+ highCap = tx
+ }
+ pool.AddRemotesSync([]*types.Transaction{tx})
+ }
+ pending, queued := pool.Stats()
+ if pending+queued != 20 {
+ t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 20)
+ }
+ }
+
+ add(false)
+ for baseFee = 0; baseFee <= 1000; baseFee += 100 {
+ pool.priced.SetBaseFee(big.NewInt(int64(baseFee)))
+ add(true)
+ check(highCap, "fee cap")
+ add(false)
+ check(highTip, "effective tip")
+ }
+
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that the pool rejects duplicate transactions.
-func TestTransactionDeduplication(t *testing.T) {
+func TestDeduplication(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1648,7 +2025,7 @@ func TestTransactionDeduplication(t *testing.T) {
// Create a test account to add transactions with
key, _ := crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
// Create a batch of transactions and add a few of them
txs := make([]*types.Transaction, common.LimitThresholdNonceInQueue)
@@ -1695,14 +2072,14 @@ func TestTransactionDeduplication(t *testing.T) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that the pool rejects replacement transactions that don't meet the minimum
// price bump required.
-func TestTransactionReplacement(t *testing.T) {
+func TestReplacement(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
@@ -1714,13 +2091,13 @@ func TestTransactionReplacement(t *testing.T) {
defer pool.Stop()
// Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
+ events := make(chan core.NewTxsEvent, 32)
sub := pool.txFeed.Subscribe(events)
defer sub.Unsubscribe()
// Create a test account to add transactions with
key, _ := crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
price := int64(100)
@@ -1776,15 +2153,125 @@ func TestTransactionReplacement(t *testing.T) {
if err := validateEvents(events, 0); err != nil {
t.Fatalf("queued replacement event firing failed: %v", err)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that the pool rejects replacement dynamic fee transactions that don't
+// meet the minimum price bump required.
+func TestReplacementDynamicFee(t *testing.T) {
+ t.Parallel()
+
+ // Create the pool to test the pricing enforcement with
+ pool, key := setupPoolWithConfig(eip1559Config)
+ defer pool.Stop()
+ testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
+
+ // Keep track of transaction events to ensure all executables get announced
+ events := make(chan core.NewTxsEvent, 32)
+ sub := pool.txFeed.Subscribe(events)
+ defer sub.Unsubscribe()
+
+ // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
+ gasFeeCap := int64(100)
+ feeCapThreshold := (gasFeeCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100
+ gasTipCap := int64(60)
+ tipThreshold := (gasTipCap * (100 + int64(testTxPoolConfig.PriceBump))) / 100
+
+ // Run the following identical checks for both the pending and queue pools:
+ // 1. Send initial tx => accept
+ // 2. Don't bump tip or fee cap => discard
+ // 3. Bump both more than min => accept
+ // 4. Check events match expected (2 new executable txs during pending, 0 during queue)
+ // 5. Send new tx with larger tip and gasFeeCap => accept
+ // 6. Bump tip max allowed so it's still underpriced => discard
+ // 7. Bump fee cap max allowed so it's still underpriced => discard
+ // 8. Bump tip min for acceptance => discard
+ // 9. Bump feecap min for acceptance => discard
+ // 10. Bump feecap and tip min for acceptance => accept
+ // 11. Check events match expected (2 new executable txs during pending, 0 during queue)
+ stages := []string{"pending", "queued"}
+ for _, stage := range stages {
+ // Since state is empty, 0 nonce txs are "executable" and can go
+ // into pending immediately. 2 nonce txs are "happed
+ nonce := uint64(0)
+ if stage == "queued" {
+ nonce = 2
+ }
+
+ // 1. Send initial tx => accept
+ tx := dynamicFeeTx(nonce, 100000, big.NewInt(2), big.NewInt(1), key)
+ if err := pool.addRemoteSync(tx); err != nil {
+ t.Fatalf("failed to add original cheap %s transaction: %v", stage, err)
+ }
+ // 2. Don't bump tip or feecap => discard
+ tx = dynamicFeeTx(nonce, 100001, big.NewInt(2), big.NewInt(1), key)
+ if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
+ t.Fatalf("original cheap %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
+ }
+ // 3. Bump both more than min => accept
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(3), big.NewInt(2), key)
+ if err := pool.AddRemote(tx); err != nil {
+ t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err)
+ }
+ // 4. Check events match expected (2 new executable txs during pending, 0 during queue)
+ count := 2
+ if stage == "queued" {
+ count = 0
+ }
+ if err := validateEvents(events, count); err != nil {
+ t.Fatalf("cheap %s replacement event firing failed: %v", stage, err)
+ }
+ // 5. Send new tx with larger tip and feeCap => accept
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(gasTipCap), key)
+ if err := pool.addRemoteSync(tx); err != nil {
+ t.Fatalf("failed to add original proper %s transaction: %v", stage, err)
+ }
+ // 6. Bump tip max allowed so it's still underpriced => discard
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold-1), key)
+ if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
+ t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
+ }
+ // 7. Bump fee cap max allowed so it's still underpriced => discard
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold-1), big.NewInt(gasTipCap), key)
+ if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
+ t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
+ }
+ // 8. Bump tip min for acceptance => accept
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(gasFeeCap), big.NewInt(tipThreshold), key)
+ if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
+ t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
+ }
+ // 9. Bump fee cap min for acceptance => accept
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(gasTipCap), key)
+ if err := pool.AddRemote(tx); err != ErrReplaceUnderpriced {
+ t.Fatalf("original proper %s transaction replacement error mismatch: have %v, want %v", stage, err, ErrReplaceUnderpriced)
+ }
+ // 10. Check events match expected (3 new executable txs during pending, 0 during queue)
+ tx = dynamicFeeTx(nonce, 100000, big.NewInt(feeCapThreshold), big.NewInt(tipThreshold), key)
+ if err := pool.AddRemote(tx); err != nil {
+ t.Fatalf("failed to replace original cheap %s transaction: %v", stage, err)
+ }
+ // 11. Check events match expected (3 new executable txs during pending, 0 during queue)
+ count = 2
+ if stage == "queued" {
+ count = 0
+ }
+ if err := validateEvents(events, count); err != nil {
+ t.Fatalf("replacement %s event firing failed: %v", stage, err)
+ }
+ }
+
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that local transactions are journaled to disk, but remote transactions
// get discarded between restarts.
-func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
-func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
+func TestJournaling(t *testing.T) { testTransactionJournaling(t, false) }
+func TestJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) }
func testTransactionJournaling(t *testing.T, nolocals bool) {
t.Parallel()
@@ -1817,8 +2304,8 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
local, _ := crypto.GenerateKey()
remote, _ := crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
- pool.currentState.AddBalance(crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(local.PublicKey), big.NewInt(1000000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(remote.PublicKey), big.NewInt(1000000000))
// Add three local and a remote transactions and ensure they are queued up
if err := pool.AddLocal(pricedTransaction(0, 100000, big.NewInt(1), local)); err != nil {
@@ -1840,7 +2327,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
@@ -1863,7 +2350,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Bump the nonce temporarily and ensure the newly invalidated transaction is removed
@@ -1889,7 +2376,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
pool.Stop()
@@ -1897,7 +2384,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
// TestTransactionStatusCheck tests that the pool can correctly retrieve the
// pending status of individual transactions.
-func TestTransactionStatusCheck(t *testing.T) {
+func TestStatusCheck(t *testing.T) {
t.Parallel()
// Create the pool to test the status retrievals with
@@ -1912,7 +2399,7 @@ func TestTransactionStatusCheck(t *testing.T) {
keys := make([]*ecdsa.PrivateKey, 3)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
+ testAddBalance(pool, crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions, both pending and queued
txs := types.Transactions{}
@@ -1932,7 +2419,7 @@ func TestTransactionStatusCheck(t *testing.T) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
- if err := validateTxPoolInternals(pool); err != nil {
+ if err := validatePoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Retrieve the status of each transaction and validate them
@@ -1953,7 +2440,7 @@ func TestTransactionStatusCheck(t *testing.T) {
}
// Test the transaction slots consumption is computed correctly
-func TestTransactionSlotCount(t *testing.T) {
+func TestSlotCount(t *testing.T) {
t.Parallel()
key, _ := crypto.GenerateKey()
@@ -1978,11 +2465,11 @@ func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 1
func benchmarkPendingDemotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
for i := 0; i < size; i++ {
tx := transaction(uint64(i), 100000, key)
@@ -2003,11 +2490,11 @@ func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 1
func benchmarkFuturePromotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
for i := 0; i < size; i++ {
tx := transaction(uint64(1+i), 100000, key)
@@ -2021,21 +2508,21 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
}
// Benchmarks the speed of batched transaction insertion.
-func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, false) }
-func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, false) }
-func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, false) }
+func BenchmarkBatchInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, false) }
+func BenchmarkBatchInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, false) }
+func BenchmarkBatchInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, false) }
-func BenchmarkPoolBatchLocalInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, true) }
-func BenchmarkPoolBatchLocalInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, true) }
-func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, true) }
+func BenchmarkBatchLocalInsert100(b *testing.B) { benchmarkBatchInsert(b, 100, true) }
+func BenchmarkBatchLocalInsert1000(b *testing.B) { benchmarkBatchInsert(b, 1000, true) }
+func BenchmarkBatchLocalInsert10000(b *testing.B) { benchmarkBatchInsert(b, 10000, true) }
-func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) {
+func benchmarkBatchInsert(b *testing.B, size int, local bool) {
// Generate a batch of transactions to enqueue into the pool
- pool, key := setupTxPool()
+ pool, key := setupPool()
defer pool.Stop()
account := crypto.PubkeyToAddress(key.PublicKey)
- pool.currentState.AddBalance(account, big.NewInt(1000000))
+ testAddBalance(pool, account, big.NewInt(1000000))
batches := make([]types.Transactions, b.N)
for i := 0; i < b.N; i++ {
@@ -2075,17 +2562,38 @@ func BenchmarkInsertRemoteWithAllLocals(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
- pool, _ := setupTxPool()
- pool.currentState.AddBalance(account, big.NewInt(100000000))
+ pool, _ := setupPool()
+ testAddBalance(pool, account, big.NewInt(100000000))
for _, local := range locals {
pool.AddLocal(local)
}
b.StartTimer()
// Assign a high enough balance for testing
- pool.currentState.AddBalance(remoteAddr, big.NewInt(100000000))
+ testAddBalance(pool, remoteAddr, big.NewInt(100000000))
for i := 0; i < len(remotes); i++ {
pool.AddRemotes([]*types.Transaction{remotes[i]})
}
pool.Stop()
}
}
+
+// Benchmarks the speed of batch transaction insertion in case of multiple accounts.
+func BenchmarkMultiAccountBatchInsert(b *testing.B) {
+ // Generate a batch of transactions to enqueue into the pool
+ pool, _ := setupPool()
+ defer pool.Stop()
+ b.ReportAllocs()
+ batches := make(types.Transactions, b.N)
+ for i := 0; i < b.N; i++ {
+ key, _ := crypto.GenerateKey()
+ account := crypto.PubkeyToAddress(key.PublicKey)
+ pool.currentState.AddBalance(account, big.NewInt(1000000))
+ tx := transaction(uint64(0), 100000, key)
+ batches[i] = tx
+ }
+ // Benchmark importing the transactions into the queue
+ b.ResetTimer()
+ for _, tx := range batches {
+ pool.AddRemotesSync([]*types.Transaction{tx})
+ }
+}
diff --git a/core/types/block.go b/core/types/block.go
index 071666a801..57c470baa1 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -32,11 +32,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/rlp"
)
-var (
- EmptyRootHash = DeriveSha(Transactions{})
- EmptyUncleHash = CalcUncleHash(nil)
-)
-
// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a sufficient amount of computation has been carried
// out on a block.
@@ -86,6 +81,9 @@ type Header struct {
Validators []byte `json:"validators" gencodec:"required"`
Validator []byte `json:"validator" gencodec:"required"`
Penalties []byte `json:"penalties" gencodec:"required"`
+
+ // BaseFee was added by EIP-1559 and is ignored in legacy headers.
+ BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
}
// field type overrides for gencodec
@@ -96,6 +94,7 @@ type headerMarshaling struct {
GasUsed hexutil.Uint64
Time *hexutil.Big
Extra hexutil.Bytes
+ BaseFee *hexutil.Big
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
}
@@ -222,7 +221,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
- b.header.TxHash = EmptyRootHash
+ b.header.TxHash = EmptyTxsHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs))
b.transactions = make(Transactions, len(txs))
@@ -230,7 +229,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
}
if len(receipts) == 0 {
- b.header.ReceiptHash = EmptyRootHash
+ b.header.ReceiptHash = EmptyReceiptsHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts))
b.header.Bloom = CreateBloom(receipts)
@@ -269,6 +268,9 @@ func CopyHeader(h *Header) *Header {
if cpy.Number = new(big.Int); h.Number != nil {
cpy.Number.Set(h.Number)
}
+ if h.BaseFee != nil {
+ cpy.BaseFee = new(big.Int).Set(h.BaseFee)
+ }
if len(h.Extra) > 0 {
cpy.Extra = make([]byte, len(h.Extra))
copy(cpy.Extra, h.Extra)
@@ -345,6 +347,13 @@ func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Ext
func (b *Block) Penalties() []byte { return common.CopyBytes(b.header.Penalties) }
func (b *Block) Validator() []byte { return common.CopyBytes(b.header.Validator) }
+func (b *Block) BaseFee() *big.Int {
+ if b.header.BaseFee == nil {
+ return nil
+ }
+ return new(big.Int).Set(b.header.BaseFee)
+}
+
func (b *Block) Header() *Header { return CopyHeader(b.header) }
// Body returns the non-header content of the block.
@@ -455,10 +464,10 @@ type Blocks []*Block
type BlockBy func(b1, b2 *Block) bool
-func (self BlockBy) Sort(blocks Blocks) {
+func (bb BlockBy) Sort(blocks Blocks) {
bs := blockSorter{
blocks: blocks,
- by: self,
+ by: bb,
}
sort.Sort(bs)
}
@@ -468,10 +477,12 @@ type blockSorter struct {
by func(b1, b2 *Block) bool
}
-func (self blockSorter) Len() int { return len(self.blocks) }
-func (self blockSorter) Swap(i, j int) {
- self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
+func (bs blockSorter) Len() int { return len(bs.blocks) }
+
+func (bs blockSorter) Swap(i, j int) {
+ bs.blocks[i], bs.blocks[j] = bs.blocks[j], bs.blocks[i]
}
-func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
+
+func (bs blockSorter) Less(i, j int) bool { return bs.by(bs.blocks[i], bs.blocks[j]) }
func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }
diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go
index dc48332426..ebe660dba9 100644
--- a/core/types/gen_header_json.go
+++ b/core/types/gen_header_json.go
@@ -13,6 +13,7 @@ import (
var _ = (*headerMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (h Header) MarshalJSON() ([]byte, error) {
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
@@ -30,6 +31,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
Nonce BlockNonce `json:"nonce" gencodec:"required"`
+ BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
Hash common.Hash `json:"hash"`
}
var enc Header
@@ -48,10 +50,12 @@ func (h Header) MarshalJSON() ([]byte, error) {
enc.Extra = h.Extra
enc.MixDigest = h.MixDigest
enc.Nonce = h.Nonce
+ enc.BaseFee = (*hexutil.Big)(h.BaseFee)
enc.Hash = h.Hash()
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (h *Header) UnmarshalJSON(input []byte) error {
type Header struct {
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
@@ -69,6 +73,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
+ BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
}
var dec Header
if err := json.Unmarshal(input, &dec); err != nil {
@@ -134,5 +139,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'nonce' for Header")
}
h.Nonce = *dec.Nonce
+ if dec.BaseFee != nil {
+ h.BaseFee = (*big.Int)(dec.BaseFee)
+ }
return nil
}
diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go
index 89379a823a..c22dc8e21e 100644
--- a/core/types/gen_receipt_json.go
+++ b/core/types/gen_receipt_json.go
@@ -25,6 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"`
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex hexutil.Uint `json:"transactionIndex"`
@@ -39,6 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
enc.TxHash = r.TxHash
enc.ContractAddress = r.ContractAddress
enc.GasUsed = hexutil.Uint64(r.GasUsed)
+ enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
enc.BlockHash = r.BlockHash
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
@@ -57,6 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
+ EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
BlockHash *common.Hash `json:"blockHash,omitempty"`
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
@@ -97,6 +100,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'gasUsed' for Receipt")
}
r.GasUsed = uint64(*dec.GasUsed)
+ if dec.EffectiveGasPrice != nil {
+ r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
+ }
if dec.BlockHash != nil {
r.BlockHash = *dec.BlockHash
}
diff --git a/core/types/hashes.go b/core/types/hashes.go
new file mode 100644
index 0000000000..bbcf0446ea
--- /dev/null
+++ b/core/types/hashes.go
@@ -0,0 +1,42 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+)
+
+var (
+ // EmptyRootHash is the known root hash of an empty trie.
+ EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyUncleHash is the known hash of the empty uncle set.
+ EmptyUncleHash = rlpHash([]*Header(nil)) // 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
+
+ // EmptyCodeHash is the known hash of the empty EVM bytecode.
+ EmptyCodeHash = crypto.Keccak256Hash(nil) // c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
+
+ // EmptyTxsHash is the known hash of the empty transaction set.
+ EmptyTxsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyReceiptsHash is the known hash of the empty receipt set.
+ EmptyReceiptsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyWithdrawalsHash is the known hash of the empty withdrawal set.
+ EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+)
diff --git a/core/types/lending_signing.go b/core/types/lending_signing.go
index 6aaaedb481..cce6e607af 100644
--- a/core/types/lending_signing.go
+++ b/core/types/lending_signing.go
@@ -23,8 +23,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
+ "golang.org/x/crypto/sha3"
)
// LendingSigner interface for lending signer transaction
@@ -86,7 +86,7 @@ func LendingSignTx(tx *LendingTransaction, s LendingSigner, prv *ecdsa.PrivateKe
return tx.WithSignature(s, sig)
}
-//LendingTxSigner signer
+// LendingTxSigner signer
type LendingTxSigner struct{}
// Equal compare two signer
@@ -95,9 +95,9 @@ func (lendingsign LendingTxSigner) Equal(s2 LendingSigner) bool {
return ok
}
-//SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1.
+// SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1.
func (lendingsign LendingTxSigner) SignatureValues(tx *LendingTransaction, sig []byte) (r, s, v *big.Int, err error) {
- if len(sig) != 65 {
+ if len(sig) != crypto.SignatureLength {
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
}
r = new(big.Int).SetBytes(sig[:32])
@@ -112,7 +112,7 @@ func (lendingsign LendingTxSigner) LendingCreateHash(tx *LendingTransaction) com
"collateral", tx.CollateralToken().Hex(), "lending", tx.LendingToken().Hex(), "quantity", tx.Quantity(), "term", tx.Term(),
"interest", tx.Interest(), "side", tx.Side, "status", tx.Status(), "type", tx.Type(), "nonce", tx.Nonce())
borrowing := tx.Side() == LendingSideBorrow
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(tx.RelayerAddress().Bytes())
sha.Write(tx.UserAddress().Bytes())
if borrowing {
@@ -140,7 +140,7 @@ func (lendingsign LendingTxSigner) LendingCreateHash(tx *LendingTransaction) com
// LendingCancelHash hash of cancelled lending transaction
func (lendingsign LendingTxSigner) LendingCancelHash(tx *LendingTransaction) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes())
sha.Write([]byte(tx.Status()))
sha.Write(tx.RelayerAddress().Bytes())
@@ -153,7 +153,7 @@ func (lendingsign LendingTxSigner) LendingCancelHash(tx *LendingTransaction) com
// LendingRepayHash hash of cancelled lending transaction
func (lendingsign LendingTxSigner) LendingRepayHash(tx *LendingTransaction) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes())
sha.Write([]byte(tx.Status()))
sha.Write(tx.RelayerAddress().Bytes())
@@ -167,7 +167,7 @@ func (lendingsign LendingTxSigner) LendingRepayHash(tx *LendingTransaction) comm
// LendingTopUpHash hash of cancelled lending transaction
func (lendingsign LendingTxSigner) LendingTopUpHash(tx *LendingTransaction) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes())
sha.Write([]byte(tx.Status()))
sha.Write(tx.RelayerAddress().Bytes())
diff --git a/core/types/order_signing.go b/core/types/order_signing.go
index 50f5591f3b..8fa5625529 100644
--- a/core/types/order_signing.go
+++ b/core/types/order_signing.go
@@ -24,7 +24,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
+ "golang.org/x/crypto/sha3"
)
// OrderSigner interface for order transaction
@@ -86,7 +86,7 @@ func OrderSignTx(tx *OrderTransaction, s OrderSigner, prv *ecdsa.PrivateKey) (*O
return tx.WithSignature(s, sig)
}
-//OrderTxSigner signer
+// OrderTxSigner signer
type OrderTxSigner struct{}
// Equal compare two signer
@@ -95,9 +95,9 @@ func (ordersign OrderTxSigner) Equal(s2 OrderSigner) bool {
return ok
}
-//SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1.
+// SignatureValues returns signature values. This signature needs to be in the [R || S || V] format where V is 0 or 1.
func (ordersign OrderTxSigner) SignatureValues(tx *OrderTransaction, sig []byte) (r, s, v *big.Int, err error) {
- if len(sig) != 65 {
+ if len(sig) != crypto.SignatureLength {
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
}
r = new(big.Int).SetBytes(sig[:32])
@@ -108,7 +108,7 @@ func (ordersign OrderTxSigner) SignatureValues(tx *OrderTransaction, sig []byte)
// OrderCreateHash hash of new order
func (ordersign OrderTxSigner) OrderCreateHash(tx *OrderTransaction) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(tx.ExchangeAddress().Bytes())
sha.Write(tx.UserAddress().Bytes())
sha.Write(tx.BaseToken().Bytes())
@@ -128,7 +128,7 @@ func (ordersign OrderTxSigner) OrderCreateHash(tx *OrderTransaction) common.Hash
// OrderCancelHash hash of cancelled order
func (ordersign OrderTxSigner) OrderCancelHash(tx *OrderTransaction) common.Hash {
- sha := sha3.NewKeccak256()
+ sha := sha3.NewLegacyKeccak256()
sha.Write(tx.OrderHash().Bytes())
sha.Write(common.BigToHash(big.NewInt(int64(tx.Nonce()))).Bytes())
sha.Write(tx.UserAddress().Bytes())
@@ -150,7 +150,7 @@ func (ordersign OrderTxSigner) Hash(tx *OrderTransaction) common.Hash {
return ordersign.OrderCreateHash(tx)
}
-//MarshalSignature encode signature
+// MarshalSignature encode signature
func MarshalSignature(R, S, V *big.Int) ([]byte, error) {
sigBytes1 := common.BigToHash(R).Bytes()
sigBytes2 := common.BigToHash(S).Bytes()
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 76507b5210..f858e8855e 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -38,8 +38,7 @@ var (
receiptStatusSuccessfulRLP = []byte{0x01}
)
-// This error is returned when a typed receipt is decoded, but the string is empty.
-var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
+var errShortTypedReceipt = errors.New("typed receipt too short")
const (
// ReceiptStatusFailed is the status code of a transaction if execution failed.
@@ -60,10 +59,10 @@ type Receipt struct {
Logs []*Log `json:"logs" gencodec:"required"`
// Implementation fields: These fields are added by geth when processing a transaction.
- // They are stored in the chain database.
- TxHash common.Hash `json:"transactionHash" gencodec:"required"`
- ContractAddress common.Address `json:"contractAddress"`
- GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+ TxHash common.Hash `json:"transactionHash" gencodec:"required"`
+ ContractAddress common.Address `json:"contractAddress"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+ EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility
// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
@@ -78,6 +77,7 @@ type receiptMarshaling struct {
Status hexutil.Uint64
CumulativeGasUsed hexutil.Uint64
GasUsed hexutil.Uint64
+ EffectiveGasPrice *hexutil.Big
BlockNumber *hexutil.Big
TransactionIndex hexutil.Uint
}
@@ -90,18 +90,8 @@ type receiptRLP struct {
Logs []*Log
}
-// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
-type v4StoredReceiptRLP struct {
- PostStateOrStatus []byte
- CumulativeGasUsed uint64
- TxHash common.Hash
- ContractAddress common.Address
- Logs []*LogForStorage
- GasUsed uint64
-}
-
-// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
-type v3StoredReceiptRLP struct {
+// receiptStorageRLP is the original storage encoding of a receipt including some unnecessary fields.
+type receiptStorageRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom Bloom
@@ -134,20 +124,33 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
if r.Type == LegacyTxType {
return rlp.Encode(w, data)
}
- // It's an EIP-2718 typed TX receipt.
- if r.Type != AccessListTxType {
- return ErrTxTypeNotSupported
- }
buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf)
buf.Reset()
buf.WriteByte(r.Type)
- if err := rlp.Encode(buf, data); err != nil {
+ if err := r.encodeTyped(data, buf); err != nil {
return err
}
return rlp.Encode(w, buf.Bytes())
}
+// encodeTyped writes the canonical encoding of a typed receipt to w.
+func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
+ w.WriteByte(r.Type)
+ return rlp.Encode(w, data)
+}
+
+// MarshalBinary returns the consensus encoding of the receipt.
+func (r *Receipt) MarshalBinary() ([]byte, error) {
+ if r.Type == LegacyTxType {
+ return rlp.EncodeToBytes(r)
+ }
+ data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
+ var buf bytes.Buffer
+ err := r.encodeTyped(data, &buf)
+ return buf.Bytes(), err
+}
+
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
// from an RLP stream.
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
@@ -163,26 +166,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
}
r.Type = LegacyTxType
return r.setFromRLP(dec)
- case kind == rlp.String:
+ default:
// It's an EIP-2718 typed tx receipt.
b, err := s.Bytes()
if err != nil {
return err
}
- if len(b) == 0 {
- return errEmptyTypedReceipt
+ return r.decodeTyped(b)
+ }
+}
+
+// UnmarshalBinary decodes the consensus encoding of receipts.
+// It supports legacy RLP receipts and EIP-2718 typed receipts.
+func (r *Receipt) UnmarshalBinary(b []byte) error {
+ if len(b) > 0 && b[0] > 0x7f {
+ // It's a legacy receipt decode the RLP
+ var data receiptRLP
+ err := rlp.DecodeBytes(b, &data)
+ if err != nil {
+ return err
+ }
+ r.Type = LegacyTxType
+ return r.setFromRLP(data)
+ }
+ // It's an EIP2718 typed transaction envelope.
+ return r.decodeTyped(b)
+}
+
+// decodeTyped decodes a typed receipt from the canonical format.
+func (r *Receipt) decodeTyped(b []byte) error {
+ if len(b) <= 1 {
+ return errShortTypedReceipt
+ }
+ switch b[0] {
+ case DynamicFeeTxType, AccessListTxType:
+ var data receiptRLP
+ err := rlp.DecodeBytes(b[1:], &data)
+ if err != nil {
+ return err
}
r.Type = b[0]
- if r.Type == AccessListTxType {
- var dec receiptRLP
- if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
- return err
- }
- return r.setFromRLP(dec)
- }
- return ErrTxTypeNotSupported
+ return r.setFromRLP(data)
default:
- return rlp.ErrExpectedList
+ return ErrTxTypeNotSupported
}
}
@@ -241,7 +267,7 @@ type ReceiptForStorage Receipt
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
// into an RLP stream.
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
- enc := &v3StoredReceiptRLP{
+ enc := &receiptStorageRLP{
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
CumulativeGasUsed: r.CumulativeGasUsed,
Bloom: r.Bloom,
@@ -264,17 +290,11 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
if err != nil {
return err
}
- // Try decoding from the newest format for future proofness, then the older one
- // for old nodes that just upgraded. V4 was an intermediate unreleased format so
- // we do need to decode it, but it's not common (try last).
- if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
- return nil
- }
- return decodeV4StoredReceiptRLP(r, blob)
+ return decodeStoredReceiptRLP(r, blob)
}
-func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
- var stored v3StoredReceiptRLP
+func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
+ var stored receiptStorageRLP
if err := rlp.DecodeBytes(blob, &stored); err != nil {
return err
}
@@ -295,36 +315,15 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
return nil
}
-func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
- var stored v4StoredReceiptRLP
- if err := rlp.DecodeBytes(blob, &stored); err != nil {
- return err
- }
- if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
- return err
- }
- r.CumulativeGasUsed = stored.CumulativeGasUsed
- r.TxHash = stored.TxHash
- r.ContractAddress = stored.ContractAddress
- r.GasUsed = stored.GasUsed
- r.Logs = make([]*Log, len(stored.Logs))
- for i, log := range stored.Logs {
- r.Logs[i] = (*Log)(log)
- }
- r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
-
- return nil
-}
-
// Receipts implements DerivableList for receipts.
type Receipts []*Receipt
// Len returns the number of receipts in this list.
-func (r Receipts) Len() int { return len(r) }
+func (rs Receipts) Len() int { return len(rs) }
// GetRlp returns the RLP encoding of one receipt from the list.
-func (r Receipts) GetRlp(i int) []byte {
- bytes, err := rlp.EncodeToBytes(r[i])
+func (rs Receipts) GetRlp(i int) []byte {
+ bytes, err := rlp.EncodeToBytes(rs[i])
if err != nil {
panic(err)
}
@@ -333,42 +332,48 @@ func (r Receipts) GetRlp(i int) []byte {
// DeriveFields fills the receipts with their computed fields based on consensus
// data and contextual infos like containing block and transactions.
-func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
+func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error {
signer := MakeSigner(config, new(big.Int).SetUint64(number))
logIndex := uint(0)
- if len(txs) != len(r) {
+ if len(txs) != len(rs) {
return errors.New("transaction and receipt count mismatch")
}
- for i := 0; i < len(r); i++ {
+ for i := 0; i < len(rs); i++ {
// The transaction type and hash can be retrieved from the transaction itself
- r[i].Type = txs[i].Type()
- r[i].TxHash = txs[i].Hash()
+ rs[i].Type = txs[i].Type()
+ rs[i].TxHash = txs[i].Hash()
+
+ rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
// block location fields
- r[i].BlockHash = hash
- r[i].BlockNumber = new(big.Int).SetUint64(number)
- r[i].TransactionIndex = uint(i)
+ rs[i].BlockHash = hash
+ rs[i].BlockNumber = new(big.Int).SetUint64(number)
+ rs[i].TransactionIndex = uint(i)
// The contract address can be derived from the transaction itself
if txs[i].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i])
- r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
+ rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
+ } else {
+ rs[i].ContractAddress = common.Address{}
}
+
// The used gas can be calculated based on previous r
if i == 0 {
- r[i].GasUsed = r[i].CumulativeGasUsed
+ rs[i].GasUsed = rs[i].CumulativeGasUsed
} else {
- r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
+ rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
}
+
// The derived log fields can simply be set from the block and transaction
- for j := 0; j < len(r[i].Logs); j++ {
- r[i].Logs[j].BlockNumber = number
- r[i].Logs[j].BlockHash = hash
- r[i].Logs[j].TxHash = r[i].TxHash
- r[i].Logs[j].TxIndex = uint(i)
- r[i].Logs[j].Index = logIndex
+ for j := 0; j < len(rs[i].Logs); j++ {
+ rs[i].Logs[j].BlockNumber = number
+ rs[i].Logs[j].BlockHash = hash
+ rs[i].Logs[j].TxHash = rs[i].TxHash
+ rs[i].Logs[j].TxIndex = uint(i)
+ rs[i].Logs[j].Index = logIndex
logIndex++
}
}
diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go
index 1ca5a28647..2fee4fa88f 100644
--- a/core/types/receipt_test.go
+++ b/core/types/receipt_test.go
@@ -18,35 +18,278 @@ package types
import (
"bytes"
+ "encoding/json"
"math"
"math/big"
"reflect"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
+ "github.com/kylelemons/godebug/diff"
+)
+
+var (
+ legacyReceipt = &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ }
+ accessListReceipt = &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ Type: AccessListTxType,
+ }
+ eip1559Receipt = &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ Data: []byte{0x01, 0x00, 0xff},
+ },
+ },
+ Type: DynamicFeeTxType,
+ }
+
+ // Create a few transactions to have receipts for
+ to2 = common.HexToAddress("0x2")
+ to3 = common.HexToAddress("0x3")
+ to4 = common.HexToAddress("0x4")
+ to5 = common.HexToAddress("0x5")
+ to6 = common.HexToAddress("0x6")
+ to7 = common.HexToAddress("0x7")
+ txs = Transactions{
+ NewTx(&LegacyTx{
+ Nonce: 1,
+ Value: big.NewInt(1),
+ Gas: 1,
+ GasPrice: big.NewInt(11),
+ }),
+ NewTx(&LegacyTx{
+ To: &to2,
+ Nonce: 2,
+ Value: big.NewInt(2),
+ Gas: 2,
+ GasPrice: big.NewInt(22),
+ }),
+ NewTx(&AccessListTx{
+ To: &to3,
+ Nonce: 3,
+ Value: big.NewInt(3),
+ Gas: 3,
+ GasPrice: big.NewInt(33),
+ }),
+ // EIP-1559 transactions.
+ NewTx(&DynamicFeeTx{
+ To: &to4,
+ Nonce: 4,
+ Value: big.NewInt(4),
+ Gas: 4,
+ GasTipCap: big.NewInt(44),
+ GasFeeCap: big.NewInt(1044),
+ }),
+ NewTx(&DynamicFeeTx{
+ To: &to5,
+ Nonce: 5,
+ Value: big.NewInt(5),
+ Gas: 5,
+ GasTipCap: big.NewInt(55),
+ GasFeeCap: big.NewInt(1055),
+ }),
+ }
+
+ blockNumber = big.NewInt(1)
+ blockTime = uint64(2)
+ blockHash = common.BytesToHash([]byte{0x03, 0x14})
+
+ // Create the corresponding receipts
+ receipts = Receipts{
+ &Receipt{
+ Status: ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[0].Hash(),
+ TxIndex: 0,
+ BlockHash: blockHash,
+ Index: 0,
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[0].Hash(),
+ TxIndex: 0,
+ BlockHash: blockHash,
+ Index: 1,
+ },
+ },
+ // derived fields:
+ TxHash: txs[0].Hash(),
+ ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
+ GasUsed: 1,
+ EffectiveGasPrice: big.NewInt(11),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 0,
+ },
+ &Receipt{
+ PostState: common.Hash{2}.Bytes(),
+ CumulativeGasUsed: 3,
+ Logs: []*Log{
+ {
+ Address: common.BytesToAddress([]byte{0x22}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[1].Hash(),
+ TxIndex: 1,
+ BlockHash: blockHash,
+ Index: 2,
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x02, 0x22}),
+ Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[1].Hash(),
+ TxIndex: 1,
+ BlockHash: blockHash,
+ Index: 3,
+ },
+ },
+ // derived fields:
+ TxHash: txs[1].Hash(),
+ GasUsed: 2,
+ EffectiveGasPrice: big.NewInt(22),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 1,
+ },
+ &Receipt{
+ Type: AccessListTxType,
+ PostState: common.Hash{3}.Bytes(),
+ CumulativeGasUsed: 6,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[2].Hash(),
+ GasUsed: 3,
+ EffectiveGasPrice: big.NewInt(33),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 2,
+ },
+ &Receipt{
+ Type: DynamicFeeTxType,
+ PostState: common.Hash{4}.Bytes(),
+ CumulativeGasUsed: 10,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[3].Hash(),
+ GasUsed: 4,
+ EffectiveGasPrice: big.NewInt(1044),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 3,
+ },
+ &Receipt{
+ Type: DynamicFeeTxType,
+ PostState: common.Hash{5}.Bytes(),
+ CumulativeGasUsed: 15,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[4].Hash(),
+ GasUsed: 5,
+ EffectiveGasPrice: big.NewInt(1055),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 4,
+ },
+ }
)
func TestDecodeEmptyTypedReceipt(t *testing.T) {
input := []byte{0x80}
var r Receipt
err := rlp.DecodeBytes(input, &r)
- if err != errEmptyTypedReceipt {
+ if err != errShortTypedReceipt {
t.Fatal("wrong error:", err)
}
}
+// Test that we can marshal/unmarshal receipts to/from json without errors.
+// This also confirms that our test receipts contain all the required fields.
+func TestReceiptJSON(t *testing.T) {
+ for i := range receipts {
+ b, err := receipts[i].MarshalJSON()
+ if err != nil {
+ t.Fatal("error marshaling receipt to json:", err)
+ }
+ r := Receipt{}
+ err = r.UnmarshalJSON(b)
+ if err != nil {
+ t.Fatal("error unmarshaling receipt from json:", err)
+ }
+ }
+}
+
+// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
+// though it is required per the spec.
+func TestEffectiveGasPriceNotRequired(t *testing.T) {
+ r := *receipts[0]
+ r.EffectiveGasPrice = nil
+ b, err := r.MarshalJSON()
+ if err != nil {
+ t.Fatal("error marshaling receipt to json:", err)
+ }
+ r2 := Receipt{}
+ err = r2.UnmarshalJSON(b)
+ if err != nil {
+ t.Fatal("error unmarshaling receipt from json:", err)
+ }
+}
+
func TestLegacyReceiptDecoding(t *testing.T) {
tests := []struct {
name string
encode func(*Receipt) ([]byte, error)
}{
- {
- "V4StoredReceiptRLP",
- encodeAsV4StoredReceiptRLP,
- },
{
"V3StoredReceiptRLP",
encodeAsV3StoredReceiptRLP,
@@ -113,23 +356,8 @@ func TestLegacyReceiptDecoding(t *testing.T) {
}
}
-func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
- stored := &v4StoredReceiptRLP{
- PostStateOrStatus: want.statusEncoding(),
- CumulativeGasUsed: want.CumulativeGasUsed,
- TxHash: want.TxHash,
- ContractAddress: want.ContractAddress,
- Logs: make([]*LogForStorage, len(want.Logs)),
- GasUsed: want.GasUsed,
- }
- for i, log := range want.Logs {
- stored.Logs[i] = (*LogForStorage)(log)
- }
- return rlp.EncodeToBytes(stored)
-}
-
func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
- stored := &v3StoredReceiptRLP{
+ stored := &receiptStorageRLP{
PostStateOrStatus: want.statusEncoding(),
CumulativeGasUsed: want.CumulativeGasUsed,
Bloom: want.Bloom,
@@ -149,162 +377,212 @@ func TestDeriveFields(t *testing.T) {
// Create a few transactions to have receipts for
to2 := common.HexToAddress("0x2")
to3 := common.HexToAddress("0x3")
+ to4 := common.HexToAddress("0x4")
+ to5 := common.HexToAddress("0x5")
txs := Transactions{
NewTx(&LegacyTx{
Nonce: 1,
Value: big.NewInt(1),
Gas: 1,
- GasPrice: big.NewInt(1),
+ GasPrice: big.NewInt(11),
}),
NewTx(&LegacyTx{
To: &to2,
Nonce: 2,
Value: big.NewInt(2),
Gas: 2,
- GasPrice: big.NewInt(2),
+ GasPrice: big.NewInt(22),
}),
NewTx(&AccessListTx{
To: &to3,
Nonce: 3,
Value: big.NewInt(3),
Gas: 3,
- GasPrice: big.NewInt(3),
+ GasPrice: big.NewInt(33),
+ }),
+ // EIP-1559 transactions.
+ NewTx(&DynamicFeeTx{
+ To: &to4,
+ Nonce: 4,
+ Value: big.NewInt(4),
+ Gas: 4,
+ GasTipCap: big.NewInt(44),
+ GasFeeCap: big.NewInt(1045),
+ }),
+ NewTx(&DynamicFeeTx{
+ To: &to5,
+ Nonce: 5,
+ Value: big.NewInt(5),
+ Gas: 5,
+ GasTipCap: big.NewInt(56),
+ GasFeeCap: big.NewInt(1055),
}),
}
+
+ blockNumber := big.NewInt(1)
+ blockHash := common.BytesToHash([]byte{0x03, 0x14})
+
// Create the corresponding receipts
receipts := Receipts{
&Receipt{
Status: ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*Log{
- {Address: common.BytesToAddress([]byte{0x11})},
- {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ {
+ Address: common.BytesToAddress([]byte{0x11}),
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[0].Hash(),
+ TxIndex: 0,
+ BlockHash: blockHash,
+ Index: 0,
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x01, 0x11}),
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[0].Hash(),
+ TxIndex: 0,
+ BlockHash: blockHash,
+ Index: 1,
+ },
},
- TxHash: txs[0].Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
- GasUsed: 1,
+ // derived fields:
+ TxHash: txs[0].Hash(),
+ ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
+ GasUsed: 1,
+ EffectiveGasPrice: big.NewInt(11),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 0,
},
&Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*Log{
- {Address: common.BytesToAddress([]byte{0x22})},
- {Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ {
+ Address: common.BytesToAddress([]byte{0x22}),
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[1].Hash(),
+ TxIndex: 1,
+ BlockHash: blockHash,
+ Index: 2,
+ },
+ {
+ Address: common.BytesToAddress([]byte{0x02, 0x22}),
+ // derived fields:
+ BlockNumber: blockNumber.Uint64(),
+ TxHash: txs[1].Hash(),
+ TxIndex: 1,
+ BlockHash: blockHash,
+ Index: 3,
+ },
},
- TxHash: txs[1].Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
- GasUsed: 2,
+ // derived fields:
+ TxHash: txs[1].Hash(),
+ GasUsed: 2,
+ EffectiveGasPrice: big.NewInt(22),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 1,
},
&Receipt{
Type: AccessListTxType,
PostState: common.Hash{3}.Bytes(),
CumulativeGasUsed: 6,
- Logs: []*Log{
- {Address: common.BytesToAddress([]byte{0x33})},
- {Address: common.BytesToAddress([]byte{0x03, 0x33})},
- },
- TxHash: txs[2].Hash(),
- ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
- GasUsed: 3,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[2].Hash(),
+ GasUsed: 3,
+ EffectiveGasPrice: big.NewInt(33),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 2,
+ },
+ &Receipt{
+ Type: DynamicFeeTxType,
+ PostState: common.Hash{4}.Bytes(),
+ CumulativeGasUsed: 10,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[3].Hash(),
+ GasUsed: 4,
+ EffectiveGasPrice: big.NewInt(1044),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 3,
+ },
+ &Receipt{
+ Type: DynamicFeeTxType,
+ PostState: common.Hash{5}.Bytes(),
+ CumulativeGasUsed: 15,
+ Logs: []*Log{},
+ // derived fields:
+ TxHash: txs[4].Hash(),
+ GasUsed: 5,
+ EffectiveGasPrice: big.NewInt(1055),
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ TransactionIndex: 4,
},
}
- // Clear all the computed fields and re-derive them
- number := big.NewInt(1)
- hash := common.BytesToHash([]byte{0x03, 0x14})
- clearComputedFieldsOnReceipts(t, receipts)
- if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
+ // Re-derive receipts.
+ basefee := big.NewInt(1000)
+ derivedReceipts := clearComputedFieldsOnReceipts(receipts)
+ err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), basefee, txs)
+ if err != nil {
t.Fatalf("DeriveFields(...) = %v, want ", err)
}
- // Iterate over all the computed fields and check that they're correct
- signer := MakeSigner(params.TestChainConfig, number)
- logIndex := uint(0)
- for i := range receipts {
- if receipts[i].Type != txs[i].Type() {
- t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
- }
- if receipts[i].TxHash != txs[i].Hash() {
- t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
- }
- if receipts[i].BlockHash != hash {
- t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String())
- }
- if receipts[i].BlockNumber.Cmp(number) != 0 {
- t.Errorf("receipts[%c].BlockNumber = %s, want %s", i, receipts[i].BlockNumber.String(), number.String())
- }
- if receipts[i].TransactionIndex != uint(i) {
- t.Errorf("receipts[%d].TransactionIndex = %d, want %d", i, receipts[i].TransactionIndex, i)
- }
- if receipts[i].GasUsed != txs[i].Gas() {
- t.Errorf("receipts[%d].GasUsed = %d, want %d", i, receipts[i].GasUsed, txs[i].Gas())
- }
- if txs[i].To() != nil && receipts[i].ContractAddress != (common.Address{}) {
- t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (common.Address{}).String())
- }
- from, _ := Sender(signer, txs[i])
- contractAddress := crypto.CreateAddress(from, txs[i].Nonce())
- if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress {
- t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String())
- }
- for j := range receipts[i].Logs {
- if receipts[i].Logs[j].BlockNumber != number.Uint64() {
- t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
- }
- if receipts[i].Logs[j].BlockHash != hash {
- t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
- }
- if receipts[i].Logs[j].TxHash != txs[i].Hash() {
- t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
- }
- if receipts[i].Logs[j].TxIndex != uint(i) {
- t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
- }
- if receipts[i].Logs[j].Index != logIndex {
- t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
- }
- logIndex++
- }
+ // Check diff of receipts against derivedReceipts.
+ r1, err := json.MarshalIndent(receipts, "", " ")
+ if err != nil {
+ t.Fatal("error marshaling input receipts:", err)
+ }
+ r2, err := json.MarshalIndent(derivedReceipts, "", " ")
+ if err != nil {
+ t.Fatal("error marshaling derived receipts:", err)
+ }
+ d := diff.Diff(string(r1), string(r2))
+ if d != "" {
+ t.Fatal("receipts differ:", d)
}
}
-func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
- t.Helper()
-
- for _, receipt := range receipts {
- clearComputedFieldsOnReceipt(t, receipt)
+func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt {
+ r := make([]*Receipt, len(receipts))
+ for i, receipt := range receipts {
+ r[i] = clearComputedFieldsOnReceipt(receipt)
}
+ return r
}
-func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) {
- t.Helper()
-
- receipt.TxHash = common.Hash{}
- receipt.BlockHash = common.Hash{}
- receipt.BlockNumber = big.NewInt(math.MaxUint32)
- receipt.TransactionIndex = math.MaxUint32
- receipt.ContractAddress = common.Address{}
- receipt.GasUsed = 0
-
- clearComputedFieldsOnLogs(t, receipt.Logs)
+func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
+ cpy := *receipt
+ cpy.TxHash = common.Hash{0xff, 0xff, 0x11}
+ cpy.BlockHash = common.Hash{0xff, 0xff, 0x22}
+ cpy.BlockNumber = big.NewInt(math.MaxUint32)
+ cpy.TransactionIndex = math.MaxUint32
+ cpy.ContractAddress = common.Address{0xff, 0xff, 0x33}
+ cpy.GasUsed = 0xffffffff
+ cpy.Logs = clearComputedFieldsOnLogs(receipt.Logs)
+ return &cpy
}
-func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) {
- t.Helper()
-
- for _, log := range logs {
- clearComputedFieldsOnLog(t, log)
+func clearComputedFieldsOnLogs(logs []*Log) []*Log {
+ l := make([]*Log, len(logs))
+ for i, log := range logs {
+ cpy := *log
+ cpy.BlockNumber = math.MaxUint32
+ cpy.BlockHash = common.Hash{}
+ cpy.TxHash = common.Hash{}
+ cpy.TxIndex = math.MaxUint32
+ cpy.Index = math.MaxUint32
+ l[i] = &cpy
}
-}
-
-func clearComputedFieldsOnLog(t *testing.T, log *Log) {
- t.Helper()
-
- log.BlockNumber = math.MaxUint32
- log.BlockHash = common.Hash{}
- log.TxHash = common.Hash{}
- log.TxIndex = math.MaxUint32
- log.Index = math.MaxUint32
+ return l
}
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
@@ -334,3 +612,38 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
check(bundle)
}
}
+
+func TestReceiptUnmarshalBinary(t *testing.T) {
+ // Legacy Receipt
+ legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ gotLegacyReceipt := new(Receipt)
+ if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
+ t.Fatalf("unmarshal binary error: %v", err)
+ }
+ legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
+ if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
+ t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
+ }
+
+ // 2930 Receipt
+ accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ gotAccessListReceipt := new(Receipt)
+ if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
+ t.Fatalf("unmarshal binary error: %v", err)
+ }
+ accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
+ if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
+ t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
+ }
+
+ // 1559 Receipt
+ eip1559RctBinary := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ got1559Receipt := new(Receipt)
+ if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
+ t.Fatalf("unmarshal binary error: %v", err)
+ }
+ eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
+ if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
+ t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
+ }
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 1c02d70537..f0afdca060 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -27,23 +27,25 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp"
)
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
var (
- ErrInvalidSig = errors.New("invalid transaction v, r, s values")
- ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
- ErrInvalidTxType = errors.New("transaction type not valid in this context")
- ErrTxTypeNotSupported = errors.New("transaction type not supported")
- ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
- errShortTypedTx = errors.New("typed transaction too short")
- errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
- errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
- errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
- errEmptyTypedTx = errors.New("empty typed transaction bytes")
- errNoSigner = errors.New("missing signing methods")
+ ErrInvalidSig = errors.New("invalid transaction v, r, s values")
+ ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
+ ErrInvalidTxType = errors.New("transaction type not valid in this context")
+ ErrTxTypeNotSupported = errors.New("transaction type not supported")
+ ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
+ errShortTypedTx = errors.New("typed transaction too short")
+ errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
+ errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
+ errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
+ errNoSigner = errors.New("missing signing methods")
+ ErrFeeCapTooLow = errors.New("fee cap less than base fee")
+
skipNonceDestinationAddress = map[common.Address]bool{
common.XDCXAddrBinary: true,
common.TradingStateAddrBinary: true,
@@ -56,6 +58,7 @@ var (
const (
LegacyTxType = iota
AccessListTxType
+ DynamicFeeTxType
)
// Transaction is an Ethereum transaction.
@@ -88,12 +91,22 @@ type TxData interface {
data() []byte
gas() uint64
gasPrice() *big.Int
+ gasTipCap() *big.Int
+ gasFeeCap() *big.Int
value() *big.Int
nonce() uint64
to() *common.Address
rawSignatureValues() (v, r, s *big.Int)
setSignatureValues(chainID, v, r, s *big.Int)
+
+ // effectiveGasPrice computes the gas price paid by the transaction, given
+ // the inclusion block baseFee.
+ //
+ // Unlike other TxData methods, the returned *big.Int should be an independent
+ // copy of the computed value, i.e. callers are allowed to mutate the result.
+ // Method implementations can use 'dst' to store the result.
+ effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
}
// EncodeRLP implements rlp.Encoder
@@ -143,7 +156,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
tx.setDecoded(&inner, int(rlp.ListSize(size)))
}
return err
- case kind == rlp.String:
+ default:
// It's an EIP-2718 typed TX envelope.
var b []byte
if b, err = s.Bytes(); err != nil {
@@ -154,8 +167,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
tx.setDecoded(inner, len(b))
}
return err
- default:
- return rlp.ErrExpectedList
}
}
@@ -183,14 +194,18 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
// decodeTyped decodes a typed transaction from the canonical format.
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
- if len(b) == 0 {
- return nil, errEmptyTypedTx
+ if len(b) <= 1 {
+ return nil, errShortTypedTx
}
switch b[0] {
case AccessListTxType:
var inner AccessListTx
err := rlp.DecodeBytes(b[1:], &inner)
return &inner, err
+ case DynamicFeeTxType:
+ var inner DynamicFeeTx
+ err := rlp.DecodeBytes(b[1:], &inner)
+ return &inner, err
default:
return nil, ErrTxTypeNotSupported
}
@@ -274,6 +289,12 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
// GasPrice returns the gas price of the transaction.
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
+// GasTipCap returns the gasTipCap per gas of the transaction.
+func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.gasTipCap()) }
+
+// GasFeeCap returns the fee cap per gas of the transaction.
+func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) }
+
// Value returns the ether amount of the transaction.
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
@@ -283,13 +304,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
// To returns the recipient address of the transaction.
// For contract-creation transactions, To returns nil.
func (tx *Transaction) To() *common.Address {
- // Copy the pointed-to address.
- ito := tx.inner.to()
- if ito == nil {
- return nil
- }
- cpy := *ito
- return &cpy
+ return copyAddressPtr(tx.inner.to())
}
func (tx *Transaction) From() *common.Address {
@@ -306,20 +321,75 @@ func (tx *Transaction) From() *common.Address {
return &from
}
+// Cost returns gas * gasPrice + value.
+func (tx *Transaction) Cost() *big.Int {
+ total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
+ total.Add(total, tx.Value())
+ return total
+}
+
// RawSignatureValues returns the V, R, S signature values of the transaction.
// The return values should not be modified by the caller.
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
return tx.inner.rawSignatureValues()
}
-// GasPriceCmp compares the gas prices of two transactions.
-func (tx *Transaction) GasPriceCmp(other *Transaction) int {
- return tx.inner.gasPrice().Cmp(other.inner.gasPrice())
+// GasFeeCapCmp compares the fee cap of two transactions.
+func (tx *Transaction) GasFeeCapCmp(other *Transaction) int {
+ return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap())
}
-// GasPriceIntCmp compares the gas price of the transaction against the given price.
-func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
- return tx.inner.gasPrice().Cmp(other)
+// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap.
+func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int {
+ return tx.inner.gasFeeCap().Cmp(other)
+}
+
+// GasTipCapCmp compares the gasTipCap of two transactions.
+func (tx *Transaction) GasTipCapCmp(other *Transaction) int {
+ return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap())
+}
+
+// GasTipCapIntCmp compares the gasTipCap of the transaction against the given gasTipCap.
+func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
+ return tx.inner.gasTipCap().Cmp(other)
+}
+
+// EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
+// Note: if the effective gasTipCap is negative, this method returns both error
+// the actual negative value, _and_ ErrGasFeeCapTooLow
+func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
+ if baseFee == nil {
+ return tx.GasTipCap(), nil
+ }
+ var err error
+ gasFeeCap := tx.GasFeeCap()
+ if gasFeeCap.Cmp(baseFee) == -1 {
+ err = ErrGasFeeCapTooLow
+ }
+ return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err
+}
+
+// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
+// error in case the effective gasTipCap is negative
+func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
+ effectiveTip, _ := tx.EffectiveGasTip(baseFee)
+ return effectiveTip
+}
+
+// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
+func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
+ if baseFee == nil {
+ return tx.GasTipCapCmp(other)
+ }
+ return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
+}
+
+// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
+func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int {
+ if baseFee == nil {
+ return tx.GasTipCapIntCmp(other)
+ }
+ return tx.EffectiveGasTipValue(baseFee).Cmp(other)
}
// Hash returns the transaction hash.
@@ -350,31 +420,43 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
+func (tx *Transaction) EffectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+ return tx.inner.effectiveGasPrice(dst, baseFee)
+}
+
// AsMessage returns the transaction as a core.Message.
-func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) (Message, error) {
+func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big.Int) (Message, error) {
msg := Message{
nonce: tx.Nonce(),
gasLimit: tx.Gas(),
gasPrice: new(big.Int).Set(tx.GasPrice()),
+ gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
+ gasTipCap: new(big.Int).Set(tx.GasTipCap()),
to: tx.To(),
amount: tx.Value(),
data: tx.Data(),
accessList: tx.AccessList(),
- checkNonce: true,
+ isFake: false,
balanceTokenFee: balanceFee,
}
+ if balanceFee != nil {
+ if blockNumber != nil {
+ if blockNumber.Cmp(common.BlockNumberGas50x) >= 0 {
+ msg.gasPrice = common.GasPrice50x
+ } else if blockNumber.Cmp(common.TIPTRC21Fee) > 0 {
+ msg.gasPrice = common.TRC21GasPrice
+ } else {
+ msg.gasPrice = common.TRC21GasPriceBefore
+ }
+ }
+ } else if baseFee != nil {
+ // If baseFee provided, set gasPrice to effectiveGasPrice.
+ msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
+ }
+
var err error
msg.from, err = Sender(s, tx)
- if balanceFee != nil {
- if number.Cmp(common.BlockNumberGas50x) >= 0 {
- msg.gasPrice = common.GasPrice50x
- } else if number.Cmp(common.TIPTRC21Fee) > 0 {
- msg.gasPrice = common.TRC21GasPrice
- } else {
- msg.gasPrice = common.TRC21GasPriceBefore
- }
- }
return msg, err
}
@@ -390,13 +472,6 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
return &Transaction{inner: cpy, time: tx.time}, nil
}
-// Cost returns gas * gasPrice + value.
-func (tx *Transaction) Cost() *big.Int {
- total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
- total.Add(total, tx.Value())
- return total
-}
-
// TxCost returns gas * gasPrice + value.
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
@@ -406,7 +481,7 @@ func (tx *Transaction) TxCost(number *big.Int) *big.Int {
func (tx *Transaction) IsSpecialTransaction() bool {
to := tx.To()
- return to != nil && (*to == common.RandomizeSMCBinary || *to == common.BlockSignersBinary)
+ return to != nil && (*to == common.BlockSignersBinary || *to == common.RandomizeSMCBinary)
}
func (tx *Transaction) IsTradingTransaction() bool {
@@ -539,7 +614,7 @@ func (tx *Transaction) String() string {
GasPrice: %#x
GasLimit %#x
Value: %#x
- Data: 0x%x
+ Data: %#x
V: %#x
R: %#x
S: %#x
@@ -742,13 +817,15 @@ type Message struct {
amount *big.Int
gasLimit uint64
gasPrice *big.Int
+ gasFeeCap *big.Int
+ gasTipCap *big.Int
data []byte
accessList AccessList
- checkNonce bool
+ isFake bool
balanceTokenFee *big.Int
}
-func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool, balanceTokenFee *big.Int, number *big.Int) Message {
if balanceTokenFee != nil {
gasPrice = common.GetGasPrice(number)
}
@@ -759,9 +836,11 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
amount: amount,
gasLimit: gasLimit,
gasPrice: gasPrice,
+ gasFeeCap: gasFeeCap,
+ gasTipCap: gasTipCap,
data: data,
accessList: accessList,
- checkNonce: checkNonce,
+ isFake: isFake,
balanceTokenFee: balanceTokenFee,
}
}
@@ -770,11 +849,13 @@ func (m Message) From() common.Address { return m.from }
func (m Message) BalanceTokenFee() *big.Int { return m.balanceTokenFee }
func (m Message) To() *common.Address { return m.to }
func (m Message) GasPrice() *big.Int { return m.gasPrice }
+func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap }
+func (m Message) GasTipCap() *big.Int { return m.gasTipCap }
func (m Message) Value() *big.Int { return m.amount }
func (m Message) Gas() uint64 { return m.gasLimit }
func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
-func (m Message) CheckNonce() bool { return m.checkNonce }
+func (m Message) IsFake() bool { return m.isFake }
func (m Message) AccessList() AccessList { return m.accessList }
func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce }
@@ -783,3 +864,16 @@ func (m *Message) SetBalanceTokenFeeForCall() {
m.balanceTokenFee = new(big.Int).SetUint64(m.gasLimit)
m.balanceTokenFee.Mul(m.balanceTokenFee, m.gasPrice)
}
+
+func (m *Message) SetBalanceTokenFee(balanceTokenFee *big.Int) {
+ m.balanceTokenFee = balanceTokenFee
+}
+
+// copyAddressPtr copies an address.
+func copyAddressPtr(a *common.Address) *common.Address {
+ if a == nil {
+ return nil
+ }
+ cpy := *a
+ return &cpy
+}
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index 91403994bf..569350784d 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -1,3 +1,19 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
package types
import (
@@ -14,15 +30,17 @@ type txJSON struct {
Type hexutil.Uint64 `json:"type"`
// Common transaction fields:
- Nonce *hexutil.Uint64 `json:"nonce"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Gas *hexutil.Uint64 `json:"gas"`
- Value *hexutil.Big `json:"value"`
- Data *hexutil.Bytes `json:"input"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
- To *common.Address `json:"to"`
+ Nonce *hexutil.Uint64 `json:"nonce"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
+ MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ Value *hexutil.Big `json:"value"`
+ Data *hexutil.Bytes `json:"input"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+ To *common.Address `json:"to"`
// Access list transaction fields:
ChainID *hexutil.Big `json:"chainId,omitempty"`
@@ -63,6 +81,19 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
enc.V = (*hexutil.Big)(tx.V)
enc.R = (*hexutil.Big)(tx.R)
enc.S = (*hexutil.Big)(tx.S)
+ case *DynamicFeeTx:
+ enc.ChainID = (*hexutil.Big)(tx.ChainID)
+ enc.AccessList = &tx.AccessList
+ enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
+ enc.Gas = (*hexutil.Uint64)(&tx.Gas)
+ enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
+ enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap)
+ enc.Value = (*hexutil.Big)(tx.Value)
+ enc.Data = (*hexutil.Bytes)(&tx.Data)
+ enc.To = t.To()
+ enc.V = (*hexutil.Big)(tx.V)
+ enc.R = (*hexutil.Big)(tx.R)
+ enc.S = (*hexutil.Big)(tx.S)
}
return json.Marshal(&enc)
}
@@ -175,6 +206,63 @@ func (t *Transaction) UnmarshalJSON(input []byte) error {
}
}
+ case DynamicFeeTxType:
+ var itx DynamicFeeTx
+ inner = &itx
+ // Access list is optional for now.
+ if dec.AccessList != nil {
+ itx.AccessList = *dec.AccessList
+ }
+ if dec.ChainID == nil {
+ return errors.New("missing required field 'chainId' in transaction")
+ }
+ itx.ChainID = (*big.Int)(dec.ChainID)
+ if dec.To != nil {
+ itx.To = dec.To
+ }
+ if dec.Nonce == nil {
+ return errors.New("missing required field 'nonce' in transaction")
+ }
+ itx.Nonce = uint64(*dec.Nonce)
+ if dec.MaxPriorityFeePerGas == nil {
+ return errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
+ }
+ itx.GasTipCap = (*big.Int)(dec.MaxPriorityFeePerGas)
+ if dec.MaxFeePerGas == nil {
+ return errors.New("missing required field 'maxFeePerGas' for txdata")
+ }
+ itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas)
+ if dec.Gas == nil {
+ return errors.New("missing required field 'gas' for txdata")
+ }
+ itx.Gas = uint64(*dec.Gas)
+ if dec.Value == nil {
+ return errors.New("missing required field 'value' in transaction")
+ }
+ itx.Value = (*big.Int)(dec.Value)
+ if dec.Data == nil {
+ return errors.New("missing required field 'input' in transaction")
+ }
+ itx.Data = *dec.Data
+ if dec.V == nil {
+ return errors.New("missing required field 'v' in transaction")
+ }
+ itx.V = (*big.Int)(dec.V)
+ if dec.R == nil {
+ return errors.New("missing required field 'r' in transaction")
+ }
+ itx.R = (*big.Int)(dec.R)
+ if dec.S == nil {
+ return errors.New("missing required field 's' in transaction")
+ }
+ itx.S = (*big.Int)(dec.S)
+ withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
+ if withSignature {
+ if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
+ return err
+ }
+ }
+
default:
return ErrTxTypeNotSupported
}
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index f4174dae48..ff8c4b530a 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -42,7 +42,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer
switch {
case config.IsEIP1559(blockNumber):
- signer = NewEIP2930Signer(config.ChainId)
+ signer = NewLondonSigner(config.ChainId)
case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainId)
case config.IsHomestead(blockNumber):
@@ -63,7 +63,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
func LatestSigner(config *params.ChainConfig) Signer {
if config.ChainId != nil {
if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
- return NewEIP2930Signer(config.ChainId)
+ return NewLondonSigner(config.ChainId)
}
if config.EIP155Block != nil {
return NewEIP155Signer(config.ChainId)
@@ -83,7 +83,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
if chainID == nil {
return HomesteadSigner{}
}
- return NewEIP2930Signer(chainID)
+ return NewLondonSigner(chainID)
}
// SignTx signs the transaction using the given signer and private key.
@@ -170,6 +170,72 @@ type Signer interface {
Equal(Signer) bool
}
+type londonSigner struct{ eip2930Signer }
+
+// NewLondonSigner returns a signer that accepts
+// - EIP-1559 dynamic fee transactions
+// - EIP-2930 access list transactions,
+// - EIP-155 replay protected transactions, and
+// - legacy Homestead transactions.
+func NewLondonSigner(chainId *big.Int) Signer {
+ return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}
+}
+
+func (s londonSigner) Sender(tx *Transaction) (common.Address, error) {
+ if tx.Type() != DynamicFeeTxType {
+ return s.eip2930Signer.Sender(tx)
+ }
+ V, R, S := tx.RawSignatureValues()
+ // DynamicFee txs are defined to use 0 and 1 as their recovery
+ // id, add 27 to become equivalent to unprotected Homestead signatures.
+ V = new(big.Int).Add(V, big.NewInt(27))
+ if tx.ChainId().Cmp(s.chainId) != 0 {
+ return common.Address{}, ErrInvalidChainId
+ }
+ return recoverPlain(s.Hash(tx), R, S, V, true)
+}
+
+func (s londonSigner) Equal(s2 Signer) bool {
+ x, ok := s2.(londonSigner)
+ return ok && x.chainId.Cmp(s.chainId) == 0
+}
+
+func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
+ txdata, ok := tx.inner.(*DynamicFeeTx)
+ if !ok {
+ return s.eip2930Signer.SignatureValues(tx, sig)
+ }
+ // Check that chain ID of tx matches the signer. We also accept ID zero here,
+ // because it indicates that the chain ID was not specified in the tx.
+ if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
+ return nil, nil, nil, ErrInvalidChainId
+ }
+ R, S, _ = decodeSignature(sig)
+ V = big.NewInt(int64(sig[64]))
+ return R, S, V, nil
+}
+
+// Hash returns the hash to be signed by the sender.
+// It does not uniquely identify the transaction.
+func (s londonSigner) Hash(tx *Transaction) common.Hash {
+ if tx.Type() != DynamicFeeTxType {
+ return s.eip2930Signer.Hash(tx)
+ }
+ return prefixedRlpHash(
+ tx.Type(),
+ []interface{}{
+ s.chainId,
+ tx.Nonce(),
+ tx.GasTipCap(),
+ tx.GasFeeCap(),
+ tx.Gas(),
+ tx.To(),
+ tx.Value(),
+ tx.Data(),
+ tx.AccessList(),
+ })
+}
+
type eip2930Signer struct{ EIP155Signer }
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
@@ -193,8 +259,8 @@ func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
case LegacyTxType:
return s.EIP155Signer.Sender(tx)
case AccessListTxType:
- // ACL txs are defined to use 0 and 1 as their recovery id, add
- // 27 to become equivalent to unprotected Homestead signatures.
+ // AL txs are defined to use 0 and 1 as their recovery
+ // id, add 27 to become equivalent to unprotected Homestead signatures.
V = new(big.Int).Add(V, big.NewInt(27))
default:
return common.Address{}, ErrTxTypeNotSupported
@@ -411,7 +477,7 @@ func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (commo
}
// encode the snature in uncompressed format
r, s := R.Bytes(), S.Bytes()
- sig := make([]byte, 65)
+ sig := make([]byte, crypto.SignatureLength)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = V
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index b5b84be08c..e43e55f79b 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -76,7 +76,7 @@ func TestDecodeEmptyTypedTx(t *testing.T) {
input := []byte{0x80}
var tx Transaction
err := rlp.DecodeBytes(input, &tx)
- if err != errEmptyTypedTx {
+ if err != errShortTypedTx {
t.Fatal("wrong error:", err)
}
}
diff --git a/core/types/access_list_tx.go b/core/types/tx_access_list.go
similarity index 92%
rename from core/types/access_list_tx.go
rename to core/types/tx_access_list.go
index f80044e108..f2d4ee4881 100644
--- a/core/types/access_list_tx.go
+++ b/core/types/tx_access_list.go
@@ -59,7 +59,7 @@ type AccessListTx struct {
func (tx *AccessListTx) copy() TxData {
cpy := &AccessListTx{
Nonce: tx.Nonce,
- To: tx.To, // TODO: copy pointed-to address
+ To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are copied below.
@@ -94,18 +94,22 @@ func (tx *AccessListTx) copy() TxData {
}
// accessors for innerTx.
-
func (tx *AccessListTx) txType() byte { return AccessListTxType }
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
-func (tx *AccessListTx) protected() bool { return true }
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
+func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
+func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
func (tx *AccessListTx) to() *common.Address { return tx.To }
+func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+ return dst.Set(tx.GasPrice)
+}
+
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}
diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go
new file mode 100644
index 0000000000..0961ac1e78
--- /dev/null
+++ b/core/types/tx_dynamic_fee.go
@@ -0,0 +1,114 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+)
+
+type DynamicFeeTx struct {
+ ChainID *big.Int
+ Nonce uint64
+ GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas
+ GasFeeCap *big.Int // a.k.a. maxFeePerGas
+ Gas uint64
+ To *common.Address `rlp:"nil"` // nil means contract creation
+ Value *big.Int
+ Data []byte
+ AccessList AccessList
+
+ // Signature values
+ V *big.Int `json:"v" gencodec:"required"`
+ R *big.Int `json:"r" gencodec:"required"`
+ S *big.Int `json:"s" gencodec:"required"`
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *DynamicFeeTx) copy() TxData {
+ cpy := &DynamicFeeTx{
+ Nonce: tx.Nonce,
+ To: copyAddressPtr(tx.To),
+ Data: common.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are copied below.
+ AccessList: make(AccessList, len(tx.AccessList)),
+ Value: new(big.Int),
+ ChainID: new(big.Int),
+ GasTipCap: new(big.Int),
+ GasFeeCap: new(big.Int),
+ V: new(big.Int),
+ R: new(big.Int),
+ S: new(big.Int),
+ }
+ copy(cpy.AccessList, tx.AccessList)
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.ChainID != nil {
+ cpy.ChainID.Set(tx.ChainID)
+ }
+ if tx.GasTipCap != nil {
+ cpy.GasTipCap.Set(tx.GasTipCap)
+ }
+ if tx.GasFeeCap != nil {
+ cpy.GasFeeCap.Set(tx.GasFeeCap)
+ }
+ if tx.V != nil {
+ cpy.V.Set(tx.V)
+ }
+ if tx.R != nil {
+ cpy.R.Set(tx.R)
+ }
+ if tx.S != nil {
+ cpy.S.Set(tx.S)
+ }
+ return cpy
+}
+
+// accessors for innerTx.
+func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
+func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
+func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
+func (tx *DynamicFeeTx) data() []byte { return tx.Data }
+func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
+func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
+func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
+func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
+func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
+func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
+func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
+
+func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+ if baseFee == nil {
+ return dst.Set(tx.GasFeeCap)
+ }
+ tip := dst.Sub(tx.GasFeeCap, baseFee)
+ if tip.Cmp(tx.GasTipCap) > 0 {
+ tip.Set(tx.GasTipCap)
+ }
+ return tip.Add(tip, baseFee)
+}
+
+func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
+ return tx.V, tx.R, tx.S
+}
+
+func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) {
+ tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
+}
diff --git a/core/types/legacy_tx.go b/core/types/tx_legacy.go
similarity index 92%
rename from core/types/legacy_tx.go
rename to core/types/tx_legacy.go
index 146a1e2877..752aff10dd 100644
--- a/core/types/legacy_tx.go
+++ b/core/types/tx_legacy.go
@@ -62,7 +62,7 @@ func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPric
func (tx *LegacyTx) copy() TxData {
cpy := &LegacyTx{
Nonce: tx.Nonce,
- To: tx.To, // TODO: copy pointed-to address
+ To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
// These are initialized below.
@@ -98,10 +98,16 @@ func (tx *LegacyTx) accessList() AccessList { return nil }
func (tx *LegacyTx) data() []byte { return tx.Data }
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
+func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
+func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
func (tx *LegacyTx) to() *common.Address { return tx.To }
+func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+ return dst.Set(tx.GasPrice)
+}
+
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}
diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go
index 97dd59fac8..db7d1022b0 100644
--- a/core/vm/access_list_tracer.go
+++ b/core/vm/access_list_tracer.go
@@ -61,16 +61,14 @@ func (al accessList) equal(other accessList) bool {
if len(al) != len(other) {
return false
}
+ // Given that len(al) == len(other), we only need to check that
+ // all the items from al are in other.
for addr := range al {
if _, ok := other[addr]; !ok {
return false
}
}
- for addr := range other {
- if _, ok := al[addr]; !ok {
- return false
- }
- }
+
// Accounts match, cross reference the storage slots too
for addr, slots := range al {
otherslots := other[addr]
@@ -78,16 +76,13 @@ func (al accessList) equal(other accessList) bool {
if len(slots) != len(otherslots) {
return false
}
+ // Given that len(slots) == len(otherslots), we only need to check that
+ // all the items from slots are in otherslots.
for hash := range slots {
if _, ok := otherslots[hash]; !ok {
return false
}
}
- for hash := range otherslots {
- if _, ok := slots[hash]; !ok {
- return false
- }
- }
}
return true
}
diff --git a/core/vm/analysis.go b/core/vm/analysis.go
index 3733bab6a7..4aa8cfe70f 100644
--- a/core/vm/analysis.go
+++ b/core/vm/analysis.go
@@ -76,7 +76,7 @@ func codeBitmapInternal(code, bits bitvec) bitvec {
for pc := uint64(0); pc < uint64(len(code)); {
op := OpCode(code[pc])
pc++
- if op < PUSH1 || op > PUSH32 {
+ if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false).
continue
}
numbits := op - PUSH1 + 1
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 2be61b51d0..95dd59ee06 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -93,16 +93,25 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
if OpCode(c.Code[udest]) != JUMPDEST {
return false
}
- // Do we have it locally already?
+ return c.isCode(udest)
+}
+
+// isCode returns true if the provided PC location is an actual opcode, as
+// opposed to a data-segment following a PUSHN operation.
+func (c *Contract) isCode(udest uint64) bool {
+ // Do we already have an analysis laying around?
if c.analysis != nil {
return c.analysis.codeSegment(udest)
}
- // If we have the code hash (but no analysis), we should look into the
- // parent analysis map and see if the analysis has been made previously
+ // Do we have a contract hash already?
+ // If we do have a hash, that means it's a 'regular' contract. For regular
+ // contracts ( not temporary initcode), we store the analysis in a map
if c.CodeHash != (common.Hash{}) {
+ // Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]
if !exist {
// Do the analysis and save in parent context
+ // We do not need to store it in c.analysis
analysis = codeBitmap(c.Code)
c.jumpdests[c.CodeHash] = analysis
}
@@ -114,7 +123,9 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
// in state trie. In that case, we do an analysis, and save it locally, so
// we don't have to recalculate it for every JUMP instruction in the execution
// However, we don't save it within the parent context
- c.analysis = codeBitmap(c.Code)
+ if c.analysis == nil {
+ c.analysis = codeBitmap(c.Code)
+ }
return c.analysis.codeSegment(udest)
}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 175db51a87..7da6949ded 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -29,8 +29,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/crypto/blake2b"
"github.com/XinFinOrg/XDPoSChain/crypto/bn256"
"github.com/XinFinOrg/XDPoSChain/params"
-
- //lint:ignore SA1019 Needed for precompile
"golang.org/x/crypto/ripemd160"
)
@@ -58,7 +56,7 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
@@ -75,7 +73,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
@@ -91,7 +89,19 @@ var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
- common.BytesToAddress([]byte{5}): &bigModExp{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+}
+
+var PrecompiledContractsEIP1559 = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
@@ -99,6 +109,7 @@ var PrecompiledContractsXDCv2 = map[common.Address]PrecompiledContract{
}
var (
+ PrecompiledAddressesEIP1559 []common.Address
PrecompiledAddressesXDCv2 []common.Address
PrecompiledAddressesIstanbul []common.Address
PrecompiledAddressesByzantium []common.Address
@@ -110,7 +121,7 @@ func init() {
PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k)
}
for k := range PrecompiledContractsByzantium {
- PrecompiledAddressesHomestead = append(PrecompiledAddressesByzantium, k)
+ PrecompiledAddressesByzantium = append(PrecompiledAddressesByzantium, k)
}
for k := range PrecompiledContractsIstanbul {
PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k)
@@ -118,11 +129,16 @@ func init() {
for k := range PrecompiledContractsXDCv2 {
PrecompiledAddressesXDCv2 = append(PrecompiledAddressesXDCv2, k)
}
+ for k := range PrecompiledContractsEIP1559 {
+ PrecompiledAddressesEIP1559 = append(PrecompiledAddressesEIP1559, k)
+ }
}
// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
+ case rules.IsEIP1559:
+ return PrecompiledAddressesEIP1559
case rules.IsXDCxDisable:
return PrecompiledAddressesXDCv2
case rules.IsIstanbul:
@@ -135,12 +151,29 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
}
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
-func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
- gas := p.RequiredGas(input)
- if contract.UseGas(gas) {
- return p.Run(input)
+// It returns
+// - the returned bytes,
+// - the _remaining_ gas,
+// - any error that occurred
+func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+ if evm != nil {
+ if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) {
+ switch p := p.(type) {
+ case *XDCxEpochPrice:
+ p.SetTradingState(evm.tradingStateDB)
+ case *XDCxLastPrice:
+ p.SetTradingState(evm.tradingStateDB)
+ }
+ }
}
- return nil, ErrOutOfGas
+
+ gasCost := p.RequiredGas(input)
+ if suppliedGas < gasCost {
+ return nil, 0, ErrOutOfGas
+ }
+ suppliedGas -= gasCost
+ output, err := p.Run(input)
+ return output, suppliedGas, err
}
// ECRECOVER implemented as a native contract.
@@ -167,7 +200,7 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
}
// We must make sure not to modify the 'input', so placing the 'v' along with
// the signature needs to be done on a new allocation
- sig := make([]byte, 65)
+ sig := make([]byte, crypto.SignatureLength)
copy(sig, input[64:128])
sig[64] = v
// v needs to be at the end for libsecp256k1
@@ -227,13 +260,19 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
}
// bigModExp implements a native big integer exponential modular operation.
-type bigModExp struct{}
+type bigModExp struct {
+ eip2565 bool
+}
var (
+ big0 = big.NewInt(0)
big1 = big.NewInt(1)
+ big3 = big.NewInt(3)
big4 = big.NewInt(4)
+ big7 = big.NewInt(7)
big8 = big.NewInt(8)
big16 = big.NewInt(16)
+ big20 = big.NewInt(20)
big32 = big.NewInt(32)
big64 = big.NewInt(64)
big96 = big.NewInt(96)
@@ -243,6 +282,35 @@ var (
big199680 = big.NewInt(199680)
)
+// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
+//
+// def mult_complexity(x):
+//
+// if x <= 64: return x ** 2
+// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
+// else: return x ** 2 // 16 + 480 * x - 199680
+//
+// where is x is max(length_of_MODULUS, length_of_BASE)
+func modexpMultComplexity(x *big.Int) *big.Int {
+ switch {
+ case x.Cmp(big64) <= 0:
+ x.Mul(x, x) // x ** 2
+ case x.Cmp(big1024) <= 0:
+ // (x ** 2 // 4 ) + ( 96 * x - 3072)
+ x = new(big.Int).Add(
+ new(big.Int).Div(new(big.Int).Mul(x, x), big4),
+ new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
+ )
+ default:
+ // (x ** 2 // 16) + (480 * x - 199680)
+ x = new(big.Int).Add(
+ new(big.Int).Div(new(big.Int).Mul(x, x), big16),
+ new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
+ )
+ }
+ return x
+}
+
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bigModExp) RequiredGas(input []byte) uint64 {
var (
@@ -277,25 +345,36 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
adjExpLen.Mul(big8, adjExpLen)
}
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
-
// Calculate the gas cost of the operation
gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
- switch {
- case gas.Cmp(big64) <= 0:
+ if c.eip2565 {
+ // EIP-2565 has three changes
+ // 1. Different multComplexity (inlined here)
+ // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
+ //
+ // def mult_complexity(x):
+ // ceiling(x/8)^2
+ //
+ // where is x is max(length_of_MODULUS, length_of_BASE)
+ gas = gas.Add(gas, big7)
+ gas = gas.Div(gas, big8)
gas.Mul(gas, gas)
- case gas.Cmp(big1024) <= 0:
- gas = new(big.Int).Add(
- new(big.Int).Div(new(big.Int).Mul(gas, gas), big4),
- new(big.Int).Sub(new(big.Int).Mul(big96, gas), big3072),
- )
- default:
- gas = new(big.Int).Add(
- new(big.Int).Div(new(big.Int).Mul(gas, gas), big16),
- new(big.Int).Sub(new(big.Int).Mul(big480, gas), big199680),
- )
+
+ gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ // 2. Different divisor (`GQUADDIVISOR`) (3)
+ gas.Div(gas, big3)
+ if gas.BitLen() > 64 {
+ return math.MaxUint64
+ }
+ // 3. Minimum price of 200 gas
+ if gas.Uint64() < 200 {
+ return 200
+ }
+ return gas.Uint64()
}
+ gas = modexpMultComplexity(gas)
gas.Mul(gas, math.BigMax(adjExpLen, big1))
- gas.Div(gas, new(big.Int).SetUint64(params.ModExpQuadCoeffDiv))
+ gas.Div(gas, big20)
if gas.BitLen() > 64 {
return math.MaxUint64
@@ -323,12 +402,19 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
+ v []byte
)
- if mod.BitLen() == 0 {
+ switch {
+ case mod.BitLen() == 0:
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil
+ case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
+ // If base == 1, then we can just return base % mod (if mod >= 1, which it is)
+ v = base.Mod(base, mod).Bytes()
+ default:
+ v = base.Exp(base, exp, mod).Bytes()
}
- return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
+ return common.LeftPadBytes(v, int(modLen)), nil
}
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
@@ -489,10 +575,10 @@ func (c *ringSignatureVerifier) RequiredGas(input []byte) uint64 {
func (c *ringSignatureVerifier) Run(proof []byte) ([]byte, error) {
der, err := privacy.Deserialize(proof)
if err != nil {
- return []byte{}, errors.New("Fail to deserialize proof")
+ return []byte{}, errors.New("fail to deserialize proof")
}
if !privacy.Verify(der, false) {
- return []byte{}, errors.New("Fail to verify ring signature")
+ return []byte{}, errors.New("fail to verify ring signature")
}
return []byte{}, nil
}
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index 4d8a4c762f..22fe467c3f 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -18,15 +18,13 @@ package vm
import (
"bytes"
+ "encoding/json"
"fmt"
"math/big"
+ "os"
"reflect"
"testing"
- "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/params"
-
"github.com/XinFinOrg/XDPoSChain/common"
)
@@ -52,6 +50,25 @@ type precompiledFailureTest struct {
name string
}
+// allPrecompiles does not map to the actual set of precompiles, as it also contains
+// repriced versions of precompiles at certain slots
+var allPrecompiles = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
+ common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+ common.BytesToAddress([]byte{30}): &ringSignatureVerifier{},
+ common.BytesToAddress([]byte{40}): &bulletproofVerifier{},
+ common.BytesToAddress([]byte{41}): &XDCxLastPrice{},
+ common.BytesToAddress([]byte{42}): &XDCxEpochPrice{},
+}
+
// modexpTests are the test and benchmark data for the modexp precompiled contract.
var modexpTests = []precompiledTest{
{
@@ -494,12 +511,11 @@ var blake2FTests = []precompiledTest{
}
func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
- p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
+ p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
- contract := NewContract(AccountRef(common.HexToAddress("1337")),
- nil, new(big.Int), p.RequiredGas(in))
- t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
- if res, err := RunPrecompiledContract(p, in, contract); err != nil {
+ gas := p.RequiredGas(in)
+ t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
+ if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.expected {
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
@@ -513,21 +529,12 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
}
func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) {
- db := rawdb.NewMemoryDatabase()
- stateCache := tradingstate.NewDatabase(db)
- tradingStateDB, _ := tradingstate.New(common.Hash{}, stateCache)
- tradingStateDB.SetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTLastPrice)
- tradingStateDB.SetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTEpochPrice)
-
- evm := NewEVM(Context{BlockNumber: common.Big1}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{})
contractAddr := common.HexToAddress(addr)
- p := PrecompiledContractsByzantium[contractAddr]
+ p := allPrecompiles[contractAddr]
in := common.Hex2Bytes(test.input)
- contract := NewContract(AccountRef(common.HexToAddress("1337")),
- nil, new(big.Int), p.RequiredGas(in))
- contract.SetCallCode(&contractAddr, common.Hash{}, []byte{})
- t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
- if res, err := run(evm, contract, in, false); err != nil {
+ gas := p.RequiredGas(in)
+ t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
+ if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.expected {
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
@@ -536,16 +543,12 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) {
}
func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) {
- evm := NewEVM(Context{BlockNumber: common.Big1}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{})
-
contractAddr := common.HexToAddress(addr)
- p := PrecompiledContractsByzantium[contractAddr]
+ p := allPrecompiles[contractAddr]
in := common.Hex2Bytes(test.input)
- contract := NewContract(AccountRef(common.HexToAddress("1337")),
- nil, new(big.Int), p.RequiredGas(in))
- contract.SetCallCode(&contractAddr, common.Hash{}, []byte{})
- t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
- if res, err := run(evm, contract, in, false); err != nil {
+ gas := p.RequiredGas(in)
+ t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) {
+ if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.expected {
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
@@ -554,12 +557,12 @@ func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *
}
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
- p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
+ p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
- contract := NewContract(AccountRef(common.HexToAddress("1337")),
- nil, new(big.Int), p.RequiredGas(in)-1)
- t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
- _, err := RunPrecompiledContract(p, in, contract)
+ gas := p.RequiredGas(in) - 1
+
+ t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
+ _, _, err := RunPrecompiledContract(nil, p, in, gas)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@@ -572,13 +575,11 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
}
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
- p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
+ p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
- contract := NewContract(AccountRef(common.HexToAddress("31337")),
- nil, new(big.Int), p.RequiredGas(in))
-
+ gas := p.RequiredGas(in)
t.Run(test.name, func(t *testing.T) {
- _, err := RunPrecompiledContract(p, in, contract)
+ _, _, err := RunPrecompiledContract(nil, p, in, gas)
if !reflect.DeepEqual(err, test.expectedError) {
t.Errorf("Expected error [%v], got [%v]", test.expectedError, err)
}
@@ -594,11 +595,9 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
if test.noBenchmark {
return
}
- p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
+ p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
reqGas := p.RequiredGas(in)
- contract := NewContract(AccountRef(common.HexToAddress("1337")),
- nil, new(big.Int), reqGas)
var (
res []byte
@@ -606,12 +605,11 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
data = make([]byte, len(in))
)
- bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(bench *testing.B) {
+ bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, reqGas), func(bench *testing.B) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- contract.Gas = reqGas
copy(data, in)
- res, err = RunPrecompiledContract(p, data, contract)
+ res, _, err = RunPrecompiledContract(nil, p, data, reqGas)
}
bench.StopTimer()
//Check if it is correct
@@ -620,7 +618,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
return
}
if common.Bytes2Hex(res) != test.expected {
- bench.Error(fmt.Sprintf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)))
+ bench.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
return
}
})
@@ -680,6 +678,9 @@ func BenchmarkPrecompiledModExp(bench *testing.B) {
}
}
+func TestPrecompiledModExpEip2565(t *testing.T) { testJson("modexp_eip2565", "f5", t) }
+func BenchmarkPrecompiledModExpEip2565(b *testing.B) { benchJson("modexp_eip2565", "f5", b) }
+
// Tests the sample inputs from the elliptic curve addition EIP 213.
func TestPrecompiledBn256Add(t *testing.T) {
for _, test := range bn256AddTests {
@@ -823,5 +824,34 @@ func TestPrecompiledEcrecover(t *testing.T) {
for _, test := range ecRecoverTests {
testPrecompiled("01", test, t)
}
-
+}
+
+func testJson(name, addr string, t *testing.T) {
+ tests, err := loadJson(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, test := range tests {
+ testPrecompiled(addr, test, t)
+ }
+}
+
+func benchJson(name, addr string, b *testing.B) {
+ tests, err := loadJson(name)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for _, test := range tests {
+ benchmarkPrecompiled(addr, test, b)
+ }
+}
+
+func loadJson(name string) ([]precompiledTest, error) {
+ data, err := os.ReadFile(fmt.Sprintf("testdata/precompiles/%v.json", name))
+ if err != nil {
+ return nil, err
+ }
+ var testcases []precompiledTest
+ err = json.Unmarshal(data, &testcases)
+ return testcases, err
}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 0d7cc71f11..532611dc94 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -26,11 +26,14 @@ import (
var activators = map[int]func(*JumpTable){
3855: enable3855,
+ 3860: enable3860,
+ 3529: enable3529,
3198: enable3198,
2929: enable2929,
2200: enable2200,
1884: enable1884,
1344: enable1344,
+ 1153: enable1153,
}
// EnableEIP enables the given EIP on the config.
@@ -104,28 +107,28 @@ func enable2929(jt *JumpTable) {
jt[SLOAD].constantGas = 0
jt[SLOAD].dynamicGas = gasSLoadEIP2929
- jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
- jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
- jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
- jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
+ jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
- jt[CALL].constantGas = WarmStorageReadCostEIP2929
+ jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
jt[CALL].dynamicGas = gasCallEIP2929
- jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
+ jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
- jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
+ jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
- jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
+ jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
// This was previously part of the dynamic cost, but we're using it as a constantGas
@@ -134,6 +137,15 @@ func enable2929(jt *JumpTable) {
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
}
+// enable3529 enabled "EIP-3529: Reduction in refunds":
+// - Removes refunds for selfdestructs
+// - Reduces refunds for SSTORE
+// - Reduces max refunds to 20% gas
+func enable3529(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP3529
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
+}
+
// enable3198 applies EIP-3198 (BASEFEE Opcode)
// - Adds an opcode that returns the current block's base fee.
func enable3198(jt *JumpTable) {
@@ -146,9 +158,48 @@ func enable3198(jt *JumpTable) {
}
}
+// enable1153 applies EIP-1153 "Transient Storage"
+// - Adds TLOAD that reads from transient storage
+// - Adds TSTORE that writes to transient storage
+func enable1153(jt *JumpTable) {
+ jt[TLOAD] = &operation{
+ execute: opTload,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ }
+
+ jt[TSTORE] = &operation{
+ execute: opTstore,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ }
+}
+
+// opTload implements TLOAD opcode
+func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ loc := scope.Stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
+ loc.SetBytes(val.Bytes())
+ return nil, nil
+}
+
+// opTstore implements TSTORE opcode
+func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ loc := scope.Stack.pop()
+ val := scope.Stack.pop()
+ interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
+ return nil, nil
+}
+
// opBaseFee implements BASEFEE opcode
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
- baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
+ baseFee, _ := uint256.FromBig(common.BaseFee)
callContext.Stack.push(baseFee)
return nil, nil
}
@@ -169,3 +220,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext)
callContext.Stack.push(new(uint256.Int))
return nil, nil
}
+
+// ebnable3860 enables "EIP-3860: Limit and meter initcode"
+// https://eips.ethereum.org/EIPS/eip-3860
+func enable3860(jt *JumpTable) {
+ jt[CREATE].dynamicGas = gasCreateEip3860
+ jt[CREATE2].dynamicGas = gasCreate2Eip3860
+}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 02ce2a678b..9f98dae2f7 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -29,6 +29,7 @@ var (
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
ErrContractAddressCollision = errors.New("contract address collision")
ErrExecutionReverted = errors.New("execution reverted")
+ ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
ErrInvalidJump = errors.New("invalid jump destination")
ErrWriteProtection = errors.New("write protection")
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 4f61fc3c80..1eff3428db 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -17,7 +17,6 @@
package vm
import (
- "errors"
"math/big"
"sync/atomic"
"time"
@@ -26,6 +25,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/holiman/uint256"
)
// emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -45,6 +45,8 @@ type (
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
+ case evm.chainRules.IsEIP1559:
+ precompiles = PrecompiledContractsEIP1559
case evm.chainRules.IsXDCxDisable:
precompiles = PrecompiledContractsXDCv2
case evm.chainRules.IsIstanbul:
@@ -58,61 +60,9 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
return p, ok
}
-func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) {
- var precompiles map[common.Address]PrecompiledContract
- switch {
- case evm.chainRules.IsXDCxDisable:
- precompiles = PrecompiledContractsXDCv2
- case evm.chainRules.IsIstanbul && evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber):
- precompiles = PrecompiledContractsIstanbul
- case evm.chainRules.IsByzantium:
- precompiles = PrecompiledContractsByzantium
- default:
- precompiles = PrecompiledContractsHomestead
- }
- p, ok := precompiles[addr]
- return p, ok
-}
-
-// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
-func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
- if contract.CodeAddr != nil {
- if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile {
- if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) {
- switch p := p.(type) {
- case *XDCxEpochPrice:
- p.SetTradingState(evm.tradingStateDB)
- case *XDCxLastPrice:
- p.SetTradingState(evm.tradingStateDB)
- }
- }
- return RunPrecompiledContract(p, input, contract)
- }
- }
- if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) {
- for _, interpreter := range evm.interpreters {
- if interpreter.CanRun(contract.Code) {
- if evm.interpreter != interpreter {
- // Ensure that the interpreter pointer is set back
- // to its current value upon return.
- defer func(i Interpreter) {
- evm.interpreter = i
- }(evm.interpreter)
- evm.interpreter = interpreter
- }
- return interpreter.Run(contract, input, readOnly)
- }
- }
- } else {
- return evm.interpreter.Run(contract, input, false)
- }
-
- return nil, errors.New("no compatible interpreter")
-}
-
-// Context provides the EVM with auxiliary information. Once provided
+// BlockContext provides the EVM with auxiliary information. Once provided
// it shouldn't be modified.
-type Context struct {
+type BlockContext struct {
// CanTransfer returns whether the account contains
// sufficient ether to transfer the value
CanTransfer CanTransferFunc
@@ -121,19 +71,24 @@ type Context struct {
// GetHash returns the hash corresponding to n
GetHash GetHashFunc
- // Message information
- Origin common.Address // Provides information for ORIGIN
- GasPrice *big.Int // Provides information for GASPRICE
-
// Block information
Coinbase common.Address // Provides information for COINBASE
GasLimit uint64 // Provides information for GASLIMIT
BlockNumber *big.Int // Provides information for NUMBER
Time *big.Int // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY
+ BaseFee *big.Int // Provides information for BASEFEE
Random *common.Hash // Provides information for PREVRANDAO
}
+// TxContext provides the EVM with information about a transaction.
+// All fields can change between transactions.
+type TxContext struct {
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+}
+
// EVM is the Ethereum Virtual Machine base object and provides
// the necessary tools to run a contract on the given state with
// the provided context. It should be noted that any error
@@ -144,8 +99,9 @@ type Context struct {
//
// The EVM should never be reused and is not thread safe.
type EVM struct {
- // Context provides auxiliary blockchain related information
- Context
+ // BlockContext provides auxiliary blockchain related information
+ Context BlockContext
+ TxContext
// StateDB gives access to the underlying state
StateDB StateDB
@@ -160,11 +116,10 @@ type EVM struct {
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
- vmConfig Config
+ Config Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
- interpreters []Interpreter
- interpreter Interpreter
+ interpreter *EVMInterpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
@@ -176,25 +131,28 @@ type EVM struct {
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
-func NewEVM(ctx Context, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
+func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
- Context: ctx,
+ Context: blockCtx,
+ TxContext: txCtx,
StateDB: statedb,
tradingStateDB: tradingStateDB,
- vmConfig: vmConfig,
+ Config: config,
chainConfig: chainConfig,
- chainRules: chainConfig.Rules(ctx.BlockNumber),
- interpreters: make([]Interpreter, 0, 1),
+ chainRules: chainConfig.Rules(blockCtx.BlockNumber),
}
- // vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
- // as we always want to have the built-in EVM as the failover option.
- evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
- evm.interpreter = evm.interpreters[0]
-
+ evm.interpreter = NewEVMInterpreter(evm)
return evm
}
+// Reset resets the EVM with a new transaction context.Reset
+// This is not threadsafe and should only be done very cautiously.
+func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
+ evm.TxContext = txCtx
+ evm.StateDB = statedb
+}
+
// Cancel cancels any running EVM operation. This may be called concurrently and
// it's safe to be called multiple times.
func (evm *EVM) Cancel() {
@@ -207,10 +165,16 @@ func (evm *EVM) Cancelled() bool {
}
// Interpreter returns the current interpreter
-func (evm *EVM) Interpreter() Interpreter {
+func (evm *EVM) Interpreter() *EVMInterpreter {
return evm.interpreter
}
+// SetBlockContext updates the block context of the EVM.
+func (evm *EVM) SetBlockContext(blockCtx BlockContext) {
+ evm.Context = blockCtx
+ evm.chainRules = evm.chainConfig.Rules(blockCtx.BlockNumber)
+}
+
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
@@ -221,59 +185,65 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
- if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
- var (
- to = AccountRef(addr)
- snapshot = evm.StateDB.Snapshot()
- )
+ snapshot := evm.StateDB.Snapshot()
+ p, isPrecompile := evm.precompile(addr)
+
if !evm.StateDB.Exist(addr) {
- _, isPrecompile := evm.precompile2(addr)
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
- if evm.vmConfig.Debug {
+ if evm.Config.Debug {
if evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
- evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
} else {
- evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
- evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, nil
}
evm.StateDB.CreateAccount(addr)
}
- evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
+ evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
// Capture the tracer start/end events in debug mode
- if evm.vmConfig.Debug {
+ if evm.Config.Debug {
if evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
- evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
+ evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
} else {
// Handle tracer events for entering and exiting a call frame
- evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
- evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
}
- // Initialise a new contract and set the code that is to be used by the EVM.
- // The contract is a scoped environment for this execution context only.
- contract := NewContract(caller, to, value, gas)
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
- // Even if the account has no code, we need to continue because it might be a precompile
-
- ret, err = run(evm, contract, input, false)
- gas = contract.Gas
-
+ if isPrecompile {
+ ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
+ } else {
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
+ code := evm.StateDB.GetCode(addr)
+ if len(code) == 0 {
+ ret, err = nil, nil // gas is unchanged
+ } else {
+ addrCopy := addr
+ // If the account has no code, we can abort here
+ // The depth-check is already done, and precompiles handled above
+ contract := NewContract(caller, AccountRef(addrCopy), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
+ }
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
@@ -282,6 +252,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if err != ErrExecutionReverted {
gas = 0
}
+ // TODO: consider clearing up unused snapshots:
+ //} else {
+ // evm.StateDB.DiscardSnapshot(snapshot)
}
return ret, gas, err
}
@@ -305,26 +278,28 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
- var (
- snapshot = evm.StateDB.Snapshot()
- to = AccountRef(caller.Address())
- )
+ var snapshot = evm.StateDB.Snapshot()
// Invoke tracer hooks that signal entering/exiting a call frame
- if evm.vmConfig.Debug {
- evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
- evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
- // Initialise a new contract and set the code that is to be used by the EVM.
- // The contract is a scoped environment for this execution context only.
- contract := NewContract(caller, to, value, gas)
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
- ret, err = run(evm, contract, input, false)
- gas = contract.Gas
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
+ } else {
+ addrCopy := addr
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
+ contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@@ -344,25 +319,27 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
- var (
- snapshot = evm.StateDB.Snapshot()
- to = AccountRef(caller.Address())
- )
+ var snapshot = evm.StateDB.Snapshot()
// Invoke tracer hooks that signal entering/exiting a call frame
- if evm.vmConfig.Debug {
- evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
- evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
- // Initialise a new contract and make initialise the delegate values
- contract := NewContract(caller, to, nil, gas).AsDelegate()
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
- ret, err = run(evm, contract, input, false)
- gas = contract.Gas
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
+ } else {
+ addrCopy := addr
+ // Initialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@@ -381,36 +358,45 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
- var (
- to = AccountRef(addr)
- snapshot = evm.StateDB.Snapshot()
- )
- // Initialise a new contract and set the code that is to be used by the EVM.
- // The contract is a scoped environment for this execution context only.
- contract := NewContract(caller, to, new(big.Int), gas)
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+ // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
+ // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
+ // after all empty accounts were deleted, so this is not required. However, if we omit this,
+ // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
+ // We could change this, but for now it's left for legacy reasons
+ var snapshot = evm.StateDB.Snapshot()
- if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) {
- // We do an AddBalance of zero here, just in order to trigger a touch.
- // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
- // but is the correct thing to do and matters on other networks, in tests, and potential
- // future scenarios
- evm.StateDB.AddBalance(addr, big.NewInt(0))
- }
+ // We do an AddBalance of zero here, just in order to trigger a touch.
+ // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
+ // but is the correct thing to do and matters on other networks, in tests, and potential
+ // future scenarios
+ evm.StateDB.AddBalance(addr, big0)
// Invoke tracer hooks that signal entering/exiting a call frame
- if evm.vmConfig.Debug {
- evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
- evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
- // When an error was returned by the EVM or when setting the creation code
- // above we revert to the snapshot and consume any gas remaining. Additionally
- // when we're in Homestead this also counts for code storage gas errors.
- ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber))
- gas = contract.Gas
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
+ } else {
+ // At this point, we use a copy of address. If we don't, the go compiler will
+ // leak the 'contract' to the outer scope, and make allocation for 'contract'
+ // even if the actual execution ends on RunPrecompiled above.
+ addrCopy := addr
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
+ contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in Homestead this also counts for code storage gas errors.
+ readOnly := evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber)
+ ret, err = evm.interpreter.Run(contract, input, readOnly)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
@@ -439,7 +425,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.depth > int(params.CallCreateDepth) {
return nil, common.Address{}, gas, ErrDepth
}
- if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
nonce := evm.StateDB.GetNonce(caller.Address())
@@ -463,23 +449,23 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.chainRules.IsEIP158 {
evm.StateDB.SetNonce(address, 1)
}
- evm.Transfer(evm.StateDB, caller.Address(), address, value)
+ evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)
- if evm.vmConfig.Debug {
+ if evm.Config.Debug {
if evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
- evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
+ evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}
start := time.Now()
- ret, err := run(evm, contract, nil, false)
+ ret, err := evm.interpreter.Run(contract, nil, false)
// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
@@ -514,11 +500,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}
- if evm.vmConfig.Debug {
+ if evm.Config.Debug {
if evm.depth == 0 {
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
} else {
- evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
+ evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
return ret, address, contract.Gas, err
@@ -534,9 +520,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
-func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
- contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
+ contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 45589157bb..748d1f3ddd 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -97,7 +97,7 @@ var (
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
- current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
)
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@@ -136,7 +136,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
@@ -163,19 +163,19 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
return params.NetSstoreDirtyGas, nil
}
-// 0. If *gasleft* is less than or equal to 2300, fail the current call.
-// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
-// 2. If current value does not equal new value:
-// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
+// 0. If *gasleft* is less than or equal to 2300, fail the current call.
+// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
+// 2. If current value does not equal new value:
+// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
-// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
+// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
// 2.2.1. If original value is not 0:
-// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
-// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
+// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
+// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset):
-// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
-// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
+// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
+// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
@@ -184,14 +184,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.Back(0)
- current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
)
value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.SloadGasEIP2200, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreSetGasEIP2200, nil
@@ -300,6 +300,40 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
return gas, nil
}
+func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := params.InitCodeWordGas * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 501a0364c6..73132023d9 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -17,8 +17,10 @@
package vm
import (
+ "bytes"
"math"
"math/big"
+ "sort"
"testing"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
@@ -88,11 +90,11 @@ func TestEIP2200(t *testing.T) {
statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
statedb.Finalise(true) // Push the state into the "original" slot
- vmctx := Context{
+ vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
}
- vmenv := NewEVM(vmctx, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
+ vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
if err != tt.failure {
@@ -106,3 +108,72 @@ func TestEIP2200(t *testing.T) {
}
}
}
+
+var createGasTests = []struct {
+ code string
+ eip3860 bool
+ gasUsed uint64
+ minimumGas uint64
+}{
+ // legacy create(0, 0, 0xc000) without 3860 used
+ {"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237},
+ // legacy create(0, 0, 0xc000) _with_ 3860
+ {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309},
+ // create2(0, 0, 0xc001, 0) without 3860
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 100_000},
+ // create2(0, 0, 0xc001, 0) (too large), with 3860
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000},
+ // create2(0, 0, 0xc000, 0)
+ // This case is trying to deploy code at (within) the limit
+ {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 100_000},
+ // create2(0, 0, 0xc001, 0)
+ // This case is trying to deploy code exceeding the limit
+ {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100_000}}
+
+func TestCreateGas(t *testing.T) {
+ for i, tt := range createGasTests {
+ var gasUsed = uint64(0)
+ doCheck := func(testGas int) bool {
+ address := common.BytesToAddress([]byte("contract"))
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
+ statedb.CreateAccount(address)
+ statedb.SetCode(address, hexutil.MustDecode(tt.code))
+ statedb.Finalise(true)
+ vmctx := BlockContext{
+ CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
+ Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
+ BlockNumber: big.NewInt(0),
+ }
+ config := Config{}
+ if tt.eip3860 {
+ config.ExtraEips = []int{3860}
+ }
+
+ vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, config)
+ var startGas = uint64(testGas)
+ ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
+ if err != nil {
+ return false
+ }
+ gasUsed = startGas - gas
+ if len(ret) != 32 {
+ t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
+ }
+ if bytes.Equal(ret, make([]byte, 32)) {
+ // Failure
+ return false
+ }
+ return true
+ }
+ minGas := sort.Search(100_000, doCheck)
+ if uint64(minGas) != tt.minimumGas {
+ t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas)
+ }
+ // If the deployment succeeded, we also check the gas used
+ if minGas < 100_000 {
+ if gasUsed != tt.gasUsed {
+ t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed)
+ }
+ }
+ }
+}
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 74c4ec39b3..1230944d57 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -6,30 +6,36 @@ import (
"encoding/json"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/holiman/uint256"
)
+var _ = (*structLogMarshaling)(nil)
+
// MarshalJSON marshals as JSON.
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
- Gas uint64 `json:"gas"`
- GasCost uint64 `json:"gasCost"`
- Memory []byte `json:"memory"`
+ Gas math.HexOrDecimal64 `json:"gas"`
+ GasCost math.HexOrDecimal64 `json:"gasCost"`
+ Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []uint256.Int `json:"stack"`
- ReturnData []byte `json:"returnData"`
+ ReturnData hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
+ OpName string `json:"opName"`
+ ErrorString string `json:"error"`
}
var enc StructLog
enc.Pc = s.Pc
enc.Op = s.Op
- enc.Gas = s.Gas
- enc.GasCost = s.GasCost
+ enc.Gas = math.HexOrDecimal64(s.Gas)
+ enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
enc.MemorySize = s.MemorySize
enc.Stack = s.Stack
@@ -38,6 +44,8 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter
enc.Err = s.Err
+ enc.OpName = s.OpName()
+ enc.ErrorString = s.ErrorString()
return json.Marshal(&enc)
}
@@ -46,12 +54,12 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
Pc *uint64 `json:"pc"`
Op *OpCode `json:"op"`
- Gas *uint64 `json:"gas"`
- GasCost *uint64 `json:"gasCost"`
- Memory []byte `json:"memory"`
+ Gas *math.HexOrDecimal64 `json:"gas"`
+ GasCost *math.HexOrDecimal64 `json:"gasCost"`
+ Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []uint256.Int `json:"stack"`
- ReturnData []byte `json:"returnData"`
+ ReturnData *hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"`
@@ -68,13 +76,13 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.Op = *dec.Op
}
if dec.Gas != nil {
- s.Gas = *dec.Gas
+ s.Gas = uint64(*dec.Gas)
}
if dec.GasCost != nil {
- s.GasCost = *dec.GasCost
+ s.GasCost = uint64(*dec.GasCost)
}
if dec.Memory != nil {
- s.Memory = dec.Memory
+ s.Memory = *dec.Memory
}
if dec.MemorySize != nil {
s.MemorySize = *dec.MemorySize
@@ -83,7 +91,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.Stack = dec.Stack
}
if dec.ReturnData != nil {
- s.ReturnData = dec.ReturnData
+ s.ReturnData = *dec.ReturnData
}
if dec.Storage != nil {
s.Storage = dec.Storage
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index b2bf296c1f..6a0ddea1b1 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -21,9 +21,9 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
- "golang.org/x/crypto/sha3"
)
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
@@ -238,7 +238,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
- interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
+ interpreter.hasher = crypto.NewKeccakState()
} else {
interpreter.hasher.Reset()
}
@@ -246,7 +246,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
interpreter.hasher.Read(interpreter.hasherBuf[:])
evm := interpreter.evm
- if evm.vmConfig.EnablePreimageRecording {
+ if evm.Config.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
@@ -343,7 +343,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
- slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
return nil, nil
}
@@ -447,14 +447,14 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
return nil, nil
}
var upper, lower uint64
- upper = interpreter.evm.BlockNumber.Uint64()
+ upper = interpreter.evm.Context.BlockNumber.Uint64()
if upper < 257 {
lower = 0
} else {
lower = upper - 256
}
if num64 >= lower && num64 < upper {
- num.SetBytes(interpreter.evm.GetHash(num64).Bytes())
+ num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
} else {
num.Clear()
}
@@ -462,24 +462,24 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
}
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
+ scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes()))
return nil, nil
}
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(interpreter.evm.Time)
+ v, _ := uint256.FromBig(interpreter.evm.Context.Time)
scope.Stack.push(v)
return nil, nil
}
func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
+ v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber)
scope.Stack.push(v)
return nil, nil
}
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(interpreter.evm.Difficulty)
+ v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty)
scope.Stack.push(v)
return nil, nil
}
@@ -496,7 +496,7 @@ func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
+ scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit))
return nil, nil
}
@@ -539,8 +539,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
loc := scope.Stack.pop()
val := scope.Stack.pop()
- interpreter.evm.StateDB.SetState(scope.Contract.Address(),
- common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
+ interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
@@ -606,7 +605,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
stackvalue := size
scope.Contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, value.ToBig())
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
+ bigVal = value.ToBig()
+ }
+
+ res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
@@ -640,14 +645,18 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
)
-
// Apply EIP150
gas -= gas / 64
scope.Contract.UseGas(gas)
// reuse size int for stackvalue
stackvalue := size
+ //TODO: use uint256.Int instead of converting with toBig()
+ bigEndowment := big0
+ if !endowment.IsZero() {
+ bigEndowment = endowment.ToBig()
+ }
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
- endowment.ToBig(), salt.ToBig())
+ bigEndowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
stackvalue.Clear()
@@ -680,10 +689,18 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
if interpreter.readOnly && !value.IsZero() {
return nil, ErrWriteProtection
}
+
+ var bigVal = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, value.ToBig())
+
+ ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal)
+
if err != nil {
temp.Clear()
} else {
@@ -691,7 +708,6 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -712,10 +728,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, value.ToBig())
+
+ ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal)
if err != nil {
temp.Clear()
} else {
@@ -723,7 +743,6 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -752,7 +771,6 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -781,7 +799,6 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- ret = common.CopyBytes(ret)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
@@ -819,11 +836,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
beneficiary := scope.Stack.pop()
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
- interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
+ interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.Suicide(scope.Contract.Address())
- if interpreter.cfg.Debug {
- interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
- interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
+ if interpreter.evm.Config.Debug {
+ interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
+ interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
}
@@ -841,7 +858,7 @@ func makeLog(size int) executionFunc {
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
addr := stack.pop()
- topics[i] = common.Hash(addr.Bytes32())
+ topics[i] = addr.Bytes32()
}
d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
@@ -851,7 +868,7 @@ func makeLog(size int) executionFunc {
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
- BlockNumber: interpreter.evm.BlockNumber.Uint64(),
+ BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(),
})
return nil, nil
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 580a008b4e..1ccc7ff505 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -26,6 +26,8 @@ import (
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/holiman/uint256"
@@ -46,8 +48,15 @@ var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffff
var commonParams []*twoOperandParams
var twoOpMethods map[string]executionFunc
-func init() {
+type contractRef struct {
+ addr common.Address
+}
+func (c contractRef) Address() common.Address {
+ return c.addr
+}
+
+func init() {
// Params is a list of common edgecases that should be used for some common tests
params := []string{
"0000000000000000000000000000000000000000000000000000000000000000", // 0
@@ -93,12 +102,11 @@ func init() {
}
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
-
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
- evmInterpreter = env.interpreter.(*EVMInterpreter)
+ evmInterpreter = env.interpreter
)
for i, test := range tests {
@@ -194,9 +202,9 @@ func TestSAR(t *testing.T) {
func TestAddMod(t *testing.T) {
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env)
pc = uint64(0)
)
tests := []struct {
@@ -230,35 +238,35 @@ func TestAddMod(t *testing.T) {
}
}
-// getResult is a convenience function to generate the expected values
-func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
- var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
- stack = newstack()
- pc = uint64(0)
- interpreter = env.interpreter.(*EVMInterpreter)
- )
- result := make([]TwoOperandTestcase, len(args))
- for i, param := range args {
- x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
- y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
- stack.push(x)
- stack.push(y)
- _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
- if err != nil {
- log.Fatalln(err)
- }
- actual := stack.pop()
- result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
- }
- return result
-}
-
// utility function to fill the json-file with testcases
// Enable this test to generate the 'testcases_xx.json' files
func TestWriteExpectedValues(t *testing.T) {
t.Skip("Enable this test to create json test cases.")
+ // getResult is a convenience function to generate the expected values
+ getResult := func(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
+ var (
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
+ stack = newstack()
+ pc = uint64(0)
+ interpreter = env.interpreter
+ )
+ result := make([]TwoOperandTestcase, len(args))
+ for i, param := range args {
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
+ stack.push(x)
+ stack.push(y)
+ _, err := opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
+ if err != nil {
+ log.Fatalln(err)
+ }
+ actual := stack.pop()
+ result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
+ }
+ return result
+ }
+
for name, method := range twoOpMethods {
data, err := json.Marshal(getResult(commonParams, method))
if err != nil {
@@ -289,28 +297,35 @@ func TestJsonTestcases(t *testing.T) {
func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ scope = &ScopeContext{nil, stack, nil}
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
// convert args
- byteArgs := make([][]byte, len(args))
+ intArgs := make([]*uint256.Int, len(args))
for i, arg := range args {
- byteArgs[i] = common.Hex2Bytes(arg)
+ intArgs[i] = new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
}
pc := uint64(0)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- for _, arg := range byteArgs {
- a := new(uint256.Int)
- a.SetBytes(arg)
- stack.push(a)
+ for _, arg := range intArgs {
+ stack.push(arg)
}
- op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
+ op(&pc, evmInterpreter, scope)
stack.pop()
}
+ bench.StopTimer()
+
+ for i, arg := range args {
+ want := new(uint256.Int).SetBytes(common.Hex2Bytes(arg))
+ if have := intArgs[i]; !want.Eq(have) {
+ bench.Fatalf("input #%d mutated, have %x want %x", i, have, want)
+ }
+ }
}
func BenchmarkOpAdd64(b *testing.B) {
@@ -523,10 +538,10 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) {
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
@@ -549,10 +564,10 @@ func TestOpMstore(t *testing.T) {
func BenchmarkOpMstore(bench *testing.B) {
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
@@ -569,12 +584,55 @@ func BenchmarkOpMstore(bench *testing.B) {
}
}
-func BenchmarkOpKeccak256(bench *testing.B) {
+func TestOpTstore(t *testing.T) {
var (
- env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
+ statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
+ env = NewEVM(BlockContext{}, TxContext{}, statedb, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env)
+ caller = common.Address{}
+ to = common.Address{1}
+ contractRef = contractRef{caller}
+ contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
+ scopeContext = ScopeContext{mem, stack, contract}
+ value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
+ )
+
+ // Add a stateObject for the caller and the contract being called
+ statedb.CreateAccount(caller)
+ statedb.CreateAccount(to)
+
+ env.interpreter = evmInterpreter
+ pc := uint64(0)
+ // push the value to the stack
+ stack.push(new(uint256.Int).SetBytes(value))
+ // push the location to the stack
+ stack.push(new(uint256.Int))
+ opTstore(&pc, evmInterpreter, &scopeContext)
+ // there should be no elements on the stack after TSTORE
+ if stack.len() != 0 {
+ t.Fatal("stack wrong size")
+ }
+ // push the location to the stack
+ stack.push(new(uint256.Int))
+ opTload(&pc, evmInterpreter, &scopeContext)
+ // there should be one element on the stack after TLOAD
+ if stack.len() != 1 {
+ t.Fatal("stack wrong size")
+ }
+ val := stack.peek()
+ if !bytes.Equal(val.Bytes(), value) {
+ t.Fatal("incorrect element read from transient storage")
+ }
+}
+
+func BenchmarkOpKeccak256(bench *testing.B) {
+ var (
+ env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
+ stack = newstack()
+ mem = NewMemory()
+ evmInterpreter = NewEVMInterpreter(env)
)
env.interpreter = evmInterpreter
mem.Resize(32)
@@ -641,7 +699,6 @@ func TestCreate2Addreses(t *testing.T) {
expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
},
} {
-
origin := common.BytesToAddress(common.FromHex(tt.origin))
salt := common.BytesToHash(common.FromHex(tt.salt))
code := common.FromHex(tt.code)
@@ -654,7 +711,7 @@ func TestCreate2Addreses(t *testing.T) {
stack.push(big.NewInt(0)) // memstart
stack.push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
- fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
+ fmt.Printf("Example %d\n* address `%#x`\n* salt `%#x`\n* init_code `%#x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/
expected := common.BytesToAddress(common.FromHex(tt.expected))
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
@@ -676,10 +733,10 @@ func TestRandom(t *testing.T) {
{name: "hash(0x010203)", random: crypto.Keccak256Hash([]byte{0x01, 0x02, 0x03})},
} {
var (
- env = NewEVM(Context{Random: &tt.random}, nil, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ evmInterpreter = NewEVMInterpreter(env)
)
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
if len(stack.data) != 1 {
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 903a957a20..c6c3ee1cfd 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -21,6 +21,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/params"
)
// StateDB is an EVM database for full state querying.
@@ -47,6 +48,9 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
+ GetTransientState(addr common.Address, key common.Hash) common.Hash
+ SetTransientState(addr common.Address, key, value common.Hash)
+
Suicide(common.Address) bool
HasSuicided(common.Address) bool
@@ -57,7 +61,6 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
- PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
@@ -66,6 +69,7 @@ type StateDB interface {
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash)
+ Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
RevertToSnapshot(int)
Snapshot() int
@@ -79,12 +83,12 @@ type StateDB interface {
// CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {
- // Call another contract
+ // Call calls another contract.
Call(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Take another's contract code and execute within our own context
+ // CallCode takes another contracts code and execute within our own context
CallCode(env *EVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
- // Same as CallCode except sender and value is propagated from parent to child scope
+ // DelegateCall is same as CallCode except sender and value is propagated from parent to child scope
DelegateCall(env *EVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
- // Create a new contract
+ // Create creates a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 6e71411ac1..b64b679b91 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -17,10 +17,9 @@
package vm
import (
- "hash"
-
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/log"
)
@@ -28,36 +27,9 @@ import (
type Config struct {
Debug bool // Enables debugging
Tracer EVMLogger // Opcode logger
+ NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
-
- JumpTable *JumpTable // EVM instruction table, automatically populated if unset
-
- EWASMInterpreter string // External EWASM interpreter options
- EVMInterpreter string // External EVM interpreter options
-
- ExtraEips []int // Additional EIPS that are to be enabled
-}
-
-// Interpreter is used to run Ethereum based contracts and will utilise the
-// passed environment to query external sources for state information.
-// The Interpreter will run the byte code VM based on the passed
-// configuration.
-type Interpreter interface {
- // Run loops and evaluates the contract's code with the given input data and returns
- // the return byte-slice and an error if one occurred.
- Run(contract *Contract, input []byte, static bool) ([]byte, error)
- // CanRun tells if the contract, passed as an argument, can be
- // run by the current interpreter. This is meant so that the
- // caller can do something like:
- //
- // ```golang
- // for _, interpreter := range interpreters {
- // if interpreter.CanRun(contract.code) {
- // interpreter.Run(contract.code, input)
- // }
- // }
- // ```
- CanRun([]byte) bool
+ ExtraEips []int // Additional EIPS that are to be enabled
}
// ScopeContext contains the things that are per-call, such as stack and memory,
@@ -68,74 +40,63 @@ type ScopeContext struct {
Contract *Contract
}
-// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
-// Read to get a variable amount of data from the hash state. Read is faster than Sum
-// because it doesn't copy the internal state, but also modifies the internal state.
-type keccakState interface {
- hash.Hash
- Read([]byte) (int, error)
-}
-
// EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct {
- evm *EVM
- cfg Config
+ evm *EVM
+ table *JumpTable
- hasher keccakState // Keccak256 hasher instance shared across opcodes
- hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
+ hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
+ hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse
}
// NewEVMInterpreter returns a new instance of the Interpreter.
-func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
+func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// If jump table was not initialised we set the default one.
- if cfg.JumpTable == nil {
- switch {
- case evm.chainRules.IsEIP1559:
- cfg.JumpTable = &eip1559InstructionSet
- case evm.chainRules.IsShanghai:
- cfg.JumpTable = &shanghaiInstructionSet
- case evm.chainRules.IsMerge:
- cfg.JumpTable = &mergeInstructionSet
- case evm.chainRules.IsLondon:
- cfg.JumpTable = &londonInstructionSet
- case evm.chainRules.IsBerlin:
- cfg.JumpTable = &berlinInstructionSet
- case evm.chainRules.IsIstanbul:
- cfg.JumpTable = &istanbulInstructionSet
- case evm.chainRules.IsConstantinople:
- cfg.JumpTable = &constantinopleInstructionSet
- case evm.chainRules.IsByzantium:
- cfg.JumpTable = &byzantiumInstructionSet
- case evm.chainRules.IsEIP158:
- cfg.JumpTable = &spuriousDragonInstructionSet
- case evm.chainRules.IsEIP150:
- cfg.JumpTable = &tangerineWhistleInstructionSet
- case evm.chainRules.IsHomestead:
- cfg.JumpTable = &homesteadInstructionSet
- default:
- cfg.JumpTable = &frontierInstructionSet
- }
- var extraEips []int
- for _, eip := range cfg.ExtraEips {
- copy := *cfg.JumpTable
- if err := EnableEIP(eip, ©); err != nil {
- // Disable it, so caller can check if it's activated or not
- log.Error("EIP activation failed", "eip", eip, "error", err)
- } else {
- extraEips = append(extraEips, eip)
- }
- cfg.JumpTable = ©
- }
- cfg.ExtraEips = extraEips
+ var table *JumpTable
+ switch {
+ case evm.chainRules.IsEIP1559:
+ table = &eip1559InstructionSet
+ case evm.chainRules.IsShanghai:
+ table = &shanghaiInstructionSet
+ case evm.chainRules.IsMerge:
+ table = &mergeInstructionSet
+ case evm.chainRules.IsLondon:
+ table = &londonInstructionSet
+ case evm.chainRules.IsBerlin:
+ table = &berlinInstructionSet
+ case evm.chainRules.IsIstanbul:
+ table = &istanbulInstructionSet
+ case evm.chainRules.IsConstantinople:
+ table = &constantinopleInstructionSet
+ case evm.chainRules.IsByzantium:
+ table = &byzantiumInstructionSet
+ case evm.chainRules.IsEIP158:
+ table = &spuriousDragonInstructionSet
+ case evm.chainRules.IsEIP150:
+ table = &tangerineWhistleInstructionSet
+ case evm.chainRules.IsHomestead:
+ table = &homesteadInstructionSet
+ default:
+ table = &frontierInstructionSet
}
-
- return &EVMInterpreter{
- evm: evm,
- cfg: cfg,
+ var extraEips []int
+ if len(evm.Config.ExtraEips) > 0 {
+ // Deep-copy jumptable to prevent modification of opcodes in other tables
+ table = copyJumpTable(table)
}
+ for _, eip := range evm.Config.ExtraEips {
+ if err := EnableEIP(eip, table); err != nil {
+ // Disable it, so caller can check if it's activated or not
+ log.Error("EIP activation failed", "eip", eip, "error", err)
+ } else {
+ extraEips = append(extraEips, eip)
+ }
+ }
+ evm.Config.ExtraEips = extraEips
+ return &EVMInterpreter{evm: evm, table: table}
}
// Run loops and evaluates the contract's code with the given input data and returns
@@ -145,7 +106,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
-
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
@@ -186,15 +146,21 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged bool // deferred EVMLogger should ignore already logged steps
res []byte // result of the opcode execution function
)
+ // Don't move this deferred function, it's placed before the capturestate-deferred method,
+ // so that it get's executed _after_: the capturestate needs the stacks before
+ // they are returned to the pools
+ defer func() {
+ returnStack(stack)
+ }()
contract.Input = input
- if in.cfg.Debug {
+ if in.evm.Config.Debug {
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
+ in.evm.Config.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
}
}
}()
@@ -204,7 +170,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// the execution of one of the operations or until the done flag is set by the
// parent context.
for {
- if in.cfg.Debug {
+ if in.evm.Config.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
}
@@ -212,55 +178,55 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
- operation := in.cfg.JumpTable[op]
+ operation := in.table[op]
+ cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
- // Static portion of gas
- cost = operation.constantGas // For tracing
- if !contract.UseGas(operation.constantGas) {
+ if !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
-
- var memorySize uint64
- // calculate the new memory size and expand the memory to fit
- // the operation
- // Memory check needs to be done prior to evaluating the dynamic gas portion,
- // to detect calculation overflows
- if operation.memorySize != nil {
- memSize, overflow := operation.memorySize(stack)
- if overflow {
- return nil, ErrGasUintOverflow
- }
- // memory is expanded in words of 32 bytes. Gas
- // is also calculated in words.
- if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
- return nil, ErrGasUintOverflow
- }
- }
- // Dynamic portion of gas
- // consume the gas and return an error if not enough gas is available.
- // cost is explicitly set so that the capture state defer method can get the proper cost
if operation.dynamicGas != nil {
+ // All ops with a dynamic memory usage also has a dynamic gas cost.
+ var memorySize uint64
+ // calculate the new memory size and expand the memory to fit
+ // the operation
+ // Memory check needs to be done prior to evaluating the dynamic gas portion,
+ // to detect calculation overflows
+ if operation.memorySize != nil {
+ memSize, overflow := operation.memorySize(stack)
+ if overflow {
+ return nil, ErrGasUintOverflow
+ }
+ // memory is expanded in words of 32 bytes. Gas
+ // is also calculated in words.
+ if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
+ return nil, ErrGasUintOverflow
+ }
+ }
+ // Consume the gas and return an error if not enough gas is available.
+ // cost is explicitly set so that the capture state defer method can get the proper cost
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
- cost += dynamicCost // total cost, for debug tracing
+ cost += dynamicCost // for tracing
if err != nil || !contract.UseGas(dynamicCost) {
return nil, ErrOutOfGas
}
- }
- if memorySize > 0 {
- mem.Resize(memorySize)
- }
-
- if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ // Do tracing before memory expansion
+ if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ logged = true
+ }
+ if memorySize > 0 {
+ mem.Resize(memorySize)
+ }
+ } else if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
-
// execute the operation
res, err = operation.execute(&pc, in, callContext)
if err != nil {
@@ -275,9 +241,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return res, err
}
-
-// CanRun tells if the contract, passed as an argument, can be
-// run by the current interpreter.
-func (in *EVMInterpreter) CanRun(code []byte) bool {
- return true
-}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 4ae7c1e8ce..fc4b9b8521 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -17,6 +17,8 @@
package vm
import (
+ "fmt"
+
"github.com/XinFinOrg/XDPoSChain/params"
)
@@ -60,16 +62,36 @@ var (
// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation
+func validate(jt JumpTable) JumpTable {
+ for i, op := range jt {
+ if op == nil {
+ panic(fmt.Sprintf("op %#x is not set", i))
+ }
+ // The interpreter has an assumption that if the memorySize function is
+ // set, then the dynamicGas function is also set. This is a somewhat
+ // arbitrary assumption, and can be removed if we need to -- but it
+ // allows us to avoid a condition check. As long as we have that assumption
+ // in there, this little sanity check prevents us from merging in a
+ // change which violates it.
+ if op.memorySize != nil && op.dynamicGas == nil {
+ panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
+ }
+ }
+ return jt
+}
+
func newEip1559InstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
- return instructionSet
+ enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
+ enable3860(&instructionSet) // Limit and meter initcode
+ return validate(instructionSet)
}
func newShanghaiInstructionSet() JumpTable {
instructionSet := newMergeInstructionSet()
enable3855(&instructionSet) // PUSH0 instruction
- return instructionSet
+ return validate(instructionSet)
}
func newMergeInstructionSet() JumpTable {
@@ -80,16 +102,15 @@ func newMergeInstructionSet() JumpTable {
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
- return instructionSet
+ return validate(instructionSet)
}
// newLondonInstructionSet returns the frontier, homestead, byzantium,
// constantinople, istanbul, petersburg, berlin and london instructions.
func newLondonInstructionSet() JumpTable {
instructionSet := newBerlinInstructionSet()
- // enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
- return instructionSet
+ return validate(instructionSet)
}
// newBerlinInstructionSet returns the frontier, homestead, byzantium,
@@ -97,7 +118,7 @@ func newLondonInstructionSet() JumpTable {
func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet()
// enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
- return instructionSet
+ return validate(instructionSet)
}
// newIstanbulInstructionSet returns the frontier, homestead
@@ -109,7 +130,7 @@ func newIstanbulInstructionSet() JumpTable {
enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
- return instructionSet
+ return validate(instructionSet)
}
// newConstantinopleInstructionSet returns the frontier, homestead
@@ -148,7 +169,7 @@ func newConstantinopleInstructionSet() JumpTable {
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
}
- return instructionSet
+ return validate(instructionSet)
}
// newByzantiumInstructionSet returns the frontier, homestead and
@@ -184,14 +205,14 @@ func newByzantiumInstructionSet() JumpTable {
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
}
- return instructionSet
+ return validate(instructionSet)
}
// EIP 158 a.k.a Spurious Dragon
func newSpuriousDragonInstructionSet() JumpTable {
instructionSet := newTangerineWhistleInstructionSet()
instructionSet[EXP].dynamicGas = gasExpEIP158
- return instructionSet
+ return validate(instructionSet)
}
@@ -205,7 +226,7 @@ func newTangerineWhistleInstructionSet() JumpTable {
instructionSet[CALL].constantGas = params.CallGasEIP150
instructionSet[CALLCODE].constantGas = params.CallGasEIP150
instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
- return instructionSet
+ return validate(instructionSet)
}
// newHomesteadInstructionSet returns the frontier and homestead
@@ -220,7 +241,7 @@ func newHomesteadInstructionSet() JumpTable {
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
}
- return instructionSet
+ return validate(instructionSet)
}
// newFrontierInstructionSet returns the frontier instructions
@@ -1036,5 +1057,16 @@ func newFrontierInstructionSet() JumpTable {
}
}
- return tbl
+ return validate(tbl)
+}
+
+func copyJumpTable(source *JumpTable) *JumpTable {
+ dest := *source
+ for i, op := range source {
+ if op != nil {
+ opCopy := *op
+ dest[i] = &opCopy
+ }
+ }
+ return &dest
}
diff --git a/mobile/logger.go b/core/vm/jump_table_test.go
similarity index 58%
rename from mobile/logger.go
rename to core/vm/jump_table_test.go
index 28295f71f1..d7c9408bca 100644
--- a/mobile/logger.go
+++ b/core/vm/jump_table_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,15 +14,22 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package geth
+package vm
import (
- "os"
+ "testing"
- "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/stretchr/testify/require"
)
-// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go).
-func SetVerbosity(level int) {
- log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table
+func TestJumpTableCopy(t *testing.T) {
+ tbl := newEip1559InstructionSet()
+ require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
+
+ // a deep copy won't modify the shared jump table
+ deepCopy := copyJumpTable(&tbl)
+ deepCopy[SLOAD].constantGas = 100
+ require.Equal(t, uint64(100), deepCopy[SLOAD].constantGas)
+ require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index e8294e3d67..ce014b17fc 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -220,7 +220,7 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
l.output = output
l.err = err
if l.cfg.Debug {
- fmt.Printf("0x%x\n", output)
+ fmt.Printf("%#x\n", output)
if err != nil {
fmt.Printf(" error: %v\n", err)
}
@@ -305,11 +305,11 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
if !create {
- fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
} else {
- fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
input, gas, value)
}
@@ -346,7 +346,7 @@ func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64
}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
- fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
+ fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index f668d068fb..108656fee1 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -50,7 +50,7 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
- env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
scope = &ScopeContext{
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 26523e53ce..c050f64e79 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -116,6 +116,8 @@ const (
MSIZE OpCode = 0x59
GAS OpCode = 0x5a
JUMPDEST OpCode = 0x5b
+ TLOAD OpCode = 0x5c
+ TSTORE OpCode = 0x5d
PUSH0 OpCode = 0x5f
)
@@ -297,6 +299,8 @@ var opCodeToString = [256]string{
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
+ TLOAD: "TLOAD",
+ TSTORE: "TSTORE",
PUSH0: "PUSH0",
// 0x60 range - push.
@@ -460,6 +464,8 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
+ "TLOAD": TLOAD,
+ "TSTORE": TSTORE,
"PUSH0": PUSH0,
"PUSH1": PUSH1,
"PUSH2": PUSH2,
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index eb3c0f43dd..dfef062856 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -24,91 +24,75 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
)
-const (
- ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
- ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
- WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
-)
-
-// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
-//
-// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
-// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
-// Additionally, modify the parameters defined in EIP 2200 as follows:
-//
-// Parameter Old value New value
-// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
-// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
-//
-// The other parameters defined in EIP 2200 are unchanged.
-// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
-func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- // If we fail the minimum gas availability invariant, fail (0)
- if contract.Gas <= params.SstoreSentryGasEIP2200 {
- return 0, errors.New("not enough gas for reentrancy sentry")
- }
- // Gas sentry honoured, do the actual gas calculation based on the stored value
- var (
- y, x = stack.Back(1), stack.peek()
- slot = common.Hash(x.Bytes32())
- current = evm.StateDB.GetState(contract.Address(), slot)
- cost = uint64(0)
- )
- // Check slot presence in the access list
- if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
- cost = ColdSloadCostEIP2929
- // If the caller cannot afford the cost, this change will be rolled back
- evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
- if !addrPresent {
- // Once we're done with YOLOv2 and schedule this for mainnet, might
- // be good to remove this panic here, which is just really a
- // canary to have during testing
- panic("impossible case: address was not present in access list during sstore op")
+func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
+ return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // If we fail the minimum gas availability invariant, fail (0)
+ if contract.Gas <= params.SstoreSentryGasEIP2200 {
+ return 0, errors.New("not enough gas for reentrancy sentry")
}
- }
- value := common.Hash(y.Bytes32())
-
- if current == value { // noop (1)
- // EIP 2200 original clause:
- // return params.SloadGasEIP2200, nil
- return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
- }
- original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
- if original == current {
- if original == (common.Hash{}) { // create slot (2.1.1)
- return cost + params.SstoreSetGasEIP2200, nil
+ // Gas sentry honoured, do the actual gas calculation based on the stored value
+ var (
+ y, x = stack.Back(1), stack.peek()
+ slot = common.Hash(x.Bytes32())
+ current = evm.StateDB.GetState(contract.Address(), slot)
+ cost = uint64(0)
+ )
+ // Check slot presence in the access list
+ if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ cost = params.ColdSloadCostEIP2929
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ if !addrPresent {
+ // Once we're done with YOLOv2 and schedule this for mainnet, might
+ // be good to remove this panic here, which is just really a
+ // canary to have during testing
+ panic("impossible case: address was not present in access list during sstore op")
+ }
}
- if value == (common.Hash{}) { // delete slot (2.1.2b)
- evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
+ value := common.Hash(y.Bytes32())
+
+ if current == value { // noop (1)
+ // EIP 2200 original clause:
+ // return params.SloadGasEIP2200, nil
+ return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return cost + params.SstoreSetGasEIP2200, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ // EIP-2200 original clause:
+ // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
+ return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
+ }
+ if original != (common.Hash{}) {
+ if current == (common.Hash{}) { // recreate slot (2.2.1.1)
+ evm.StateDB.SubRefund(clearingRefund)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ // EIP 2200 Original clause:
+ //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
+ } else { // reset to original existing slot (2.2.2.2)
+ // EIP 2200 Original clause:
+ // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
+ // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
+ // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
+ // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
+ evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
+ }
}
// EIP-2200 original clause:
- // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
- return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
+ //return params.SloadGasEIP2200, nil // dirty update (2.2)
+ return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}
- if original != (common.Hash{}) {
- if current == (common.Hash{}) { // recreate slot (2.2.1.1)
- evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
- } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
- evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
- }
- }
- if original == value {
- if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
- // EIP 2200 Original clause:
- //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
- evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
- } else { // reset to original existing slot (2.2.2.2)
- // EIP 2200 Original clause:
- // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
- // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
- // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
- // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
- evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
- }
- }
- // EIP-2200 original clause:
- //return params.SloadGasEIP2200, nil // dirty update (2.2)
- return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
@@ -124,9 +108,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// If the caller cannot afford the cost, this change will be rolled back
// If he does afford it, we can skip checking the same thing later on, during execution
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
- return ColdSloadCostEIP2929, nil
+ return params.ColdSloadCostEIP2929, nil
}
- return WarmStorageReadCostEIP2929, nil
+ return params.WarmStorageReadCostEIP2929, nil
}
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
@@ -146,7 +130,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
evm.StateDB.AddAddressToAccessList(addr)
var overflow bool
// We charge (cold-warm), since 'warm' is already charged as constantGas
- if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
+ if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
@@ -168,7 +152,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(addr)
// The warm storage read cost is already charged as constantGas
- return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
+ return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
}
return 0, nil
}
@@ -177,10 +161,15 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(1).Bytes20())
// Check slot presence in the access list
- if !evm.StateDB.AddressInAccessList(addr) {
+ warmAccess := evm.StateDB.AddressInAccessList(addr)
+ // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
+ // the cost to charge for cold access, if any, is Cold - Warm
+ coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
+ if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr)
- // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
- if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
+ // Charge the remaining difference here already, to correctly calculate available
+ // gas for call
+ if !contract.UseGas(coldCost) {
return 0, ErrOutOfGas
}
}
@@ -189,7 +178,16 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
// - transfer value
// - memory expansion
// - 63/64ths rule
- return oldCalculator(evm, contract, stack, mem, memorySize)
+ gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
+ if warmAccess || err != nil {
+ return gas, err
+ }
+ // In case of a cold access, we temporarily add the cold charge back, and also
+ // add it to the returned gas. By adding it to the return, it will be charged
+ // outside of this function, as part of the dynamic gas, and that will make it
+ // also become correctly reported to tracers.
+ contract.Gas += coldCost
+ return gas + coldCost, nil
}
}
@@ -198,24 +196,49 @@ var (
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
+ gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
+ // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
+ gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
+
+ // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
+ //
+ // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
+ // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
+ // Additionally, modify the parameters defined in EIP 2200 as follows:
+ //
+ // Parameter Old value New value
+ // SLOAD_GAS 800 = WARM_STORAGE_READ_COST
+ // SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
+ //
+ //The other parameters defined in EIP 2200 are unchanged.
+ // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
+ gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
+
+ // gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
+ // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
+ gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
)
-func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- var (
- gas uint64
- address = common.Address(stack.peek().Bytes20())
- )
- if !evm.StateDB.AddressInAccessList(address) {
- // If the caller cannot afford the cost, this change will be rolled back
- evm.StateDB.AddAddressToAccessList(address)
- gas = ColdAccountAccessCostEIP2929
+// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
+func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
+ gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ address = common.Address(stack.peek().Bytes20())
+ )
+ if !evm.StateDB.AddressInAccessList(address) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(address)
+ gas = params.ColdAccountAccessCostEIP2929
+ }
+ // if empty and transfers value
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
+ gas += params.CreateBySelfdestructGas
+ }
+ if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(params.SelfdestructRefundGas)
+ }
+ return gas, nil
}
- // if empty and transfers value
- if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
- gas += params.CreateBySelfdestructGas
- }
- if !evm.StateDB.HasSuicided(contract.Address()) {
- evm.StateDB.AddRefund(params.SelfdestructRefundGas)
- }
- return gas, nil
+ return gasFunc
}
diff --git a/core/vm/privacy/bulletproof.go b/core/vm/privacy/bulletproof.go
index f620d230b2..4c1972ffbc 100644
--- a/core/vm/privacy/bulletproof.go
+++ b/core/vm/privacy/bulletproof.go
@@ -608,8 +608,6 @@ Delta is a helper function that is used in the range proof
*/
func Delta(y []*big.Int, z *big.Int) *big.Int {
- result := big.NewInt(0)
-
// (z-z^2)<1^n, y^n>
z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N)
t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N)
@@ -620,21 +618,14 @@ func Delta(y []*big.Int, z *big.Int) *big.Int {
po2sum := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(EC.V)), EC.N), big.NewInt(1))
t3 := new(big.Int).Mod(new(big.Int).Mul(z3, po2sum), EC.N)
- result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N)
-
- return result
+ return new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N)
}
// Calculates (aL - z*1^n) + sL*x
func CalculateL(aL, sL []*big.Int, z, x *big.Int) []*big.Int {
- result := make([]*big.Int, len(aL))
-
tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z))
tmp2 := ScalarVectorMul(sL, x)
-
- result = VectorAdd(tmp1, tmp2)
-
- return result
+ return VectorAdd(tmp1, tmp2)
}
func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int {
@@ -642,29 +633,20 @@ func CalculateR(aR, sR, y, po2 []*big.Int, z, x *big.Int) []*big.Int {
log.Info("CalculateR: Arrays not of the same length")
}
- result := make([]*big.Int, len(aR))
-
z2 := new(big.Int).Exp(z, big.NewInt(2), EC.N)
tmp11 := VectorAddScalar(aR, z)
tmp12 := ScalarVectorMul(sR, x)
tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12))
tmp2 := ScalarVectorMul(po2, z2)
- result = VectorAdd(tmp1, tmp2)
-
- return result
+ return VectorAdd(tmp1, tmp2)
}
// Calculates (aL - z*1^n) + sL*x
func CalculateLMRP(aL, sL []*big.Int, z, x *big.Int) []*big.Int {
- result := make([]*big.Int, len(aL))
-
tmp1 := VectorAddScalar(aL, new(big.Int).Neg(z))
tmp2 := ScalarVectorMul(sL, x)
-
- result = VectorAdd(tmp1, tmp2)
-
- return result
+ return VectorAdd(tmp1, tmp2)
}
func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int {
@@ -672,15 +654,11 @@ func CalculateRMRP(aR, sR, y, zTimesTwo []*big.Int, z, x *big.Int) []*big.Int {
log.Info("CalculateRMRP: Arrays not of the same length")
}
- result := make([]*big.Int, len(aR))
-
tmp11 := VectorAddScalar(aR, z)
tmp12 := ScalarVectorMul(sR, x)
tmp1 := VectorHadamard(y, VectorAdd(tmp11, tmp12))
- result = VectorAdd(tmp1, zTimesTwo)
-
- return result
+ return VectorAdd(tmp1, zTimesTwo)
}
/*
@@ -689,8 +667,6 @@ DeltaMRP is a helper function that is used in the multi range proof
*/
func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int {
- result := big.NewInt(0)
-
// (z-z^2)<1^n, y^n>
z2 := new(big.Int).Mod(new(big.Int).Mul(z, z), EC.N)
t1 := new(big.Int).Mod(new(big.Int).Sub(z, z2), EC.N)
@@ -709,9 +685,7 @@ func DeltaMRP(y []*big.Int, z *big.Int, m int) *big.Int {
t3 = new(big.Int).Mod(new(big.Int).Add(t3, tmp1), EC.N)
}
- result = new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N)
-
- return result
+ return new(big.Int).Mod(new(big.Int).Sub(t2, t3), EC.N)
}
type MultiRangeProof struct {
@@ -985,7 +959,7 @@ func MRPProve(values []*big.Int) (MultiRangeProof, error) {
}
if !acceptedInputNumber {
- return MultiRangeProof{}, errors.New("Value number is not supported - just 1, 2, 4, 8")
+ return MultiRangeProof{}, errors.New("value number is not supported - just 1, 2, 4, 8")
}
EC = genECPrimeGroupKey(m * bitsPerValue)
@@ -1002,15 +976,15 @@ func MRPProve(values []*big.Int) (MultiRangeProof, error) {
for j := range values {
v := values[j]
if v.Cmp(big.NewInt(0)) == -1 {
- return MultiRangeProof{}, errors.New("Value is below range! Not proving")
+ return MultiRangeProof{}, errors.New("value is below range! Not proving")
}
if v.Cmp(MAX_64_BITS) == 1 {
- return MultiRangeProof{}, errors.New("Value is above range! Not proving")
+ return MultiRangeProof{}, errors.New("value is above range! Not proving")
}
if v.Cmp(new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bitsPerValue)), EC.N)) == 1 {
- return MultiRangeProof{}, errors.New("Value is above range! Not proving")
+ return MultiRangeProof{}, errors.New("value is above range! Not proving")
}
gamma, err := rand.Int(rand.Reader, EC.N)
diff --git a/core/vm/privacy/ringct.go b/core/vm/privacy/ringct.go
index 93c97c56ce..91ea3096c1 100644
--- a/core/vm/privacy/ringct.go
+++ b/core/vm/privacy/ringct.go
@@ -171,7 +171,7 @@ func (r *RingSignature) Serialize() ([]byte, error) {
}
if len(sig) != 8+8+32+32+(32+33)*r.NumRing*r.Size+33*r.NumRing {
- return []byte{}, errors.New("Could not serialize ring signature")
+ return []byte{}, errors.New("could not serialize ring signature")
}
return sig, nil
@@ -198,7 +198,7 @@ func computeSignatureSize(numRing int, ringSize int) int {
// deserializes the byteified signature into a RingSignature struct
func Deserialize(r []byte) (*RingSignature, error) {
if len(r) < 16 {
- return nil, errors.New("Failed to deserialize ring signature")
+ return nil, errors.New("failed to deserialize ring signature")
}
offset := 0
sig := new(RingSignature)
@@ -455,7 +455,7 @@ func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingS
px, py := curve.ScalarMult(rings[j][idx].X, rings[j][idx].Y, PadTo32Bytes(C[idx].Bytes())) // px, py = c_i*P_i
sx, sy := curve.ScalarBaseMult(PadTo32Bytes(S[j][idx].Bytes())) // sx, sy = s[n-1]*G
if px == nil || py == nil || sx == nil || sy == nil {
- return nil, errors.New("Could not create ring signature")
+ return nil, errors.New("could not create ring signature")
}
l_x, l_y := curve.Add(sx, sy, px, py)
L[j][idx] = &ecdsa.PublicKey{curve, l_x, l_y}
@@ -467,7 +467,7 @@ func Sign(m [32]byte, rings []Ring, privkeys []*ecdsa.PrivateKey, s int) (*RingS
hx, hy := HashPoint(rings[j][idx])
sx, sy = curve.ScalarMult(hx, hy, S[j][idx].Bytes()) // sx, sy = s[n-1]*H_p(P_i)
if px == nil || py == nil || sx == nil || sy == nil {
- return nil, errors.New("Could not create ring signature")
+ return nil, errors.New("could not create ring signature")
}
r_x, r_y := curve.Add(sx, sy, px, py)
R[j][idx] = &ecdsa.PublicKey{curve, r_x, r_y}
diff --git a/core/vm/privacy/ringct_test.go b/core/vm/privacy/ringct_test.go
index 3e5b771c0b..b3370497a0 100644
--- a/core/vm/privacy/ringct_test.go
+++ b/core/vm/privacy/ringct_test.go
@@ -21,7 +21,7 @@ func TestSign(t *testing.T) {
ringSize := 10
s := 9
fmt.Println("Generate random ring parameter ")
- rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s)
+ rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s)
fmt.Println("numRing ", numRing)
fmt.Println("ringSize ", ringSize)
@@ -57,7 +57,7 @@ func TestDeserialize(t *testing.T) {
numRing := 5
ringSize := 10
s := 5
- rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s)
+ rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s)
ringSignature, err := Sign(m, rings, privkeys, s)
if err != nil {
@@ -235,7 +235,7 @@ func TestOnCurveVerify(t *testing.T) {
numRing := 5
ringSize := 10
s := 5
- rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s)
+ rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s)
ringSignature, err := Sign(m, rings, privkeys, s)
if err != nil {
t.Error("Failed to create Ring signature")
@@ -259,7 +259,7 @@ func TestOnCurveDeserialize(t *testing.T) {
numRing := 5
ringSize := 10
s := 5
- rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s)
+ rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s)
ringSignature, err := Sign(m, rings, privkeys, s)
if err != nil {
t.Error("Failed to create Ring signature")
@@ -304,7 +304,7 @@ func TestNilPointerDereferencePanic(t *testing.T) {
numRing := 5
ringSize := 10
s := 7
- rings, privkeys, m, err := GenerateMultiRingParams(numRing, ringSize, s)
+ rings, privkeys, m, _ := GenerateMultiRingParams(numRing, ringSize, s)
ringSig, err := Sign(m, rings, privkeys, s)
if err != nil {
@@ -318,7 +318,7 @@ func TestNilPointerDereferencePanic(t *testing.T) {
t.Error("Failed to Serialize input Ring signature")
}
- _ , err = Deserialize(sig)
+ _, err = Deserialize(sig)
// Should failed to verify Ring signature as the signature is invalid
assert.EqualError(t, err, "failed to deserialize, invalid ring signature")
}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 24d3a79301..d525e8b312 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -22,18 +22,21 @@ import (
)
func NewEnv(cfg *Config) *vm.EVM {
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: cfg.Origin,
+ GasPrice: cfg.GasPrice,
+ }
+ blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
GetHash: cfg.GetHashFn,
- Origin: cfg.Origin,
Coinbase: cfg.Coinbase,
BlockNumber: cfg.BlockNumber,
Time: cfg.Time,
Difficulty: cfg.Difficulty,
GasLimit: cfg.GasLimit,
- GasPrice: cfg.GasPrice,
+ BaseFee: cfg.BaseFee,
}
- return vm.NewEVM(context, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig)
+ return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig)
}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index a8741fa12d..5e7d7b8f55 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -43,6 +43,7 @@ type Config struct {
Value *big.Int
Debug bool
EVMConfig vm.Config
+ BaseFee *big.Int
State *state.StateDB
GetHashFn func(n uint64) common.Hash
@@ -52,13 +53,23 @@ type Config struct {
func setDefaults(cfg *Config) {
if cfg.ChainConfig == nil {
cfg.ChainConfig = ¶ms.ChainConfig{
- ChainId: big.NewInt(1),
- HomesteadBlock: new(big.Int),
- DAOForkBlock: new(big.Int),
- DAOForkSupport: false,
- EIP150Block: new(big.Int),
- EIP155Block: new(big.Int),
- EIP158Block: new(big.Int),
+ ChainId: big.NewInt(1),
+ HomesteadBlock: new(big.Int),
+ DAOForkBlock: new(big.Int),
+ DAOForkSupport: false,
+ EIP150Block: new(big.Int),
+ EIP150Hash: common.Hash{},
+ EIP155Block: new(big.Int),
+ EIP158Block: new(big.Int),
+ ByzantiumBlock: new(big.Int),
+ ConstantinopleBlock: new(big.Int),
+ PetersburgBlock: new(big.Int),
+ IstanbulBlock: new(big.Int),
+ BerlinBlock: new(big.Int),
+ LondonBlock: new(big.Int),
+ MergeBlock: new(big.Int),
+ ShanghaiBlock: new(big.Int),
+ Eip1559Block: new(big.Int),
}
}
@@ -85,6 +96,9 @@ func setDefaults(cfg *Config) {
return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
}
}
+ if cfg.BaseFee == nil {
+ cfg.BaseFee = big.NewInt(params.InitialBaseFee)
+ }
}
// Execute executes the code using the input as call data during the execution.
@@ -106,10 +120,13 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
address = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
)
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
- cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
- }
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
+
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(address, code)
@@ -121,7 +138,6 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.GasLimit,
cfg.Value,
)
-
return ret, cfg.State, err
}
@@ -139,10 +155,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
var (
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
)
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
- cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
- }
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
+
// Call the code with the given configuration.
code, address, leftOverGas, err := vmenv.Create(
sender,
@@ -161,13 +180,16 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) {
setDefaults(cfg)
- vmenv := NewEnv(cfg)
-
- sender := cfg.State.GetOrNewStateObject(cfg.Origin)
- statedb := cfg.State
- if rules := cfg.ChainConfig.Rules(vmenv.Context.BlockNumber); rules.IsEIP1559 {
- statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
- }
+ var (
+ vmenv = NewEnv(cfg)
+ sender = cfg.State.GetOrNewStateObject(cfg.Origin)
+ statedb = cfg.State
+ rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
+ )
+ // Execute the preparatory steps for state transition which includes:
+ // - prepare accessList(post-berlin)
+ // - reset transient storage(eip 1153)
+ statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
// Call the code with the given configuration.
ret, leftOverGas, err := vmenv.Call(
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 826b431f48..48c707f006 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -374,7 +374,6 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.
// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
// 55 ms
func BenchmarkSimpleLoop(b *testing.B) {
-
staticCallIdentity := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
@@ -453,7 +452,7 @@ func BenchmarkSimpleLoop(b *testing.B) {
byte(vm.JUMP),
}
- calllRevertingContractWithInput := []byte{
+ callRevertingContractWithInput := []byte{
byte(vm.JUMPDEST), //
// push args for the call
byte(vm.PUSH1), 0, // out size
@@ -481,7 +480,7 @@ func BenchmarkSimpleLoop(b *testing.B) {
benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b)
benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b)
benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b)
- benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b)
+ benchmarkNonModifyingCode(100000000, callRevertingContractWithInput, "call-reverting-100M", b)
//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
@@ -498,7 +497,7 @@ func TestEip2929Cases(t *testing.T) {
it := asm.NewInstructionIterator(code)
for it.Next() {
if it.Arg() != nil && 0 < len(it.Arg()) {
- instrs = append(instrs, fmt.Sprintf("%v 0x%x", it.Op(), it.Arg()))
+ instrs = append(instrs, fmt.Sprintf("%v %#x", it.Op(), it.Arg()))
} else {
instrs = append(instrs, fmt.Sprintf("%v", it.Op()))
}
@@ -506,7 +505,7 @@ func TestEip2929Cases(t *testing.T) {
ops := strings.Join(instrs, ", ")
fmt.Printf("### Case %d\n\n", id)
id++
- fmt.Printf("%v\n\nBytecode: \n```\n0x%x\n```\nOperations: \n```\n%v\n```\n\n",
+ fmt.Printf("%v\n\nBytecode: \n```\n%#x\n```\nOperations: \n```\n%v\n```\n\n",
comment,
code, ops)
Execute(code, nil, &Config{
@@ -598,3 +597,83 @@ func TestEip2929Cases(t *testing.T) {
"account (cheap)", code)
}
}
+
+// TestColdAccountAccessCost test that the cold account access cost is reported
+// correctly
+// see: https://github.com/ethereum/go-ethereum/issues/22649
+func TestColdAccountAccessCost(t *testing.T) {
+ for i, tc := range []struct {
+ code []byte
+ step int
+ want uint64
+ }{
+ { // EXTCODEHASH(0xff)
+ code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.EXTCODEHASH), byte(vm.POP)},
+ step: 1,
+ want: 2600,
+ },
+ { // BALANCE(0xff)
+ code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.BALANCE), byte(vm.POP)},
+ step: 1,
+ want: 2600,
+ },
+ { // CALL(0xff)
+ code: []byte{
+ byte(vm.PUSH1), 0x0,
+ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
+ byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
+ },
+ step: 7,
+ want: 2855,
+ },
+ { // CALLCODE(0xff)
+ code: []byte{
+ byte(vm.PUSH1), 0x0,
+ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
+ byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
+ },
+ step: 7,
+ want: 2855,
+ },
+ { // DELEGATECALL(0xff)
+ code: []byte{
+ byte(vm.PUSH1), 0x0,
+ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
+ byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
+ },
+ step: 6,
+ want: 2855,
+ },
+ { // STATICCALL(0xff)
+ code: []byte{
+ byte(vm.PUSH1), 0x0,
+ byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
+ byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
+ },
+ step: 6,
+ want: 2855,
+ },
+ { // SELFDESTRUCT(0xff)
+ code: []byte{
+ byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
+ },
+ step: 1,
+ want: 7600,
+ },
+ } {
+ tracer := vm.NewStructLogger(nil)
+ Execute(tc.code, nil, &Config{
+ EVMConfig: vm.Config{
+ Debug: true,
+ Tracer: tracer,
+ },
+ })
+ have := tracer.StructLogs()[tc.step].GasCost
+ if want := tc.want; have != want {
+ for ii, op := range tracer.StructLogs() {
+ t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
+ }
+ t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
+ }
+ }
+}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index a389c04b72..e1a957e244 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -17,9 +17,17 @@
package vm
import (
+ "sync"
+
"github.com/holiman/uint256"
)
+var stackPool = sync.Pool{
+ New: func() interface{} {
+ return &Stack{data: make([]uint256.Int, 0, 16)}
+ },
+}
+
// Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
@@ -28,7 +36,12 @@ type Stack struct {
}
func newstack() *Stack {
- return &Stack{data: make([]uint256.Int, 0, 16)}
+ return stackPool.Get().(*Stack)
+}
+
+func returnStack(s *Stack) {
+ s.data = s.data[:0]
+ stackPool.Put(s)
}
// Data returns the underlying uint256.Int array.
diff --git a/core/vm/testdata/precompiles/modexp_eip2565.json b/core/vm/testdata/precompiles/modexp_eip2565.json
new file mode 100644
index 0000000000..c55441439e
--- /dev/null
+++ b/core/vm/testdata/precompiles/modexp_eip2565.json
@@ -0,0 +1,121 @@
+[
+ {
+ "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
+ "Expected": "0000000000000000000000000000000000000000000000000000000000000001",
+ "Name": "eip_example1",
+ "Gas": 1360,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
+ "Expected": "0000000000000000000000000000000000000000000000000000000000000000",
+ "Name": "eip_example2",
+ "Gas": 1360,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
+ "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc",
+ "Name": "nagydani-1-square",
+ "Gas": 200,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
+ "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec",
+ "Name": "nagydani-1-qube",
+ "Gas": 200,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b",
+ "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2",
+ "Name": "nagydani-1-pow0x10001",
+ "Gas": 341,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
+ "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70",
+ "Name": "nagydani-2-square",
+ "Gas": 200,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
+ "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f",
+ "Name": "nagydani-2-qube",
+ "Gas": 200,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087",
+ "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2",
+ "Name": "nagydani-2-pow0x10001",
+ "Gas": 1365,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
+ "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8",
+ "Name": "nagydani-3-square",
+ "Gas": 341,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
+ "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e",
+ "Name": "nagydani-3-qube",
+ "Gas": 341,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d",
+ "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76",
+ "Name": "nagydani-3-pow0x10001",
+ "Gas": 5461,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
+ "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10",
+ "Name": "nagydani-4-square",
+ "Gas": 1365,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
+ "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc",
+ "Name": "nagydani-4-qube",
+ "Gas": 1365,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f",
+ "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963",
+ "Name": "nagydani-4-pow0x10001",
+ "Gas": 21845,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
+ "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb",
+ "Name": "nagydani-5-square",
+ "Gas": 5461,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
+ "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f",
+ "Name": "nagydani-5-qube",
+ "Gas": 5461,
+ "NoBenchmark": false
+ },
+ {
+ "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad",
+ "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500",
+ "Name": "nagydani-5-pow0x10001",
+ "Gas": 87381,
+ "NoBenchmark": false
+ }
+]
\ No newline at end of file
diff --git a/crypto/blake2b/blake2b.go b/crypto/blake2b/blake2b.go
index 5da50cab6f..7ecaab8139 100644
--- a/crypto/blake2b/blake2b.go
+++ b/crypto/blake2b/blake2b.go
@@ -302,6 +302,7 @@ func appendUint64(b []byte, x uint64) []byte {
return append(b, a[:]...)
}
+//nolint:unused,deadcode
func appendUint32(b []byte, x uint32) []byte {
var a [4]byte
binary.BigEndian.PutUint32(a[:], x)
@@ -313,6 +314,7 @@ func consumeUint64(b []byte) ([]byte, uint64) {
return b[8:], x
}
+//nolint:unused,deadcode
func consumeUint32(b []byte) ([]byte, uint32) {
x := binary.BigEndian.Uint32(b)
return b[4:], x
diff --git a/crypto/blake2b/blake2bAVX2_amd64.go b/crypto/blake2b/blake2bAVX2_amd64.go
index 0d52b18699..3a85d0e73a 100644
--- a/crypto/blake2b/blake2bAVX2_amd64.go
+++ b/crypto/blake2b/blake2bAVX2_amd64.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.7 && amd64 && !gccgo && !appengine
// +build go1.7,amd64,!gccgo,!appengine
package blake2b
diff --git a/crypto/blake2b/blake2b_amd64.go b/crypto/blake2b/blake2b_amd64.go
index 4dbe90da8f..a318b2b617 100644
--- a/crypto/blake2b/blake2b_amd64.go
+++ b/crypto/blake2b/blake2b_amd64.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !go1.7 && amd64 && !gccgo && !appengine
// +build !go1.7,amd64,!gccgo,!appengine
package blake2b
diff --git a/crypto/blake2b/blake2b_f_fuzz.go b/crypto/blake2b/blake2b_f_fuzz_test.go
similarity index 55%
rename from crypto/blake2b/blake2b_f_fuzz.go
rename to crypto/blake2b/blake2b_f_fuzz_test.go
index ab73342803..1de9a62de9 100644
--- a/crypto/blake2b/blake2b_f_fuzz.go
+++ b/crypto/blake2b/blake2b_f_fuzz_test.go
@@ -1,15 +1,24 @@
-// +build gofuzz
+// Only enable fuzzer on platforms with AVX enabled
+//go:build go1.7 && amd64 && !gccgo && !appengine
+// +build go1.7,amd64,!gccgo,!appengine
package blake2b
import (
"encoding/binary"
+ "testing"
)
-func Fuzz(data []byte) int {
+func Fuzz(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzz(data)
+ })
+}
+
+func fuzz(data []byte) {
// Make sure the data confirms to the input model
if len(data) != 211 {
- return 0
+ return
}
// Parse everything and call all the implementations
var (
@@ -20,6 +29,7 @@ func Fuzz(data []byte) int {
t [2]uint64
f uint64
)
+
for i := 0; i < 8; i++ {
offset := 2 + i*8
h[i] = binary.LittleEndian.Uint64(data[offset : offset+8])
@@ -34,24 +44,32 @@ func Fuzz(data []byte) int {
if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1
f = 0xFFFFFFFFFFFFFFFF
}
+
// Run the blake2b compression on all instruction sets and cross reference
want := h
fGeneric(&want, &m, t[0], t[1], f, uint64(rounds))
have := h
- fSSE4(&have, &m, t[0], t[1], f, uint64(rounds))
- if have != want {
- panic("SSE4 mismatches generic algo")
+ if useSSE4 {
+ fSSE4(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("SSE4 mismatches generic algo")
+ }
}
- have = h
- fAVX(&have, &m, t[0], t[1], f, uint64(rounds))
- if have != want {
- panic("AVX mismatches generic algo")
+
+ if useAVX {
+ have = h
+ fAVX(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("AVX mismatches generic algo")
+ }
}
- have = h
- fAVX2(&have, &m, t[0], t[1], f, uint64(rounds))
- if have != want {
- panic("AVX2 mismatches generic algo")
+
+ if useAVX2 {
+ have = h
+ fAVX2(&have, &m, t[0], t[1], f, uint64(rounds))
+ if have != want {
+ panic("AVX2 mismatches generic algo")
+ }
}
- return 1
}
diff --git a/crypto/blake2b/blake2b_generic.go b/crypto/blake2b/blake2b_generic.go
index 35c40cc924..61e678fdf5 100644
--- a/crypto/blake2b/blake2b_generic.go
+++ b/crypto/blake2b/blake2b_generic.go
@@ -25,6 +25,7 @@ var precomputed = [10][16]byte{
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
}
+// nolint:unused,deadcode
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
var m [16]uint64
c0, c1 := c[0], c[1]
diff --git a/crypto/blake2b/blake2b_ref.go b/crypto/blake2b/blake2b_ref.go
index 9d0ade473a..095c71a648 100644
--- a/crypto/blake2b/blake2b_ref.go
+++ b/crypto/blake2b/blake2b_ref.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !amd64 || appengine || gccgo
// +build !amd64 appengine gccgo
package blake2b
diff --git a/crypto/blake2b/blake2b_test.go b/crypto/blake2b/blake2b_test.go
index 9e7297da16..9d24444a27 100644
--- a/crypto/blake2b/blake2b_test.go
+++ b/crypto/blake2b/blake2b_test.go
@@ -14,14 +14,6 @@ import (
"testing"
)
-func fromHex(s string) []byte {
- b, err := hex.DecodeString(s)
- if err != nil {
- panic(err)
- }
- return b
-}
-
func TestHashes(t *testing.T) {
defer func(sse4, avx, avx2 bool) {
useSSE4, useAVX, useAVX2 = sse4, avx, avx2
diff --git a/crypto/blake2b/register.go b/crypto/blake2b/register.go
index efd689af4b..9d8633963c 100644
--- a/crypto/blake2b/register.go
+++ b/crypto/blake2b/register.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.9
// +build go1.9
package blake2b
diff --git a/crypto/bn256/bn256_fast.go b/crypto/bn256/bn256_fast.go
index be0264a730..9ea5d8fbd5 100644
--- a/crypto/bn256/bn256_fast.go
+++ b/crypto/bn256/bn256_fast.go
@@ -14,22 +14,25 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build amd64 || arm64
// +build amd64 arm64
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
package bn256
-import "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare"
+import (
+ bn256cf "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare"
+)
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
-type G1 = bn256.G1
+type G1 = bn256cf.G1
// G2 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
-type G2 = bn256.G2
+type G2 = bn256cf.G2
// PairingCheck calculates the Optimal Ate pairing for a set of points.
func PairingCheck(a []*G1, b []*G2) bool {
- return bn256.PairingCheck(a, b)
+ return bn256cf.PairingCheck(a, b)
}
diff --git a/crypto/bn256/bn256_fuzz.go b/crypto/bn256/bn256_fuzz.go
deleted file mode 100644
index c3e1833ef2..0000000000
--- a/crypto/bn256/bn256_fuzz.go
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// +build gofuzz
-
-package bn256
-
-import (
- "bytes"
- "fmt"
- "io"
- "math/big"
-
- cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare"
- google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google"
-)
-
-func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1) {
- _, xc, err := cloudflare.RandomG1(input)
- if err != nil {
- // insufficient input
- return nil, nil
- }
- xg := new(google.G1)
- if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
- panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err))
- }
- return xc, xg
-}
-
-func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2) {
- _, xc, err := cloudflare.RandomG2(input)
- if err != nil {
- // insufficient input
- return nil, nil
- }
- xg := new(google.G2)
- if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
- panic(fmt.Sprintf("Could not marshal cloudflare -> google:", err))
- }
- return xc, xg
-}
-
-// FuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries.
-func FuzzAdd(data []byte) int {
- input := bytes.NewReader(data)
- xc, xg := getG1Points(input)
- if xc == nil {
- return 0
- }
- yc, yg := getG1Points(input)
- if yc == nil {
- return 0
- }
- // Ensure both libs can parse the second curve point
- // Add the two points and ensure they result in the same output
- rc := new(cloudflare.G1)
- rc.Add(xc, yc)
-
- rg := new(google.G1)
- rg.Add(xg, yg)
-
- if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
- panic("add mismatch")
- }
- return 1
-}
-
-// FuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare
-// libraries.
-func FuzzMul(data []byte) int {
- input := bytes.NewReader(data)
- pc, pg := getG1Points(input)
- if pc == nil {
- return 0
- }
- // Add the two points and ensure they result in the same output
- remaining := input.Len()
- if remaining == 0 {
- return 0
- }
- buf := make([]byte, remaining)
- input.Read(buf)
-
- rc := new(cloudflare.G1)
- rc.ScalarMult(pc, new(big.Int).SetBytes(buf))
-
- rg := new(google.G1)
- rg.ScalarMult(pg, new(big.Int).SetBytes(buf))
-
- if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
- panic("scalar mul mismatch")
- }
- return 1
-}
-
-func FuzzPair(data []byte) int {
- input := bytes.NewReader(data)
- pc, pg := getG1Points(input)
- if pc == nil {
- return 0
- }
- tc, tg := getG2Points(input)
- if tc == nil {
- return 0
- }
- // Pair the two points and ensure thet result in the same output
- if cloudflare.PairingCheck([]*cloudflare.G1{pc}, []*cloudflare.G2{tc}) != google.PairingCheck([]*google.G1{pg}, []*google.G2{tg}) {
- panic("pair mismatch")
- }
- return 1
-}
diff --git a/crypto/bn256/bn256_slow.go b/crypto/bn256/bn256_slow.go
index cf1053562d..273c248d14 100644
--- a/crypto/bn256/bn256_slow.go
+++ b/crypto/bn256/bn256_slow.go
@@ -14,12 +14,13 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build !amd64 && !arm64
// +build !amd64,!arm64
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
package bn256
-import "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google"
+import bn256 "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google"
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
diff --git a/crypto/bn256/cloudflare/bn256.go b/crypto/bn256/cloudflare/bn256.go
index a6dd972ba8..4f607af2ad 100644
--- a/crypto/bn256/cloudflare/bn256.go
+++ b/crypto/bn256/cloudflare/bn256.go
@@ -9,8 +9,13 @@
//
// This package specifically implements the Optimal Ate pairing over a 256-bit
// Barreto-Naehrig curve as described in
-// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
-// with the implementation described in that paper.
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not
+// compatible with the implementation described in that paper, as different
+// parameters are chosen.
+//
+// (This package previously claimed to operate at a 128-bit security level.
+// However, recent improvements in attacks mean that is no longer true. See
+// https://moderncrypto.org/mail-archive/curves/2016/000740.html.)
package bn256
import (
diff --git a/crypto/bn256/cloudflare/constants.go b/crypto/bn256/cloudflare/constants.go
index 5122aae64f..f7d2c7c001 100644
--- a/crypto/bn256/cloudflare/constants.go
+++ b/crypto/bn256/cloudflare/constants.go
@@ -13,10 +13,13 @@ func bigFromBase10(s string) *big.Int {
return n
}
-// u is the BN parameter that determines the prime: 1868033³.
+// u is the BN parameter.
var u = bigFromBase10("4965661367192848881")
// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
+// Needs to be highly 2-adic for efficient SNARK key and proof generation.
+// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081.
+// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters.
var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617")
// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
diff --git a/crypto/bn256/cloudflare/gfp6.go b/crypto/bn256/cloudflare/gfp6.go
index 83d61b781f..a42734911c 100644
--- a/crypto/bn256/cloudflare/gfp6.go
+++ b/crypto/bn256/cloudflare/gfp6.go
@@ -5,7 +5,7 @@ package bn256
// http://eprint.iacr.org/2006/471.pdf.
// gfP6 implements the field of size p⁶ as a cubic extension of gfP2 where τ³=ξ
-// and ξ=i+3.
+// and ξ=i+9.
type gfP6 struct {
x, y, z gfP2 // value is xτ² + yτ + z
}
diff --git a/crypto/bn256/cloudflare/gfp_decl.go b/crypto/bn256/cloudflare/gfp_decl.go
index fdea5c11a5..1954d14a4a 100644
--- a/crypto/bn256/cloudflare/gfp_decl.go
+++ b/crypto/bn256/cloudflare/gfp_decl.go
@@ -1,3 +1,4 @@
+//go:build (amd64 && !generic) || (arm64 && !generic)
// +build amd64,!generic arm64,!generic
package bn256
@@ -9,10 +10,10 @@ import (
"golang.org/x/sys/cpu"
)
-//nolint:varcheck
+//nolint:varcheck,unused,deadcode
var hasBMI2 = cpu.X86.HasBMI2
-// go:noescape
+//go:noescape
func gfpNeg(c, a *gfP)
//go:noescape
diff --git a/crypto/bn256/cloudflare/gfp_generic.go b/crypto/bn256/cloudflare/gfp_generic.go
index 8e6be95961..7742dda4c9 100644
--- a/crypto/bn256/cloudflare/gfp_generic.go
+++ b/crypto/bn256/cloudflare/gfp_generic.go
@@ -1,3 +1,4 @@
+//go:build (!amd64 && !arm64) || generic
// +build !amd64,!arm64 generic
package bn256
diff --git a/crypto/bn256/cloudflare/optate.go b/crypto/bn256/cloudflare/optate.go
index b71e50e3a2..e8caa7a086 100644
--- a/crypto/bn256/cloudflare/optate.go
+++ b/crypto/bn256/cloudflare/optate.go
@@ -199,9 +199,8 @@ func miller(q *twistPoint, p *curvePoint) *gfP12 {
r = newR
r2.Square(&minusQ2.y)
- a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2)
+ a, b, c, _ = lineFunctionAdd(r, minusQ2, bAffine, r2)
mulLine(ret, a, b, c)
- r = newR
return ret
}
diff --git a/crypto/bn256/google/bn256.go b/crypto/bn256/google/bn256.go
index e0402e51f0..93953e23a9 100644
--- a/crypto/bn256/google/bn256.go
+++ b/crypto/bn256/google/bn256.go
@@ -12,8 +12,9 @@
//
// This package specifically implements the Optimal Ate pairing over a 256-bit
// Barreto-Naehrig curve as described in
-// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
-// with the implementation described in that paper.
+// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is not
+// compatible with the implementation described in that paper, as different
+// parameters are chosen.
//
// (This package previously claimed to operate at a 128-bit security level.
// However, recent improvements in attacks mean that is no longer true. See
@@ -165,7 +166,7 @@ type G2 struct {
p *twistPoint
}
-// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r.
+// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
func RandomG2(r io.Reader) (*big.Int, *G2, error) {
var k *big.Int
var err error
diff --git a/crypto/bn256/google/constants.go b/crypto/bn256/google/constants.go
index ab649d7f3f..2990bd9512 100644
--- a/crypto/bn256/google/constants.go
+++ b/crypto/bn256/google/constants.go
@@ -13,13 +13,16 @@ func bigFromBase10(s string) *big.Int {
return n
}
-// u is the BN parameter that determines the prime: 1868033³.
+// u is the BN parameter that determines the prime.
var u = bigFromBase10("4965661367192848881")
-// p is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
+// P is a prime over which we form a basic field: 36u⁴+36u³+24u²+6u+1.
var P = bigFromBase10("21888242871839275222246405745257275088696311157297823662689037894645226208583")
// Order is the number of elements in both G₁ and G₂: 36u⁴+36u³+18u²+6u+1.
+// Needs to be highly 2-adic for efficient SNARK key and proof generation.
+// Order - 1 = 2^28 * 3^2 * 13 * 29 * 983 * 11003 * 237073 * 405928799 * 1670836401704629 * 13818364434197438864469338081.
+// Refer to https://eprint.iacr.org/2013/879.pdf and https://eprint.iacr.org/2013/507.pdf for more information on these parameters.
var Order = bigFromBase10("21888242871839275222246405745257275088548364400416034343698204186575808495617")
// xiToPMinus1Over6 is ξ^((p-1)/6) where ξ = i+9.
diff --git a/crypto/crypto.go b/crypto/crypto.go
index f8387cb733..22849aa6ea 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -26,7 +26,6 @@ import (
"fmt"
"hash"
"io"
- "io/ioutil"
"math/big"
"os"
@@ -250,7 +249,7 @@ func checkKeyFileEnd(r *bufio.Reader) error {
// restrictive permissions. The key data is saved hex-encoded.
func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
k := hex.EncodeToString(FromECDSA(key))
- return ioutil.WriteFile(file, []byte(k), 0600)
+ return os.WriteFile(file, []byte(k), 0600)
}
// GenerateKey generates a new private key.
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 9e1bb2639b..e0e66185ce 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -20,7 +20,6 @@ import (
"bytes"
"crypto/ecdsa"
"encoding/hex"
- "io/ioutil"
"math/big"
"os"
"reflect"
@@ -42,6 +41,13 @@ func TestKeccak256Hash(t *testing.T) {
checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp)
}
+func TestKeccak256Hasher(t *testing.T) {
+ msg := []byte("abc")
+ exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
+ hasher := NewKeccakState()
+ checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := HashData(hasher, in); return h[:] }, msg, exp)
+}
+
func TestToECDSAErrors(t *testing.T) {
if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil {
t.Fatal("HexToECDSA should've returned error")
@@ -175,7 +181,7 @@ func TestLoadECDSA(t *testing.T) {
}
for _, test := range tests {
- f, err := ioutil.TempFile("", "loadecdsa_test.*.txt")
+ f, err := os.CreateTemp("", "loadecdsa_test.*.txt")
if err != nil {
t.Fatal(err)
}
@@ -196,7 +202,7 @@ func TestLoadECDSA(t *testing.T) {
}
func TestSaveECDSA(t *testing.T) {
- f, err := ioutil.TempFile("", "saveecdsa_test.*.txt")
+ f, err := os.CreateTemp("", "saveecdsa_test.*.txt")
if err != nil {
t.Fatal(err)
}
diff --git a/crypto/ecies/ecies.go b/crypto/ecies/ecies.go
index bb1c8d2ff4..738bb8f584 100644
--- a/crypto/ecies/ecies.go
+++ b/crypto/ecies/ecies.go
@@ -35,8 +35,8 @@ import (
"crypto/elliptic"
"crypto/hmac"
"crypto/subtle"
+ "encoding/binary"
"errors"
- "fmt"
"hash"
"io"
"math/big"
@@ -45,7 +45,6 @@ import (
var (
ErrImport = errors.New("ecies: failed to import key")
ErrInvalidCurve = errors.New("ecies: invalid elliptic curve")
- ErrInvalidParams = errors.New("ecies: invalid ECIES parameters")
ErrInvalidPublicKey = errors.New("ecies: invalid public key")
ErrSharedKeyIsPointAtInfinity = errors.New("ecies: shared key is point at infinity")
ErrSharedKeyTooBig = errors.New("ecies: shared key params are too big")
@@ -139,57 +138,39 @@ func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []b
}
var (
- ErrKeyDataTooLong = errors.New("ecies: can't supply requested key data")
ErrSharedTooLong = errors.New("ecies: shared secret is too long")
ErrInvalidMessage = errors.New("ecies: invalid message")
)
-var (
- big2To32 = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil)
- big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1))
-)
-
-func incCounter(ctr []byte) {
- if ctr[3]++; ctr[3] != 0 {
- return
- }
- if ctr[2]++; ctr[2] != 0 {
- return
- }
- if ctr[1]++; ctr[1] != 0 {
- return
- }
- if ctr[0]++; ctr[0] != 0 {
- return
- }
-}
-
// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
-func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) {
- if s1 == nil {
- s1 = make([]byte, 0)
- }
-
- reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8)
- if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 {
- fmt.Println(big2To32M1)
- return nil, ErrKeyDataTooLong
- }
-
- counter := []byte{0, 0, 0, 1}
- k = make([]byte, 0)
-
- for i := 0; i <= reps; i++ {
- hash.Write(counter)
+func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) []byte {
+ counterBytes := make([]byte, 4)
+ k := make([]byte, 0, roundup(kdLen, hash.Size()))
+ for counter := uint32(1); len(k) < kdLen; counter++ {
+ binary.BigEndian.PutUint32(counterBytes, counter)
+ hash.Reset()
+ hash.Write(counterBytes)
hash.Write(z)
hash.Write(s1)
- k = append(k, hash.Sum(nil)...)
- hash.Reset()
- incCounter(counter)
+ k = hash.Sum(k)
}
+ return k[:kdLen]
+}
- k = k[:kdLen]
- return
+// roundup rounds size up to the next multiple of blocksize.
+func roundup(size, blocksize int) int {
+ return size + blocksize - (size % blocksize)
+}
+
+// deriveKeys creates the encryption and MAC keys using concatKDF.
+func deriveKeys(hash hash.Hash, z, s1 []byte, keyLen int) (Ke, Km []byte) {
+ K := concatKDF(hash, z, s1, 2*keyLen)
+ Ke = K[:keyLen]
+ Km = K[keyLen:]
+ hash.Reset()
+ hash.Write(Km)
+ Km = hash.Sum(Km[:0])
+ return Ke, Km
}
// messageTag computes the MAC of a message (called the tag) as per
@@ -210,7 +191,6 @@ func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
}
// symEncrypt carries out CTR encryption using the block cipher specified in the
-// parameters.
func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
c, err := params.Cipher(key)
if err != nil {
@@ -250,36 +230,27 @@ func symDecrypt(params *ECIESParams, key, ct []byte) (m []byte, err error) {
// ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
// shared information parameters aren't being used, they should be nil.
func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
- params := pub.Params
- if params == nil {
- if params = ParamsFromCurve(pub.Curve); params == nil {
- err = ErrUnsupportedECIESParameters
- return
- }
+ params, err := pubkeyParams(pub)
+ if err != nil {
+ return nil, err
}
+
R, err := GenerateKey(rand, pub.Curve, params)
if err != nil {
- return
+ return nil, err
+ }
+
+ z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
+ if err != nil {
+ return nil, err
}
hash := params.Hash()
- z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
- if err != nil {
- return
- }
- K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
- if err != nil {
- return
- }
- Ke := K[:params.KeyLen]
- Km := K[params.KeyLen:]
- hash.Write(Km)
- Km = hash.Sum(nil)
- hash.Reset()
+ Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
em, err := symEncrypt(rand, params, Ke, m)
if err != nil || len(em) <= params.BlockSize {
- return
+ return nil, err
}
d := messageTag(params.Hash, Km, em, s2)
@@ -289,7 +260,7 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
copy(ct, Rb)
copy(ct[len(Rb):], em)
copy(ct[len(Rb)+len(em):], d)
- return
+ return ct, nil
}
// Decrypt decrypts an ECIES ciphertext.
@@ -297,13 +268,11 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
if len(c) == 0 {
return nil, ErrInvalidMessage
}
- params := prv.PublicKey.Params
- if params == nil {
- if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil {
- err = ErrUnsupportedECIESParameters
- return
- }
+ params, err := pubkeyParams(&prv.PublicKey)
+ if err != nil {
+ return nil, err
}
+
hash := params.Hash()
var (
@@ -317,12 +286,10 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
case 2, 3, 4:
rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
if len(c) < (rLen + hLen + 1) {
- err = ErrInvalidMessage
- return
+ return nil, ErrInvalidMessage
}
default:
- err = ErrInvalidPublicKey
- return
+ return nil, ErrInvalidPublicKey
}
mStart = rLen
@@ -332,36 +299,19 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
R.Curve = prv.PublicKey.Curve
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
if R.X == nil {
- err = ErrInvalidPublicKey
- return
- }
- if !R.Curve.IsOnCurve(R.X, R.Y) {
- err = ErrInvalidCurve
- return
+ return nil, ErrInvalidPublicKey
}
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
if err != nil {
- return
+ return nil, err
}
-
- K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
- if err != nil {
- return
- }
-
- Ke := K[:params.KeyLen]
- Km := K[params.KeyLen:]
- hash.Write(Km)
- Km = hash.Sum(nil)
- hash.Reset()
+ Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
- err = ErrInvalidMessage
- return
+ return nil, ErrInvalidMessage
}
- m, err = symDecrypt(params, Ke, c[mStart:mEnd])
- return
+ return symDecrypt(params, Ke, c[mStart:mEnd])
}
diff --git a/crypto/ecies/ecies_test.go b/crypto/ecies/ecies_test.go
index 0d3fb3bbac..83c08f3e57 100644
--- a/crypto/ecies/ecies_test.go
+++ b/crypto/ecies/ecies_test.go
@@ -36,32 +36,29 @@ import (
"crypto/sha256"
"encoding/hex"
"errors"
- "fmt"
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/crypto"
)
-//var dumpEnc bool
-//
-//func init() {
-// flDump := flag.Bool("dump", false, "write encrypted test message to file")
-// flag.Parse()
-// dumpEnc = *flDump
-//}
-
-// Ensure the KDF generates appropriately sized keys.
func TestKDF(t *testing.T) {
- msg := []byte("Hello, world")
- h := sha256.New()
-
- k, err := concatKDF(h, msg, nil, 64)
- if err != nil {
- t.Fatal(err)
+ tests := []struct {
+ length int
+ output []byte
+ }{
+ {6, decode("858b192fa2ed")},
+ {32, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0")},
+ {48, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955")},
+ {64, decode("858b192fa2ed4395e2bf88dd8d5770d67dc284ee539f12da8bceaa45d06ebae0700f1ab918a5f0413b8140f9940d6955f3467fd6672cce1024c5b1effccc0f61")},
}
- if len(k) != 64 {
- t.Fatalf("KDF: generated key is the wrong size (%d instead of 64\n", len(k))
+
+ for _, test := range tests {
+ h := sha256.New()
+ k := concatKDF(h, []byte("input"), nil, test.length)
+ if !bytes.Equal(k, test.output) {
+ t.Fatalf("KDF: generated key %x does not match expected output %x", k, test.output)
+ }
}
}
@@ -75,35 +72,6 @@ func cmpParams(p1, p2 *ECIESParams) bool {
p1.BlockSize == p2.BlockSize
}
-// cmpPublic returns true if the two public keys represent the same pojnt.
-func cmpPublic(pub1, pub2 PublicKey) bool {
- if pub1.X == nil || pub1.Y == nil {
- fmt.Println(ErrInvalidPublicKey.Error())
- return false
- }
- if pub2.X == nil || pub2.Y == nil {
- fmt.Println(ErrInvalidPublicKey.Error())
- return false
- }
- pub1Out := elliptic.Marshal(pub1.Curve, pub1.X, pub1.Y)
- pub2Out := elliptic.Marshal(pub2.Curve, pub2.X, pub2.Y)
-
- return bytes.Equal(pub1Out, pub2Out)
-}
-
-// cmpPrivate returns true if the two private keys are the same.
-func cmpPrivate(prv1, prv2 *PrivateKey) bool {
- if prv1 == nil || prv1.D == nil {
- return false
- } else if prv2 == nil || prv2.D == nil {
- return false
- } else if prv1.D.Cmp(prv2.D) != 0 {
- return false
- } else {
- return cmpPublic(prv1.PublicKey, prv2.PublicKey)
- }
-}
-
// Validate the ECDH component.
func TestSharedKey(t *testing.T) {
prv1, err := GenerateKey(rand.Reader, DefaultCurve, nil)
@@ -114,25 +82,21 @@ func TestSharedKey(t *testing.T) {
prv2, err := GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil {
- fmt.Println(err.Error())
- t.FailNow()
+ t.Fatal(err)
}
sk1, err := prv1.GenerateShared(&prv2.PublicKey, skLen, skLen)
if err != nil {
- fmt.Println(err.Error())
- t.FailNow()
+ t.Fatal(err)
}
sk2, err := prv2.GenerateShared(&prv1.PublicKey, skLen, skLen)
if err != nil {
- fmt.Println(err.Error())
- t.FailNow()
+ t.Fatal(err)
}
if !bytes.Equal(sk1, sk2) {
- fmt.Println(ErrBadSharedKeys.Error())
- t.FailNow()
+ t.Fatal(err)
}
}
@@ -315,7 +279,7 @@ var testCases = []testCase{
{
Curve: elliptic.P384(),
Name: "P384",
- Expected: ECIES_AES256_SHA384,
+ Expected: ECIES_AES192_SHA384,
},
{
Curve: elliptic.P521(),
@@ -335,8 +299,8 @@ func TestParamSelection(t *testing.T) {
func testParamSelection(t *testing.T, c testCase) {
params := ParamsFromCurve(c.Curve)
- if params == nil && c.Expected != nil {
- t.Fatalf("%s (%s)\n", ErrInvalidParams.Error(), c.Name)
+ if params == nil {
+ t.Fatal("ParamsFromCurve returned nil")
} else if params != nil && !cmpParams(params, c.Expected) {
t.Fatalf("ecies: parameters should be invalid (%s)\n", c.Name)
}
@@ -370,7 +334,6 @@ func testParamSelection(t *testing.T, c testCase) {
if err == nil {
t.Fatalf("ecies: encryption should not have succeeded (%s)\n", c.Name)
}
-
}
// Ensure that the basic public key validation in the decryption operation
@@ -456,3 +419,11 @@ func hexKey(prv string) *PrivateKey {
}
return ImportECDSA(key)
}
+
+func decode(s string) []byte {
+ bytes, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return bytes
+}
diff --git a/crypto/ecies/params.go b/crypto/ecies/params.go
index bd5969f1a9..49bc447355 100644
--- a/crypto/ecies/params.go
+++ b/crypto/ecies/params.go
@@ -40,6 +40,7 @@ import (
"crypto/sha256"
"crypto/sha512"
"errors"
+ "fmt"
"hash"
ethcrypto "github.com/XinFinOrg/XDPoSChain/crypto"
@@ -49,8 +50,14 @@ var (
DefaultCurve = ethcrypto.S256()
ErrUnsupportedECDHAlgorithm = errors.New("ecies: unsupported ECDH algorithm")
ErrUnsupportedECIESParameters = errors.New("ecies: unsupported ECIES parameters")
+ ErrInvalidKeyLen = fmt.Errorf("ecies: invalid key size (> %d) in ECIESParams", maxKeyLen)
)
+// KeyLen is limited to prevent overflow of the counter
+// in concatKDF. While the theoretical limit is much higher,
+// no known cipher uses keys larger than 512 bytes.
+const maxKeyLen = 512
+
type ECIESParams struct {
Hash func() hash.Hash // hash function
hashAlgo crypto.Hash
@@ -74,6 +81,14 @@ var (
KeyLen: 16,
}
+ ECIES_AES192_SHA384 = &ECIESParams{
+ Hash: sha512.New384,
+ hashAlgo: crypto.SHA384,
+ Cipher: aes.NewCipher,
+ BlockSize: aes.BlockSize,
+ KeyLen: 24,
+ }
+
ECIES_AES256_SHA256 = &ECIESParams{
Hash: sha256.New,
hashAlgo: crypto.SHA256,
@@ -102,7 +117,7 @@ var (
var paramsFromCurve = map[elliptic.Curve]*ECIESParams{
ethcrypto.S256(): ECIES_AES128_SHA256,
elliptic.P256(): ECIES_AES128_SHA256,
- elliptic.P384(): ECIES_AES256_SHA384,
+ elliptic.P384(): ECIES_AES192_SHA384,
elliptic.P521(): ECIES_AES256_SHA512,
}
@@ -115,3 +130,16 @@ func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {
func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
return paramsFromCurve[curve]
}
+
+func pubkeyParams(key *PublicKey) (*ECIESParams, error) {
+ params := key.Params
+ if params == nil {
+ if params = ParamsFromCurve(key.Curve); params == nil {
+ return nil, ErrUnsupportedECIESParameters
+ }
+ }
+ if params.KeyLen > maxKeyLen {
+ return nil, ErrInvalidKeyLen
+ }
+ return params, nil
+}
diff --git a/crypto/kzg4844/kzg4844.go b/crypto/kzg4844/kzg4844.go
new file mode 100644
index 0000000000..8424557b19
--- /dev/null
+++ b/crypto/kzg4844/kzg4844.go
@@ -0,0 +1,168 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package kzg4844 implements the KZG crypto for EIP-4844.
+package kzg4844
+
+import (
+ "embed"
+ "errors"
+ "hash"
+ "reflect"
+ "sync/atomic"
+
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+)
+
+//go:embed trusted_setup.json
+var content embed.FS
+
+var (
+ blobT = reflect.TypeOf(Blob{})
+ commitmentT = reflect.TypeOf(Commitment{})
+ proofT = reflect.TypeOf(Proof{})
+)
+
+// Blob represents a 4844 data blob.
+type Blob [131072]byte
+
+// UnmarshalJSON parses a blob in hex syntax.
+func (b *Blob) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(blobT, input, b[:])
+}
+
+// MarshalText returns the hex representation of b.
+func (b Blob) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(b[:]).MarshalText()
+}
+
+// Commitment is a serialized commitment to a polynomial.
+type Commitment [48]byte
+
+// UnmarshalJSON parses a commitment in hex syntax.
+func (c *Commitment) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:])
+}
+
+// MarshalText returns the hex representation of c.
+func (c Commitment) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(c[:]).MarshalText()
+}
+
+// Proof is a serialized commitment to the quotient polynomial.
+type Proof [48]byte
+
+// UnmarshalJSON parses a proof in hex syntax.
+func (p *Proof) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(proofT, input, p[:])
+}
+
+// MarshalText returns the hex representation of p.
+func (p Proof) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(p[:]).MarshalText()
+}
+
+// Point is a BLS field element.
+type Point [32]byte
+
+// Claim is a claimed evaluation value in a specific point.
+type Claim [32]byte
+
+// useCKZG controls whether the cryptography should use the Go or C backend.
+var useCKZG atomic.Bool
+
+// UseCKZG can be called to switch the default Go implementation of KZG to the C
+// library if for some reason the user wishes to do so (e.g. consensus bug in one
+// or the other).
+func UseCKZG(use bool) error {
+ if use && !ckzgAvailable {
+ return errors.New("CKZG unavailable on your platform")
+ }
+ useCKZG.Store(use)
+
+ // Initializing the library can take 2-4 seconds - and can potentially crash
+ // on CKZG and non-ADX CPUs - so might as well do it now and don't wait until
+ // a crypto operation is actually needed live.
+ if use {
+ ckzgIniter.Do(ckzgInit)
+ } else {
+ gokzgIniter.Do(gokzgInit)
+ }
+ return nil
+}
+
+// BlobToCommitment creates a small commitment out of a data blob.
+func BlobToCommitment(blob Blob) (Commitment, error) {
+ if useCKZG.Load() {
+ return ckzgBlobToCommitment(blob)
+ }
+ return gokzgBlobToCommitment(blob)
+}
+
+// ComputeProof computes the KZG proof at the given point for the polynomial
+// represented by the blob.
+func ComputeProof(blob Blob, point Point) (Proof, Claim, error) {
+ if useCKZG.Load() {
+ return ckzgComputeProof(blob, point)
+ }
+ return gokzgComputeProof(blob, point)
+}
+
+// VerifyProof verifies the KZG proof that the polynomial represented by the blob
+// evaluated at the given point is the claimed value.
+func VerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error {
+ if useCKZG.Load() {
+ return ckzgVerifyProof(commitment, point, claim, proof)
+ }
+ return gokzgVerifyProof(commitment, point, claim, proof)
+}
+
+// ComputeBlobProof returns the KZG proof that is used to verify the blob against
+// the commitment.
+//
+// This method does not verify that the commitment is correct with respect to blob.
+func ComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) {
+ if useCKZG.Load() {
+ return ckzgComputeBlobProof(blob, commitment)
+ }
+ return gokzgComputeBlobProof(blob, commitment)
+}
+
+// VerifyBlobProof verifies that the blob data corresponds to the provided commitment.
+func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
+ if useCKZG.Load() {
+ return ckzgVerifyBlobProof(blob, commitment, proof)
+ }
+ return gokzgVerifyBlobProof(blob, commitment, proof)
+}
+
+// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
+// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
+func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
+ if hasher.Size() != 32 {
+ panic("wrong hash size")
+ }
+ hasher.Reset()
+ hasher.Write(commit[:])
+ hasher.Sum(vh[:0])
+ vh[0] = 0x01 // version
+ return vh
+}
+
+// IsValidVersionedHash checks that h is a structurally-valid versioned blob hash.
+func IsValidVersionedHash(h []byte) bool {
+ return len(h) == 32 && h[0] == 0x01
+}
diff --git a/crypto/kzg4844/kzg4844_ckzg_cgo.go b/crypto/kzg4844/kzg4844_ckzg_cgo.go
new file mode 100644
index 0000000000..df211f94d4
--- /dev/null
+++ b/crypto/kzg4844/kzg4844_ckzg_cgo.go
@@ -0,0 +1,127 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+//go:build ckzg && !nacl && !js && cgo && !gofuzz
+
+package kzg4844
+
+import (
+ "encoding/json"
+ "errors"
+ "sync"
+
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ gokzg4844 "github.com/crate-crypto/go-kzg-4844"
+ ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
+)
+
+// ckzgAvailable signals whether the library was compiled into Geth.
+const ckzgAvailable = true
+
+// ckzgIniter ensures that we initialize the KZG library once before using it.
+var ckzgIniter sync.Once
+
+// ckzgInit initializes the KZG library with the provided trusted setup.
+func ckzgInit() {
+ config, err := content.ReadFile("trusted_setup.json")
+ if err != nil {
+ panic(err)
+ }
+ params := new(gokzg4844.JSONTrustedSetup)
+ if err = json.Unmarshal(config, params); err != nil {
+ panic(err)
+ }
+ if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
+ panic(err)
+ }
+ g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
+ for i, g1 := range params.SetupG1Lagrange {
+ copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
+ }
+ g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
+ for i, g2 := range params.SetupG2 {
+ copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
+ }
+ if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil {
+ panic(err)
+ }
+}
+
+// ckzgBlobToCommitment creates a small commitment out of a data blob.
+func ckzgBlobToCommitment(blob Blob) (Commitment, error) {
+ ckzgIniter.Do(ckzgInit)
+
+ commitment, err := ckzg4844.BlobToKZGCommitment((ckzg4844.Blob)(blob))
+ if err != nil {
+ return Commitment{}, err
+ }
+ return (Commitment)(commitment), nil
+}
+
+// ckzgComputeProof computes the KZG proof at the given point for the polynomial
+// represented by the blob.
+func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) {
+ ckzgIniter.Do(ckzgInit)
+
+ proof, claim, err := ckzg4844.ComputeKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes32)(point))
+ if err != nil {
+ return Proof{}, Claim{}, err
+ }
+ return (Proof)(proof), (Claim)(claim), nil
+}
+
+// ckzgVerifyProof verifies the KZG proof that the polynomial represented by the blob
+// evaluated at the given point is the claimed value.
+func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error {
+ ckzgIniter.Do(ckzgInit)
+
+ valid, err := ckzg4844.VerifyKZGProof((ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes32)(point), (ckzg4844.Bytes32)(claim), (ckzg4844.Bytes48)(proof))
+ if err != nil {
+ return err
+ }
+ if !valid {
+ return errors.New("invalid proof")
+ }
+ return nil
+}
+
+// ckzgComputeBlobProof returns the KZG proof that is used to verify the blob against
+// the commitment.
+//
+// This method does not verify that the commitment is correct with respect to blob.
+func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) {
+ ckzgIniter.Do(ckzgInit)
+
+ proof, err := ckzg4844.ComputeBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment))
+ if err != nil {
+ return Proof{}, err
+ }
+ return (Proof)(proof), nil
+}
+
+// ckzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment.
+func ckzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
+ ckzgIniter.Do(ckzgInit)
+
+ valid, err := ckzg4844.VerifyBlobKZGProof((ckzg4844.Blob)(blob), (ckzg4844.Bytes48)(commitment), (ckzg4844.Bytes48)(proof))
+ if err != nil {
+ return err
+ }
+ if !valid {
+ return errors.New("invalid proof")
+ }
+ return nil
+}
diff --git a/crypto/kzg4844/kzg4844_ckzg_nocgo.go b/crypto/kzg4844/kzg4844_ckzg_nocgo.go
new file mode 100644
index 0000000000..ed840c75bb
--- /dev/null
+++ b/crypto/kzg4844/kzg4844_ckzg_nocgo.go
@@ -0,0 +1,62 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+//go:build !ckzg || nacl || js || !cgo || gofuzz
+
+package kzg4844
+
+import "sync"
+
+// ckzgAvailable signals whether the library was compiled into Geth.
+const ckzgAvailable = false
+
+// ckzgIniter ensures that we initialize the KZG library once before using it.
+var ckzgIniter sync.Once
+
+// ckzgInit initializes the KZG library with the provided trusted setup.
+func ckzgInit() {
+ panic("unsupported platform")
+}
+
+// ckzgBlobToCommitment creates a small commitment out of a data blob.
+func ckzgBlobToCommitment(blob Blob) (Commitment, error) {
+ panic("unsupported platform")
+}
+
+// ckzgComputeProof computes the KZG proof at the given point for the polynomial
+// represented by the blob.
+func ckzgComputeProof(blob Blob, point Point) (Proof, Claim, error) {
+ panic("unsupported platform")
+}
+
+// ckzgVerifyProof verifies the KZG proof that the polynomial represented by the blob
+// evaluated at the given point is the claimed value.
+func ckzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error {
+ panic("unsupported platform")
+}
+
+// ckzgComputeBlobProof returns the KZG proof that is used to verify the blob against
+// the commitment.
+//
+// This method does not verify that the commitment is correct with respect to blob.
+func ckzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) {
+ panic("unsupported platform")
+}
+
+// ckzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment.
+func ckzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
+ panic("unsupported platform")
+}
diff --git a/crypto/kzg4844/kzg4844_gokzg.go b/crypto/kzg4844/kzg4844_gokzg.go
new file mode 100644
index 0000000000..3f03bb5273
--- /dev/null
+++ b/crypto/kzg4844/kzg4844_gokzg.go
@@ -0,0 +1,98 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package kzg4844
+
+import (
+ "encoding/json"
+ "sync"
+
+ gokzg4844 "github.com/crate-crypto/go-kzg-4844"
+)
+
+// context is the crypto primitive pre-seeded with the trusted setup parameters.
+var context *gokzg4844.Context
+
+// gokzgIniter ensures that we initialize the KZG library once before using it.
+var gokzgIniter sync.Once
+
+// gokzgInit initializes the KZG library with the provided trusted setup.
+func gokzgInit() {
+ config, err := content.ReadFile("trusted_setup.json")
+ if err != nil {
+ panic(err)
+ }
+ params := new(gokzg4844.JSONTrustedSetup)
+ if err = json.Unmarshal(config, params); err != nil {
+ panic(err)
+ }
+ context, err = gokzg4844.NewContext4096(params)
+ if err != nil {
+ panic(err)
+ }
+}
+
+// gokzgBlobToCommitment creates a small commitment out of a data blob.
+func gokzgBlobToCommitment(blob Blob) (Commitment, error) {
+ gokzgIniter.Do(gokzgInit)
+
+ commitment, err := context.BlobToKZGCommitment((gokzg4844.Blob)(blob), 0)
+ if err != nil {
+ return Commitment{}, err
+ }
+ return (Commitment)(commitment), nil
+}
+
+// gokzgComputeProof computes the KZG proof at the given point for the polynomial
+// represented by the blob.
+func gokzgComputeProof(blob Blob, point Point) (Proof, Claim, error) {
+ gokzgIniter.Do(gokzgInit)
+
+ proof, claim, err := context.ComputeKZGProof((gokzg4844.Blob)(blob), (gokzg4844.Scalar)(point), 0)
+ if err != nil {
+ return Proof{}, Claim{}, err
+ }
+ return (Proof)(proof), (Claim)(claim), nil
+}
+
+// gokzgVerifyProof verifies the KZG proof that the polynomial represented by the blob
+// evaluated at the given point is the claimed value.
+func gokzgVerifyProof(commitment Commitment, point Point, claim Claim, proof Proof) error {
+ gokzgIniter.Do(gokzgInit)
+
+ return context.VerifyKZGProof((gokzg4844.KZGCommitment)(commitment), (gokzg4844.Scalar)(point), (gokzg4844.Scalar)(claim), (gokzg4844.KZGProof)(proof))
+}
+
+// gokzgComputeBlobProof returns the KZG proof that is used to verify the blob against
+// the commitment.
+//
+// This method does not verify that the commitment is correct with respect to blob.
+func gokzgComputeBlobProof(blob Blob, commitment Commitment) (Proof, error) {
+ gokzgIniter.Do(gokzgInit)
+
+ proof, err := context.ComputeBlobKZGProof((gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), 0)
+ if err != nil {
+ return Proof{}, err
+ }
+ return (Proof)(proof), nil
+}
+
+// gokzgVerifyBlobProof verifies that the blob data corresponds to the provided commitment.
+func gokzgVerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
+ gokzgIniter.Do(gokzgInit)
+
+ return context.VerifyBlobKZGProof((gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
+}
diff --git a/crypto/kzg4844/kzg4844_test.go b/crypto/kzg4844/kzg4844_test.go
new file mode 100644
index 0000000000..fae8a7a76e
--- /dev/null
+++ b/crypto/kzg4844/kzg4844_test.go
@@ -0,0 +1,195 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package kzg4844
+
+import (
+ "crypto/rand"
+ "testing"
+
+ "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
+ gokzg4844 "github.com/crate-crypto/go-kzg-4844"
+)
+
+func randFieldElement() [32]byte {
+ bytes := make([]byte, 32)
+ _, err := rand.Read(bytes)
+ if err != nil {
+ panic("failed to get random field element")
+ }
+ var r fr.Element
+ r.SetBytes(bytes)
+
+ return gokzg4844.SerializeScalar(r)
+}
+
+func randBlob() Blob {
+ var blob Blob
+ for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize {
+ fieldElementBytes := randFieldElement()
+ copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:])
+ }
+ return blob
+}
+
+func TestCKZGWithPoint(t *testing.T) { testKZGWithPoint(t, true) }
+func TestGoKZGWithPoint(t *testing.T) { testKZGWithPoint(t, false) }
+func testKZGWithPoint(t *testing.T, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ t.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ blob := randBlob()
+
+ commitment, err := BlobToCommitment(blob)
+ if err != nil {
+ t.Fatalf("failed to create KZG commitment from blob: %v", err)
+ }
+ point := randFieldElement()
+ proof, claim, err := ComputeProof(blob, point)
+ if err != nil {
+ t.Fatalf("failed to create KZG proof at point: %v", err)
+ }
+ if err := VerifyProof(commitment, point, claim, proof); err != nil {
+ t.Fatalf("failed to verify KZG proof at point: %v", err)
+ }
+}
+
+func TestCKZGWithBlob(t *testing.T) { testKZGWithBlob(t, true) }
+func TestGoKZGWithBlob(t *testing.T) { testKZGWithBlob(t, false) }
+func testKZGWithBlob(t *testing.T, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ t.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ blob := randBlob()
+
+ commitment, err := BlobToCommitment(blob)
+ if err != nil {
+ t.Fatalf("failed to create KZG commitment from blob: %v", err)
+ }
+ proof, err := ComputeBlobProof(blob, commitment)
+ if err != nil {
+ t.Fatalf("failed to create KZG proof for blob: %v", err)
+ }
+ if err := VerifyBlobProof(blob, commitment, proof); err != nil {
+ t.Fatalf("failed to verify KZG proof for blob: %v", err)
+ }
+}
+
+func BenchmarkCKZGBlobToCommitment(b *testing.B) { benchmarkBlobToCommitment(b, true) }
+func BenchmarkGoKZGBlobToCommitment(b *testing.B) { benchmarkBlobToCommitment(b, false) }
+func benchmarkBlobToCommitment(b *testing.B, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ b.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ blob := randBlob()
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ BlobToCommitment(blob)
+ }
+}
+
+func BenchmarkCKZGComputeProof(b *testing.B) { benchmarkComputeProof(b, true) }
+func BenchmarkGoKZGComputeProof(b *testing.B) { benchmarkComputeProof(b, false) }
+func benchmarkComputeProof(b *testing.B, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ b.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ var (
+ blob = randBlob()
+ point = randFieldElement()
+ )
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ComputeProof(blob, point)
+ }
+}
+
+func BenchmarkCKZGVerifyProof(b *testing.B) { benchmarkVerifyProof(b, true) }
+func BenchmarkGoKZGVerifyProof(b *testing.B) { benchmarkVerifyProof(b, false) }
+func benchmarkVerifyProof(b *testing.B, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ b.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ var (
+ blob = randBlob()
+ point = randFieldElement()
+ commitment, _ = BlobToCommitment(blob)
+ proof, claim, _ = ComputeProof(blob, point)
+ )
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ VerifyProof(commitment, point, claim, proof)
+ }
+}
+
+func BenchmarkCKZGComputeBlobProof(b *testing.B) { benchmarkComputeBlobProof(b, true) }
+func BenchmarkGoKZGComputeBlobProof(b *testing.B) { benchmarkComputeBlobProof(b, false) }
+func benchmarkComputeBlobProof(b *testing.B, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ b.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ var (
+ blob = randBlob()
+ commitment, _ = BlobToCommitment(blob)
+ )
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ ComputeBlobProof(blob, commitment)
+ }
+}
+
+func BenchmarkCKZGVerifyBlobProof(b *testing.B) { benchmarkVerifyBlobProof(b, true) }
+func BenchmarkGoKZGVerifyBlobProof(b *testing.B) { benchmarkVerifyBlobProof(b, false) }
+func benchmarkVerifyBlobProof(b *testing.B, ckzg bool) {
+ if ckzg && !ckzgAvailable {
+ b.Skip("CKZG unavailable in this test build")
+ }
+ defer func(old bool) { useCKZG.Store(old) }(useCKZG.Load())
+ useCKZG.Store(ckzg)
+
+ var (
+ blob = randBlob()
+ commitment, _ = BlobToCommitment(blob)
+ proof, _ = ComputeBlobProof(blob, commitment)
+ )
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ VerifyBlobProof(blob, commitment, proof)
+ }
+}
diff --git a/crypto/kzg4844/trusted_setup.json b/crypto/kzg4844/trusted_setup.json
new file mode 100644
index 0000000000..c6d724efaf
--- /dev/null
+++ b/crypto/kzg4844/trusted_setup.json
@@ -0,0 +1,4167 @@
+{
+ "g1_lagrange": [
+ "0xa0413c0dcafec6dbc9f47d66785cf1e8c981044f7d13cfe3e4fcbb71b5408dfde6312493cb3c1d30516cb3ca88c03654",
+ "0x8b997fb25730d661918371bb41f2a6e899cac23f04fc5365800b75433c0a953250e15e7a98fb5ca5cc56a8cd34c20c57",
+ "0x83302852db89424d5699f3f157e79e91dc1380f8d5895c5a772bb4ea3a5928e7c26c07db6775203ce33e62a114adaa99",
+ "0xa759c48b7e4a685e735c01e5aa6ef9c248705001f470f9ad856cd87806983e917a8742a3bd5ee27db8d76080269b7c83",
+ "0x967f8dc45ebc3be14c8705f43249a30ff48e96205fb02ae28daeab47b72eb3f45df0625928582aa1eb4368381c33e127",
+ "0xa418eb1e9fb84cb32b370610f56f3cb470706a40ac5a47c411c464299c45c91f25b63ae3fcd623172aa0f273c0526c13",
+ "0x8f44e3f0387293bc7931e978165abbaed08f53acd72a0a23ac85f6da0091196b886233bcee5b4a194db02f3d5a9b3f78",
+ "0x97173434b336be73c89412a6d70d416e170ea355bf1956c32d464090b107c090ef2d4e1a467a5632fbc332eeb679bf2d",
+ "0xa24052ad8d55ad04bc5d951f78e14213435681594110fd18173482609d5019105b8045182d53ffce4fc29fc8810516c1",
+ "0xb950768136b260277590b5bec3f56bbc2f7a8bc383d44ce8600e85bf8cf19f479898bcc999d96dfbd2001ede01d94949",
+ "0x92ab8077871037bd3b57b95cbb9fb10eb11efde9191690dcac655356986fd02841d8fdb25396faa0feadfe3f50baf56d",
+ "0xa79b096dff98038ac30f91112dd14b78f8ad428268af36d20c292e2b3b6d9ed4fb28480bb04e465071cc67d05786b6d1",
+ "0xb9ff71461328f370ce68bf591aa7fb13027044f42a575517f3319e2be4aa4843fa281e756d0aa5645428d6dfa857cef2",
+ "0x8d765808c00b3543ff182e2d159c38ae174b12d1314da88ea08e13bd9d1c37184cb515e6bf6420531b5d41767987d7ce",
+ "0xb8c9a837d20c3b53e6f578e4a257bb7ef8fc43178614ec2a154915b267ad2be135981d01ed2ee1b5fbd9d9bb27f0800a",
+ "0xa9773d92cf23f65f98ef68f6cf95c72b53d0683af2f9bf886bb9036e4a38184b1131b26fd24397910b494fbef856f3aa",
+ "0xb41ebe38962d112da4a01bf101cb248d808fbd50aaf749fc7c151cf332032eb3e3bdbd716db899724b734d392f26c412",
+ "0x90fbb030167fb47dcc13d604a726c0339418567c1d287d1d87423fa0cb92eec3455fbb46bcbe2e697144a2d3972142e4",
+ "0xb11d298bd167464b35fb923520d14832bd9ed50ed841bf6d7618424fd6f3699190af21759e351b89142d355952149da1",
+ "0x8bc36066f69dc89f7c4d1e58d67497675050c6aa002244cebd9fc957ec5e364c46bab4735ea3db02b73b3ca43c96e019",
+ "0xab7ab92c5d4d773068e485aa5831941ebd63db7118674ca38089635f3b4186833af2455a6fb9ed2b745df53b3ce96727",
+ "0xaf191ca3089892cb943cd97cf11a51f38e38bd9be50844a4e8da99f27e305e876f9ed4ab0628e8ae3939066b7d34a15f",
+ "0xa3204c1747feabc2c11339a542195e7cb6628fd3964f846e71e2e3f2d6bb379a5e51700682ea1844eba12756adb13216",
+ "0x903a29883846b7c50c15968b20e30c471aeac07b872c40a4d19eb1a42da18b649d5bbfde4b4cf6225d215a461b0deb6d",
+ "0x8e6e9c15ffbf1e16e5865a5fef7ed751dc81957a9757b535cb38b649e1098cda25d42381dc4f776778573cdf90c3e6e0",
+ "0xa8f6dd26100b512a8c96c52e00715c4b2cb9ac457f17aed8ffe1cf1ea524068fe5a1ddf218149845fc1417b789ecfc98",
+ "0xa5b0ffc819451ea639cfd1c18cbc9365cc79368d3b2e736c0ae54eba2f0801e6eb0ee14a5f373f4a70ca463bdb696c09",
+ "0x879f91ccd56a1b9736fbfd20d8747354da743fb121f0e308a0d298ff0d9344431890e41da66b5009af3f442c636b4f43",
+ "0x81bf3a2d9755e206b515a508ac4d1109bf933c282a46a4ae4a1b4cb4a94e1d23642fad6bd452428845afa155742ade7e",
+ "0x8de778d4742f945df40004964e165592f9c6b1946263adcdd5a88b00244bda46c7bb49098c8eb6b3d97a0dd46148a8ca",
+ "0xb7a57b21d13121907ee28c5c1f80ee2e3e83a3135a8101e933cf57171209a96173ff5037f5af606e9fd6d066de6ed693",
+ "0xb0877d1963fd9200414a38753dffd9f23a10eb3198912790d7eddbc9f6b477019d52ddd4ebdcb9f60818db076938a5a9",
+ "0x88da2d7a6611bc16adc55fc1c377480c828aba4496c645e3efe0e1a67f333c05a0307f7f1d2df8ac013602c655c6e209",
+ "0x95719eb02e8a9dede1a888c656a778b1c69b7716fbe3d1538fe8afd4a1bc972183c7d32aa7d6073376f7701df80116d8",
+ "0x8e8a1ca971f2444b35af3376e85dccda3abb8e8e11d095d0a4c37628dfe5d3e043a377c3de68289ef142e4308e9941a0",
+ "0xb720caaff02f6d798ac84c4f527203e823ff685869e3943c979e388e1c34c3f77f5c242c6daa7e3b30e511aab917b866",
+ "0x86040d55809afeec10e315d1ad950d269d37cfee8c144cd8dd4126459e3b15a53b3e68df5981df3c2346d23c7b4baaf4",
+ "0x82d8cabf13ab853db0377504f0aec00dba3a5cd3119787e8ad378ddf2c40b022ecfc67c642b7acc8c1e3dd03ab50993e",
+ "0xb8d873927936719d2484cd03a6687d65697e17dcf4f0d5aed6f5e4750f52ef2133d4645894e7ebfc4ef6ce6788d404c8",
+ "0xb1235594dbb15b674a419ff2b2deb644ad2a93791ca05af402823f87114483d6aa1689b7a9bea0f547ad12fe270e4344",
+ "0xa53fda86571b0651f5affb74312551a082fffc0385cfd24c1d779985b72a5b1cf7c78b42b4f7e51e77055f8e5e915b00",
+ "0xb579adcfd9c6ef916a5a999e77a0cb21d378c4ea67e13b7c58709d5da23a56c2e54218691fc4ac39a4a3d74f88cc31f7",
+ "0xab79e584011713e8a2f583e483a91a0c2a40771b77d91475825b5acbea82db4262132901cb3e4a108c46d7c9ee217a4e",
+ "0xa0fe58ea9eb982d7654c8aaf9366230578fc1362f6faae0594f8b9e659bcb405dff4aac0c7888bbe07f614ecf0d800a6",
+ "0x867e50e74281f28ecd4925560e2e7a6f8911b135557b688254623acce0dbc41e23ac3e706a184a45d54c586edc416eb0",
+ "0x89f81b61adda20ea9d0b387a36d0ab073dc7c7cbff518501962038be19867042f11fcc7ff78096e5d3b68c6d8dc04d9b",
+ "0xa58ee91bb556d43cf01f1398c5811f76dc0f11efdd569eed9ef178b3b0715e122060ec8f945b4dbf6eebfa2b90af6fa6",
+ "0xac460be540f4c840def2eef19fc754a9af34608d107cbadb53334cf194cc91138d53b9538fcd0ec970b5d4aa455b224a",
+ "0xb09b91f929de52c09d48ca0893be6eb44e2f5210a6c394689dc1f7729d4be4e11d0474b178e80cea8c2ac0d081f0e811",
+ "0x8d37a442a76b06a02a4e64c2504aea72c8b9b020ab7bcc94580fe2b9603c7c50d7b1e9d70d2a7daea19c68667e8f8c31",
+ "0xa9838d4c4e3f3a0075a952cf7dd623307ec633fcc81a7cf9e52e66c31780de33dbb3d74c320dc7f0a4b72f7a49949515",
+ "0xa44766b6251af458fe4f5f9ed1e02950f35703520b8656f09fc42d9a2d38a700c11a7c8a0436ac2e5e9f053d0bb8ff91",
+ "0xad78d9481c840f5202546bea0d13c776826feb8b1b7c72e83d99a947622f0bf38a4208551c4c41beb1270d7792075457",
+ "0xb619ffa8733b470039451e224b777845021e8dc1125f247a4ff2476cc774657d0ff9c5279da841fc1236047de9d81c60",
+ "0xaf760b0a30a1d6af3bc5cd6686f396bd41779aeeb6e0d70a09349bd5da17ca2e7965afc5c8ec22744198fbe3f02fb331",
+ "0xa0cc209abdb768b589fcb7b376b6e1cac07743288c95a1cf1a0354b47f0cf91fca78a75c1fcafa6f5926d6c379116608",
+ "0x864add673c89c41c754eeb3cd8dcff5cdde1d739fce65c30e474a082bb5d813cba6412e61154ce88fdb6c12c5d9be35b",
+ "0xb091443b0ce279327dc37cb484e9a5b69b257a714ce21895d67539172f95ffa326903747b64a3649e99aea7bb10d03f7",
+ "0xa8c452b8c4ca8e0a61942a8e08e28f17fb0ef4c5b018b4e6d1a64038280afa2bf1169202f05f14af24a06ca72f448ccd",
+ "0xa23c24721d18bc48d5dcf70effcbef89a7ae24e67158d70ae1d8169ee75d9a051d34b14e9cf06488bac324fe58549f26",
+ "0x92a730e30eb5f3231feb85f6720489dbb1afd42c43f05a1610c6b3c67bb949ec8fde507e924498f4ffc646f7b07d9123",
+ "0x8dbe5abf4031ec9ba6bb06d1a47dd1121fb9e03b652804069250967fd5e9577d0039e233441b7f837a7c9d67ba18c28e",
+ "0xaa456bcfef6a21bb88181482b279df260297b3778e84594ebddbdf337e85d9e3d46ca1d0b516622fb0b103df8ec519b7",
+ "0xa3b31ae621bd210a2b767e0e6f22eb28fe3c4943498a7e91753225426168b9a26da0e02f1dc5264da53a5ad240d9f51b",
+ "0xaa8d66857127e6e71874ce2202923385a7d2818b84cb73a6c42d71afe70972a70c6bdd2aad1a6e8c5e4ca728382a8ea8",
+ "0xac7e8e7a82f439127a5e40558d90d17990f8229852d21c13d753c2e97facf077cf59582b603984c3dd3faebd80aff4f5",
+ "0x93a8bcf4159f455d1baa73d2ef2450dcd4100420de84169bbe28b8b7a5d1746273f870091a87a057e834f754f34204b1",
+ "0x89d0ebb287c3613cdcae7f5acc43f17f09c0213fc40c074660120b755d664109ffb9902ed981ede79e018ddb0c845698",
+ "0xa87ccbfad431406aadbee878d9cf7d91b13649d5f7e19938b7dfd32645a43b114eef64ff3a13201398bd9b0337832e5a",
+ "0x833c51d0d0048f70c3eefb4e70e4ff66d0809c41838e8d2c21c288dd3ae9d9dfaf26d1742bf4976dab83a2b381677011",
+ "0x8bcd6b1c3b02fffead432e8b1680bad0a1ac5a712d4225e220690ee18df3e7406e2769e1f309e2e803b850bc96f0e768",
+ "0xb61e3dbd88aaf4ff1401521781e2eea9ef8b66d1fac5387c83b1da9e65c2aa2a56c262dea9eceeb4ad86c90211672db0",
+ "0x866d3090db944ecf190dd0651abf67659caafd31ae861bab9992c1e3915cb0952da7c561cc7e203560a610f48fae633b",
+ "0xa5e8971543c14274a8dc892b0be188c1b4fbc75c692ed29f166e0ea80874bc5520c2791342b7c1d2fb5dd454b03b8a5b",
+ "0x8f2f9fc50471bae9ea87487ebd1bc8576ef844cc42d606af5c4c0969670fdf2189afd643e4de3145864e7773d215f37f",
+ "0xb1bb0f2527db6d51f42b9224383c0f96048bbc03d469bf01fe1383173ef8b1cc9455d9dd8ba04d46057f46949bfc92b5",
+ "0xaa7c99d906b4d7922296cfe2520473fc50137c03d68b7865c5bfb8adbc316b1034310ec4b5670c47295f4a80fb8d61e9",
+ "0xa5d1da4d6aba555919df44cbaa8ff79378a1c9e2cfdfbf9d39c63a4a00f284c5a5724e28ecbc2d9dba27fe4ee5018bd5",
+ "0xa8db53224f70af4d991b9aae4ffe92d2aa5b618ad9137784b55843e9f16cefbfd25ada355d308e9bbf55f6d2f7976fb3",
+ "0xb6536c4232bb20e22af1a8bb12de76d5fec2ad9a3b48af1f38fa67e0f8504ef60f305a73d19385095bb6a9603fe29889",
+ "0x87f7e371a1817a63d6838a8cf4ab3a8473d19ce0d4f40fd013c03d5ddd5f4985df2956531cc9f187928ef54c68f4f9a9",
+ "0xae13530b1dbc5e4dced9d909ea61286ec09e25c12f37a1ed2f309b0eb99863d236c3b25ed3484acc8c076ad2fa8cd430",
+ "0x98928d850247c6f7606190e687d5c94a627550198dbdbea0161ef9515eacdb1a0f195cae3bb293112179082daccf8b35",
+ "0x918528bb8e6a055ad4db6230d3a405e9e55866da15c4721f5ddd1f1f37962d4904aad7a419218fe6d906fe191a991806",
+ "0xb71e31a06afe065773dd3f4a6e9ef81c3292e27a3b7fdfdd452d03e05af3b6dd654c355f7516b2a93553360c6681a73a",
+ "0x8870b83ab78a98820866f91ac643af9f3ff792a2b7fda34185a9456a63abdce42bfe8ad4dc67f08a6392f250d4062df4",
+ "0x91eea1b668e52f7a7a5087fabf1cab803b0316f78d9fff469fbfde2162f660c250e4336a9eea4cb0450bd30ac067bc8b",
+ "0x8b74990946de7b72a92147ceac1bd9d55999a8b576e8df68639e40ed5dc2062cfcd727903133de482b6dca19d0aaed82",
+ "0x8ebad537fece090ebbab662bdf2618e21ca30cf6329c50935e8346d1217dcbe3c1fe1ea28efca369c6003ce0a94703c1",
+ "0xa8640479556fb59ebd1c40c5f368fbd960932fdbb782665e4a0e24e2bdb598fc0164ce8c0726d7759cfc59e60a62e182",
+ "0xa9a52a6bf98ee4d749f6d38be2c60a6d54b64d5cbe4e67266633dc096cf28c97fe998596707d31968cbe2064b72256bf",
+ "0x847953c48a4ce6032780e9b39d0ed4384e0be202c2bbe2dfda3910f5d87aa5cd3c2ffbfcfae4dddce16d6ab657599b95",
+ "0xb6f6e1485d3ec2a06abaecd23028b200b2e4a0096c16144d07403e1720ff8f9ba9d919016b5eb8dc5103880a7a77a1d3",
+ "0x98dfc2065b1622f596dbe27131ea60bef7a193b12922cecb27f8c571404f483014f8014572e86ae2e341ab738e4887ef",
+ "0xacb0d205566bacc87bbe2e25d10793f63f7a1f27fd9e58f4f653ceae3ffeba511eaf658e068fad289eeb28f9edbeb35b",
+ "0xae4411ed5b263673cee894c11fe4abc72a4bf642d94022a5c0f3369380fcdfc1c21e277f2902972252503f91ada3029a",
+ "0xac4a7a27ba390a75d0a247d93d4a8ef1f0485f8d373a4af4e1139369ec274b91b3464d9738eeaceb19cd6f509e2f8262",
+ "0x87379c3bf231fdafcf6472a79e9e55a938d851d4dd662ab6e0d95fd47a478ed99e2ad1e6e39be3c0fc4f6d996a7dd833",
+ "0x81316904b035a8bcc2041199a789a2e6879486ba9fddcba0a82c745cc8dd8374a39e523b91792170cd30be7aa3005b85",
+ "0xb8206809c6cd027ed019f472581b45f7e12288f89047928ba32b4856b6560ad30395830d71e5e30c556f6f182b1fe690",
+ "0x88d76c028f534a62e019b4a52967bb8642ede6becfa3807be68fdd36d366fc84a4ac8dc176e80a68bc59eb62caf5dff9",
+ "0x8c3b8be685b0f8aad131ee7544d0e12f223f08a6f8edaf464b385ac644e0ddc9eff7cc7cb5c1b50ab5d71ea0f41d2213",
+ "0x8d91410e004f76c50fdc05784157b4d839cb5090022c629c7c97a5e0c3536eeafee17a527b54b1165c3cd81774bb54ce",
+ "0xb25c2863bc28ec5281ce800ddf91a7e1a53f4c6d5da1e6c86ef4616e93bcf55ed49e297216d01379f5c6e7b3c1e46728",
+ "0x865f7b09ac3ca03f20be90c48f6975dd2588838c2536c7a3532a6aa5187ed0b709cd03d91ff4048061c10d0aa72b69ce",
+ "0xb3f7477c90c11596eb4f8bbf34adbcb832638c4ff3cdd090d4d477ee50472ac9ddaf5be9ad7eca3f148960d362bbd098",
+ "0x8db35fd53fca04faecd1c76a8227160b3ab46ac1af070f2492445a19d8ff7c25bbaef6c9fa0c8c088444561e9f7e4eb2",
+ "0xa478b6e9d058a2e01d2fc053b739092e113c23a6a2770a16afbef044a3709a9e32f425ace9ba7981325f02667c3f9609",
+ "0x98caa6bd38916c08cf221722a675a4f7577f33452623de801d2b3429595f988090907a7e99960fff7c076d6d8e877b31",
+ "0xb79aaaacefc49c3038a14d2ac468cfec8c2161e88bdae91798d63552cdbe39e0e02f9225717436b9b8a40a022c633c6e",
+ "0x845a31006c680ee6a0cc41d3dc6c0c95d833fcf426f2e7c573fa15b2c4c641fbd6fe5ebb0e23720cc3467d6ee1d80dc4",
+ "0xa1bc287e272cf8b74dbf6405b3a5190883195806aa351f1dc8e525aa342283f0a35ff687e3b434324dedee74946dd185",
+ "0xa4fd2dc8db75d3783a020856e2b3aa266dc6926e84f5c491ef739a3bddd46dc8e9e0fc1177937839ef1b18d062ffbb9e",
+ "0xacbf0d3c697f57c202bb8c5dc4f3fc341b8fc509a455d44bd86acc67cad2a04495d5537bcd3e98680185e8aa286f2587",
+ "0xa5caf423a917352e1b8e844f5968a6da4fdeae467d10c6f4bbd82b5eea46a660b82d2f5440d3641c717b2c3c9ed0be52",
+ "0x8a39d763c08b926599ab1233219c49c825368fad14d9afc7c0c039224d37c00d8743293fd21645bf0b91eaf579a99867",
+ "0xb2b53a496def0ba06e80b28f36530fbe0fb5d70a601a2f10722e59abee529369c1ae8fd0f2db9184dd4a2519bb832d94",
+ "0xa73980fcef053f1b60ebbb5d78ba6332a475e0b96a0c724741a3abf3b59dd344772527f07203cf4c9cb5155ebed81fa0",
+ "0xa070d20acce42518ece322c9db096f16aed620303a39d8d5735a0df6e70fbeceb940e8d9f5cc38f3314b2240394ec47b",
+ "0xa50cf591f522f19ca337b73089557f75929d9f645f3e57d4f241e14cdd1ea3fb48d84bcf05e4f0377afbb789fbdb5d20",
+ "0x82a5ffce451096aca8eeb0cd2ae9d83db3ed76da3f531a80d9a70a346359bf05d74863ce6a7c848522b526156a5e20cd",
+ "0x88e0e84d358cbb93755a906f329db1537c3894845f32b9b0b691c29cbb455373d9452fadd1e77e20a623f6eaf624de6f",
+ "0xaa07ac7b84a6d6838826e0b9e350d8ec75e398a52e9824e6b0da6ae4010e5943fec4f00239e96433f291fef9d1d1e609",
+ "0xac8887bf39366034bc63f6cc5db0c26fd27307cbc3d6cce47894a8a019c22dd51322fb5096edc018227edfafc053a8f6",
+ "0xb7d26c26c5b33f77422191dca94977588ab1d4b9ce7d0e19c4a3b4cd1c25211b78c328dbf81e755e78cd7d1d622ad23e",
+ "0x99a676d5af49f0ba44047009298d8474cabf2d5bca1a76ba21eff7ee3c4691a102fdefea27bc948ccad8894a658abd02",
+ "0xb0d09a91909ab3620c183bdf1d53d43d39eb750dc7a722c661c3de3a1a5d383ad221f71bae374f8a71867505958a3f76",
+ "0x84681a883de8e4b93d68ac10e91899c2bbb815ce2de74bb48a11a6113b2a3f4df8aceabda1f5f67bc5aacac8c9da7221",
+ "0x9470259957780fa9b43521fab3644f555f5343281c72582b56d2efd11991d897b3b481cafa48681c5aeb80c9663b68f7",
+ "0xab1b29f7ece686e6fa968a4815da1d64f3579fed3bc92e1f3e51cd13a3c076b6cf695ed269d373300a62463dc98a4234",
+ "0x8ab415bfcd5f1061f7687597024c96dd9c7cb4942b5989379a7a3b5742f7d394337886317659cbeacaf030234a24f972",
+ "0xb9b524aad924f9acc63d002d617488f31b0016e0f0548f050cada285ce7491b74a125621638f19e9c96eabb091d945be",
+ "0x8c4c373e79415061837dd0def4f28a2d5d74d21cb13a76c9049ad678ca40228405ab0c3941df49249847ecdefc1a5b78",
+ "0xa8edf4710b5ab2929d3db6c1c0e3e242261bbaa8bcec56908ddadd7d2dad2dca9d6eb9de630b960b122ebeea41040421",
+ "0x8d66bb3b50b9df8f373163629f9221b3d4b6980a05ea81dc3741bfe9519cf3ebba7ab98e98390bae475e8ede5821bd5c",
+ "0x8d3c21bae7f0cfb97c56952bb22084b58e7bb718890935b73103f33adf5e4d99cd262f929c6eeab96209814f0dbae50a",
+ "0xa5c66cfab3d9ebf733c4af24bebc97070e7989fe3c73e79ac85fb0e4d40ae44fb571e0fad4ad72560e13ed453900d14f",
+ "0x9362e6b50b43dbefbc3254471372297b5dcce809cd3b60bf74a1268ab68bdb50e46e462cbd78f0d6c056330e982846af",
+ "0x854630d08e3f0243d570cc2e856234cb4c1a158d9c1883bf028a76525aaa34be897fe918d5f6da9764a3735fa9ebd24a",
+ "0x8c7d246985469ff252c3f4df6c7c9196fc79f05c1c66a609d84725c78001d0837c7a7049394ba5cf7e863e2d58af8417",
+ "0xae050271e01b528925302e71903f785b782f7bf4e4e7a7f537140219bc352dc7540c657ed03d3a297ad36798ecdb98cd",
+ "0x8d2ae9179fcf2b0c69850554580b52c1f4a5bd865af5f3028f222f4acad9c1ad69a8ef6c7dc7b03715ee5c506b74325e",
+ "0xb8ef8de6ce6369a8851cd36db0ccf00a85077e816c14c4e601f533330af9e3acf0743a95d28962ed8bfcfc2520ef3cfe",
+ "0xa6ecad6fdfb851b40356a8b1060f38235407a0f2706e7b8bb4a13465ca3f81d4f5b99466ac2565c60af15f022d26732e",
+ "0x819ff14cdea3ab89d98e133cd2d0379361e2e2c67ad94eeddcdb9232efd509f51d12f4f03ebd4dd953bd262a886281f7",
+ "0x8561cd0f7a6dbcddd83fcd7f472d7dbcba95b2d4fb98276f48fccf69f76d284e626d7e41314b633352df8e6333fd52a1",
+ "0xb42557ccce32d9a894d538c48712cb3e212d06ac05cd5e0527ccd2db1078ee6ae399bf6a601ffdab1f5913d35fc0b20c",
+ "0x89b4008d767aad3c6f93c349d3b956e28307311a5b1cec237e8d74bb0dee7e972c24f347fd56afd915a2342bd7bc32f0",
+ "0x877487384b207e53f5492f4e36c832c2227f92d1bb60542cfeb35e025a4a7afc2b885fae2528b33b40ab09510398f83e",
+ "0x8c411050b63c9053dd0cd81dacb48753c3d7f162028098e024d17cd6348482703a69df31ad6256e3d25a8bbf7783de39",
+ "0xa8506b54a88d17ac10fb1b0d1fe4aa40eae7553a064863d7f6b52ccc4236dd4b82d01dca6ba87da9a239e3069ba879fb",
+ "0xb1a24caef9df64750c1350789bb8d8a0db0f39474a1c74ea9ba064b1516db6923f00af8d57c632d58844fb8786c3d47a",
+ "0x959d6e255f212b0708c58a2f75cb1fe932248c9d93424612c1b8d1e640149656059737e4db2139afd5556bcdacf3eda2",
+ "0x84525af21a8d78748680b6535bbc9dc2f0cf9a1d1740d12f382f6ecb2e73811d6c1da2ad9956070b1a617c61fcff9fe5",
+ "0xb74417d84597a485d0a8e1be07bf78f17ebb2e7b3521b748f73935b9afbbd82f34b710fb7749e7d4ab55b0c7f9de127d",
+ "0xa4a9aecb19a6bab167af96d8b9d9aa5308eab19e6bfb78f5a580f9bf89bdf250a7b52a09b75f715d651cb73febd08e84",
+ "0x9777b30be2c5ffe7d29cc2803a562a32fb43b59d8c3f05a707ab60ec05b28293716230a7d264d7cd9dd358fc031cc13e",
+ "0x95dce7a3d4f23ac0050c510999f5fbf8042f771e8f8f94192e17bcbfa213470802ebdbe33a876cb621cf42e275cbfc8b",
+ "0xb0b963ebcbbee847ab8ae740478544350b3ac7e86887e4dfb2299ee5096247cd2b03c1de74c774d9bde94ae2ee2dcd59",
+ "0xa4ab20bafa316030264e13f7ef5891a2c3b29ab62e1668fcb5881f50a9acac6adbe3d706c07e62f2539715db768f6c43",
+ "0x901478a297669d608e406fe4989be75264b6c8be12169aa9e0ad5234f459ca377f78484ffd2099a2fe2db5e457826427",
+ "0x88c76e5c250810c057004a03408b85cd918e0c8903dc55a0dd8bb9b4fc2b25c87f9b8cf5943eb19fbbe99d36490050c5",
+ "0x91607322bbad4a4f03fc0012d0821eff5f8c516fda45d1ec1133bface6f858bf04b25547be24159cab931a7aa08344d4",
+ "0x843203e07fce3c6c81f84bc6dc5fb5e9d1c50c8811ace522dc66e8658433a0ef9784c947e6a62c11bf705307ef05212e",
+ "0x91dd8813a5d6dddcda7b0f87f672b83198cd0959d8311b2b26fb1fae745185c01f796fbd03aad9db9b58482483fdadd8",
+ "0x8d15911aacf76c8bcd7136e958febd6963104addcd751ce5c06b6c37213f9c4fb0ffd4e0d12c8e40c36d658999724bfd",
+ "0x8a36c5732d3f1b497ebe9250610605ee62a78eaa9e1a45f329d09aaa1061131cf1d9df00f3a7d0fe8ad614a1ff9caaae",
+ "0xa407d06affae03660881ce20dab5e2d2d6cddc23cd09b95502a9181c465e57597841144cb34d22889902aff23a76d049",
+ "0xb5fd856d0578620a7e25674d9503be7d97a2222900e1b4738c1d81ff6483b144e19e46802e91161e246271f90270e6cf",
+ "0x91b7708869cdb5a7317f88c0312d103f8ce90be14fb4f219c2e074045a2a83636fdc3e69e862049fc7c1ef000e832541",
+ "0xb64719cc5480709d1dae958f1d3082b32a43376da446c8f9f64cb02a301effc9c34d9102051733315a8179aed94d53cc",
+ "0x94347a9542ff9d18f7d9eaa2f4d9b832d0e535fe49d52aa2de08aa8192400eddabdb6444a2a78883e27c779eed7fdf5a",
+ "0x840ef44a733ff1376466698cd26f82cf56bb44811e196340467f932efa3ae1ef9958a0701b3b032f50fd9c1d2aed9ab5",
+ "0x90ab3f6f67688888a31ffc2a882bb37adab32d1a4b278951a21646f90d03385fc976715fc639a785d015751171016f10",
+ "0xb56f35d164c24b557dbcbc8a4bfa681ec916f8741ffcb27fb389c164f4e3ed2be325210ef5bdaeae7a172ca9599ab442",
+ "0xa7921a5a80d7cf6ae81ba9ee05e0579b18c20cd2852762c89d6496aa4c8ca9d1ca2434a67b2c16d333ea8e382cdab1e3",
+ "0xa506bcfbd7e7e5a92f68a1bd87d07ad5fe3b97aeee40af2bf2cae4efcd77fff03f872732c5b7883aa6584bee65d6f8cb",
+ "0xa8c46cff58931a1ce9cbe1501e1da90b174cddd6d50f3dfdfb759d1d4ad4673c0a8feed6c1f24c7af32865a7d6c984e5",
+ "0xb45686265a83bff69e312c5149db7bb70ac3ec790dc92e392b54d9c85a656e2bf58596ce269f014a906eafc97461aa5f",
+ "0x8d4009a75ccb2f29f54a5f16684b93202c570d7a56ec1a8b20173269c5f7115894f210c26b41e8d54d4072de2d1c75d0",
+ "0xaef8810af4fc676bf84a0d57b189760ddc3375c64e982539107422e3de2580b89bd27aa6da44e827b56db1b5555e4ee8",
+ "0x888f0e1e4a34f48eb9a18ef4de334c27564d72f2cf8073e3d46d881853ac1424d79e88d8ddb251914890588937c8f711",
+ "0xb64b0aa7b3a8f6e0d4b3499fe54e751b8c3e946377c0d5a6dbb677be23736b86a7e8a6be022411601dd75012012c3555",
+ "0x8d57776f519f0dd912ea14f79fbab53a30624e102f9575c0bad08d2dc754e6be54f39b11278c290977d9b9c7c0e1e0ad",
+ "0xa018fc00d532ceb2e4de908a15606db9b6e0665dd77190e2338da7c87a1713e6b9b61554e7c1462f0f6d4934b960b15c",
+ "0x8c932be83ace46f65c78e145b384f58e41546dc0395270c1397874d88626fdeda395c8a289d602b4c312fe98c1311856",
+ "0x89174838e21639d6bdd91a0621f04dc056907b88e305dd66e46a08f6d65f731dea72ae87ca5e3042d609e8de8de9aa26",
+ "0xb7b7f508bb74f7a827ac8189daa855598ff1d96fa3a02394891fd105d8f0816224cd50ac4bf2ed1cf469ace516c48184",
+ "0xb31877ad682583283baadd68dc1bebd83f5748b165aadd7fe9ef61a343773b88bcd3a022f36d6c92f339b7bfd72820a9",
+ "0xb79d77260b25daf9126dab7a193df2d7d30542786fa1733ffaf6261734770275d3ca8bae1d9915d1181a78510b3439db",
+ "0x91894fb94cd4c1dd2ceaf9c53a7020c5799ba1217cf2d251ea5bc91ed26e1159dd758e98282ebe35a0395ef9f1ed15a0",
+ "0xab59895cdafd33934ceedfc3f0d5d89880482cba6c99a6db93245f9e41987efd76e0640e80aef31782c9a8c7a83fccec",
+ "0xaa22ea63654315e033e09d4d4432331904a6fc5fb1732557987846e3c564668ca67c60a324b4af01663a23af11a9ce4b",
+ "0xb53ba3ef342601467e1f71aa280e100fbabbd38518fa0193e0099505036ee517c1ac78e96e9baeb549bb6879bb698fb0",
+ "0x943fd69fd656f37487cca3605dc7e5a215fddd811caf228595ec428751fc1de484a0cb84c667fe4d7c35599bfa0e5e34",
+ "0x9353128b5ebe0dddc555093cf3e5942754f938173541033e8788d7331fafc56f68d9f97b4131e37963ab7f1c8946f5f1",
+ "0xa76cd3c566691f65cfb86453b5b31dbaf3cab8f84fe1f795dd1e570784b9b01bdd5f0b3c1e233942b1b5838290e00598",
+ "0x983d84b2e53ffa4ae7f3ba29ef2345247ea2377686b74a10479a0ef105ecf90427bf53b74c96dfa346d0f842b6ffb25b",
+ "0x92e0fe9063306894a2c6970c001781cff416c87e87cb5fbac927a3192655c3da4063e6fa93539f6ff58efac6adcc5514",
+ "0xb00a81f03c2b8703acd4e2e4c21e06973aba696415d0ea1a648ace2b0ea19b242fede10e4f9d7dcd61c546ab878bc8f9",
+ "0xb0d08d880f3b456a10bf65cff983f754f545c840c413aea90ce7101a66eb0a0b9b1549d6c4d57725315828607963f15a",
+ "0x90cb64d03534f913b411375cce88a9e8b1329ce67a9f89ca5df8a22b8c1c97707fec727dbcbb9737f20c4cf751359277",
+ "0x8327c2d42590dfcdb78477fc18dcf71608686ad66c49bce64d7ee874668be7e1c17cc1042a754bbc77c9daf50b2dae07",
+ "0x8532171ea13aa7e37178e51a6c775da469d2e26ec854eb16e60f3307db4acec110d2155832c202e9ba525fc99174e3b0",
+ "0x83ca44b15393d021de2a511fa5511c5bd4e0ac7d67259dce5a5328f38a3cce9c3a269405959a2486016bc27bb140f9ff",
+ "0xb1d36e8ca812be545505c8214943b36cabee48112cf0de369957afa796d37f86bf7249d9f36e8e990f26f1076f292b13",
+ "0x9803abf45be5271e2f3164c328d449efc4b8fc92dfc1225d38e09630909fe92e90a5c77618daa5f592d23fc3ad667094",
+ "0xb268ad68c7bf432a01039cd889afae815c3e120f57930d463aece10af4fd330b5bd7d8869ef1bcf6b2e78e4229922edc",
+ "0xa4c91a0d6f16b1553264592b4cbbbf3ca5da32ab053ffbdd3dbb1aed1afb650fb6e0dc5274f71a51d7160856477228db",
+ "0xad89d043c2f0f17806277ffdf3ecf007448e93968663f8a0b674254f36170447b7527d5906035e5e56f4146b89b5af56",
+ "0x8b6964f757a72a22a642e4d69102951897e20c21449184e44717bd0681d75f7c5bfa5ee5397f6e53febf85a1810d6ed1",
+ "0xb08f5cdaabec910856920cd6e836c830b863eb578423edf0b32529488f71fe8257d90aed4a127448204df498b6815d79",
+ "0xaf26bb3358be9d280d39b21d831bb53145c4527a642446073fee5a86215c4c89ff49a3877a7a549486262f6f57a0f476",
+ "0xb4010b37ec4d7c2af20800e272539200a6b623ae4636ecbd0e619484f4ab9240d02bc5541ace3a3fb955dc0a3d774212",
+ "0x82752ab52bdcc3cc2fc405cb05a2e694d3df4a3a68f2179ec0652536d067b43660b96f85f573f26fbd664a9ef899f650",
+ "0x96d392dde067473a81faf2d1fea55b6429126b88b160e39b4210d31d0a82833ffd3a80e07d24d495aea2d96be7251547",
+ "0xa76d8236d6671204d440c33ac5b8deb71fa389f6563d80e73be8b043ec77d4c9b06f9a586117c7f957f4af0331cbc871",
+ "0xb6c90961f68b5e385d85c9830ec765d22a425f506904c4d506b87d8944c2b2c09615e740ed351df0f9321a7b93979cae",
+ "0xa6ec5ea80c7558403485b3b1869cdc63bde239bafdf936d9b62a37031628402a36a2cfa5cfbb8e26ac922cb0a209b3ba",
+ "0x8c3195bbdbf9bc0fc95fa7e3d7f739353c947f7767d1e3cb24d8c8602d8ea0a1790ac30b815be2a2ba26caa5227891e2",
+ "0xa7f8a63d809f1155722c57f375ea00412b00147776ae4444f342550279ef4415450d6f400000a326bf11fea6c77bf941",
+ "0x97fa404df48433a00c85793440e89bb1af44c7267588ae937a1f5d53e01e1c4d4fc8e4a6d517f3978bfdd6c2dfde012f",
+ "0xa984a0a3836de3d8d909c4629a2636aacb85393f6f214a2ef68860081e9db05ad608024762db0dc35e895dc00e2d4cdd",
+ "0x9526cf088ab90335add1db4d3a4ac631b58cbfbe88fa0845a877d33247d1cfeb85994522e1eb8f8874651bfb1df03e2a",
+ "0xac83443fd0afe99ad49de9bf8230158c118e2814c9c89db5ac951c240d6c2ce45e7677221279d9e97848ec466b99aafe",
+ "0xaeeefdbaba612e971697798ceaf63b247949dc823a0ad771ae5b988a5e882b338a98d3d0796230f49d533ec5ba411b39",
+ "0xae3f248b5a7b0f92b7820a6c5ae21e5bd8f4265d4f6e21a22512079b8ee9be06393fd3133ce8ebac0faf23f4f8517e36",
+ "0xa64a831b908eee784b8388b45447d2885ec0551b26b0c2b15e5f417d0a12c79e867fb7bd3d008d0af98b44336f8ec1ad",
+ "0xb242238cd8362b6e440ba21806905714dd55172db25ec7195f3fc4937b2aba146d5cbf3cf691a1384b4752dc3b54d627",
+ "0x819f97f337eea1ffb2a678cc25f556f1aab751c6b048993a1d430fe1a3ddd8bb411c152e12ca60ec6e057c190cd1db9a",
+ "0xb9d7d187407380df54ee9fef224c54eec1bfabf17dc8abf60765b7951f538f59aa26fffd5846cfe05546c35f59b573f4",
+ "0xaa6e3c14efa6a5962812e3f94f8ce673a433f4a82d07a67577285ea0eaa07f8be7115853122d12d6d4e1fdf64c504be1",
+ "0x82268bee9c1662d3ddb5fb785abfae6fb8b774190f30267f1d47091d2cd4b3874db4372625aa36c32f27b0eee986269b",
+ "0xb236459565b7b966166c4a35b2fa71030b40321821b8e96879d95f0e83a0baf33fa25721f30af4a631df209e25b96061",
+ "0x8708d752632d2435d2d5b1db4ad1fa2558d776a013655f88e9a3556d86b71976e7dfe5b8834fdec97682cd94560d0d0d",
+ "0xae1424a68ae2dbfb0f01211f11773732a50510b5585c1fb005cb892b2c6a58f4a55490b5c5b4483c6fce40e9d3236a52",
+ "0xb3f5f722af9dddb07293c871ce97abbccba0093ca98c8d74b1318fa21396fc1b45b69c15084f63d728f9908442024506",
+ "0x9606f3ce5e63886853ca476dc0949e7f1051889d529365c0cb0296fdc02abd088f0f0318ecd2cf36740a3634132d36f6",
+ "0xb11a833a49fa138db46b25ff8cdda665295226595bc212c0931b4931d0a55c99da972c12b4ef753f7e37c6332356e350",
+ "0xafede34e7dab0a9e074bc19a7daddb27df65735581ca24ad70c891c98b1349fcebbcf3ba6b32c2617fe06a5818dabc2d",
+ "0x97993d456e459e66322d01f8eb13918979761c3e8590910453944bdff90b24091bb018ac6499792515c9923be289f99f",
+ "0x977e3e967eff19290a192cd11df3667d511b398fb3ac9a5114a0f3707e25a0edcb56105648b1b85a8b7519fc529fc6f6",
+ "0xb873a7c88bf58731fe1bf61ff6828bf114cf5228f254083304a4570e854e83748fc98683ddba62d978fff7909f2c5c47",
+ "0xad4b2691f6f19da1d123aaa23cca3e876247ed9a4ab23c599afdbc0d3aa49776442a7ceaa996ac550d0313d9b9a36cee",
+ "0xb9210713c78e19685608c6475bfa974b57ac276808a443f8b280945c5d5f9c39da43effa294bfb1a6c6f7b6b9f85bf6c",
+ "0xa65152f376113e61a0e468759de38d742caa260291b4753391ee408dea55927af08a4d4a9918600a3bdf1df462dffe76",
+ "0x8bf8c27ad5140dde7f3d2280fd4cc6b29ab76537e8d7aa7011a9d2796ee3e56e9a60c27b5c2da6c5e14fc866301dc195",
+ "0x92fde8effc9f61393a2771155812b863cff2a0c5423d7d40aa04d621d396b44af94ddd376c28e7d2f53c930aea947484",
+ "0x97a01d1dd9ee30553ce676011aea97fa93d55038ada95f0057d2362ae9437f3ed13de8290e2ff21e3167dd7ba10b9c3f",
+ "0x89affffaa63cb2df3490f76f0d1e1d6ca35c221dd34057176ba739fa18d492355e6d2a5a5ad93a136d3b1fed0bb8aa19",
+ "0x928b8e255a77e1f0495c86d3c63b83677b4561a5fcbbe5d3210f1e0fc947496e426d6bf3b49394a5df796c9f25673fc4",
+ "0x842a0af91799c9b533e79ee081efe2a634cac6c584c2f054fb7d1db67dde90ae36de36cbf712ec9cd1a0c7ee79e151ea",
+ "0xa65b946cf637e090baf2107c9a42f354b390e7316beb8913638130dbc67c918926eb87bec3b1fe92ef72bc77a170fa3b",
+ "0xaafc0f19bfd71ab5ae4a8510c7861458b70ad062a44107b1b1dbacbfa44ba3217028c2824bd7058e2fa32455f624040b",
+ "0x95269dc787653814e0be899c95dba8cfa384f575a25e671c0806fd80816ad6797dc819d30ae06e1d0ed9cb01c3950d47",
+ "0xa1e760f7fa5775a1b2964b719ff961a92083c5c617f637fc46e0c9c20ab233f8686f7f38c3cb27d825c54dd95e93a59b",
+ "0xac3b8a7c2317ea967f229eddc3e23e279427f665c4705c7532ed33443f1243d33453c1088f57088d2ab1e3df690a9cc9",
+ "0xb787beeddfbfe36dd51ec4efd9cf83e59e84d354c3353cc9c447be53ae53d366ed1c59b686e52a92f002142c8652bfe0",
+ "0xb7a64198300cb6716aa7ac6b25621f8bdec46ad5c07a27e165b3f774cdf65bcfdbf31e9bae0c16b44de4b00ada7a4244",
+ "0xb8ae9f1452909e0c412c7a7fe075027691ea8df1347f65a5507bc8848f1d2c833d69748076db1129e5b4fb912f65c86c",
+ "0x9682e41872456b9fa67def89e71f06d362d6c8ca85c9c48536615bc401442711e1c9803f10ab7f8ab5feaec0f9df20a6",
+ "0x88889ff4e271dc1c7e21989cc39f73cde2f0475acd98078281591ff6c944fadeb9954e72334319050205d745d4df73df",
+ "0x8f79b5b8159e7fd0d93b0645f3c416464f39aec353b57d99ecf24f96272df8a068ad67a6c90c78d82c63b40bb73989bb",
+ "0x838c01a009a3d8558a3f0bdd5e22de21af71ca1aefc8423c91dc577d50920e9516880e87dce3e6d086e11cd45c9052d9",
+ "0xb97f1c6eee8a78f137c840667cc288256e39294268a3009419298a04a1d0087c9c9077b33c917c65caf76637702dda8a",
+ "0x972284ce72f96a61c899260203dfa06fc3268981732bef74060641c1a5068ead723e3399431c247ca034b0dae861e8df",
+ "0x945a8d52d6d3db6663dbd3110c6587f9e9c44132045eeffba15621576d178315cb52870fa5861669f84f0bee646183fe",
+ "0xa0a547b5f0967b1c3e5ec6c6a9a99f0578521489180dfdfbb5561f4d166baac43a2f06f950f645ce991664e167537eed",
+ "0xa0592cda5cdddf1340033a745fd13a6eff2021f2e26587116c61c60edead067e0f217bc2bef4172a3c9839b0b978ab35",
+ "0xb9c223b65a3281587fa44ec829e609154b32f801fd1de6950e01eafb07a8324243b960d5735288d0f89f0078b2c42b5b",
+ "0x99ebfc3b8f9f98249f4d37a0023149ed85edd7a5abe062c8fb30c8c84555258b998bdcdd1d400bc0fa2a4aaa8b224466",
+ "0x955b68526e6cb3937b26843270f4e60f9c6c8ece2fa9308fe3e23afa433309c068c66a4bc16ee2cf04220f095e9afce4",
+ "0xb766caeafcc00378135ae53397f8a67ed586f5e30795462c4a35853de6681b1f17401a1c40958de32b197c083b7279c1",
+ "0x921bf87cad947c2c33fa596d819423c10337a76fe5a63813c0a9dc78a728207ae7b339407a402fc4d0f7cba3af6da6fc",
+ "0xa74ba1f3bc3e6c025db411308f49b347ec91da1c916bda9da61e510ec8d71d25e0ac0f124811b7860e5204f93099af27",
+ "0xa29b4d144e0bf17a7e8353f2824cef0ce85621396babe8a0b873ca1e8a5f8d508b87866cf86da348470649fceefd735c",
+ "0xa8040e12ffc3480dd83a349d06741d1572ef91932c46f5cf03aee8454254156ee95786fd013d5654725e674c920cec32",
+ "0x8c4cf34ca60afd33923f219ffed054f90cd3f253ffeb2204a3b61b0183417e366c16c07fae860e362b0f2bfe3e1a1d35",
+ "0x8195eede4ddb1c950459df6c396b2e99d83059f282b420acc34220cadeed16ab65c856f2c52568d86d3c682818ed7b37",
+ "0x91fff19e54c15932260aa990c7fcb3c3c3da94845cc5aa8740ef56cf9f58d19b4c3c55596f8d6c877f9f4d22921d93aa",
+ "0xa3e0bf7e5d02a80b75cf75f2db7e66cb625250c45436e3c136d86297d652590ec97c2311bafe407ad357c79ab29d107b",
+ "0x81917ff87e5ed2ae4656b481a63ced9e6e5ff653b8aa6b7986911b8bc1ee5b8ef4f4d7882c3f250f2238e141b227e510",
+ "0x915fdbe5e7de09c66c0416ae14a8750db9412e11dc576cf6158755fdcaf67abdbf0fa79b554cac4fe91c4ec245be073f",
+ "0x8df27eafb5c3996ba4dc5773c1a45ca77e626b52e454dc1c4058aa94c2067c18332280630cc3d364821ee53bf2b8c130",
+ "0x934f8a17c5cbb827d7868f5c8ca00cb027728a841000a16a3428ab16aa28733f16b52f58c9c4fbf75ccc45df72d9c4df",
+ "0xb83f4da811f9183c25de8958bc73b504cf790e0f357cbe74ef696efa7aca97ad3b7ead1faf76e9f982c65b6a4d888fc2",
+ "0x87188213c8b5c268dc2b6da413f0501c95749e953791b727450af3e43714149c115b596b33b63a2f006a1a271b87efd0",
+ "0x83e9e888ab9c3e30761de635d9aabd31248cdd92f7675fc43e4b21fd96a03ec1dc4ad2ec94fec857ffb52683ac98e360",
+ "0xb4b9a1823fe2d983dc4ec4e3aaea297e581c3fc5ab4b4af5fa1370caa37af2d1cc7fc6bfc5e7da60ad8fdce27dfe4b24",
+ "0x856388bc78aef465dbcdd1f559252e028c9e9a2225c37d645c138e78f008f764124522705822a61326a6d1c79781e189",
+ "0xa6431b36db93c3b47353ba22e7c9592c9cdfb9cbdd052ecf2cc3793f5b60c1e89bc96e6bae117bfd047f2308da00dd2f",
+ "0xb619972d48e7e4291542dcde08f7a9cdc883c892986ded2f23ccb216e245cd8d9ad1d285347b0f9d7611d63bf4cee2bc",
+ "0x8845cca6ff8595955f37440232f8e61d5351500bd016dfadd182b9d39544db77a62f4e0102ff74dd4173ae2c181d24ef",
+ "0xb2f5f7fa26dcd3b6550879520172db2d64ee6aaa213cbef1a12befbce03f0973a22eb4e5d7b977f466ac2bf8323dcedd",
+ "0x858b7f7e2d44bdf5235841164aa8b4f3d33934e8cb122794d90e0c1cac726417b220529e4f896d7b77902ab0ccd35b3a",
+ "0x80b0408a092dae2b287a5e32ea1ad52b78b10e9c12f49282976cd738f5d834e03d1ad59b09c5ccaccc39818b87d06092",
+ "0xb996b0a9c6a2d14d984edcd6ab56bc941674102980d65b3ad9733455f49473d3f587c8cbf661228a7e125ddbe07e3198",
+ "0x90224fcebb36865293bd63af786e0c5ade6b67c4938d77eb0cbae730d514fdd0fe2d6632788e858afd29d46310cf86df",
+ "0xb71351fdfff7168b0a5ec48397ecc27ac36657a8033d9981e97002dcca0303e3715ce6dd3f39423bc8ef286fa2e9e669",
+ "0xae2a3f078b89fb753ce4ed87e0c1a58bb19b4f0cfb6586dedb9fcab99d097d659a489fb40e14651741e1375cfc4b6c5f",
+ "0x8ef476b118e0b868caed297c161f4231bbeb863cdfa5e2eaa0fc6b6669425ce7af50dc374abceac154c287de50c22307",
+ "0x92e46ab472c56cfc6458955270d3c72b7bde563bb32f7d4ab4d959db6f885764a3d864e1aa19802fefaa5e16b0cb0b54",
+ "0x96a3f68323d1c94e73d5938a18a377af31b782f56212de3f489d22bc289cf24793a95b37f1d6776edf88114b5c1fa695",
+ "0x962cc068cfce6faaa27213c4e43e44eeff0dfbb6d25b814e82c7da981fb81d7d91868fa2344f05fb552362f98cfd4a72",
+ "0x895d4e4c4ad670abf66d43d59675b1add7afad7438ada8f42a0360c704cee2060f9ac15b4d27e9b9d0996bb801276fe3",
+ "0xb3ad18d7ece71f89f2ef749b853c45dc56bf1c796250024b39a1e91ed11ca32713864049c9aaaea60cde309b47486bbf",
+ "0x8f05404e0c0258fdbae50e97ccb9b72ee17e0bd2400d9102c0dad981dac8c4c71585f03e9b5d50086d0a2d3334cb55d1",
+ "0x8bd877e9d4591d02c63c6f9fc9976c109de2d0d2df2bfa5f6a3232bab5b0b8b46e255679520480c2d7a318545efa1245",
+ "0x8d4c16b5d98957c9da13d3f36c46f176e64e5be879f22be3179a2c0e624fe4758a82bf8c8027410002f973a3b84cd55a",
+ "0x86e2a8dea86427b424fa8eada881bdff896907084a495546e66556cbdf070b78ba312bf441eb1be6a80006d25d5097a3",
+ "0x8608b0c117fd8652fdab0495b08fadbeba95d9c37068e570de6fddfef1ba4a1773b42ac2be212836141d1bdcdef11a17",
+ "0xa13d6febf5fb993ae76cae08423ca28da8b818d6ef0fde32976a4db57839cd45b085026b28ee5795f10a9a8e3098c683",
+ "0x8e261967fa6de96f00bc94a199d7f72896a6ad8a7bbb1d6187cca8fad824e522880e20f766620f4f7e191c53321d70f9",
+ "0x8b8e8972ac0218d7e3d922c734302803878ad508ca19f5f012bc047babd8a5c5a53deb5fe7c15a4c00fd6d1cb9b1dbd0",
+ "0xb5616b233fb3574a2717d125a434a2682ff68546dccf116dd8a3b750a096982f185614b9fb6c7678107ff40a451f56fa",
+ "0xaa6adf9b0c3334b0d0663f583a4914523b2ac2e7adffdb026ab9109295ff6af003ef8357026dbcf789896d2afded8d73",
+ "0xacb72df56a0b65496cd534448ed4f62950bb1e11e50873b6ed349c088ee364441821294ce0f7c61bd7d38105bea3b442",
+ "0xabae12df83e01ec947249fedd0115dc501d2b03ff7232092979eda531dbbca29ace1d46923427c7dde4c17bdf3fd7708",
+ "0x820b4fc2b63a9fda7964acf5caf19a2fc4965007cb6d6b511fcafcb1f71c3f673a1c0791d3f86e3a9a1eb6955b191cc0",
+ "0xaf277259d78c6b0f4f030a10c53577555df5e83319ddbad91afbd7c30bc58e7671c56d00d66ec3ab5ef56470cd910cee",
+ "0xad4a861c59f1f5ca1beedd488fb3d131dea924fffd8e038741a1a7371fad7370ca5cf80dc01f177fbb9576713bb9a5b3",
+ "0xb67a5162982ce6a55ccfb2f177b1ec26b110043cf18abd6a6c451cf140b5af2d634591eb4f28ad92177d8c7e5cd0a5e8",
+ "0x96176d0a83816330187798072d449cbfccff682561e668faf6b1220c9a6535b32a6e4f852e8abb00f79abb87493df16b",
+ "0xb0afe6e7cb672e18f0206e4423f51f8bd0017bf464c4b186d46332c5a5847647f89ff7fa4801a41c1b0b42f6135bcc92",
+ "0x8fc5e7a95ef20c1278c645892811f6fe3f15c431ebc998a32ec0da44e7213ea934ed2be65239f3f49b8ec471e9914160",
+ "0xb7793e41adda6c82ba1f2a31f656f6205f65bf8a3d50d836ee631bc7ce77c153345a2d0fc5c60edf8b37457c3729c4ec",
+ "0xa504dd7e4d6b2f4379f22cc867c65535079c75ccc575955f961677fa63ecb9f74026fa2f60c9fb6323c1699259e5e9c8",
+ "0xab899d00ae693649cc1afdf30fb80d728973d2177c006e428bf61c7be01e183866614e05410041bc82cb14a33330e69c",
+ "0x8a3bd8b0b1be570b65c4432a0f6dc42f48a2000e30ab089cf781d38f4090467b54f79c0d472fcbf18ef6a00df69cc6f3",
+ "0xb4d7028f7f76a96a3d7803fca7f507ae11a77c5346e9cdfccb120a833a59bda1f4264e425aa588e7a16f8e7638061d84",
+ "0xb9c7511a76ea5fb105de905d44b02edb17008335766ee357ed386b7b3cf19640a98b38785cb14603c1192bee5886c9b6",
+ "0x8563afb12e53aed71ac7103ab8602bfa8371ae095207cb0d59e8fd389b6ad1aff0641147e53cb6a7ca16c7f37c9c5e6b",
+ "0x8e108be614604e09974a9ed90960c28c4ea330a3d9a0cb4af6dd6f193f84ab282b243ecdf549b3131036bebc8905690c",
+ "0xb794d127fbedb9c5b58e31822361706ffac55ce023fbfe55716c3c48c2fd2f2c7660a67346864dfe588812d369cb50b6",
+ "0xb797a3442fc3b44f41baefd30346f9ac7f96e770d010d53c146ce74ce424c10fb62758b7e108b8abfdc5fafd89d745cb",
+ "0x993bb71e031e8096442e6205625e1bfddfe6dd6a83a81f3e2f84fafa9e5082ab4cad80a099f21eff2e81c83457c725c3",
+ "0x8711ab833fc03e37acf2e1e74cfd9133b101ff4144fe30260654398ae48912ab46549d552eb9d15d2ea57760d35ac62e",
+ "0xb21321fd2a12083863a1576c5930e1aecb330391ef83326d9d92e1f6f0d066d1394519284ddab55b2cb77417d4b0292f",
+ "0x877d98f731ffe3ee94b0b5b72d127630fa8a96f6ca4f913d2aa581f67732df6709493693053b3e22b0181632ac6c1e3b",
+ "0xae391c12e0eb8c145103c62ea64f41345973311c3bf7281fa6bf9b7faafac87bcf0998e5649b9ef81e288c369c827e07",
+ "0xb83a2842f36998890492ab1cd5a088d9423d192681b9a3a90ec518d4c541bce63e6c5f4df0f734f31fbfdd87785a2463",
+ "0xa21b6a790011396e1569ec5b2a423857b9bec16f543e63af28024e116c1ea24a3b96e8e4c75c6537c3e4611fd265e896",
+ "0xb4251a9c4aab3a495da7a42e684ba4860dbcf940ad1da4b6d5ec46050cbe8dab0ab9ae6b63b5879de97b905723a41576",
+ "0x8222f70aebfe6ac037f8543a08498f4cadb3edaac00336fc00437eb09f2cba758f6c38e887cc634b4d5b7112b6334836",
+ "0x86f05038e060594c46b5d94621a1d9620aa8ba59a6995baf448734e21f58e23c1ea2993d3002ad5250d6edd5ba59b34f",
+ "0xa7c0c749baef811ab31b973c39ceb1d94750e2bc559c90dc5eeb20d8bb6b78586a2b363c599ba2107d6be65cd435f24e",
+ "0x861d46a5d70b38d6c1cd72817a2813803d9f34c00320c8b62f8b9deb67f5b5687bc0b37c16d28fd017367b92e05da9ca",
+ "0xb3365d3dab639bffbe38e35383686a435c8c88b397b717cd4aeced2772ea1053ceb670f811f883f4e02975e5f1c4ac58",
+ "0xa5750285f61ab8f64cd771f6466e2c0395e01b692fd878f2ef2d5c78bdd8212a73a3b1dfa5e4c8d9e1afda7c84857d3b",
+ "0x835a10809ccf939bc46cf950a33b36d71be418774f51861f1cd98a016ade30f289114a88225a2c11e771b8b346cbe6ef",
+ "0xa4f59473a037077181a0a62f1856ec271028546ca9452b45cedfcb229d0f4d1aabfc13062b07e536cc8a0d4b113156a2",
+ "0x95cd14802180b224d44a73cc1ed599d6c4ca62ddcaa503513ccdc80aaa8be050cc98bd4b4f3b639549beb4587ac6caf9",
+ "0x973b731992a3e69996253d7f36dd7a0af1982b5ed21624b77a7965d69e9a377b010d6dabf88a8a97eec2a476259859cc",
+ "0xaf8a1655d6f9c78c8eb9a95051aa3baaf9c811adf0ae8c944a8d3fcba87b15f61021f3baf6996fa0aa51c81b3cb69de1",
+ "0x835aad5c56872d2a2d6c252507b85dd742bf9b8c211ccb6b25b52d15c07245b6d89b2a40f722aeb5083a47cca159c947",
+ "0xabf4e970b02bef8a102df983e22e97e2541dd3650b46e26be9ee394a3ea8b577019331857241d3d12b41d4eacd29a3ac",
+ "0xa13c32449dbedf158721c13db9539ae076a6ce5aeaf68491e90e6ad4e20e20d1cdcc4a89ed9fd49cb8c0dd50c17633c1",
+ "0x8c8f78f88b7e22dd7e9150ab1c000f10c28e696e21d85d6469a6fe315254740f32e73d81ab1f3c1cf8f544c86df506e8",
+ "0xb4b77f2acfe945abf81f2605f906c10b88fb4d28628487fb4feb3a09f17f28e9780445dfcee4878349d4c6387a9d17d4",
+ "0x8d255c235f3812c6ecc646f855fa3832be5cb4dbb9c9e544989fafdf3f69f05bfd370732eaf954012f0044aa013fc9c6",
+ "0xb982efd3f34b47df37c910148ac56a84e8116647bea24145a49e34e0a6c0176e3284d838dae6230cb40d0be91c078b85",
+ "0x983f365aa09bd85df2a6a2ad8e4318996b1e27d02090755391d4486144e40d80b1fbfe1c798d626db92f52e33aa634da",
+ "0x95fd1981271f3ea3a41d654cf497e6696730d9ff7369f26bc4d7d15c7adb4823dd0c42e4a005a810af12d234065e5390",
+ "0xa9f5219bd4b913c186ef30c02f995a08f0f6f1462614ea5f236964e02bdaa33db9d9b816c4aee5829947840a9a07ba60",
+ "0x9210e6ceb05c09b46fd09d036287ca33c45124ab86315e5d6911ff89054f1101faaa3e83d123b7805056d388bcec6664",
+ "0x8ed9cbf69c6ff3a5c62dd9fe0d7264578c0f826a29e614bc2fb4d621d90c8c9992438accdd7a614b1dca5d1bb73dc315",
+ "0x85cf2a8cca93e00da459e3cecd22c342d697eee13c74d5851634844fc215f60053cf84b0e03c327cb395f48d1c71a8a4",
+ "0x8818a18e9a2ec90a271b784400c1903089ffb0e0b40bc5abbbe12fbebe0f731f91959d98c5519ef1694543e31e2016d4",
+ "0x8dabc130f296fa7a82870bf9a8405aaf542b222ed9276bba9bd3c3555a0f473acb97d655ee7280baff766a827a8993f0",
+ "0xac7952b84b0dc60c4d858f034093b4d322c35959605a3dad2b806af9813a4680cb038c6d7f4485b4d6b2ff502aaeca25",
+ "0xad65cb6d57b48a2602568d2ec8010baed0eb440eec7638c5ec8f02687d764e9de5b5d42ad5582934e592b48471c22d26",
+ "0xa02ab8bd4c3d114ea23aebdd880952f9495912817da8c0c08eabc4e6755439899d635034413d51134c72a6320f807f1c",
+ "0x8319567764b8295402ec1ebef4c2930a138480b37e6d7d01c8b4c9cd1f2fc3f6e9a44ae6e380a0c469b25b06db23305f",
+ "0xafec53b2301dc0caa8034cd9daef78c48905e6068d692ca23d589b84a6fa9ddc2ed24a39480597e19cb3e83eec213b3f",
+ "0xac0b4ffdb5ae08e586a9cdb98f9fe56f4712af3a97065e89e274feacfb52b53c839565aee93c4cfaaccfe51432c4fab0",
+ "0x8972cbf07a738549205b1094c5987818124144bf187bc0a85287c94fdb22ce038c0f11df1aa16ec5992e91b44d1af793",
+ "0xb7267aa6f9e3de864179b7da30319f1d4cb2a3560f2ea980254775963f1523b44c680f917095879bebfa3dc2b603efcf",
+ "0x80f68f4bfc337952e29504ee5149f15093824ea7ab02507efd1317a670f6cbc3611201848560312e3e52e9d9af72eccf",
+ "0x8897fee93ce8fc1e1122e46b6d640bba309384dbd92e46e185e6364aa8210ebf5f9ee7e5e604b6ffba99aa80a10dd7d0",
+ "0xb58ea6c02f2360be60595223d692e82ee64874fda41a9f75930f7d28586f89be34b1083e03bbc1575bbfdda2d30db1ea",
+ "0x85a523a33d903280d70ac5938770453a58293480170c84926457ac2df45c10d5ff34322ab130ef4a38c916e70d81af53",
+ "0xa2cbf045e1bed38937492c1f2f93a5ba41875f1f262291914bc1fc40c60bd0740fb3fea428faf6da38b7c180fe8ac109",
+ "0x8c09328770ed8eb17afc6ac7ddd87bb476de18ed63cab80027234a605806895959990c47bd10d259d7f3e2ecb50074c9",
+ "0xb4b9e19edb4a33bde8b7289956568a5b6b6557404e0a34584b5721fe6f564821091013fbb158e2858c6d398293bb4b59",
+ "0x8a47377df61733a2aa5a0e945fce00267f8e950f37e109d4487d92d878fb8b573317bb382d902de515b544e9e233458d",
+ "0xb5804c9d97efeff5ca94f3689b8088c62422d92a1506fd1d8d3b1b30e8a866ad0d6dad4abfa051dfc4471250cac4c5d9",
+ "0x9084a6ee8ec22d4881e9dcc8a9eb3c2513523d8bc141942370fd191ad2601bf9537a0b1e84316f3209b3d8a54368051e",
+ "0x85447eea2fa26656a649f8519fa67279183044791d61cf8563d0783d46d747d96af31d0a93507bbb2242666aa87d3720",
+ "0x97566a84481027b60116c751aec552adfff2d9038e68d48c4db9811fb0cbfdb3f1d91fc176a0b0d988a765f8a020bce1",
+ "0xae87e5c1b9e86c49a23dceda4ecfd1dcf08567f1db8e5b6ec752ebd45433c11e7da4988573cdaebbb6f4135814fc059e",
+ "0xabee05cf9abdbc52897ac1ce9ed157f5466ed6c383d6497de28616238d60409e5e92619e528af8b62cc552bf09970dc2",
+ "0xae6d31cd7bf9599e5ee0828bab00ceb4856d829bba967278a73706b5f388465367aa8a6c7da24b5e5f1fdd3256ef8e63",
+ "0xac33e7b1ee47e1ee4af472e37ab9e9175260e506a4e5ce449788075da1b53c44cb035f3792d1eea2aa24b1f688cc6ed3",
+ "0x80f65b205666b0e089bb62152251c48c380a831e5f277f11f3ef4f0d52533f0851c1b612267042802f019ec900dc0e8f",
+ "0x858520ad7aa1c9fed738e3b583c84168f2927837ad0e1d326afe9935c26e9b473d7f8c382e82ef1fe37d2b39bb40a1ee",
+ "0xb842dd4af8befe00a97c2d0f0c33c93974761e2cb9e5ab8331b25170318ddd5e4bdbc02d8f90cbfdd5f348f4f371c1f7",
+ "0x8bf2cb79bc783cb57088aae7363320cbeaabd078ffdec9d41bc74ff49e0043d0dad0086a30e5112b689fd2f5a606365d",
+ "0x982eb03bbe563e8850847cd37e6a3306d298ab08c4d63ab6334e6b8c1fa13fce80cf2693b09714c7621d74261a0ff306",
+ "0xb143edb113dec9f1e5105d4a93fbe502b859e587640d3db2f628c09a17060e6aec9e900e2c8c411cda99bc301ff96625",
+ "0xaf472d9befa750dcebc5428fe1a024f18ec1c07bca0f95643ce6b5f4189892a910285afb03fd7ed7068fbe614e80d33c",
+ "0xa97e3bc57ede73ecd1bbf02de8f51b4e7c1a067da68a3cd719f4ba26a0156cbf1cef2169fd35a18c5a4cced50d475998",
+ "0xa862253c937cf3d75d7183e5f5be6a4385d526aeda5171c1c60a8381fea79f88f5f52a4fab244ecc70765d5765e6dfd5",
+ "0x90cb776f8e5a108f1719df4a355bebb04bf023349356382cae55991b31720f0fd03206b895fa10c56c98f52453be8778",
+ "0xa7614e8d0769dccd520ea4b46f7646e12489951efaef5176bc889e9eb65f6e31758df136b5bf1e9107e68472fa9b46ec",
+ "0xac3a9b80a3254c42e5ed3a090a0dd7aee2352f480de96ad187027a3bb6c791eddfc3074b6ffd74eea825188f107cda4d",
+ "0x82a01d0168238ef04180d4b6e0a0e39024c02c2d75b065017c2928039e154d093e1af4503f4d1f3d8a948917abb5d09f",
+ "0x8fab000a2b0eef851a483aec8d2dd85fe60504794411a2f73ed82e116960547ac58766cb73df71aea71079302630258d",
+ "0x872451a35c6db61c63e9b8bb9f16b217f985c20be4451c14282c814adb29d7fb13f201367c664435c7f1d4d9375d7a58",
+ "0x887d9ff54cc96b35d562df4a537ff972d7c4b3fd91ab06354969a4cfede0b9fc68bbffb61d0dbf1a58948dc701e54f5a",
+ "0x8cb5c2a6bd956875d88f41ae24574434f1308514d44057b55c9c70f13a3366ed054150eed0955a38fda3f757be73d55f",
+ "0x89ad0163cad93e24129d63f8e38422b7674632a8d0a9016ee8636184cab177659a676c4ee7efba3abe1a68807c656d60",
+ "0xb9ec01c7cab6d00359b5a0b4a1573467d09476e05ca51a9227cd16b589a9943d161eef62dcc73f0de2ec504d81f4d252",
+ "0x8031d17635d39dfe9705c485d2c94830b6fc9bc67b91300d9d2591b51e36a782e77ab5904662effa9382d9cca201f525",
+ "0x8be5a5f6bc8d680e5092d6f9a6585acbaaaa2ddc671da560dcf5cfa4472f4f184b9597b5b539438accd40dda885687cc",
+ "0xb1fc0f052fae038a2e3de3b3a96b0a1024b009de8457b8b3adb2d315ae68a89af905720108a30038e5ab8d0d97087785",
+ "0x8b8bdc77bd3a6bc7ca5492b6f8c614852c39a70d6c8a74916eaca0aeb4533b11898b8820a4c2620a97bf35e275480029",
+ "0xaf35f4dc538d4ad5cdf710caa38fd1eb496c3fa890a047b6a659619c5ad3054158371d1e88e0894428282eed9f47f76b",
+ "0x8166454a7089cc07758ad78724654f4e7a1a13e305bbf88ddb86f1a4b2904c4fc8ab872d7da364cdd6a6c0365239e2ad",
+ "0xab287c7d3addce74ce40491871c768abe01daaa0833481276ff2e56926b38a7c6d2681ffe837d2cc323045ad1a4414f9",
+ "0xb90317f4505793094d89365beb35537f55a6b5618904236258dd04ca61f21476837624a2f45fef8168acf732cab65579",
+ "0x98ae5ea27448e236b6657ab5ef7b1cccb5372f92ab25f5fa651fbac97d08353a1dae1b280b1cd42b17d2c6a70a63ab9d",
+ "0xadcf54e752d32cbaa6cb98fbca48d8cd087b1db1d131d465705a0d8042c8393c8f4d26b59006eb50129b21e6240f0c06",
+ "0xb591a3e4db18a7345fa935a8dd7994bbac5cc270b8ebd84c8304c44484c7a74afb45471fdbe4ab22156a30fae1149b40",
+ "0x806b53ac049a42f1dcc1d6335505371da0bf27c614f441b03bbf2e356be7b2fb4eed7117eabcce9e427a542eaa2bf7d8",
+ "0x800482e7a772d49210b81c4a907f5ce97f270b959e745621ee293cf8c71e8989363d61f66a98f2d16914439544ca84c7",
+ "0x99de9eafdad3617445312341644f2bb888680ff01ce95ca9276b1d2e5ef83fa02dab5e948ebf66c17df0752f1bd37b70",
+ "0x961ee30810aa4c93ae157fbe9009b8e443c082192bd36a73a6764ff9b2ad8b0948fe9a73344556e01399dd77badb4257",
+ "0xae0a361067c52efbe56c8adf982c00432cd478929459fc7f74052c8ee9531cd031fe1335418fde53f7c2ef34254eb7ac",
+ "0xa3503d16b6b27eb20c1b177bcf90d13706169220523a6271b85b2ce35a9a2b9c5bed088540031c0a4ebfdae3a4c6ab04",
+ "0x909420122c3e723289ca4e7b81c2df5aff312972a2203f4c45821b176e7c862bf9cac7f7df3adf1d59278f02694d06e7",
+ "0x989f42380ae904b982f85d0c6186c1aef5d6bcba29bcfbb658e811b587eb2749c65c6e4a8cc6409c229a107499a4f5d7",
+ "0x8037a6337195c8e26a27ea4ef218c6e7d79a9720aaab43932d343192abc2320fe72955f5e431c109093bda074103330a",
+ "0xb312e168663842099b88445e940249cc508f080ab0c94331f672e7760258dbd86be5267e4cf25ea25facb80bff82a7e9",
+ "0xaaa3ff8639496864fcdbfdda1ac97edc4f08e3c9288b768f6c8073038c9fbbf7e1c4bea169b4d45c31935cdf0680d45e",
+ "0x97dbd3df37f0b481a311dfc5f40e59227720f367912200d71908ef6650f32cc985cb05b981e3eea38958f7e48d10a15d",
+ "0xa89d49d1e267bb452d6cb621b9a90826fe55e9b489c0427b94442d02a16f390eed758e209991687f73f6b5a032321f42",
+ "0x9530dea4e0e19d6496f536f2e75cf7d814d65fde567055eb20db48fd8d20d501cd2a22fb506db566b94c9ee10f413d43",
+ "0x81a7009b9e67f1965fa7da6a57591c307de91bf0cd35ab4348dc4a98a4961e096d004d7e7ad318000011dc4342c1b809",
+ "0x83440a9402b766045d7aca61a58bba2aa29cac1cf718199e472ba086f5d48093d9dda4d135292ba51d049a23964eceae",
+ "0xa06c9ce5e802df14f6b064a3d1a0735d429b452f0e2e276042800b0a4f16df988fd94cf3945921d5dd3802ab2636f867",
+ "0xb1359e358b89936dee9e678a187aad3e9ab14ac40e96a0a68f70ee2583cdcf467ae03bef4215e92893f4e12f902adec8",
+ "0x835304f8619188b4d14674d803103d5a3fa594d48e96d9699e653115dd05fdc2dda6ba3641cf7ad53994d448da155f02",
+ "0x8327cba5a9ff0d3f5cd0ae55e77167448926d5fcf76550c0ad978092a14122723090c51c415e88e42a2b62eb07cc3981",
+ "0xb373dcdaea85f85ce9978b1426a7ef4945f65f2d3467a9f1cc551a99766aac95df4a09e2251d3f89ca8c9d1a7cfd7b0e",
+ "0xab1422dc41af2a227b973a6fd124dfcb2367e2a11a21faa1d381d404f51b7257e5bc82e9cf20cd7fe37d7ae761a2ab37",
+ "0xa93774a03519d2f20fdf2ef46547b0a5b77c137d6a3434b48d56a2cbef9e77120d1b85d0092cf8842909213826699477",
+ "0x8eb967a495a38130ea28711580b7e61bcd1d051cd9e4f2dbf62f1380bd86e0d60e978d72f6f31e909eb97b3b9a2b867c",
+ "0xae8213378da1287ba1fe4242e1acaec19b877b6fe872400013c6eac1084b8d03156792fa3020201725b08228a1e80f49",
+ "0xb143daf6893d674d607772b3b02d8ac48f294237e2f2c87963c0d4e26d9227d94a2a13512457c3d5883544bbc259f0ef",
+ "0xb343bd2aca8973888e42542218924e2dda2e938fd1150d06878af76f777546213912b7c7a34a0f94186817d80ffa185c",
+ "0xb188ebc6a8c3007001aa347ae72cc0b15d09bc6c19a80e386ee4b334734ec0cc2fe8b493c2422f38d1e6d133cc3db6fe",
+ "0xb795f6a8b9b826aaeee18ccd6baf6c5adeeec85f95eb5b6d19450085ec7217e95a2d9e221d77f583b297d0872073ba0e",
+ "0xb1c7dbd998ad32ae57bfa95deafa147024afd57389e98992c36b6e52df915d3d5a39db585141ec2423173e85d212fed8",
+ "0x812bcdeb9fe5f12d0e1df9964798056e1f1c3de3b17b6bd2919b6356c4b86d8e763c01933efbe0224c86a96d5198a4be",
+ "0xb19ebeda61c23d255cbf472ef0b8a441f4c55b70f0d8ed47078c248b1d3c7c62e076b43b95c00a958ec8b16d5a7cb0d7",
+ "0xb02adc9aaa20e0368a989c2af14ff48b67233d28ebee44ff3418bb0473592e6b681af1cc45450bd4b175df9051df63d9",
+ "0x8d87f0714acee522eb58cec00360e762adc411901dba46adc9227124fa70ee679f9a47e91a6306d6030dd4eb8de2f3c1",
+ "0x8be54cec21e74bcc71de29dc621444263737db15f16d0bb13670f64e42f818154e04b484593d19ef95f2ee17e4b3fe21",
+ "0xab8e20546c1db38d31493b5d5f535758afb17e459645c1b70813b1cf7d242fd5d1f4354a7c929e8f7259f6a25302e351",
+ "0x89f035a1ed8a1e302ac893349ba8ddf967580fcb6e73d44af09e3929cde445e97ff60c87dafe489e2c0ab9c9986cfa00",
+ "0x8b2b0851a795c19191a692af55f7e72ad2474efdc5401bc3733cfdd910e34c918aaebe69d5ea951bdddf3c01cabbfc67",
+ "0xa4edb52c2b51495ccd1ee6450fc14b7b3ede8b3d106808929d02fb31475bacb403e112ba9c818d2857651e508b3a7dd1",
+ "0x9569341fded45d19f00bcf3cbf3f20eb2b4d82ef92aba3c8abd95866398438a2387437e580d8b646f17cf6fde8c5af23",
+ "0xaa4b671c6d20f72f2f18a939a6ff21cc37e0084b44b4a717f1be859a80b39fb1be026b3205adec2a66a608ec2bcd578f",
+ "0x94902e980de23c4de394ad8aec91b46f888d18f045753541492bfbb92c59d3daa8de37ae755a6853744af8472ba7b72b",
+ "0xaf651ef1b2a0d30a7884557edfad95b6b5d445a7561caebdc46a485aedd25932c62c0798465c340a76f6feaa196dd712",
+ "0xb7b669b8e5a763452128846dd46b530dca4893ace5cc5881c7ddcd3d45969d7e73fbebdb0e78aa81686e5f7b22ec5759",
+ "0x82507fd4ebe9fa656a7f2e084d64a1fa6777a2b0bc106d686e2d9d2edafc58997e58cb6bfd0453b2bf415704aa82ae62",
+ "0xb40bce2b42b88678400ecd52955bbdadd15f8b9e1b3751a1a3375dc0efb5ca3ee258cf201e1140b3c09ad41217d1d49e",
+ "0xb0210d0cbb3fbf3b8cdb39e862f036b0ff941cd838e7aaf3a8354e24246e64778d22f3de34572e6b2a580614fb6425be",
+ "0x876693cba4301b251523c7d034108831df3ce133d8be5a514e7a2ca494c268ca0556fa2ad8310a1d92a16b55bcd99ea9",
+ "0x8660281406d22a4950f5ef050bf71dd3090edb16eff27fa29ef600cdea628315e2054211ed2cc6eaf8f2a1771ef689fd",
+ "0xa610e7e41e41ab66955b809ba4ade0330b8e9057d8efc9144753caed81995edeb1a42a53f93ce93540feca1fae708dac",
+ "0xa49e2c176a350251daef1218efaccc07a1e06203386ede59c136699d25ca5cb2ac1b800c25b28dd05678f14e78e51891",
+ "0x83e0915aa2b09359604566080d411874af8c993beba97d4547782fdbe1a68e59324b800ff1f07b8db30c71adcbd102a8",
+ "0xa19e84e3541fb6498e9bb8a099c495cbfcad113330e0262a7e4c6544495bb8a754b2208d0c2d895c93463558013a5a32",
+ "0x87f2bd49859a364912023aca7b19a592c60214b8d6239e2be887ae80b69ebdeb59742bdebcfa73a586ab23b2c945586c",
+ "0xb8e8fdddae934a14b57bc274b8dcd0d45ebb95ddbaabef4454e0f6ce7d3a5a61c86181929546b3d60c447a15134d08e1",
+ "0x87e0c31dcb736ea4604727e92dc1d9a3cf00adcff79df3546e02108355260f3dd171531c3c0f57be78d8b28058fcc8c0",
+ "0x9617d74e8f808a4165a8ac2e30878c349e1c3d40972006f0787b31ea62d248c2d9f3fc3da83181c6e57e95feedfd0e8c",
+ "0x8949e2cee582a2f8db86e89785a6e46bc1565c2d8627d5b6bf43ba71ffadfab7e3c5710f88dcb5fb2fc6edf6f4fae216",
+ "0xad3fa7b0edceb83118972a2935a09f409d09a8db3869f30be3a76f67aa9fb379cabb3a3aff805ba023a331cad7d7eb64",
+ "0x8c95718a4112512c4efbd496be38bf3ca6cdcaad8a0d128f32a3f9aae57f3a57bdf295a3b372a8c549fda8f4707cffed",
+ "0x88f3261d1e28a58b2dee3fcc799777ad1c0eb68b3560f9b4410d134672d9533532a91ea7be28a041784872632d3c9d80",
+ "0xb47472a41d72dd2e8b72f5c4f8ad626737dde3717f63d6bc776639ab299e564cbad0a2ad5452a07f02ff49a359c437e5",
+ "0x9896d21dc2e8aad87b76d6df1654f10cd7bceed4884159d50a818bea391f8e473e01e14684814c7780235f28e69dca6e",
+ "0x82d47c332bbd31bbe83b5eb44a23da76d4a7a06c45d7f80f395035822bc27f62f59281d5174e6f8e77cc9b5c3193d6f0",
+ "0x95c74cd46206e7f70c9766117c34c0ec45c2b0f927a15ea167901a160e1530d8522943c29b61e03568aa0f9c55926c53",
+ "0xa89d7757825ae73a6e81829ff788ea7b3d7409857b378ebccd7df73fdbe62c8d9073741cf038314971b39af6c29c9030",
+ "0x8c1cd212d0b010905d560688cfc036ae6535bc334fa8b812519d810b7e7dcf1bb7c5f43deaa40f097158358987324a7f",
+ "0xb86993c383c015ed8d847c6b795164114dd3e9efd25143f509da318bfba89389ea72a420699e339423afd68b6512fafb",
+ "0x8d06bd379c6d87c6ed841d8c6e9d2d0de21653a073725ff74be1934301cc3a79b81ef6dd0aad4e7a9dc6eac9b73019bc",
+ "0x81af4d2d87219985b9b1202d724fe39ef988f14fef07dfe3c3b11714e90ffba2a97250838e8535eb63f107abfe645e96",
+ "0x8c5e0af6330a8becb787e4b502f34f528ef5756e298a77dc0c7467433454347f3a2e0bd2641fbc2a45b95e231c6e1c02",
+ "0x8e2a8f0f04562820dc8e7da681d5cad9fe2e85dd11c785fb6fba6786c57a857e0b3bd838fb849b0376c34ce1665e4837",
+ "0xa39be8269449bfdfc61b1f62077033649f18dae9bef7c6163b9314ca8923691fb832f42776f0160b9e8abd4d143aa4e1",
+ "0x8c154e665706355e1cc98e0a4cabf294ab019545ba9c4c399d666e6ec5c869ca9e1faf8fb06cd9c0a5c2f51a7d51b70a",
+ "0xa046a7d4de879d3ebd4284f08f24398e9e3bf006cd4e25b5c67273ade248689c69affff92ae810c07941e4904296a563",
+ "0xafd94c1cb48758e5917804df03fb38a6da0e48cd9b6262413ea13b26973f9e266690a1b7d9d24bbaf7e82718e0e594b0",
+ "0x859e21080310c8d6a38e12e2ac9f90a156578cdeb4bb2e324700e97d9a5511cd6045dc39d1d0de3f94aeed043a24119d",
+ "0xa219fb0303c379d0ab50893264919f598e753aac9065e1f23ef2949abc992577ab43c636a1d2c089203ec9ddb941e27d",
+ "0xb0fdb639d449588a2ca730afcba59334e7c387342d56defdfb7ef79c493f7fd0e5277eff18e7203e756c7bdda5803047",
+ "0x87f9c3b7ed01f54368aca6dbcf2f6e06bff96e183c4b2c65f8baa23b377988863a0a125d5cdd41a072da8462ced4c070",
+ "0x99ef7a5d5ac2f1c567160e1f8c95f2f38d41881850f30c461a205f7b1b9fb181277311333839b13fb3ae203447e17727",
+ "0xaeaca9b1c2afd24e443326cc68de67b4d9cedb22ad7b501a799d30d39c85bb2ea910d4672673e39e154d699e12d9b3dc",
+ "0xa11675a1721a4ba24dd3d0e4c3c33a6edf4cd1b9f6b471070b4386c61f77452266eae6e3f566a40cfc885eada9a29f23",
+ "0xb228334445e37b9b49cb4f2cc56b454575e92173ddb01370a553bba665adadd52df353ad74470d512561c2c3473c7bb9",
+ "0xa18177087c996572d76f81178d18ed1ceebc8362a396348ce289f1d8bd708b9e99539be6fccd4acb1112381cfc5749b4",
+ "0x8e7b8bf460f0d3c99abb19803b9e43422e91507a1c0c22b29ee8b2c52d1a384da4b87c292e28eff040db5be7b1f8641f",
+ "0xb03d038d813e29688b6e6f444eb56fec3abba64c3d6f890a6bcf2e916507091cdb2b9d2c7484617be6b26552ed1c56cb",
+ "0xa1c88ccd30e934adfc5494b72655f8afe1865a84196abfb376968f22ddc07761210b6a9fb7638f1413d1b4073d430290",
+ "0x961b714faebf172ad2dbc11902461e286e4f24a99a939152a53406117767682a571057044decbeb3d3feef81f4488497",
+ "0xa03dc4059b46effdd786a0a03cc17cfee8585683faa35bb07936ded3fa3f3a097f518c0b8e2db92fd700149db1937789",
+ "0xadf60180c99ca574191cbcc23e8d025b2f931f98ca7dfcebfc380226239b6329347100fcb8b0fcb12db108c6ad101c07",
+ "0x805d4f5ef24d46911cbf942f62cb84b0346e5e712284f82b0db223db26d51aabf43204755eb19519b00e665c7719fcaa",
+ "0x8dea7243e9c139662a7fe3526c6c601eee72fd8847c54c8e1f2ad93ef7f9e1826b170afe58817dac212427164a88e87f",
+ "0xa2ba42356606d651b077983de1ad643650997bb2babb188c9a3b27245bb65d2036e46667c37d4ce02cb1be5ae8547abe",
+ "0xaf2ae50b392bdc013db2d12ce2544883472d72424fc767d3f5cb0ca2d973fc7d1f425880101e61970e1a988d0670c81b",
+ "0x98e6bec0568d3939b31d00eb1040e9b8b2a35db46ddf4369bdaee41bbb63cc84423d29ee510a170fb5b0e2df434ba589",
+ "0x822ff3cd12fbef4f508f3ca813c04a2e0b9b799c99848e5ad3563265979e753ee61a48f6adc2984a850f1b46c1a43d35",
+ "0x891e8b8b92a394f36653d55725ef514bd2e2a46840a0a2975c76c2a935577f85289026aaa74384da0afe26775cbddfb9",
+ "0xb2a3131a5d2fe7c8967047aa66e4524babae941d90552171cc109527f345f42aa0df06dcbb2fa01b33d0043917bbed69",
+ "0x80c869469900431f3eeefafdbe07b8afd8cee7739e659e6d0109b397cacff85a88247698f87dc4e2fe39a592f250ac64",
+ "0x9091594f488b38f9d2bb5df49fd8b4f8829d9c2f11a197dd1431ed5abbc5c954bbde3387088f9ee3a5a834beb7619bce",
+ "0xb472e241e6956146cca57b97a8a204668d050423b4e76f857bad5b47f43b203a04c8391ba9d9c3e95093c071f9d376a1",
+ "0xb7dd2de0284844392f7dfb56fe7ca3ede41e27519753ffc579a0a8d2d65ceb8108d06b6b0d4c3c1a2588951297bd1a1e",
+ "0x902116ce70d0a079ac190321c1f48701318c05f8e69ee09694754885d33a835a849cafe56f499a2f49f6cda413ddf9a7",
+ "0xb18105cc736787fafaf7c3c11c448bce9466e683159dff52723b7951dff429565e466e4841d982e3aaa9ee2066838666",
+ "0x97ab9911f3f659691762d568ae0b7faa1047b0aed1009c319fa79d15d0db8db9f808fc385dc9a68fa388c10224985379",
+ "0xb2a2cba65f5b927e64d2904ba412e2bac1cf18c9c3eda9c72fb70262497ecf505b640827e2afebecf10eebbcf48ccd3e",
+ "0xb36a3fd677baa0d3ef0dac4f1548ff50a1730286b8c99d276a0a45d576e17b39b3cbadd2fe55e003796d370d4be43ce3",
+ "0xa5dfec96ca3c272566e89dc453a458909247e3895d3e44831528130bc47cc9d0a0dac78dd3cad680a4351d399d241967",
+ "0x8029382113909af6340959c3e61db27392531d62d90f92370a432aec3eb1e4c36ae1d4ef2ba8ec6edb4d7320c7a453f6",
+ "0x971d85121ea108e6769d54f9c51299b0381ece8b51d46d49c89f65bedc123bab4d5a8bc14d6f67f4f680077529cbae4c",
+ "0x98ff6afc01d0bec80a278f25912e1b1ebff80117adae72e31d5b9fa4d9624db4ba2065b444df49b489b0607c45e26c4c",
+ "0x8fa29be10fb3ab30ce25920fec0187e6e91e458947009dabb869aade7136c8ba23602682b71e390c251f3743164cbdaa",
+ "0xb3345c89eb1653418fe3940cf3e56a9a9c66526389b98f45ca02dd62bfb37baa69a4baaa7132d7320695f8ea6ad1fd94",
+ "0xb72c7f5541c9ac6b60a7ec9f5415e7fb14da03f7164ea529952a29399f3a071576608dbbcc0d45994f21f92ddbeb1e19",
+ "0xaa3450bb155a5f9043d0ef95f546a2e6ade167280bfb75c9f09c6f9cdb1fffb7ce8181436161a538433afa3681c7a141",
+ "0x92a18fecaded7854b349f441e7102b638ababa75b1b0281dd0bded6541abe7aa37d96693595be0b01fe0a2e2133d50f9",
+ "0x980756ddf9d2253cfe6c94960b516c94889d09e612810935150892627d2ecee9a2517e04968eea295d0106850c04ca44",
+ "0xae68c6ccc454318cdd92f32b11d89116a3b8350207a36d22a0f626718cad671d960090e054c0c77ac3162ae180ecfd4b",
+ "0x99f31f66eaaa551749ad91d48a0d4e3ff4d82ef0e8b28f3184c54e852422ba1bdafd53b1e753f3a070f3b55f3c23b6a2",
+ "0xa44eaeaa6589206069e9c0a45ff9fc51c68da38d4edff1d15529b7932e6f403d12b9387019c44a1488a5d5f27782a51f",
+ "0xb80b5d54d4b344840e45b79e621bd77a3f83fb4ce6d8796b7d6915107b3f3c34d2e7d95bdafd120f285669e5acf2437a",
+ "0xb36c069ec085a612b5908314d6b84c00a83031780261d1c77a0384c406867c9847d5b0845deddfa512cc04a8df2046fb",
+ "0xb09dbe501583220f640d201acea7ee3e39bf9eda8b91aa07b5c50b7641d86d71acb619b38d27835ce97c3759787f08e9",
+ "0x87403d46a2bf63170fff0b857acacf42ee801afe9ccba8e5b4aea967b68eac73a499a65ca46906c2eb4c8f27bc739faa",
+ "0x82b93669f42a0a2aa5e250ffe6097269da06a9c02fcd1801abbad415a7729a64f830754bafc702e64600ba47671c2208",
+ "0x8e3a3029be7edb8dd3ab1f8216664c8dc50d395f603736061d802cef77627db7b859ef287ed850382c13b4d22d6a2d80",
+ "0x968e9ec7194ff424409d182ce0259acd950c384c163c04463bc8700a40b79beba6146d22b7fa7016875a249b7b31c602",
+ "0x8b42c984bbe4996e0c20862059167c6bdc5164b1ffcd928f29512664459212d263e89f0f0e30eed4e672ffa5ed0b01b5",
+ "0x96bac54062110dada905363211133f1f15dc7e4fd80a4c6e4a83bc9a0bcbbaba11cd2c7a13debcf0985e1a954c1da66b",
+ "0xa16dc8a653d67a7cd7ae90b2fffac0bf1ca587005430fe5ba9403edd70ca33e38ba5661d2ed6e9d2864400d997626a62",
+ "0xa68ab11a570a27853c8d67e491591dcba746bfbee08a2e75ae0790399130d027ed387f41ef1d7de8df38b472df309161",
+ "0x92532b74886874447c0300d07eda9bbe4b41ed25349a3da2e072a93fe32c89d280f740d8ff70d5816793d7f2b97373cc",
+ "0x88e35711b471e89218fd5f4d0eadea8a29405af1cd81974427bc4a5fb26ed60798daaf94f726c96e779b403a2cd82820",
+ "0xb5c72aa4147c19f8c4f3a0a62d32315b0f4606e0a7025edc5445571eaf4daff64f4b7a585464821574dd50dbe1b49d08",
+ "0x9305d9b4095258e79744338683fd93f9e657367b3ab32d78080e51d54eec331edbc224fad5093ebf8ee4bd4286757eb8",
+ "0xb2a17abb3f6a05bcb14dc7b98321fa8b46d299626c73d7c6eb12140bf4c3f8e1795250870947af817834f033c88a59d6",
+ "0xb3477004837dbd8ba594e4296f960fc91ab3f13551458445e6c232eb04b326da803c4d93e2e8dcd268b4413305ff84da",
+ "0x924b4b2ebaafdcfdfedb2829a8bf46cd32e1407d8d725a5bd28bdc821f1bafb3614f030ea4352c671076a63494275a3f",
+ "0x8b81b9ef6125c82a9bece6fdcb9888a767ac16e70527753428cc87c56a1236e437da8be4f7ecfe57b9296dc3ae7ba807",
+ "0x906e19ec8b8edd58bdf9ae05610a86e4ea2282b1bbc1e8b00b7021d093194e0837d74cf27ac9916bdb8ec308b00da3da",
+ "0xb41c5185869071760ac786078a57a2ab4e2af60a890037ac0c0c28d6826f15c2cf028fddd42a9b6de632c3d550bfbc14",
+ "0xa646e5dec1b713ae9dfdf7bdc6cd474d5731a320403c7dfcfd666ffc9ae0cff4b5a79530e8df3f4aa9cb80568cb138e9",
+ "0xb0efad22827e562bd3c3e925acbd0d9425d19057868608d78c2209a531cccd0f2c43dc5673acf9822247428ffa2bb821",
+ "0xa94c19468d14b6f99002fc52ac06bbe59e5c472e4a0cdb225144a62f8870b3f10593749df7a2de0bd3c9476ce682e148",
+ "0x803864a91162f0273d49271dafaab632d93d494d1af935aefa522768af058fce52165018512e8d6774976d52bd797e22",
+ "0xa08711c2f7d45c68fb340ac23597332e1bcaec9198f72967b9921204b9d48a7843561ff318f87908c05a44fc35e3cc9d",
+ "0x91c3cad94a11a3197ae4f9461faab91a669e0dddb0371d3cab3ed9aeb1267badc797d8375181130e461eadd05099b2a2",
+ "0x81bdaaf48aae4f7b480fc13f1e7f4dd3023a41439ba231760409ce9292c11128ab2b0bdbbf28b98af4f97b3551f363af",
+ "0x8d60f9df9fd303f625af90e8272c4ecb95bb94e6efc5da17b8ab663ee3b3f673e9f6420d890ccc94acf4d2cae7a860d8",
+ "0xa7b75901520c06e9495ab983f70b61483504c7ff2a0980c51115d11e0744683ce022d76e3e09f4e99e698cbd21432a0d",
+ "0x82956072df0586562fda7e7738226f694e1c73518dd86e0799d2e820d7f79233667192c9236dcb27637e4c65ef19d493",
+ "0xa586beb9b6ffd06ad200957490803a7cd8c9bf76e782734e0f55e04a3dc38949de75dc607822ec405736c576cf83bca3",
+ "0xa179a30d00def9b34a7e85607a447eea0401e32ab5abeee1a281f2acd1cf6ec81a178020666f641d9492b1bdf66f05a3",
+ "0x83e129705c538787ed8e0fdc1275e6466a3f4ee21a1e6abedd239393b1df72244723b92f9d9d9339a0cab6ebf28f5a16",
+ "0x811bd8d1e3722b64cd2f5b431167e7f91456e8bba2cc669d3fbbce7d553e29c3c19f629fcedd2498bc26d33a24891d17",
+ "0xa243c030c858f1f60cccd26b45b024698cc6d9d9e6198c1ed4964a235d9f8d0baf9cde10c8e63dfaa47f8e74e51a6e85",
+ "0xab839eb82e23ca52663281f863b55b0a3d6d4425c33ffb4eeb1d7979488ab068bf99e2a60e82cea4dc42c56c26cbfebe",
+ "0x8b896f9bb21d49343e67aec6ad175b58c0c81a3ca73d44d113ae4354a0065d98eb1a5cafedaf232a2bb9cdc62152f309",
+ "0xaf6230340cc0b66f5bf845540ed4fc3e7d6077f361d60762e488d57834c3e7eb7eacc1b0ed73a7d134f174a01410e50c",
+ "0x88975e1b1af678d1b5179f72300a30900736af580dd748fd9461ef7afccc91ccd9bed33f9da55c8711a7635b800e831f",
+ "0xa97486bb9047391661718a54b8dd5a5e363964e495eae6c692730264478c927cf3e66dd3602413189a3699fbeae26e15",
+ "0xa5973c161ab38732885d1d2785fd74bf156ba34881980cba27fe239caef06b24a533ffe6dbbbeca5e6566682cc00300a",
+ "0xa24776e9a840afda0003fa73b415d5bd6ecd9b5c2cc842b643ee51b8c6087f4eead4d0bfbd987eb174c489a7b952ff2a",
+ "0xa8a6ee06e3af053b705a12b59777267c546f33ba8a0f49493af8e6df4e15cf8dd2d4fb4daf7e84c6b5d3a7363118ff03",
+ "0xa28e59ce6ad02c2ce725067c0123117e12ac5a52c8f5af13eec75f4a9efc4f696777db18a374fa33bcae82e0734ebd16",
+ "0x86dfc3b78e841c708aff677baa8ee654c808e5d257158715097c1025d46ece94993efe12c9d188252ad98a1e0e331fec",
+ "0xa88d0275510f242eab11fdb0410ff6e1b9d7a3cbd3658333539815f1b450a84816e6613d15aa8a8eb15d87cdad4b27a2",
+ "0x8440acea2931118a5b481268ff9f180ee4ede85d14a52c026adc882410825b8275caa44aff0b50c2b88d39f21b1a0696",
+ "0xa7c3182eab25bd6785bacf12079d0afb0a9b165d6ed327814e2177148539f249eb9b5b2554538f54f3c882d37c0a8abe",
+ "0x85291fbe10538d7da38efdd55a7acebf03b1848428a2f664c3ce55367aece60039f4f320b1771c9c89a35941797f717c",
+ "0xa2c6414eeb1234728ab0de94aa98fc06433a58efa646ca3fcbd97dbfb8d98ae59f7ce6d528f669c8149e1e13266f69c9",
+ "0x840c8462785591ee93aee2538d9f1ec44ba2ca61a569ab51d335ac873f5d48099ae8d7a7efa0725d9ff8f9475bfa4f56",
+ "0xa7065a9d02fb3673acf7702a488fbc01aa69580964932f6f40b6c2d1c386b19e50b0e104fcac24ea26c4e723611d0238",
+ "0xb72db6d141267438279e032c95e6106c2ccb3164b842ba857a2018f3a35f4b040da92680881eb17cd61d0920d5b8f006",
+ "0xa8005d6c5960e090374747307ef0be2871a7a43fa4e76a16c35d2baab808e9777b496e9f57a4218b23390887c33a0b55",
+ "0x8e152cea1e00a451ca47c20a1e8875873419700af15a5f38ee2268d3fbc974d4bd5f4be38008fa6f404dbdedd6e6e710",
+ "0xa3391aed1fcd68761f06a7d1008ec62a09b1cb3d0203cd04e300a0c91adfed1812d8bc1e4a3fd7976dc0aae0e99f52f1",
+ "0x967eb57bf2aa503ee0c6e67438098149eac305089c155f1762cf5e84e31f0fbf27c34a9af05621e34645c1ec96afaec8",
+ "0x88af97ddc4937a95ec0dcd25e4173127260f91c8db2f6eac84afb789b363705fb3196235af631c70cafd09411d233589",
+ "0xa32df75b3f2c921b8767638fd289bcfc61e08597170186637a7128ffedd52c798c434485ac2c7de07014f9e895c2c3d8",
+ "0xb0a783832153650aa0d766a3a73ec208b6ce5caeb40b87177ffc035ab03c7705ecdd1090b6456a29f5fb7e90e2fa8930",
+ "0xb59c8e803b4c3486777d15fc2311b97f9ded1602fa570c7b0200bada36a49ee9ef4d4c1474265af8e1c38a93eb66b18b",
+ "0x982f2c85f83e852022998ff91bafbb6ff093ef22cf9d5063e083a48b29175ccbd51b9c6557151409e439096300981a6c",
+ "0x939e3b5989fefebb9d272a954659a4eb125b98c9da6953f5e628d26266bd0525ec38304b8d56f08d65abc4d6da4a8dbb",
+ "0x8898212fe05bc8de7d18503cb84a1c1337cc2c09d1eeef2b475aa79185b7322bf1f8e065f1bf871c0c927dd19faf1f6d",
+ "0x94b0393a41cd00f724aee2d4bc72103d626a5aecb4b5486dd1ef8ac27528398edf56df9db5c3d238d8579af368afeb09",
+ "0x96ac564450d998e7445dd2ea8e3fc7974d575508fa19e1c60c308d83b645864c029f2f6b7396d4ff4c1b24e92e3bac37",
+ "0x8adf6638e18aff3eb3b47617da696eb6c4bdfbecbbc3c45d3d0ab0b12cbad00e462fdfbe0c35780d21aa973fc150285e",
+ "0xb53f94612f818571b5565bbb295e74bada9b5f9794b3b91125915e44d6ddcc4da25510eab718e251a09c99534d6042d9",
+ "0x8b96462508d77ee083c376cd90807aebad8de96bca43983c84a4a6f196d5faf6619a2351f43bfeec101864c3bf255519",
+ "0xaeadf34657083fc71df33bd44af73bf5281c9ca6d906b9c745536e1819ea90b56107c55e2178ebad08f3ba75b3f81c86",
+ "0x9784ba29b2f0057b5af1d3ab2796d439b8753f1f749c73e791037461bdfc3f7097394283105b8ab01788ea5255a96710",
+ "0x8756241bda159d4a33bf74faba0d4594d963c370fb6a18431f279b4a865b070b0547a6d1613cf45b8cfb5f9236bbf831",
+ "0xb03ebfd6b71421dfd49a30460f9f57063eebfe31b9ceaa2a05c37c61522b35bdc09d7db3ad75c76c253c00ba282d3cd2",
+ "0xb34e7e6341fa9d854b2d3153bdda0c4ae2b2f442ab7af6f99a0975d45725aa48e36ae5f7011edd249862e91f499687d4",
+ "0xb462ee09dc3963a14354244313e3444de5cc37ea5ccfbf14cd9aca8027b59c4cb2a949bc30474497cab8123e768460e6",
+ "0xaea753290e51e2f6a21a9a0ee67d3a2713f95c2a5c17fe41116c87d3aa77b1683761264d704df1ac34f8b873bc88ef7b",
+ "0x98430592afd414394f98ddfff9f280fcb1c322dbe3510f45e1e9c4bb8ee306b3e0cf0282c0ee73ebb8ba087d4d9e0858",
+ "0xb95d3b5aaf54ffca11f4be8d57f76e14afdb20afc859dc7c7471e0b42031e8f3d461b726ecb979bdb2f353498dfe95ea",
+ "0x984d17f9b11a683132e0b5a9ee5945e3ff7054c2d5c716be73b29078db1d36f54c6e652fd2f52a19da313112e97ade07",
+ "0xab232f756b3fff3262be418a1af61a7e0c95ceebbc775389622a8e10610508cd6784ab7960441917a83cc191c58829ea",
+ "0xa28f41678d6e60de76b0e36ab10e4516e53e02e9c77d2b5af3cfeee3ce94cfa30c5797bd1daab20c98e1cad83ad0f633",
+ "0xb55395fca84dd3ccc05dd480cb9b430bf8631ff06e24cb51d54519703d667268c2f8afcde4ba4ed16bece8cc7bc8c6e0",
+ "0x8a8a5392a0e2ea3c7a8c51328fab11156004e84a9c63483b64e8f8ebf18a58b6ffa8fe8b9d95af0a2f655f601d096396",
+ "0xab480000fe194d23f08a7a9ec1c392334e9c687e06851f083845121ce502c06b54dda8c43092bcc1035df45cc752fe9b",
+ "0xb265644c29f628d1c7e8e25a5e845cabb21799371814730a41a363e1bda8a7be50fee7c3996a365b7fcba4642add10db",
+ "0xb8a915a3c685c2d4728f6931c4d29487cad764c5ce23c25e64b1a3259ac27235e41b23bfe7ae982921b4cb84463097df",
+ "0x8efa7338442a4b6318145a5440fc213b97869647eeae41b9aa3c0a27ee51285b73e3ae3b4a9423df255e6add58864aa9",
+ "0x9106d65444f74d217f4187dfc8fcf3810b916d1e4275f94f6a86d1c4f3565b131fd6cde1fa708bc05fe183c49f14941a",
+ "0x948252dac8026bbbdb0a06b3c9d66ec4cf9532163bab68076fda1bd2357b69e4b514729c15aaa83b5618b1977bbc60c4",
+ "0xae6596ccfdf5cbbc5782efe3bb0b101bb132dbe1d568854ca24cacc0b2e0e9fabcb2ca7ab42aecec412efd15cf8cb7a2",
+ "0x84a0b6c198ff64fd7958dfd1b40eac9638e8e0b2c4cd8cf5d8cdf80419baee76a05184bce6c5b635f6bf2d30055476a7",
+ "0x8893118be4a055c2b3da593dbca51b1ae2ea2469911acfb27ee42faf3e6c3ad0693d3914c508c0b05b36a88c8b312b76",
+ "0xb097479e967504deb6734785db7e60d1d8034d6ca5ba9552887e937f5e17bb413fccac2c1d1082154ed76609127860ad",
+ "0xa0294e6b9958f244d29943debf24b00b538b3da1116269b6e452bb12dc742226712fd1a15b9c88195afeb5d2415f505c",
+ "0xb3cc15f635080bc038f61b615f62b5b5c6f2870586191f59476e8368a73641d6ac2f7d0c1f54621982defdb318020230",
+ "0x99856f49b9fe1604d917c94d09cc0ed753d13d015d30587a94e6631ffd964b214e607deb8a69a8b5e349a7edf4309206",
+ "0xa8571e113ea22b4b4fce41a094da8c70de37830ae32e62c65c2fa5ad06a9bc29e884b945e73d448c72b176d6ecebfb58",
+ "0xa9e9c6e52beb0013273c29844956b3ce291023678107cdc785f7b44eff5003462841ad8780761b86aefc6b734adde7cf",
+ "0x80a784b0b27edb51ef2bad3aee80e51778dcaa0f3f5d3dcb5dc5d4f4b2cf7ae35b08de6680ea9dac53f8438b92eb09ef",
+ "0x827b543e609ea328e97e373f70ad72d4915a2d1daae0c60d44ac637231070e164c43a2a58db80a64df1c624a042b38f9",
+ "0xb449c65e8195202efdcb9bdb4e869a437313b118fef8b510cbbf8b79a4e99376adb749b37e9c20b51b31ed3310169e27",
+ "0x8ea3028f4548a79a94c717e1ed28ad4d8725b8d6ab18b021063ce46f665c79da3c49440c6577319dab2d036b7e08f387",
+ "0x897798431cfb17fe39f08f5f854005dc37b1c1ec1edba6c24bc8acb3b88838d0534a75475325a5ea98b326ad47dbad75",
+ "0x89cf232e6303b0751561960fd4dea5754a28c594daf930326b4541274ffb03c7dd75938e411eb9a375006a70ce38097f",
+ "0x9727c6ae7f0840f0b6c8bfb3a1a5582ceee705e0b5c59b97def7a7a2283edd4d3f47b7971e902a3a2079e40b53ff69b8",
+ "0xb76ed72b122c48679d221072efc0eeea063cb205cbf5f9ef0101fd10cb1075b8628166c83577cced654e1c001c7882f7",
+ "0xae908c42d208759da5ee9b405df85a6532ea35c6f0f6a1288d22870f59d98edc896841b8ac890a538e6c8d1e8b02d359",
+ "0x809d12fe4039a0ec80dc9be6a89acaab7797e5f7f9b163378f52f9a75a1d73b2e9ae6e3dd49e32ced439783c1cabbef5",
+ "0xa4149530b7f85d1098ba534d69548c6c612c416e8d35992fc1f64f4deeb41e09e49c6cf7aadbed7e846b91299358fe2d",
+ "0xa49342eacd1ec1148b8df1e253b1c015f603c39de11fa0a364ccb86ea32d69c34fd7aa6980a1fadcd8e785a57fa46f60",
+ "0x87d43eff5a006dc4dddcf76cc96c656a1f3a68f19f124181feab86c6cc9a52cb9189cdbb423414defdd9bb0ca8ff1ddc",
+ "0x861367e87a9aa2f0f68296ba50aa5dbc5713008d260cc2c7e62d407c2063064749324c4e8156dc21b749656cfebce26b",
+ "0xb5303c2f72e84e170e66ae1b0fbd51b8c7a6f27476eaf5694b64e8737d5c84b51fe90100b256465a4c4156dd873cddb0",
+ "0xb62849a4f891415d74f434cdc1d23c4a69074487659ca96e1762466b2b7a5d8525b056b891d0feea6fe6845cba8bc7fb",
+ "0x923dd9e0d6590a9307e8c4c23f13bae3306b580e297a937711a8b13e8de85e41a61462f25b7d352b682e8437bf2b4ab3",
+ "0x9147379860cd713cd46c94b8cdf75125d36c37517fbecf81ace9680b98ce6291cd1c3e472f84249cc3b2b445e314b1b6",
+ "0xa808a4f17ac21e3fb5cfef404e61fae3693ca3e688d375f99b6116779696059a146c27b06de3ac36da349b0649befd56",
+ "0x87787e9322e1b75e66c1f0d9ea0915722a232770930c2d2a95e9478c4b950d15ab767e30cea128f9ed65893bfc2d0743",
+ "0x9036a6ee2577223be105defe1081c48ea7319e112fff9110eb9f61110c319da25a6cea0464ce65e858635b079691ef1f",
+ "0xaf5548c7c24e1088c23b57ee14d26c12a83484c9fd9296edf1012d8dcf88243f20039b43c8c548c265ef9a1ffe9c1c88",
+ "0xa0fff520045e14065965fb8accd17e878d3fcaf9e0af2962c8954e50be6683d31fa0bf4816ab68f08630dbac6bfce52a",
+ "0xb4c1b249e079f6ae1781af1d97a60b15855f49864c50496c09c91fe1946266915b799f0406084d7783f5b1039116dd8b",
+ "0x8b0ffa5e7c498cb3879dddca34743b41eee8e2dea3d4317a6e961b58adb699ef0c92400c068d5228881a2b08121226bf",
+ "0x852ae8b19a1d80aa8ae5382e7ee5c8e7670ceb16640871c56b20b96b66b3b60e00015a3dde039446972e57b49a999ddd",
+ "0xa49942f04234a7d8492169da232cfff8051df86e8e1ba3db46aede02422c689c87dc1d99699c25f96cb763f5ca0983e5",
+ "0xb04b597b7760cf5dcf411ef896d1661e6d5b0db3257ac2cf64b20b60c6cc18fa10523bb958a48d010b55bac7b02ab3b1",
+ "0xa494591b51ea8285daecc194b5e5bd45ae35767d0246ac94fae204d674ee180c8e97ff15f71f28b7aeb175b8aea59710",
+ "0x97d2624919e78406e7460730680dea8e71c8571cf988e11441aeea54512b95bd820e78562c99372d535d96f7e200d20d",
+ "0xac693ddb00e48f76e667243b9b6a7008424043fb779e4f2252330285232c3fccac4da25cbd6d95fe9ad959ff305a91f6",
+ "0x8d20ca0a71a64a3f702a0825bb46bd810d03bebfb227683680d474a52f965716ff99e19a165ebaf6567987f4f9ee3c94",
+ "0xa5c516a438f916d1d68ca76996404792e0a66e97b7f18fc54c917bf10cf3211b62387932756e39e67e47b0bd6e88385a",
+ "0xb089614d830abc0afa435034cec7f851f2f095d479cacf1a3fb57272da826c499a52e7dcbc0eb85f4166fb94778e18e9",
+ "0xa8dacc943765d930848288192f4c69e2461c4b9bc6e79e30eeef9a543318cf9ae9569d6986c65c5668a89d49993f8e07",
+ "0xab5a9361fa339eec8c621bdad0a58078983abd8942d4282b22835d7a3a47e132d42414b7c359694986f7db39386c2e19",
+ "0x94230517fb57bd8eb26c6f64129b8b2abd0282323bf7b94b8bac7fab27b4ecc2c4290c294275e1a759de19f2216134f3",
+ "0xb8f158ea5006bc3b90b285246625faaa6ac9b5f5030dc69701b12f3b79a53ec7e92eeb5a63bbd1f9509a0a3469ff3ffc",
+ "0x8b6944fd8cb8540957a91a142fdcda827762aa777a31e8810ca6d026e50370ee1636fc351724767e817ca38804ebe005",
+ "0x82d1ee40fe1569c29644f79fa6c4033b7ed45cd2c3b343881f6eb0de2e79548fded4787fae19bed6ee76ed76ff9f2f11",
+ "0xa8924c7035e99eaed244ca165607e7e568b6c8085510dcdbaf6ebdbed405af2e6c14ee27d94ffef10d30aa52a60bf66d",
+ "0x956f82a6c2ae044635e85812581e4866c5fa2f427b01942047d81f6d79a14192f66fbbe77c9ffeaef4e6147097fdd2b5",
+ "0xb1100255a1bcf5e05b6aff1dfeb6e1d55b5d68d43a7457ba10cc76b61885f67f4d0d5179abda786e037ae95deb8eea45",
+ "0x99510799025e3e5e8fbf06dedb14c060c6548ba2bda824f687d3999dc395e794b1fb6514b9013f3892b6cf65cb0d65aa",
+ "0x8f9091cebf5e9c809aab415942172258f894e66e625d7388a05289183f01b8d994d52e05a8e69f784fba41db9ea357f0",
+ "0xa13d2eeb0776bdee9820ecb6693536720232848c51936bb4ef4fe65588d3f920d08a21907e1fdb881c1ad70b3725e726",
+ "0xa68b8f18922d550284c5e5dc2dda771f24c21965a6a4d5e7a71678178f46df4d8a421497aad8fcb4c7e241aba26378a0",
+ "0x8b7601f0a3c6ad27f03f2d23e785c81c1460d60100f91ea9d1cab978aa03b523150206c6d52ce7c7769c71d2c8228e9e",
+ "0xa8e02926430813caa851bb2b46de7f0420f0a64eb5f6b805401c11c9091d3b6d67d841b5674fa2b1dce0867714124cd8",
+ "0xb7968ecba568b8193b3058400af02c183f0a6df995a744450b3f7e0af7a772454677c3857f99c140bbdb2a09e832e8e0",
+ "0x8f20b1e9ba87d0a3f35309b985f3c18d2e8800f1ca7f0c52cadef773f1496b6070c936eea48c4a1cae83fd2524e9d233",
+ "0x88aef260042db0d641a51f40639dbeeefa9e9811df30bee695f3791f88a2f84d318f04e8926b7f47bf25956cb9e3754f",
+ "0x9725345893b647e9ba4e6a29e12f96751f1ae25fcaec2173e9a259921a1a7edb7a47159b3c8767e44d9e2689f5aa0f72",
+ "0x8c281e6f72752cb11e239e4df9341c45106eb7993c160e54423c2bffe10bc39d42624b45a1f673936ef2e1a02fc92f1a",
+ "0x90aba2f68bddb2fcce6c51430dacdfeec43ea8dc379660c99095df11017691ccf5faa27665cf4b9f0eea7728ae53c327",
+ "0xb7022695c16521c5704f49b7ddbdbec9b5f57ce0ceebe537bc0ebb0906d8196cc855a9afeb8950a1710f6a654464d93f",
+ "0x8fe1b9dd3c6a258116415d36e08374e094b22f0afb104385a5da48be17123e86fb8327baacc4f0d9ebae923d55d99bb5",
+ "0x817e85d8e3d19a4cbc1dec31597142c2daa4871bda89c2177fa719c00eda3344eb08b82eb92d4aa91a9eaacb3fc09783",
+ "0xb59053e1081d2603f1ca0ba553804d6fa696e1fd996631db8f62087b26a40dfef02098b0326bb75f99ec83b9267ca738",
+ "0x990a173d857d3ba81ff3789b931bfc9f5609cde0169b7f055fa3cb56451748d593d62d46ba33f80f9cafffe02b68dd14",
+ "0xb0c538dbba4954b809ab26f9f94a3cf1dcb77ce289eaec1d19f556c0ae4be1fa03af4a9b7057837541c3cc0a80538736",
+ "0xac3ba42f5f44f9e1fc453ce49c4ab79d0e1d5c42d3b30b1e098f3ab3f414c4c262fa12fb2be249f52d4aaf3c5224beb9",
+ "0xaf47467eb152e59870e21f0d4da2f43e093daf40180ab01438030684b114d025326928eaab12c41b81a066d94fce8436",
+ "0x98d1b58ba22e7289b1c45c79a24624f19b1d89e00f778eef327ec4856a9a897278e6f1a9a7e673844b31dde949153000",
+ "0x97ccb15dfadc7c59dca08cfe0d22df2e52c684cf97de1d94bc00d7ba24e020025130b0a39c0f4d46e4fc872771ee7875",
+ "0xb699e4ed9a000ff96ca296b2f09dce278832bc8ac96851ff3cff99ed3f6f752cfc0fea8571be28cd9b5a7ec36f1a08ee",
+ "0xb9f49f0edb7941cc296435ff0a912e3ad16848ee8765ab5f60a050b280d6ea585e5b34051b15f6b8934ef01ceb85f648",
+ "0xac3893df7b4ceab23c6b9054e48e8ba40d6e5beda8fbe90b814f992f52494186969b35d8c4cdc3c99890a222c9c09008",
+ "0xa41293ad22fae81dea94467bc1488c3707f3d4765059173980be93995fa4fcc3c9340796e3eed0beeb0ba0d9bb4fa3aa",
+ "0xa0543e77acd2aeecde13d18d258aeb2c7397b77f17c35a1992e8666ea7abcd8a38ec6c2741bd929abba2f766138618cc",
+ "0x92e79b22bc40e69f6527c969500ca543899105837b6b1075fa1796755c723462059b3d1b028e0b3df2559fa440e09175",
+ "0xa1fa1eac8f41a5197a6fb4aa1eae1a031c89f9c13ff9448338b222780cf9022e0b0925d930c37501a0ef7b2b00fdaf83",
+ "0xb3cb29ff73229f0637335f28a08ad8c5f166066f27c6c175164d0f26766a927f843b987ee9b309ed71cbf0a65d483831",
+ "0x84d4ab787f0ac00f104f4a734dc693d62d48c2aeb03913153da62c2ae2c27d11b1110dcef8980368dd84682ea2c1a308",
+ "0xab6a8e4bbc78d4a7b291ad3e9a8fe2d65f640524ba3181123b09d2d18a9e300e2509ccf7000fe47e75b65f3e992a2e7e",
+ "0xb7805ebe4f1a4df414003dc10bca805f2ab86ca75820012653e8f9b79c405196b0e2cab099f2ab953d67f0d60d31a0f9",
+ "0xb12c582454148338ea605d22bd00a754109063e22617f1f8ac8ddf5502c22a181c50c216c3617b9852aa5f26af56b323",
+ "0x86333ad9f898947e31ce747728dc8c887479e18d36ff3013f69ebef807d82c6981543b5c3788af93c4d912ba084d3cba",
+ "0xb514efa310dc4ad1258add138891e540d8c87142a881b5f46563cc58ecd1488e6d3a2fca54c0b72a929f3364ca8c333e",
+ "0xaa0a30f92843cf2f484066a783a1d75a7aa6f41f00b421d4baf20a6ac7886c468d0eea7ca8b17dd22f4f74631b62b640",
+ "0xb3b7dc63baec9a752e8433c0cdee4d0f9bc41f66f2b8d132faf925eef9cf89aae756fc132c45910f057122462605dc10",
+ "0xb9b8190dac5bfdeb59fd44f4da41a57e7f1e7d2c21faba9da91fa45cbeca06dcf299c9ae22f0c89ece11ac46352d619f",
+ "0x89f8cf36501ad8bdfeab863752a9090e3bfda57cf8fdeca2944864dc05925f501e252c048221bcc57136ab09a64b64b2",
+ "0xb0cbfaf317f05f97be47fc9d69eda2dd82500e00d42612f271a1fe24626408c28881f171e855bd5bd67409f9847502b4",
+ "0xa7c21a8fcede581bfd9847b6835eda62ba250bea81f1bb17372c800a19c732abe03064e64a2f865d974fb636cab4b859",
+ "0x95f9df524ba7a4667351696c4176b505d8ea3659f5ff2701173064acc624af69a0fad4970963736383b979830cb32260",
+ "0x856a74fe8b37a2e3afeac858c8632200485d438422a16ae3b29f359e470e8244995c63ad79c7e007ed063f178d0306fd",
+ "0xb37faa4d78fdc0bb9d403674dbea0176c2014a171c7be8527b54f7d1a32a76883d3422a3e7a5f5fcc5e9b31b57822eeb",
+ "0x8d37234d8594ec3fe75670b5c9cc1ec3537564d4739b2682a75b18b08401869a4264c0f264354219d8d896cded715db4",
+ "0xb5289ee5737f0e0bde485d32096d23387d68dab8f01f47821ab4f06cc79a967afe7355e72dc0c751d96b2747b26f6255",
+ "0x9085e1fdf9f813e9c3b8232d3c8863cd84ab30d45e8e0d3d6a0abd9ebc6fd70cdf749ff4d04390000e14c7d8c6655fc7",
+ "0x93a388c83630331eca4da37ea4a97b3b453238af474817cc0a0727fd3138dcb4a22de38c04783ec829c22cb459cb4e8e",
+ "0xa5377116027c5d061dbe24c240b891c08cdd8cd3f0899e848d682c873aff5b8132c1e7cfe76d2e5ed97ee0eb1d42cb68",
+ "0xa274c84b04338ed28d74683e2a7519c2591a3ce37c294d6f6e678f7d628be2db8eff253ede21823e2df7183e6552f622",
+ "0x8bc201147a842453a50bec3ac97671397bc086d6dfc9377fa38c2124cdc286abda69b7324f47d64da094ae011d98d9d9",
+ "0x9842d0c066c524592b76fbec5132bc628e5e1d21c424bec4555efca8619cc1fd8ea3161febcb8b9e8ab54702f4e815e2",
+ "0xa19191b713a07efe85c266f839d14e25660ee74452e6c691cd9997d85ae4f732052d802d3deb018bdd847caa298a894b",
+ "0xa24f71fc0db504da4e287dd118a4a74301cbcd16033937ba2abc8417956fcb4ae19b8e63b931795544a978137eff51cb",
+ "0xa90eec4a6a3a4b8f9a5b93d978b5026fcf812fe65585b008d7e08c4aaf21195a1d0699f12fc16f79b6a18a369af45771",
+ "0x8b551cf89737d7d06d9b3b9c4c1c73b41f2ea0af4540999c70b82dabff8580797cf0a3caf34c86c59a7069eb2e38f087",
+ "0xb8d312e6c635e7a216a1cda075ae77ba3e1d2fd501dc31e83496e6e81ed5d9c7799f8e578869c2e0e256fb29f5de10a7",
+ "0x8d144bdb8cae0b2cdb5b33d44bbc96984a5925202506a8cc65eb67ac904b466f5a7fe3e1cbf04aa785bbb7348c4bb73c",
+ "0xa101b3d58b7a98659244b88de0b478b3fb87dc5fc6031f6e689b99edf498abd43e151fd32bd4bbd240e0b3e59c440359",
+ "0x907453abca7d8e7151a05cc3d506c988007692fe7401395dc93177d0d07d114ab6cca0cc658eb94c0223fe8658295cad",
+ "0x825329ffbe2147ddb68f63a0a67f32d7f309657b8e5d9ab5bb34b3730bfa2c77a23eaaadb05def7d9f94a9e08fdc1e96",
+ "0x88ee923c95c1dac99ae7ed6067906d734d793c5dc5d26339c1bb3314abe201c5dccb33b9007351885eb2754e9a8ea06c",
+ "0x98bc9798543f5f1adc9f2cfcfa72331989420e9c3f6598c45269f0dc9b7c8607bbeaf03faa0aea2ddde2b8f17fdceff5",
+ "0x8ee87877702a79aef923ab970db6fa81561b3c07d5bf1a072af0a7bad765b4cbaec910afe1a91703feacc7822fa38a94",
+ "0x8060b9584aa294fe8adc2b22f67e988bc6da768eae91e429dcc43ddc53cfcc5d6753fdc1b420b268c7eb2fb50736a970",
+ "0xb344a5524d80a2f051870c7001f74fcf348a70fcf78dbd20c6ff9ca85d81567d2318c8b8089f2c4f195d6aec9fc15fa6",
+ "0x8f5a5d893e1936ed062149d20eb73d98b62b7f50ab5d93a6429c03656b36688d1c80cb5010e4977491e51fa0d7dd35d5",
+ "0x86fa32ebbf97328c5f5f15564e1238297e289ec3219b9a741724e9f3ae8d5c15277008f555863a478b247ba5dc601d44",
+ "0x9557e55377e279f4b6b5e0ffe01eca037cc13aac242d67dfcd0374a1e775c5ed5cb30c25fe21143fee54e3302d34a3ea",
+ "0x8cb6bcbc39372d23464a416ea7039f57ba8413cf3f00d9a7a5b356ab20dcb8ed11b3561f7bce372b8534d2870c7ee270",
+ "0xb5d59075cb5abde5391f64b6c3b8b50adc6e1f654e2a580b6d6d6eff3f4fbdd8fffc92e06809c393f5c8eab37f774c4b",
+ "0xafcfb6903ef13e493a1f7308675582f15af0403b6553e8c37afb8b2808ad21b88b347dc139464367dc260df075fea1ad",
+ "0x810fbbe808375735dd22d5bc7fc3828dc49fdd22cc2d7661604e7ac9c4535c1df578780affb3b895a0831640a945bcad",
+ "0x8056b0c678803b416f924e09a6299a33cf9ad7da6fe1ad7accefe95c179e0077da36815fde3716711c394e2c5ea7127f",
+ "0x8b67403702d06979be19f1d6dc3ec73cc2e81254d6b7d0cc49cd4fdda8cd51ab0835c1d2d26fc0ecab5df90585c2f351",
+ "0x87f97f9e6d4be07e8db250e5dd2bffdf1390665bc5709f2b631a6fa69a7fca958f19bd7cc617183da1f50ee63e9352b5",
+ "0xae151310985940471e6803fcf37600d7fa98830613e381e00dab943aec32c14162d51c4598e8847148148000d6e5af5c",
+ "0x81eb537b35b7602c45441cfc61b27fa9a30d3998fad35a064e05bc9479e9f10b62eba2b234b348219eea3cadcaac64bb",
+ "0x8a441434934180ab6f5bc541f86ebd06eadbee01f438836d797e930fa803a51510e005c9248cecc231a775b74d12b5e9",
+ "0x81f3c250a27ba14d8496a5092b145629eb2c2e6a5298438670375363f57e2798207832c8027c3e9238ad94ecdadfc4df",
+ "0xa6217c311f2f3db02ceaa5b6096849fe92b6f4b6f1491535ef8525f6ccee6130bed2809e625073ecbaddd4a3eb3df186",
+ "0x82d1c396f0388b942cf22b119d7ef1ad03d3dad49a74d9d01649ee284f377c8daddd095d596871669e16160299a210db",
+ "0xa40ddf7043c5d72a7246bd727b07f7fff1549f0e443d611de6f9976c37448b21664c5089c57f20105102d935ab82f27b",
+ "0xb6c03c1c97adf0c4bf4447ec71366c6c1bff401ba46236cd4a33d39291e7a1f0bb34bd078ba3a18d15c98993b153a279",
+ "0x8a94f5f632068399c359c4b3a3653cb6df2b207379b3d0cdace51afdf70d6d5cce6b89a2b0fee66744eba86c98fb21c2",
+ "0xb2f19e78ee85073f680c3bba1f07fd31b057c00b97040357d97855b54a0b5accb0d3b05b2a294568fcd6a4be6f266950",
+ "0xa74632d13bbe2d64b51d7a9c3ae0a5a971c19f51cf7596a807cea053e6a0f3719700976d4e394b356c0329a2dced9aa2",
+ "0xafef616d341a9bc94393b8dfba68ff0581436aa3a3adb7c26a1bbf2cf19fa877066191681f71f17f3cd6f9cf6bf70b5a",
+ "0x8ce96d93ae217408acf7eb0f9cbb9563363e5c7002e19bbe1e80760bc9d449daee2118f3878b955163ed664516b97294",
+ "0x8414f79b496176bc8b8e25f8e4cfee28f4f1c2ddab099d63d2aca1b6403d26a571152fc3edb97794767a7c4686ad557c",
+ "0xb6c61d01fd8ce087ef9f079bf25bf10090db483dd4f88c4a786d31c1bdf52065651c1f5523f20c21e75cea17df69ab73",
+ "0xa5790fd629be70545093631efadddc136661f63b65ec682609c38ef7d3d7fa4e56bdf94f06e263bc055b90cb1c6bcefe",
+ "0xb515a767e95704fb7597bca9e46f1753abacdc0e56e867ee3c6f4cd382643c2a28e65312c05ad040eaa3a8cbe7217a65",
+ "0x8135806a02ead6aa92e9adb6fefb91349837ab73105aaa7be488ef966aa8dfaafdfa64bbae30fcbfa55dd135a036a863",
+ "0x8f22435702716d76b1369750694540742d909d5e72b54d0878245fab7c269953b1c6f2b29c66f08d5e0263ca3a731771",
+ "0x8e0f8a8e8753e077dac95848212aeffd51c23d9b6d611df8b102f654089401954413ecbedc6367561ca599512ae5dda7",
+ "0x815a9084e3e2345f24c5fa559deec21ee1352fb60f4025c0779be65057f2d528a3d91593bd30d3a185f5ec53a9950676",
+ "0x967e6555ccba395b2cc1605f8484c5112c7b263f41ce8439a99fd1c71c5ed14ad02684d6f636364199ca48afbbde13be",
+ "0x8cd0ccf17682950b34c796a41e2ea7dd5367aba5e80a907e01f4cdc611e4a411918215e5aebf4292f8b24765d73314a6",
+ "0xa58bf1bbb377e4b3915df6f058a0f53b8fb8130fdec8c391f6bc82065694d0be59bb67ffb540e6c42cc8b380c6e36359",
+ "0x92af3151d9e6bfb3383d85433e953c0160859f759b0988431ec5893542ba40288f65db43c78a904325ef8d324988f09d",
+ "0x8011bbb05705167afb47d4425065630f54cb86cd462095e83b81dfebf348f846e4d8fbcf1c13208f5de1931f81da40b9",
+ "0x81c743c104fc3cb047885c9fa0fb9705c3a83ee24f690f539f4985509c3dafd507af3f6a2128276f45d5939ef70c167f",
+ "0xa2c9679b151c041aaf5efeac5a737a8f70d1631d931609fca16be1905682f35e291292874cb3b03f14994f98573c6f44",
+ "0xa4949b86c4e5b1d5c82a337e5ce6b2718b1f7c215148c8bfb7e7c44ec86c5c9476048fc5c01f57cb0920876478c41ad6",
+ "0x86c2495088bd1772152e527a1da0ef473f924ea9ab0e5b8077df859c28078f73c4e22e3a906b507fdf217c3c80808b5c",
+ "0x892e0a910dcf162bcea379763c3e2349349e4cda9402949255ac4a78dd5a47e0bf42f5bd0913951576b1d206dc1e536a",
+ "0xa7009b2c6b396138afe4754b7cc10dee557c51c7f1a357a11486b3253818531f781ea8107360c8d4c3b1cd96282353c0",
+ "0x911763ef439c086065cc7b4e57484ed6d693ea44acee4b18c9fd998116da55fbe7dcb8d2a0f0f9b32132fca82d73dff6",
+ "0xa722000b95a4a2d40bed81870793f15ba2af633f9892df507f2842e52452e02b5ea8dea6a043c2b2611d82376e33742a",
+ "0x9387ac49477bd719c2f92240d0bdfcf9767aad247ca93dc51e56106463206bc343a8ec855eb803471629a66fffb565d6",
+ "0x92819a1fa48ab4902939bb72a0a4e6143c058ea42b42f9bc6cea5df45f49724e2530daf3fc4f097cceefa2a8b9db0076",
+ "0x98eac7b04537653bc0f4941aae732e4b1f84bd276c992c64a219b8715eb1fb829b5cbd997d57feb15c7694c468f95f70",
+ "0xb275e7ba848ce21bf7996e12dbeb8dadb5d0e4f1cb5a0248a4f8f9c9fe6c74e3c93f4b61edbcb0a51af5a141e1c14bc7",
+ "0x97243189285aba4d49c53770c242f2faf5fd3914451da4931472e3290164f7663c726cf86020f8f181e568c72fd172d1",
+ "0x839b0b3c25dd412bee3dc24653b873cc65454f8f16186bb707bcd58259c0b6765fa4c195403209179192a4455c95f3b8",
+ "0x8689d1a870514568a074a38232e2ceb4d7df30fabeb76cff0aed5b42bf7f02baea12c5fadf69f4713464dbd52aafa55f",
+ "0x8958ae7b290f0b00d17c3e9fdb4dbf168432b457c7676829299dd428984aba892de1966fc106cfc58a772862ecce3976",
+ "0xa422bc6bd68b8870cfa5bc4ce71781fd7f4368b564d7f1e0917f6013c8bbb5b240a257f89ecfdbecb40fe0f3aa31d310",
+ "0xaa61f78130cebe09bc9a2c0a37f0dd57ed2d702962e37d38b1df7f17dc554b1d4b7a39a44182a452ce4c5eb31fa4cfcc",
+ "0xb7918bd114f37869bf1a459023386825821bfadce545201929d13ac3256d92a431e34f690a55d944f77d0b652cefeffc",
+ "0x819bba35fb6ace1510920d4dcff30aa682a3c9af9022e287751a6a6649b00c5402f14b6309f0aeef8fce312a0402915e",
+ "0x8b7c9ad446c6f63c11e1c24e24014bd570862b65d53684e107ba9ad381e81a2eaa96731b4b33536efd55e0f055071274",
+ "0x8fe79b53f06d33386c0ec7d6d521183c13199498594a46d44a8a716932c3ec480c60be398650bbfa044fa791c4e99b65",
+ "0x9558e10fb81250b9844c99648cf38fa05ec1e65d0ccbb18aa17f2d1f503144baf59d802c25be8cc0879fff82ed5034ad",
+ "0xb538a7b97fbd702ba84645ca0a63725be1e2891c784b1d599e54e3480e4670d0025526674ef5cf2f87dddf2290ba09f0",
+ "0x92eafe2e869a3dd8519bbbceb630585c6eb21712b2f31e1b63067c0acb5f9bdbbcbdb612db4ea7f9cc4e7be83d31973f",
+ "0xb40d21390bb813ab7b70a010dff64c57178418c62685761784e37d327ba3cb9ef62df87ecb84277c325a637fe3709732",
+ "0xb349e6fbf778c4af35fbed33130bd8a7216ed3ba0a79163ebb556e8eb8e1a7dad3456ddd700dad9d08d202491c51b939",
+ "0xa8fdaedecb251f892b66c669e34137f2650509ade5d38fbe8a05d9b9184bb3b2d416186a3640429bd1f3e4b903c159dd",
+ "0xac6167ebfee1dbab338eff7642f5e785fc21ef0b4ddd6660333fe398068cbd6c42585f62e81e4edbb72161ce852a1a4f",
+ "0x874b1fbf2ebe140c683bd7e4e0ab017afa5d4ad38055aaa83ee6bbef77dbc88a6ce8eb0dcc48f0155244af6f86f34c2d",
+ "0x903c58e57ddd9c446afab8256a6bb6c911121e6ccfb4f9b4ed3e2ed922a0e500a5cb7fa379d5285bc16e11dac90d1fda",
+ "0x8dae7a0cffa2fd166859cd1bf10ff82dd1932e488af377366b7efc0d5dec85f85fe5e8150ff86a79a39cefc29631733a",
+ "0xaa047857a47cc4dfc08585f28640420fcf105b881fd59a6cf7890a36516af0644d143b73f3515ab48faaa621168f8c31",
+ "0x864508f7077c266cc0cb3f7f001cb6e27125ebfe79ab57a123a8195f2e27d3799ff98413e8483c533b46a816a3557f1f",
+ "0x8bcd45ab1f9cbab36937a27e724af819838f66dfeb15923f8113654ff877bd8667c54f6307aaf0c35027ca11b6229bfd",
+ "0xb21aa34da9ab0a48fcfdd291df224697ce0c1ebc0e9b022fdee8750a1a4b5ba421c419541ed5c98b461eecf363047471",
+ "0xa9a18a2ab2fae14542dc336269fe612e9c1af6cf0c9ac933679a2f2cb77d3c304114f4d219ca66fe288adde30716775b",
+ "0xb5205989b92c58bdda71817f9a897e84100b5c4e708de1fced5c286f7a6f01ae96b1c8d845f3a320d77c8e2703c0e8b1",
+ "0xa364059412bbcc17b8907d43ac8e5df90bc87fd1724b5f99832d0d24559fae6fa76a74cff1d1eac8cbac6ec80b44af20",
+ "0xae709f2c339886b31450834cf29a38b26eb3b0779bd77c9ac269a8a925d1d78ea3837876c654b61a8fe834b3b6940808",
+ "0x8802581bba66e1952ac4dab36af371f66778958f4612901d95e5cac17f59165e6064371d02de8fb6fccf89c6dc8bd118",
+ "0xa313252df653e29c672cbcfd2d4f775089cb77be1077381cf4dc9533790e88af6cedc8a119158e7da5bf6806ad9b91a1",
+ "0x992a065b4152c7ef11515cd54ba9d191fda44032a01aed954acff3443377ee16680c7248d530b746b8c6dee2d634e68c",
+ "0xb627b683ee2b32c1ab4ccd27b9f6cce2fe097d96386fa0e5c182ad997c4c422ab8dfc03870cd830b8c774feb66537282",
+ "0xb823cf8a9aee03dadd013eb9efe40a201b4b57ef67efaae9f99683005f5d1bf55e950bf4af0774f50859d743642d3fea",
+ "0xb8a7449ffac0a3f206677097baf7ce00ca07a4d2bd9b5356fbcb83f3649b0fda07cfebad220c1066afba89e5a52abf4b",
+ "0xb2dd1a2f986395bb4e3e960fbbe823dbb154f823284ebc9068502c19a7609790ec0073d08bfa63f71e30c7161b6ef966",
+ "0x98e5236de4281245234f5d40a25b503505af140b503a035fc25a26159a9074ec81512b28f324c56ea2c9a5aa7ce90805",
+ "0x89070847dc8bbf5bc4ed073aa2e2a1f699cf0c2ca226f185a0671cecc54e7d3e14cd475c7752314a7a8e7476829da4bc",
+ "0xa9402dc9117fdb39c4734c0688254f23aed3dce94f5f53f5b7ef2b4bf1b71a67f85ab1a38ec224a59691f3bee050aeb3",
+ "0x957288f9866a4bf56a4204218ccc583f717d7ce45c01ea27142a7e245ad04a07f289cc044f8cf1f21d35e67e39299e9c",
+ "0xb2fb31ccb4e69113763d7247d0fc8edaae69b550c5c56aecacfd780c7217dc672f9fb7496edf4aba65dacf3361268e5b",
+ "0xb44a4526b2f1d6eb2aa8dba23bfa385ff7634572ab2afddd0546c3beb630fbfe85a32f42dd287a7fec069041411537f7",
+ "0x8db5a6660c3ac7fd7a093573940f068ee79a82bc17312af900b51c8c439336bc86ca646c6b7ab13aaaa008a24ca508ab",
+ "0x8f9899a6d7e8eb4367beb5c060a1f8e94d8a21099033ae582118477265155ba9e72176a67f7f25d7bad75a152b56e21a",
+ "0xa67de0e91ade8d69a0e00c9ff33ee2909b8a609357095fa12319e6158570c232e5b6f4647522efb7345ce0052aa9d489",
+ "0x82eb2414898e9c3023d57907a2b17de8e7eea5269029d05a94bfd7bf5685ac4a799110fbb375eb5e0e2bd16acf6458ae",
+ "0x94451fc7fea3c5a89ba701004a9693bab555cb622caf0896b678faba040409fdfd14a978979038b2a81e8f0abc4994d2",
+ "0xac879a5bb433998e289809a4a966bd02b4bf6a9c1cc276454e39c886efcf4fc68baebed575826bde577ab5aa71d735a9",
+ "0x880c0f8f49c875dfd62b4ddedde0f5c8b19f5687e693717f7e5c031bc580e58e13ab497d48b4874130a18743c59fdce3",
+ "0xb582af8d8ff0bf76f0a3934775e0b54c0e8fed893245d7d89cae65b03c8125b7237edc29dc45b4fe1a3fe6db45d280ee",
+ "0x89f337882ed3ae060aaee98efa20d79b6822bde9708c1c5fcee365d0ec9297f694cae37d38fd8e3d49717c1e86f078e7",
+ "0x826d2c1faea54061848b484e288a5f4de0d221258178cf87f72e14baaa4acc21322f8c9eab5dde612ef497f2d2e1d60b",
+ "0xa5333d4f227543e9cd741ccf3b81db79f2f03ca9e649e40d6a6e8ff9073e06da83683566d3b3c8d7b258c62970fb24d1",
+ "0xa28f08c473db06aaf4c043a2fae82b3c8cfaa160bce793a4c208e4e168fb1c65115ff8139dea06453c5963d95e922b94",
+ "0x8162546135cc5e124e9683bdfaa45833c18553ff06a0861c887dc84a5b12ae8cd4697f6794c7ef6230492c32faba7014",
+ "0xb23f0d05b74c08d6a7df1760792be83a761b36e3f8ae360f3c363fb196e2a9dd2de2e492e49d36561366e14daa77155c",
+ "0xb6f70d6c546722d3907c708d630dbe289771d2c8bf059c2e32b77f224696d750b4dda9b3a014debda38e7d02c9a77585",
+ "0x83bf4c4a9f3ca022c631017e7a30ea205ba97f7f5927cba8fc8489a4646eac6712cb821c5668c9ffe94d69d524374a27",
+ "0xb0371475425a8076d0dd5f733f55aabbe42d20a7c8ea7da352e736d4d35a327b2beb370dfcb05284e22cfd69c5f6c4cc",
+ "0xa0031ba7522c79211416c2cca3aa5450f96f8fee711552a30889910970ba13608646538781a2c08b834b140aadd7166f",
+ "0x99d273c80c7f2dc6045d4ed355d9fc6f74e93549d961f4a3b73cd38683f905934d359058cd1fc4da8083c7d75070487f",
+ "0xb0e4b0efa3237793e9dcce86d75aafe9879c5fa23f0d628649aef2130454dcf72578f9bf227b9d2b9e05617468e82588",
+ "0xa5ab076fa2e1c5c51f3ae101afdd596ad9d106bba7882b359c43d8548b64f528af19afa76cd6f40da1e6c5fca4def3fa",
+ "0x8ce2299e570331d60f6a6eff1b271097cd5f1c0e1113fc69b89c6a0f685dabea3e5bc2ac6bd789aa492ab189f89be494",
+ "0x91b829068874d911a310a5f9dee001021f97471307b5a3de9ec336870ec597413e1d92010ce320b619f38bed7c4f7910",
+ "0xb14fe91f4b07bf33b046e9285b66cb07927f3a8da0af548ac2569b4c4fb1309d3ced76d733051a20814e90dd5b75ffd1",
+ "0xabaab92ea6152d40f82940277c725aa768a631ee0b37f5961667f82fb990fc11e6d3a6a2752b0c6f94563ed9bb28265c",
+ "0xb7fe28543eca2a716859a76ab9092f135337e28109544f6bd2727728d0a7650428af5713171ea60bfc273d1c821d992c",
+ "0x8a4917b2ab749fc7343fc64bdf51b6c0698ff15d740cc7baf248c030475c097097d5a473bcc00d8c25817563fe0447b4",
+ "0xaa96156d1379553256350a0a3250166add75948fb9cde62aa555a0a9dc0a9cb7f2f7b8428aff66097bf6bfedaf14bbe2",
+ "0xae4ffeb9bdc76830d3eca2b705f30c1bdede6412fa064260a21562c8850c7fb611ec62bc68479fe48f692833e6f66d8d",
+ "0xb96543caaba9d051600a14997765d49e4ab10b07c7a92cccf0c90b309e6da334fdd6d18c96806cbb67a7801024fbd3c7",
+ "0x97b2b9ad76f19f500fcc94ca8e434176249f542ac66e5881a3dccd07354bdab6a2157018b19f8459437a68d8b86ba8e0",
+ "0xa8d206f6c5a14c80005849474fde44b1e7bcf0b2d52068f5f97504c3c035b09e65e56d1cf4b5322791ae2c2fdbd61859",
+ "0x936bad397ad577a70cf99bf9056584a61bd7f02d2d5a6cf219c05d770ae30a5cd902ba38366ce636067fc1dd10108d31",
+ "0xa77e30195ee402b84f3882e2286bf5380c0ed374a112dbd11e16cef6b6b61ab209d4635e6f35cdaaa72c1a1981d5dabe",
+ "0xa46ba4d3947188590a43c180757886a453a0503f79cc435322d92490446f37419c7b999fdf868a023601078070e03346",
+ "0x80d8d4c5542f223d48240b445d4d8cf6a75d120b060bc08c45e99a13028b809d910b534d2ac47fb7068930c54efd8da9",
+ "0x803be9c68c91b42b68e1f55e58917a477a9a6265e679ca44ee30d3eb92453f8c89c64eafc04c970d6831edd33d066902",
+ "0xb14b2b3d0dfe2bb57cee4cd72765b60ac33c1056580950be005790176543826c1d4fbd737f6cfeada6c735543244ab57",
+ "0xa9e480188bba1b8fb7105ff12215706665fd35bf1117bacfb6ab6985f4dbc181229873b82e5e18323c2b8f5de03258e0",
+ "0xa66a0f0779436a9a3999996d1e6d3000f22c2cac8e0b29cddef9636393c7f1457fb188a293b6c875b05d68d138a7cc4a",
+ "0x848397366300ab40c52d0dbbdafbafef6cd3dadf1503bb14b430f52bb9724188928ac26f6292a2412bc7d7aa620763c8",
+ "0x95466cc1a78c9f33a9aaa3829a4c8a690af074916b56f43ae46a67a12bb537a5ac6dbe61590344a25b44e8512355a4a7",
+ "0x8b5f7a959f818e3baf0887f140f4575cac093d0aece27e23b823cf421f34d6e4ff4bb8384426e33e8ec7b5eed51f6b5c",
+ "0x8d5e1368ec7e3c65640d216bcc5d076f3d9845924c734a34f3558ac0f16e40597c1a775a25bf38b187213fbdba17c93b",
+ "0xb4647c1b823516880f60d20c5cc38c7f80b363c19d191e8992226799718ee26b522a12ecb66556ed3d483aa4824f3326",
+ "0xac3abaea9cd283eb347efda4ed9086ea3acf495043e08d0d19945876329e8675224b685612a6badf8fd72fb6274902b1",
+ "0x8eae1ce292d317aaa71bcf6e77e654914edd5090e2e1ebab78b18bb41b9b1bc2e697439f54a44c0c8aa0d436ebe6e1a9",
+ "0x94dc7d1aec2c28eb43d93b111fa59aaa0d77d5a09501220bd411768c3e52208806abf973c6a452fd8292ff6490e0c9e2",
+ "0x8fd8967f8e506fef27d17b435d6b86b232ec71c1036351f12e6fb8a2e12daf01d0ee04451fb944d0f1bf7fd20e714d02",
+ "0x824e6865be55d43032f0fec65b3480ea89b0a2bf860872237a19a54bc186a85d2f8f9989cc837fbb325b7c72d9babe2c",
+ "0x8bd361f5adb27fd6f4e3f5de866e2befda6a8454efeb704aacc606f528c03f0faae888f60310e49440496abd84083ce2",
+ "0xb098a3c49f2aaa28b6b3e85bc40ce6a9cdd02134ee522ae73771e667ad7629c8d82c393fba9f27f5416986af4c261438",
+ "0xb385f5ca285ff2cfe64dcaa32dcde869c28996ed091542600a0b46f65f3f5a38428cca46029ede72b6cf43e12279e3d3",
+ "0x8196b03d011e5be5288196ef7d47137d6f9237a635ab913acdf9c595fa521d9e2df722090ec7eb0203544ee88178fc5f",
+ "0x8ed1270211ef928db18e502271b7edf24d0bbd11d97f2786aee772d70c2029e28095cf8f650b0328cc8a4c38d045316d",
+ "0xa52ab60e28d69b333d597a445884d44fd2a7e1923dd60f763951e1e45f83e27a4dac745f3b9eff75977b3280e132c15d",
+ "0x91e9fe78cdac578f4a4687f71b800b35da54b824b1886dafec073a3c977ce7a25038a2f3a5b1e35c2c8c9d1a7312417c",
+ "0xa42832173f9d9491c7bd93b21497fbfa4121687cd4d2ab572e80753d7edcbb42cfa49f460026fbde52f420786751a138",
+ "0x97b947126d84dcc70c97be3c04b3de3f239b1c4914342fa643b1a4bb8c4fe45c0fcb585700d13a7ed50784790c54bef9",
+ "0x860e407d353eac070e2418ef6cb80b96fc5f6661d6333e634f6f306779651588037be4c2419562c89c61f9aa2c4947f5",
+ "0xb2c9d93c3ba4e511b0560b55d3501bf28a510745fd666b3cb532db051e6a8617841ea2f071dda6c9f15619c7bfd2737f",
+ "0x8596f4d239aeeac78311207904d1bd863ef68e769629cc379db60e019aaf05a9d5cd31dc8e630b31e106a3a93e47cbc5",
+ "0x8b26e14e2e136b65c5e9e5c2022cee8c255834ea427552f780a6ca130a6446102f2a6f334c3f9a0308c53df09e3dba7e",
+ "0xb54724354eb515a3c8bed0d0677ff1db94ac0a07043459b4358cb90e3e1aa38ac23f2caa3072cf9647275d7cd61d0e80",
+ "0xb7ce9fe0e515e7a6b2d7ddcb92bc0196416ff04199326aea57996eef8c5b1548bd8569012210da317f7c0074691d01b7",
+ "0xa1a13549c82c877253ddefa36a29ea6a23695ee401fdd48e65f6f61e5ebd956d5e0edeff99484e9075cb35071fec41e2",
+ "0x838ba0c1e5bd1a6da05611ff1822b8622457ebd019cb065ece36a2d176bd2d889511328120b8a357e44569e7f640c1e6",
+ "0xb916eccff2a95519400bbf76b5f576cbe53cf200410370a19d77734dc04c05b585cfe382e8864e67142d548cd3c4c2f4",
+ "0xa610447cb7ca6eea53a6ff1f5fe562377dcb7f4aaa7300f755a4f5e8eba61e863c51dc2aa9a29b35525b550fbc32a0fe",
+ "0x9620e8f0f0ee9a4719aa9685eeb1049c5c77659ba6149ec4c158f999cfd09514794b23388879931fe26fea03fa471fd3",
+ "0xa9dcf8b679e276583cf5b9360702a185470d09aea463dc474ee9c8aee91ef089dacb073e334e47fbc78ec5417c90465c",
+ "0x8c9adee8410bdd99e5b285744cee61e2593b6300ff31a8a83b0ec28da59475a5c6fb9346fe43aadea2e6c3dad2a8e30a",
+ "0x97d5afe9b3897d7b8bb628b7220cf02d8ee4e9d0b78f5000d500aaf4c1df9251aaaabfd1601626519f9d66f00a821d4e",
+ "0x8a382418157b601ce4c3501d3b8409ca98136a4ef6abcbf62885e16e215b76b035c94d149cc41ff92e42ccd7c43b9b3d",
+ "0xb64b8d11fb3b01abb2646ac99fdb9c02b804ce15d98f9fe0fbf1c9df8440c71417487feb6cdf51e3e81d37104b19e012",
+ "0x849d7d044f9d8f0aab346a9374f0b3a5d14a9d1faa83dbacccbdc629ad1ef903a990940255564770537f8567521d17f0",
+ "0x829dbb0c76b996c2a91b4cbbe93ba455ca0d5729755e5f0c92aaee37dff7f36fcdc06f33aca41f1b609c784127b67d88",
+ "0x85a7c0069047b978422d264d831ab816435f63938015d2e977222b6b5746066c0071b7f89267027f8a975206ed25c1b0",
+ "0x84b9fbc1cfb302df1acdcf3dc5d66fd1edfe7839f7a3b2fb3a0d5548656249dd556104d7c32b73967bccf0f5bdcf9e3b",
+ "0x972220ac5b807f53eac37dccfc2ad355d8b21ea6a9c9b011c09fe440ddcdf7513e0b43d7692c09ded80d7040e26aa28f",
+ "0x855885ed0b21350baeca890811f344c553cf9c21024649c722453138ba29193c6b02c4b4994cd414035486f923472e28",
+ "0x841874783ae6d9d0e59daea03e96a01cbbe4ecaced91ae4f2c8386e0d87b3128e6d893c98d17c59e4de1098e1ad519dd",
+ "0x827e50fc9ce56f97a4c3f2f4cbaf0b22f1c3ce6f844ff0ef93a9c57a09b8bf91ebfbd2ba9c7f83c442920bffdaf288cc",
+ "0xa441f9136c7aa4c08d5b3534921b730e41ee91ab506313e1ba5f7c6f19fd2d2e1594e88c219834e92e6fb95356385aa7",
+ "0x97d75b144471bf580099dd6842b823ec0e6c1fb86dd0da0db195e65524129ea8b6fd4a7a9bbf37146269e938a6956596",
+ "0xa4b6fa87f09d5a29252efb2b3aaab6b3b6ea9fab343132a651630206254a25378e3e9d6c96c3d14c150d01817d375a8e",
+ "0xa31a671876d5d1e95fe2b8858dc69967231190880529d57d3cab7f9f4a2b9b458ac9ee5bdaa3289158141bf18f559efb",
+ "0x90bee6fff4338ba825974021b3b2a84e36d617e53857321f13d2b3d4a28954e6de3b3c0e629d61823d18a9763313b3bf",
+ "0x96b622a63153f393bb419bfcf88272ea8b3560dbd46b0aa07ada3a6223990d0abdd6c2adb356ef4be5641688c8d83941",
+ "0x84c202adeaff9293698022bc0381adba2cd959f9a35a4e8472288fd68f96f6de8be9da314c526d88e291c96b1f3d6db9",
+ "0x8ca01a143b8d13809e5a8024d03e6bc9492e22226073ef6e327edf1328ef4aff82d0bcccee92cb8e212831fa35fe1204",
+ "0xb2f970dbad15bfbefb38903c9bcc043d1367055c55dc1100a850f5eb816a4252c8c194b3132c929105511e14ea10a67d",
+ "0xa5e36556472a95ad57eb90c3b6623671b03eafd842238f01a081997ffc6e2401f76e781d049bb4aa94d899313577a9cf",
+ "0x8d1057071051772f7c8bedce53a862af6fd530dd56ae6321eaf2b9fc6a68beff5ed745e1c429ad09d5a118650bfd420a",
+ "0x8aadc4f70ace4fcb8d93a78610779748dcffc36182d45b932c226dc90e48238ea5daa91f137c65ed532352c4c4d57416",
+ "0xa2ea05ae37e673b4343232ae685ee14e6b88b867aef6dfac35db3589cbcd76f99540fed5c2641d5bb5a4a9f808e9bf0d",
+ "0x947f1abad982d65648ae4978e094332b4ecb90f482c9be5741d5d1cf5a28acf4680f1977bf6e49dd2174c37f11e01296",
+ "0xa27b144f1565e4047ba0e3f4840ef19b5095d1e281eaa463c5358f932114cbd018aa6dcf97546465cf2946d014d8e6d6",
+ "0x8574e1fc3acade47cd4539df578ce9205e745e161b91e59e4d088711a7ab5aa3b410d517d7304b92109924d9e2af8895",
+ "0xa48ee6b86b88015d6f0d282c1ae01d2a5b9e8c7aa3d0c18b35943dceb1af580d08a65f54dc6903cde82fd0d73ce94722",
+ "0x8875650cec543a7bf02ea4f2848a61d167a66c91ffaefe31a9e38dc8511c6a25bde431007eefe27a62af3655aca208dc",
+ "0x999b0a6e040372e61937bf0d68374e230346b654b5a0f591a59d33a4f95bdb2f3581db7c7ccb420cd7699ed709c50713",
+ "0x878c9e56c7100c5e47bbe77dc8da5c5fe706cec94d37fa729633bca63cace7c40102eee780fcdabb655f5fa47a99600e",
+ "0x865006fb5b475ada5e935f27b96f9425fc2d5449a3c106aa366e55ebed3b4ee42adc3c3f0ac19fd129b40bc7d6bc4f63",
+ "0xb7a7da847f1202e7bc1672553e68904715e84fd897d529243e3ecda59faa4e17ba99c649a802d53f6b8dfdd51f01fb74",
+ "0x8b2fb4432c05653303d8c8436473682933a5cb604da10c118ecfcd2c8a0e3132e125afef562bdbcc3df936164e5ce4f2",
+ "0x808d95762d33ddfa5d0ee3d7d9f327de21a994d681a5f372e2e3632963ea974da7f1f9e5bac8ccce24293509d1f54d27",
+ "0x932946532e3c397990a1df0e94c90e1e45133e347a39b6714c695be21aeb2d309504cb6b1dde7228ff6f6353f73e1ca2",
+ "0x9705e7c93f0cdfaa3fa96821f830fe53402ad0806036cd1b48adc2f022d8e781c1fbdab60215ce85c653203d98426da3",
+ "0xaa180819531c3ec1feb829d789cb2092964c069974ae4faad60e04a6afcce5c3a59aec9f11291e6d110a788d22532bc6",
+ "0x88f755097f7e25cb7dd3c449520c89b83ae9e119778efabb54fbd5c5714b6f37c5f9e0346c58c6ab09c1aef2483f895d",
+ "0x99fc03ab7810e94104c494f7e40b900f475fde65bdec853e60807ffd3f531d74de43335c3b2646b5b8c26804a7448898",
+ "0xaf2dea9683086bed1a179110efb227c9c00e76cd00a2015b089ccbcee46d1134aa18bda5d6cab6f82ae4c5cd2461ac21",
+ "0xa500f87ba9744787fdbb8e750702a3fd229de6b8817594348dec9a723b3c4240ddfa066262d002844b9e38240ce55658",
+ "0x924d0e45c780f5bc1c1f35d15dfc3da28036bdb59e4c5440606750ecc991b85be18bc9a240b6c983bc5430baa4c68287",
+ "0x865b11e0157b8bf4c5f336024b016a0162fc093069d44ac494723f56648bc4ded13dfb3896e924959ea11c96321afefc",
+ "0x93672d8607d4143a8f7894f1dcca83fb84906dc8d6dd7dd063bb0049cfc20c1efd933e06ca7bd03ea4cb5a5037990bfe",
+ "0x826891efbdff0360446825a61cd1fa04326dd90dae8c33dfb1ed97b045e165766dd070bd7105560994d0b2044bdea418",
+ "0x93c4a4a8bcbc8b190485cc3bc04175b7c0ed002c28c98a540919effd6ed908e540e6594f6db95cd65823017258fb3b1c",
+ "0xaeb2a0af2d2239fda9aa6b8234b019708e8f792834ff0dd9c487fa09d29800ddceddd6d7929faa9a3edcb9e1b3aa0d6b",
+ "0x87f11de7236d387863ec660d2b04db9ac08143a9a2c4dfff87727c95b4b1477e3bc473a91e5797313c58754905079643",
+ "0x80dc1db20067a844fe8baceca77f80db171a5ca967acb24e2d480eae9ceb91a3343c31ad1c95b721f390829084f0eae6",
+ "0x9825c31f1c18da0de3fa84399c8b40f8002c3cae211fb6a0623c76b097b4d39f5c50058f57a16362f7a575909d0a44a2",
+ "0xa99fc8de0c38dbf7b9e946de83943a6b46a762167bafe2a603fb9b86f094da30d6de7ed55d639aafc91936923ee414b3",
+ "0xad594678b407db5d6ea2e90528121f84f2b96a4113a252a30d359a721429857c204c1c1c4ff71d8bb5768c833f82e80e",
+ "0xb33d985e847b54510b9b007e31053732c8a495e43be158bd2ffcea25c6765bcbc7ca815f7c60b36ad088b955dd6e9350",
+ "0x815f8dfc6f90b3342ca3fbd968c67f324dae8f74245cbf8bc3bef10e9440c65d3a2151f951e8d18959ba01c1b50b0ec1",
+ "0x94c608a362dd732a1abc56e338637c900d59013db8668e49398b3c7a0cae3f7e2f1d1bf94c0299eeafe6af7f76c88618",
+ "0x8ebd8446b23e5adfcc393adc5c52fe172f030a73e63cd2d515245ca0dd02782ceed5bcdd9ccd9c1b4c5953dfac9c340c",
+ "0x820437f3f6f9ad0f5d7502815b221b83755eb8dc56cd92c29e9535eb0b48fb8d08c9e4fcc26945f9c8cca60d89c44710",
+ "0x8910e4e8a56bf4be9cc3bbf0bf6b1182a2f48837a2ed3c2aaec7099bfd7f0c83e14e608876b17893a98021ff4ab2f20d",
+ "0x9633918fde348573eec15ce0ad53ac7e1823aac86429710a376ad661002ae6d049ded879383faaa139435122f64047c6",
+ "0xa1f5e3fa558a9e89318ca87978492f0fb4f6e54a9735c1b8d2ecfb1d1c57194ded6e0dd82d077b2d54251f3bee1279e1",
+ "0xb208e22d04896abfd515a95c429ff318e87ff81a5d534c8ac2c33c052d6ffb73ef1dccd39c0bbe0734b596c384014766",
+ "0x986d5d7d2b5bde6d16336f378bd13d0e671ad23a8ec8a10b3fc09036faeeb069f60662138d7a6df3dfb8e0d36180f770",
+ "0xa2d4e6c5f5569e9cef1cddb569515d4b6ace38c8aed594f06da7434ba6b24477392cc67ba867c2b079545ca0c625c457",
+ "0xb5ac32b1d231957d91c8b7fc43115ce3c5c0d8c13ca633374402fa8000b6d9fb19499f9181844f0c10b47357f3f757ce",
+ "0x96b8bf2504b4d28fa34a4ec378e0e0b684890c5f44b7a6bb6e19d7b3db2ab27b1e2686389d1de9fbd981962833a313ea",
+ "0x953bfd7f6c3a0469ad432072b9679a25486f5f4828092401eff494cfb46656c958641a4e6d0d97d400bc59d92dba0030",
+ "0x876ab3cea7484bbfd0db621ec085b9ac885d94ab55c4bb671168d82b92e609754b86aaf472c55df3d81421d768fd108a",
+ "0x885ff4e67d9ece646d02dd425aa5a087e485c3f280c3471b77532b0db6145b69b0fbefb18aa2e3fa5b64928b43a94e57",
+ "0xb91931d93f806d0b0e6cc62a53c718c099526140f50f45d94b8bbb57d71e78647e06ee7b42aa5714aed9a5c05ac8533f",
+ "0xa0313eeadd39c720c9c27b3d671215331ab8d0a794e71e7e690f06bcd87722b531d6525060c358f35f5705dbb7109ccb",
+ "0x874c0944b7fedc6701e53344100612ddcb495351e29305c00ec40a7276ea5455465ffb7bded898886c1853139dfb1fc7",
+ "0x8dc31701a01ee8137059ca1874a015130d3024823c0576aa9243e6942ec99d377e7715ed1444cd9b750a64b85dcaa3e5",
+ "0x836d2a757405e922ec9a2dfdcf489a58bd48b5f9683dd46bf6047688f778c8dee9bc456de806f70464df0b25f3f3d238",
+ "0xb30b0a1e454a503ea3e2efdec7483eaf20b0a5c3cefc42069e891952b35d4b2c955cf615f3066285ed8fafd9fcfbb8f6",
+ "0x8e6d4044b55ab747e83ec8762ea86845f1785cc7be0279c075dadf08aca3ccc5a096c015bb3c3f738f647a4eadea3ba5",
+ "0xad7735d16ab03cbe09c029610aa625133a6daecfc990b297205b6da98eda8c136a7c50db90f426d35069708510d5ae9c",
+ "0x8d62d858bbb59ec3c8cc9acda002e08addab4d3ad143b3812098f3d9087a1b4a1bb255dcb1635da2402487d8d0249161",
+ "0x805beec33238b832e8530645a3254aeef957e8f7ea24bcfc1054f8b9c69421145ebb8f9d893237e8a001c857fedfc77e",
+ "0xb1005644be4b085e3f5775aa9bd3e09a283e87ddada3082c04e7a62d303dcef3b8cf8f92944c200c7ae6bb6bdf63f832",
+ "0xb4ba0e0790dc29063e577474ffe3b61f5ea2508169f5adc1e394934ebb473e356239413a17962bc3e5d3762d72cce8c2",
+ "0xa157ba9169c9e3e6748d9f1dd67fbe08b9114ade4c5d8fc475f87a764fb7e6f1d21f66d7905cd730f28a1c2d8378682a",
+ "0x913e52b5c93989b5d15e0d91aa0f19f78d592bc28bcfdfddc885a9980c732b1f4debb8166a7c4083c42aeda93a702898",
+ "0x90fbfc1567e7cd4e096a38433704d3f96a2de2f6ed3371515ccc30bc4dd0721a704487d25a97f3c3d7e4344472702d8d",
+ "0x89646043028ffee4b69d346907586fd12c2c0730f024acb1481abea478e61031966e72072ff1d5e65cb8c64a69ad4eb1",
+ "0xb125a45e86117ee11d2fb42f680ab4a7894edd67ff927ae2c808920c66c3e55f6a9d4588eee906f33a05d592e5ec3c04",
+ "0xaad47f5b41eae9be55fb4f67674ff1e4ae2482897676f964a4d2dcb6982252ee4ff56aac49578b23f72d1fced707525e",
+ "0xb9ddff8986145e33851b4de54d3e81faa3352e8385895f357734085a1616ef61c692d925fe62a5ed3be8ca49f5d66306",
+ "0xb3cb0963387ed28c0c0adf7fe645f02606e6e1780a24d6cecef5b7c642499109974c81a7c2a198b19862eedcea2c2d8c",
+ "0xac9c53c885457aaf5cb36c717a6f4077af701e0098eebd7aa600f5e4b14e6c1067255b3a0bc40e4a552025231be7de60",
+ "0x8e1a8d823c4603f6648ec21d064101094f2a762a4ed37dd2f0a2d9aa97b2d850ce1e76f4a4b8cae58819b058180f7031",
+ "0xb268b73bf7a179b6d22bd37e5e8cb514e9f5f8968c78e14e4f6d5700ca0d0ca5081d0344bb73b028970eebde3cb4124e",
+ "0xa7f57d71940f0edbd29ed8473d0149cae71d921dd15d1ff589774003e816b54b24de2620871108cec1ab9fa956ad6ce6",
+ "0x8053e6416c8b120e2b999cc2fc420a6a55094c61ac7f2a6c6f0a2c108a320890e389af96cbe378936132363c0d551277",
+ "0xb3823f4511125e5aa0f4269e991b435a0d6ceb523ebd91c04d7add5534e3df5fc951c504b4fd412a309fd3726b7f940b",
+ "0xae6eb04674d04e982ca9a6add30370ab90e303c71486f43ed3efbe431af1b0e43e9d06c11c3412651f304c473e7dbf39",
+ "0x96ab55e641ed2e677591f7379a3cd126449614181fce403e93e89b1645d82c4af524381ff986cae7f9cebe676878646d",
+ "0xb52423b4a8c37d3c3e2eca8f0ddbf7abe0938855f33a0af50f117fab26415fb0a3da5405908ec5fdc22a2c1f2ca64892",
+ "0x82a69ce1ee92a09cc709d0e3cd22116c9f69d28ea507fe5901f5676000b5179b9abe4c1875d052b0dd42d39925e186bb",
+ "0xa84c8cb84b9d5cfb69a5414f0a5283a5f2e90739e9362a1e8c784b96381b59ac6c18723a4aa45988ee8ef5c1f45cc97d",
+ "0xafd7efce6b36813082eb98257aae22a4c1ae97d51cac7ea9c852d4a66d05ef2732116137d8432e3f117119725a817d24",
+ "0xa0f5fe25af3ce021b706fcff05f3d825384a272284d04735574ce5fb256bf27100fad0b1f1ba0e54ae9dcbb9570ecad3",
+ "0x8751786cb80e2e1ff819fc7fa31c2833d25086534eb12b373d31f826382430acfd87023d2a688c65b5e983927e146336",
+ "0x8cf5c4b17fa4f3d35c78ce41e1dc86988fd1135cd5e6b2bb0c108ee13538d0d09ae7102609c6070f39f937b439b31e33",
+ "0xa9108967a2fedd7c322711eca8159c533dd561bedcb181b646de98bf5c3079449478eab579731bee8d215ae8852c7e21",
+ "0xb54c5171704f42a6f0f4e70767cdb3d96ffc4888c842eece343a01557da405961d53ffdc34d2f902ea25d3e1ed867cad",
+ "0xae8d4b764a7a25330ba205bf77e9f46182cd60f94a336bbd96773cf8064e3d39caf04c310680943dc89ed1fbad2c6e0d",
+ "0xaa5150e911a8e1346868e1b71c5a01e2a4bb8632c195861fb6c3038a0e9b85f0e09b3822e9283654a4d7bb17db2fc5f4",
+ "0x9685d3756ce9069bf8bb716cf7d5063ebfafe37e15b137fc8c3159633c4e006ff4887ddd0ae90360767a25c3f90cba7f",
+ "0x82155fd70f107ab3c8e414eadf226c797e07b65911508c76c554445422325e71af8c9a8e77fd52d94412a6fc29417cd3",
+ "0xabfae52f53a4b6e00760468d973a267f29321997c3dbb5aee36dc1f20619551229c0c45b9d9749f410e7f531b73378e8",
+ "0x81a76d921f8ef88e774fd985e786a4a330d779b93fad7def718c014685ca0247379e2e2a007ad63ee7f729cd9ed6ce1b",
+ "0x81947c84bc5e28e26e2e533af5ae8fe10407a7b77436dbf8f1d5b0bbe86fc659eae10f974659dc7c826c6dabd03e3a4b",
+ "0x92b8c07050d635b8dd4fd09df9054efe4edae6b86a63c292e73cc819a12a21dd7d104ce51fa56af6539dedf6dbe6f7b6",
+ "0xb44c579e3881f32b32d20c82c207307eca08e44995dd2aac3b2692d2c8eb2a325626c80ac81c26eeb38c4137ff95add5",
+ "0x97efab8941c90c30860926dea69a841f2dcd02980bf5413b9fd78d85904588bf0c1021798dbc16c8bbb32cce66c82621",
+ "0x913363012528b50698e904de0588bf55c8ec5cf6f0367cfd42095c4468fcc64954fbf784508073e542fee242d0743867",
+ "0x8ed203cf215148296454012bd10fddaf119203db1919a7b3d2cdc9f80e66729464fdfae42f1f2fc5af1ed53a42b40024",
+ "0xab84312db7b87d711e9a60824f4fe50e7a6190bf92e1628688dfcb38930fe87b2d53f9e14dd4de509b2216856d8d9188",
+ "0x880726def069c160278b12d2258eac8fa63f729cd351a710d28b7e601c6712903c3ac1e7bbd0d21e4a15f13ca49db5aa",
+ "0x980699cd51bac6283959765f5174e543ed1e5f5584b5127980cbc2ef18d984ecabba45042c6773b447b8e694db066028",
+ "0xaeb019cb80dc4cb4207430d0f2cd24c9888998b6f21d9bf286cc638449668d2eec0018a4cf3fe6448673cd6729335e2b",
+ "0xb29852f6aa6c60effdffe96ae88590c88abae732561d35cc19e82d3a51e26cb35ea00986193e07f90060756240f5346e",
+ "0xa0fa855adc5ba469f35800c48414b8921455950a5c0a49945d1ef6e8f2a1881f2e2dfae47de6417270a6bf49deeb091d",
+ "0xb6c7332e3b14813641e7272d4f69ecc7e09081df0037d6dab97ce13a9e58510f5c930d300633f208181d9205c5534001",
+ "0x85a6c050f42fce560b5a8d54a11c3bbb8407abbadd859647a7b0c21c4b579ec65671098b74f10a16245dc779dff7838e",
+ "0x8f3eb34bb68759d53c6677de4de78a6c24dd32c8962a7fb355ed362572ef8253733e6b52bc21c9f92ecd875020a9b8de",
+ "0xa17dd44181e5dab4dbc128e1af93ec22624b57a448ca65d2d9e246797e4af7d079e09c6e0dfb62db3a9957ce92f098d5",
+ "0xa56a1b854c3183082543a8685bb34cae1289f86cfa8123a579049dbd059e77982886bfeb61bf6e05b4b1fe4e620932e7",
+ "0xaedae3033cb2fb7628cb4803435bdd7757370a86f808ae4cecb9a268ad0e875f308c048c80cbcac523de16b609683887",
+ "0x9344905376aa3982b1179497fac5a1d74b14b7038fd15e3b002db4c11c8bfc7c39430db492cdaf58b9c47996c9901f28",
+ "0xa3bfafdae011a19f030c749c3b071f83580dee97dd6f949e790366f95618ca9f828f1daaeabad6dcd664fcef81b6556d",
+ "0x81c03d8429129e7e04434dee2c529194ddb01b414feda3adee2271eb680f6c85ec872a55c9fa9d2096f517e13ed5abcc",
+ "0x98205ef3a72dff54c5a9c82d293c3e45d908946fa74bb749c3aabe1ab994ea93c269bcce1a266d2fe67a8f02133c5985",
+ "0x85a70aeed09fda24412fadbafbbbf5ba1e00ac92885df329e147bfafa97b57629a3582115b780d8549d07d19b7867715",
+ "0xb0fbe81c719f89a57d9ea3397705f898175808c5f75f8eb81c2193a0b555869ba7bd2e6bc54ee8a60cea11735e21c68c",
+ "0xb03a0bd160495ee626ff3a5c7d95bc79d7da7e5a96f6d10116600c8fa20bedd1132f5170f25a22371a34a2d763f2d6d0",
+ "0xa90ab04091fbca9f433b885e6c1d60ab45f6f1daf4b35ec22b09909d493a6aab65ce41a6f30c98239cbca27022f61a8b",
+ "0xb66f92aa3bf2549f9b60b86f99a0bd19cbdd97036d4ae71ca4b83d669607f275260a497208f6476cde1931d9712c2402",
+ "0xb08e1fdf20e6a9b0b4942f14fa339551c3175c1ffc5d0ab5b226b6e6a322e9eb0ba96adc5c8d59ca4259e2bdd04a7eb0",
+ "0xa2812231e92c1ce74d4f5ac3ab6698520288db6a38398bb38a914ac9326519580af17ae3e27cde26607e698294022c81",
+ "0xabfcbbcf1d3b9e84c02499003e490a1d5d9a2841a9e50c7babbef0b2dd20d7483371d4dc629ba07faf46db659459d296",
+ "0xb0fe9f98c3da70927c23f2975a9dc4789194d81932d2ad0f3b00843dd9cbd7fb60747a1da8fe5a79f136a601becf279d",
+ "0xb130a6dba7645165348cb90f023713bed0eefbd90a976b313521c60a36d34f02032e69a2bdcf5361e343ed46911297ec",
+ "0x862f0cffe3020cea7a5fd4703353aa1eb1be335e3b712b29d079ff9f7090d1d8b12013011e1bdcbaa80c44641fd37c9f",
+ "0x8c6f11123b26633e1abb9ed857e0bce845b2b3df91cc7b013b2fc77b477eee445da0285fc6fc793e29d5912977f40916",
+ "0x91381846126ea819d40f84d3005e9fb233dc80071d1f9bb07f102bf015f813f61e5884ffffb4f5cd333c1b1e38a05a58",
+ "0x8add7d908de6e1775adbd39c29a391f06692b936518db1f8fde74eb4f533fc510673a59afb86e3a9b52ade96e3004c57",
+ "0x8780e086a244a092206edcde625cafb87c9ab1f89cc3e0d378bc9ee776313836160960a82ec397bc3800c0a0ec3da283",
+ "0xa6cb4cd9481e22870fdd757fae0785edf4635e7aacb18072fe8dc5876d0bab53fb99ce40964a7d3e8bcfff6f0ab1332f",
+ "0xaf30ff47ecc5b543efba1ba4706921066ca8bb625f40e530fb668aea0551c7647a9d126e8aba282fbcce168c3e7e0130",
+ "0x91b0bcf408ce3c11555dcb80c4410b5bc2386d3c05caec0b653352377efdcb6bab4827f2018671fc8e4a0e90d772acc1",
+ "0xa9430b975ef138b6b2944c7baded8fe102d31da4cfe3bd3d8778bda79189c99d38176a19c848a19e2d1ee0bddd9a13c1",
+ "0xaa5a4eef849d7c9d2f4b018bd01271c1dd83f771de860c4261f385d3bdcc130218495860a1de298f14b703ec32fa235f",
+ "0xb0ce79e7f9ae57abe4ff366146c3b9bfb38b0dee09c28c28f5981a5d234c6810ad4d582751948affb480d6ae1c8c31c4",
+ "0xb75122748560f73d15c01a8907d36d06dc068e82ce22b84b322ac1f727034493572f7907dec34ebc3ddcc976f2f89ed7",
+ "0xb0fc7836369a3e4411d34792d6bd5617c14f61d9bba023dda64e89dc5fb0f423244e9b48ee64869258931daa9753a56f",
+ "0x8956d7455ae9009d70c6e4a0bcd7610e55f37494cf9897a8f9e1b904cc8febc3fd2d642ebd09025cfff4609ad7e3bc52",
+ "0xad741efe9e472026aa49ae3d9914cb9c1a6f37a54f1a6fe6419bebd8c7d68dca105a751c7859f4389505ede40a0de786",
+ "0xb52f418797d719f0d0d0ffb0846788b5cba5d0454a69a2925de4b0b80fa4dd7e8c445e5eac40afd92897ed28ca650566",
+ "0xa0ab65fb9d42dd966cd93b1de01d7c822694669dd2b7a0c04d99cd0f3c3de795f387b9c92da11353412f33af5c950e9a",
+ "0xa0052f44a31e5741a331f7cac515a08b3325666d388880162d9a7b97598fde8b61f9ff35ff220df224eb5c4e40ef0567",
+ "0xa0101cfdc94e42b2b976c0d89612a720e55d145a5ef6ef6f1f78cf6de084a49973d9b5d45915349c34ce712512191e3c",
+ "0xa0dd99fcf3f5cead5aaf08e82212df3a8bb543c407a4d6fab88dc5130c1769df3f147e934a46f291d6c1a55d92b86917",
+ "0xa5939153f0d1931bbda5cf6bdf20562519ea55fbfa978d6dbc6828d298260c0da7a50c37c34f386e59431301a96c2232",
+ "0x9568269f3f5257200f9ca44afe1174a5d3cf92950a7f553e50e279c239e156a9faaa2a67f288e3d5100b4142efe64856",
+ "0xb746b0832866c23288e07f24991bbf687cad794e7b794d3d3b79367566ca617d38af586cdc8d6f4a85a34835be41d54f",
+ "0xa871ce28e39ab467706e32fec1669fda5a4abba2f8c209c6745df9f7a0fa36bbf1919cf14cb89ea26fa214c4c907ae03",
+ "0xa08dacdd758e523cb8484f6bd070642c0c20e184abdf8e2a601f61507e93952d5b8b0c723c34fcbdd70a8485eec29db2",
+ "0x85bdb78d501382bb95f1166b8d032941005661aefd17a5ac32df9a3a18e9df2fc5dc2c1f07075f9641af10353cecc0c9",
+ "0x98d730c28f6fa692a389e97e368b58f4d95382fad8f0baa58e71a3d7baaea1988ead47b13742ce587456f083636fa98e",
+ "0xa557198c6f3d5382be9fb363feb02e2e243b0c3c61337b3f1801c4a0943f18e38ce1a1c36b5c289c8fa2aa9d58742bab",
+ "0x89174f79201742220ac689c403fc7b243eed4f8e3f2f8aba0bf183e6f5d4907cb55ade3e238e3623d9885f03155c4d2b",
+ "0xb891d600132a86709e06f3381158db300975f73ea4c1f7c100358e14e98c5fbe792a9af666b85c4e402707c3f2db321e",
+ "0xb9e5b2529ef1043278c939373fc0dbafe446def52ddd0a8edecd3e4b736de87e63e187df853c54c28d865de18a358bb6",
+ "0x8589b2e9770340c64679062c5badb7bbef68f55476289b19511a158a9a721f197da03ece3309e059fc4468b15ac33aa3",
+ "0xaad8c6cd01d785a881b446f06f1e9cd71bca74ba98674c2dcddc8af01c40aa7a6d469037498b5602e76e9c91a58d3dbd",
+ "0xabaccb1bd918a8465f1bf8dbe2c9ad4775c620b055550b949a399f30cf0d9eb909f3851f5b55e38f9e461e762f88f499",
+ "0xae62339d26db46e85f157c0151bd29916d5cc619bd4b832814b3fd2f00af8f38e7f0f09932ffe5bba692005dab2d9a74",
+ "0x93a6ff30a5c0edf8058c89aba8c3259e0f1b1be1b80e67682de651e5346f7e1b4b4ac3d87cbaebf198cf779524aff6bf",
+ "0x8980a2b1d8f574af45b459193c952400b10a86122b71fca2acb75ee0dbd492e7e1ef5b959baf609a5172115e371f3177",
+ "0x8c2f49f3666faee6940c75e8c7f6f8edc3f704cca7a858bbb7ee5e96bba3b0cf0993996f781ba6be3b0821ef4cb75039",
+ "0xb14b9e348215b278696018330f63c38db100b0542cfc5be11dc33046e3bca6a13034c4ae40d9cef9ea8b34fef0910c4e",
+ "0xb59bc3d0a30d66c16e6a411cb641f348cb1135186d5f69fda8b0a0934a5a2e7f6199095ba319ec87d3fe8f1ec4a06368",
+ "0x8874aca2a3767aa198e4c3fec2d9c62d496bc41ff71ce242e9e082b7f38cdf356089295f80a301a3cf1182bde5308c97",
+ "0xb1820ebd61376d91232423fc20bf008b2ba37e761199f4ef0648ea2bd70282766799b4de814846d2f4d516d525c8daa7",
+ "0xa6b202e5dedc16a4073e04a11af3a8509b23dfe5a1952f899adeb240e75c3f5bde0c424f811a81ea48d343591faffe46",
+ "0xa69becee9c93734805523b92150a59a62eed4934f66056b645728740d42223f2925a1ad38359ba644da24d9414f4cdda",
+ "0xad72f0f1305e37c7e6b48c272323ee883320994cb2e0d850905d6655fafc9f361389bcb9c66b3ff8d2051dbb58c8aa96",
+ "0xb563600bd56fad7c8853af21c6a02a16ed9d8a8bbeea2c31731d63b976d83cb05b9779372d898233e8fd597a75424797",
+ "0xb0abb78ce465bf7051f563c62e8be9c57a2cc997f47c82819300f36e301fefd908894bb2053a9d27ce2d0f8c46d88b5b",
+ "0xa071a85fb8274bac2202e0cb8e0e2028a5e138a82d6e0374d39ca1884a549c7c401312f00071b91f455c3a2afcfe0cda",
+ "0xb931c271513a0f267b9f41444a5650b1918100b8f1a64959c552aff4e2193cc1b9927906c6fa7b8a8c68ef13d79aaa52",
+ "0xa6a1bb9c7d32cb0ca44d8b75af7e40479fbce67d216b48a2bb680d3f3a772003a49d3cd675fc64e9e0f8fabeb86d6d61",
+ "0xb98d609858671543e1c3b8564162ad828808bb50ded261a9f8690ded5b665ed8368c58f947365ed6e84e5a12e27b423d",
+ "0xb3dca58cd69ec855e2701a1d66cad86717ff103ef862c490399c771ad28f675680f9500cb97be48de34bcdc1e4503ffd",
+ "0xb34867c6735d3c49865e246ddf6c3b33baf8e6f164db3406a64ebce4768cb46b0309635e11be985fee09ab7a31d81402",
+ "0xacb966c554188c5b266624208f31fab250b3aa197adbdd14aee5ab27d7fb886eb4350985c553b20fdf66d5d332bfd3fe",
+ "0x943c36a18223d6c870d54c3b051ef08d802b85e9dd6de37a51c932f90191890656c06adfa883c87b906557ae32d09da0",
+ "0x81bca7954d0b9b6c3d4528aadf83e4bc2ef9ea143d6209bc45ae9e7ae9787dbcd8333c41f12c0b6deee8dcb6805e826a",
+ "0xaba176b92256efb68f574e543479e5cf0376889fb48e3db4ebfb7cba91e4d9bcf19dcfec444c6622d9398f06de29e2b9",
+ "0xb9f743691448053216f6ece7cd699871fff4217a1409ceb8ab7bdf3312d11696d62c74b0664ba0a631b1e0237a8a0361",
+ "0xa383c2b6276fa9af346b21609326b53fb14fdf6f61676683076e80f375b603645f2051985706d0401e6fbed7eb0666b6",
+ "0xa9ef2f63ec6d9beb8f3d04e36807d84bda87bdd6b351a3e4a9bf7edcb5618c46c1f58cfbf89e64b40f550915c6988447",
+ "0xa141b2d7a82f5005eaea7ae7d112c6788b9b95121e5b70b7168d971812f3381de8b0082ac1f0a82c7d365922ebd2d26a",
+ "0xb1b76ef8120e66e1535c17038b75255a07849935d3128e3e99e56567b842fb1e8d56ef932d508d2fb18b82f7868fe1a9",
+ "0x8e2e234684c81f21099f5c54f6bbe2dd01e3b172623836c77668a0c49ce1fe218786c3827e4d9ae2ea25c50a8924fb3c",
+ "0xa5caf5ff948bfd3c4ca3ffbdfcd91eec83214a6c6017235f309a0bbf7061d3b0b466307c00b44a1009cf575163898b43",
+ "0x986415a82ca16ebb107b4c50b0c023c28714281db0bcdab589f6cb13d80e473a3034b7081b3c358e725833f6d845cb14",
+ "0xb94836bf406ac2cbacb10e6df5bcdfcc9d9124ae1062767ca4e322d287fd5e353fdcebd0e52407cb3cd68571258a8900",
+ "0x83c6d70a640b33087454a4788dfd9ef3ed00272da084a8d36be817296f71c086b23b576f98178ab8ca6a74f04524b46b",
+ "0xad4115182ad784cfe11bcfc5ce21fd56229cc2ce77ac82746e91a2f0aa53ca6593a22efd2dc4ed8d00f84542643d9c58",
+ "0xab1434c5e5065da826d10c2a2dba0facccab0e52b506ce0ce42fbe47ced5a741797151d9ecc99dc7d6373cfa1779bbf6",
+ "0x8a8b591d82358d55e6938f67ea87a89097ab5f5496f7260adb9f649abb289da12b498c5b2539c2f9614fb4e21b1f66b0",
+ "0x964f355d603264bc1f44c64d6d64debca66f37dff39c971d9fc924f2bc68e6c187b48564a6dc82660a98b035f8addb5d",
+ "0xb66235eaaf47456bc1dc4bde454a028e2ce494ece6b713a94cd6bf27cf18c717fd0c57a5681caaa2ad73a473593cdd7a",
+ "0x9103e3bb74304186fa4e3e355a02da77da4aca9b7e702982fc2082af67127ebb23a455098313c88465bc9b7d26820dd5",
+ "0xb6a42ff407c9dd132670cdb83cbad4b20871716e44133b59a932cd1c3f97c7ac8ff7f61acfaf8628372508d8dc8cad7c",
+ "0x883a9c21c16a167a4171b0f084565c13b6f28ba7c4977a0de69f0a25911f64099e7bbb4da8858f2e93068f4155d04e18",
+ "0x8dbb3220abc6a43220adf0331e3903d3bfd1d5213aadfbd8dfcdf4b2864ce2e96a71f35ecfb7a07c3bbabf0372b50271",
+ "0xb4ad08aee48e176bda390b7d9acf2f8d5eb008f30d20994707b757dc6a3974b2902d29cd9b4d85e032810ad25ac49e97",
+ "0x865bb0f33f7636ec501bb634e5b65751c8a230ae1fa807a961a8289bbf9c7fe8c59e01fbc4c04f8d59b7f539cf79ddd5",
+ "0x86a54d4c12ad1e3605b9f93d4a37082fd26e888d2329847d89afa7802e815f33f38185c5b7292293d788ad7d7da1df97",
+ "0xb26c8615c5e47691c9ff3deca3021714662d236c4d8401c5d27b50152ce7e566266b9d512d14eb63e65bc1d38a16f914",
+ "0x827639d5ce7db43ba40152c8a0eaad443af21dc92636cc8cc2b35f10647da7d475a1e408901cd220552fddad79db74df",
+ "0xa2b79a582191a85dbe22dc384c9ca3de345e69f6aa370aa6d3ff1e1c3de513e30b72df9555b15a46586bd27ea2854d9d",
+ "0xae0d74644aba9a49521d3e9553813bcb9e18f0b43515e4c74366e503c52f47236be92dfbd99c7285b3248c267b1de5a0",
+ "0x80fb0c116e0fd6822a04b9c25f456bdca704e2be7bdc5d141dbf5d1c5eeb0a2c4f5d80db583b03ef3e47517e4f9a1b10",
+ "0xac3a1fa3b4a2f30ea7e0a114cdc479eb51773573804c2a158d603ad9902ae8e39ffe95df09c0d871725a5d7f9ba71a57",
+ "0xb56b2b0d601cba7f817fa76102c68c2e518c6f20ff693aad3ff2e07d6c4c76203753f7f91686b1801e8c4659e4d45c48",
+ "0x89d50c1fc56e656fb9d3915964ebce703cb723fe411ab3c9eaa88ccc5d2b155a9b2e515363d9c600d3c0cee782c43f41",
+ "0xb24207e61462f6230f3cd8ccf6828357d03e725769f7d1de35099ef9ee4dca57dbce699bb49ed994462bee17059d25ce",
+ "0xb886f17fcbcbfcd08ac07f04bb9543ef58510189decaccea4b4158c9174a067cb67d14b6be3c934e6e2a18c77efa9c9c",
+ "0xb9c050ad9cafd41c6e2e192b70d080076eed59ed38ea19a12bd92fa17b5d8947d58d5546aaf5e8e27e1d3b5481a6ce51",
+ "0xaaf7a34d3267e3b1ddbc54c641e3922e89303f7c86ebebc7347ebca4cffad5b76117dac0cbae1a133053492799cd936f",
+ "0xa9ee604ada50adef82e29e893070649d2d4b7136cc24fa20e281ce1a07bd736bf0de7c420369676bcbcecff26fb6e900",
+ "0x9855315a12a4b4cf80ab90b8bd13003223ba25206e52fd4fe6a409232fbed938f30120a3db23eab9c53f308bd8b9db81",
+ "0x8cd488dd7a24f548a3cf03c54dec7ff61d0685cb0f6e5c46c2d728e3500d8c7bd6bba0156f4bf600466fda53e5b20444",
+ "0x890ad4942ebac8f5b16c777701ab80c68f56fa542002b0786f8fea0fb073154369920ac3dbfc07ea598b82f4985b8ced",
+ "0x8de0cf9ddc84c9b92c59b9b044387597799246b30b9f4d7626fc12c51f6e423e08ee4cbfe9289984983c1f9521c3e19d",
+ "0xb474dfb5b5f4231d7775b3c3a8744956b3f0c7a871d835d7e4fd9cc895222c7b868d6c6ce250de568a65851151fac860",
+ "0x86433b6135d9ed9b5ee8cb7a6c40e5c9d30a68774cec04988117302b8a02a11a71a1e03fd8e0264ef6611d219f103007",
+ "0x80b9ed4adbe9538fb1ef69dd44ec0ec5b57cbfea820054d8d445b4261962624b4c70ac330480594bc5168184378379c3",
+ "0x8b2e83562ccd23b7ad2d17f55b1ab7ef5fbef64b3a284e6725b800f3222b8bdf49937f4a873917ada9c4ddfb090938c2",
+ "0xabe78cebc0f5a45d754140d1f685e387489acbfa46d297a8592aaa0d676a470654f417a4f7d666fc0b2508fab37d908e",
+ "0xa9c5f8ff1f8568e252b06d10e1558326db9901840e6b3c26bbd0cd5e850cb5fb3af3f117dbb0f282740276f6fd84126f",
+ "0x975f8dc4fb55032a5df3b42b96c8c0ffecb75456f01d4aef66f973cb7270d4eff32c71520ceefc1adcf38d77b6b80c67",
+ "0xb043306ed2c3d8a5b9a056565afd8b5e354c8c4569fda66b0d797a50a3ce2c08cffbae9bbe292da69f39e89d5dc7911e",
+ "0x8d2afc36b1e44386ba350c14a6c1bb31ff6ea77128a0c5287584ac3584282d18516901ce402b4644a53db1ed8e7fa581",
+ "0x8c294058bed53d7290325c363fe243f6ec4f4ea2343692f4bac8f0cb86f115c069ccb8334b53d2e42c067691ad110dba",
+ "0xb92157b926751aaf7ef82c1aa8c654907dccab6376187ee8b3e8c0c82811eae01242832de953faa13ebaff7da8698b3e",
+ "0xa780c4bdd9e4ba57254b09d745075cecab87feda78c88ffee489625c5a3cf96aa6b3c9503a374a37927d9b78de9bd22b",
+ "0x811f548ef3a2e6a654f7dcb28ac9378de9515ed61e5a428515d9594a83e80b35c60f96a5cf743e6fab0d3cb526149f49",
+ "0x85a4dccf6d90ee8e094731eec53bd00b3887aec6bd81a0740efddf812fd35e3e4fe4f983afb49a8588691c202dabf942",
+ "0xb152c2da6f2e01c8913079ae2b40a09b1f361a80f5408a0237a8131b429677c3157295e11b365b1b1841924b9efb922e",
+ "0x849b9efee8742502ffd981c4517c88ed33e4dd518a330802caff168abae3cd09956a5ee5eda15900243bc2e829016b74",
+ "0x955a933f3c18ec0f1c0e38fa931e4427a5372c46a3906ebe95082bcf878c35246523c23f0266644ace1fa590ffa6d119",
+ "0x911989e9f43e580c886656377c6f856cdd4ff1bd001b6db3bbd86e590a821d34a5c6688a29b8d90f28680e9fdf03ba69",
+ "0xb73b8b4f1fd6049fb68d47cd96a18fcba3f716e0a1061aa5a2596302795354e0c39dea04d91d232aec86b0bf2ba10522",
+ "0x90f87456d9156e6a1f029a833bf3c7dbed98ca2f2f147a8564922c25ae197a55f7ea9b2ee1f81bf7383197c4bad2e20c",
+ "0x903cba8b1e088574cb04a05ca1899ab00d8960580c884bd3c8a4c98d680c2ad11410f2b75739d6050f91d7208cac33a5",
+ "0x9329987d42529c261bd15ecedd360be0ea8966e7838f32896522c965adfc4febf187db392bd441fb43bbd10c38fdf68b",
+ "0x8178ee93acf5353baa349285067b20e9bb41aa32d77b5aeb7384fe5220c1fe64a2461bd7a83142694fe673e8bbf61b7c",
+ "0xa06a8e53abcff271b1394bcc647440f81fb1c1a5f29c27a226e08f961c3353f4891620f2d59b9d1902bf2f5cc07a4553",
+ "0xaaf5fe493b337810889e777980e6bbea6cac39ac66bc0875c680c4208807ac866e9fda9b5952aa1d04539b9f4a4bec57",
+ "0xaa058abb1953eceac14ccfa7c0cc482a146e1232905dcecc86dd27f75575285f06bbae16a8c9fe8e35d8713717f5f19f",
+ "0x8f15dd732799c879ca46d2763453b359ff483ca33adb1d0e0a57262352e0476c235987dc3a8a243c74bc768f93d3014c",
+ "0xa61cc8263e9bc03cce985f1663b8a72928a607121005a301b28a278e9654727fd1b22bc8a949af73929c56d9d3d4a273",
+ "0x98d6dc78502d19eb9f921225475a6ebcc7b44f01a2df6f55ccf6908d65b27af1891be2a37735f0315b6e0f1576c1f8d8",
+ "0x8bd258b883f3b3793ec5be9472ad1ff3dc4b51bc5a58e9f944acfb927349ead8231a523cc2175c1f98e7e1e2b9f363b8",
+ "0xaeacc2ecb6e807ad09bedd99654b097a6f39840e932873ace02eabd64ccfbb475abdcb62939a698abf17572d2034c51e",
+ "0xb8ccf78c08ccd8df59fd6eda2e01de328bc6d8a65824d6f1fc0537654e9bc6bf6f89c422dd3a295cce628749da85c864",
+ "0x8f91fd8cb253ba2e71cc6f13da5e05f62c2c3b485c24f5d68397d04665673167fce1fc1aec6085c69e87e66ec555d3fd",
+ "0xa254baa10cb26d04136886073bb4c159af8a8532e3fd36b1e9c3a2e41b5b2b6a86c4ebc14dbe624ee07b7ccdaf59f9ab",
+ "0x94e3286fe5cd68c4c7b9a7d33ae3d714a7f265cf77cd0e9bc19fc51015b1d1c34ad7e3a5221c459e89f5a043ee84e3a9",
+ "0xa279da8878af8d449a9539bec4b17cea94f0242911f66fab275b5143ab040825f78c89cb32a793930609415cfa3a1078",
+ "0xac846ceb89c9e5d43a2991c8443079dc32298cd63e370e64149cec98cf48a6351c09c856f2632fd2f2b3d685a18bbf8b",
+ "0xa847b27995c8a2e2454aaeb983879fb5d3a23105c33175839f7300b7e1e8ec3efd6450e9fa3f10323609dee7b98c6fd5",
+ "0xa2f432d147d904d185ff4b2de8c6b82fbea278a2956bc406855b44c18041854c4f0ecccd472d1d0dff1d8aa8e281cb1d",
+ "0x94a48ad40326f95bd63dff4755f863a1b79e1df771a1173b17937f9baba57b39e651e7695be9f66a472f098b339364fc",
+ "0xa12a0ccd8f96e96e1bc6494341f7ebce959899341b3a084aa1aa87d1c0d489ac908552b7770b887bb47e7b8cbc3d8e66",
+ "0x81a1f1681bda923bd274bfe0fbb9181d6d164fe738e54e25e8d4849193d311e2c4253614ed673c98af2c798f19a93468",
+ "0xabf71106a05d501e84cc54610d349d7d5eae21a70bd0250f1bebbf412a130414d1c8dbe673ffdb80208fd72f1defa4d4",
+ "0x96266dc2e0df18d8136d79f5b59e489978eee0e6b04926687fe389d4293c14f36f055c550657a8e27be4118b64254901",
+ "0x8df5dcbefbfb4810ae3a413ca6b4bf08619ca53cd50eb1dde2a1c035efffc7b7ac7dff18d403253fd80104bd83dc029e",
+ "0x9610b87ff02e391a43324a7122736876d5b3af2a137d749c52f75d07b17f19900b151b7f439d564f4529e77aa057ad12",
+ "0xa90a5572198b40fe2fcf47c422274ff36c9624df7db7a89c0eb47eb48a73a03c985f4ac5016161c76ca317f64339bce1",
+ "0x98e5e61a6ab6462ba692124dba7794b6c6bde4249ab4fcc98c9edd631592d5bc2fb5e38466691a0970a38e48d87c2e43",
+ "0x918cefb8f292f78d4db81462c633daf73b395e772f47b3a7d2cea598025b1d8c3ec0cbff46cdb23597e74929981cde40",
+ "0xa98918a5dc7cf610fe55f725e4fd24ce581d594cb957bb9b4e888672e9c0137003e1041f83e3f1d7b9caab06462c87d4",
+ "0xb92b74ac015262ca66c33f2d950221e19d940ba3bf4cf17845f961dc1729ae227aa9e1f2017829f2135b489064565c29",
+ "0xa053ee339f359665feb178b4e7ee30a85df37debd17cacc5a27d6b3369d170b0114e67ad1712ed26d828f1df641bcd99",
+ "0x8c3c8bad510b35da5ce5bd84b35c958797fbea024ad1c97091d2ff71d9b962e9222f65a9b776e5b3cc29c36e1063d2ee",
+ "0xaf99dc7330fe7c37e850283eb47cc3257888e7c197cb0d102edf94439e1e02267b6a56306d246c326c4c79f9dc8c6986",
+ "0xafecb2dc34d57a725efbd7eb93d61eb29dbe8409b668ab9ea040791f5b796d9be6d4fc10d7f627bf693452f330cf0435",
+ "0x93334fedf19a3727a81a6b6f2459db859186227b96fe7a391263f69f1a0884e4235de64d29edebc7b99c44d19e7c7d7a",
+ "0x89579c51ac405ad7e9df13c904061670ce4b38372492764170e4d3d667ed52e5d15c7cd5c5991bbfa3a5e4e3fa16363e",
+ "0x9778f3e8639030f7ef1c344014f124e375acb8045bd13d8e97a92c5265c52de9d1ffebaa5bc3e1ad2719da0083222991",
+ "0x88f77f34ee92b3d36791bdf3326532524a67d544297dcf1a47ff00b47c1b8219ff11e34034eab7d23b507caa2fd3c6b9",
+ "0xa699c1e654e7c484431d81d90657892efeb4adcf72c43618e71ca7bd7c7a7ebbb1db7e06e75b75dc4c74efd306b5df3f",
+ "0x81d13153baebb2ef672b5bdb069d3cd669ce0be96b742c94e04038f689ff92a61376341366b286eee6bf3ae85156f694",
+ "0x81efb17de94400fdacc1deec2550cbe3eecb27c7af99d8207e2f9be397e26be24a40446d2a09536bb5172c28959318d9",
+ "0x989b21ebe9ceab02488992673dc071d4d5edec24bff0e17a4306c8cb4b3c83df53a2063d1827edd8ed16d6e837f0d222",
+ "0x8d6005d6536825661b13c5fdce177cb37c04e8b109b7eb2b6d82ea1cb70efecf6a0022b64f84d753d165edc2bba784a3",
+ "0xa32607360a71d5e34af2271211652d73d7756d393161f4cf0da000c2d66a84c6826e09e759bd787d4fd0305e2439d342",
+ "0xaaad8d6f6e260db45d51b2da723be6fa832e76f5fbcb77a9a31e7f090dd38446d3b631b96230d78208cae408c288ac4e",
+ "0xabcfe425255fd3c5cffd3a818af7650190c957b6b07b632443f9e33e970a8a4c3bf79ac9b71f4d45f238a04d1c049857",
+ "0xaeabf026d4c783adc4414b5923dbd0be4b039cc7201219f7260d321f55e9a5b166d7b5875af6129c034d0108fdc5d666",
+ "0xaf49e740c752d7b6f17048014851f437ffd17413c59797e5078eaaa36f73f0017c3e7da020310cfe7d3c85f94a99f203",
+ "0x8854ca600d842566e3090040cd66bb0b3c46dae6962a13946f0024c4a8aca447e2ccf6f240045f1ceee799a88cb9210c",
+ "0xb6c03b93b1ab1b88ded8edfa1b487a1ed8bdce8535244dddb558ffb78f89b1c74058f80f4db2320ad060d0c2a9c351cc",
+ "0xb5bd7d17372faff4898a7517009b61a7c8f6f0e7ed4192c555db264618e3f6e57fb30a472d169fea01bf2bf0362a19a8",
+ "0x96eb1d38319dc74afe7e7eb076fcd230d19983f645abd14a71e6103545c01301b31c47ae931e025f3ecc01fb3d2f31fa",
+ "0xb55a8d30d4403067def9b65e16f867299f8f64c9b391d0846d4780bc196569622e7e5b64ce799b5aefac8f965b2a7a7b",
+ "0x8356d199a991e5cbbff608752b6291731b6b6771aed292f8948b1f41c6543e4ab1bedc82dd26d10206c907c03508df06",
+ "0x97f4137445c2d98b0d1d478049de952610ad698c91c9d0f0e7227d2aae690e9935e914ec4a2ea1fbf3fc1dddfeeacebb",
+ "0xaf5621707e0938320b15ddfc87584ab325fbdfd85c30efea36f8f9bd0707d7ec12c344eff3ec21761189518d192df035",
+ "0x8ac7817e71ea0825b292687928e349da7140285d035e1e1abff0c3704fa8453faaae343a441b7143a74ec56539687cc4",
+ "0x8a5e0a9e4758449489df10f3386029ada828d1762e4fb0a8ffe6b79e5b6d5d713cb64ed95960e126398b0cdb89002bc9",
+ "0x81324be4a71208bbb9bca74b77177f8f1abb9d3d5d9db195d1854651f2cf333cd618d35400da0f060f3e1b025124e4b2",
+ "0x849971d9d095ae067525b3cbc4a7dfae81f739537ade6d6cec1b42fb692d923176197a8770907c58069754b8882822d6",
+ "0x89f830825416802477cc81fdf11084885865ee6607aa15aa4eb28e351c569c49b8a1b9b5e95ddc04fa0ebafe20071313",
+ "0x9240aeeaff37a91af55f860b9badd466e8243af9e8c96a7aa8cf348cd270685ab6301bc135b246dca9eda696f8b0e350",
+ "0xacf74db78cc33138273127599eba35b0fb4e7b9a69fe02dae18fc6692d748ca332bd00b22afa8e654ed587aab11833f3",
+ "0xb091e6d37b157b50d76bd297ad752220cd5c9390fac16dc838f8557aed6d9833fc920b61519df21265406216315e883f",
+ "0xa6446c429ebf1c7793c622250e23594c836b2fbcaf6c5b3d0995e1595a37f50ea643f3e549b0be8bbdadd69044d72ab9",
+ "0x93e675353bd60e996bf1c914d5267eeaa8a52fc3077987ccc796710ef9becc6b7a00e3d82671a6bdfb8145ee3c80245a",
+ "0xa2f731e43251d04ed3364aa2f072d05355f299626f2d71a8a38b6f76cf08c544133f7d72dd0ab4162814b674b9fc7fa6",
+ "0x97a8b791a5a8f6e1d0de192d78615d73d0c38f1e557e4e15d15adc663d649e655bc8da3bcc499ef70112eafe7fb45c7a",
+ "0x98cd624cbbd6c53a94469be4643c13130916b91143425bcb7d7028adbbfede38eff7a21092af43b12d4fab703c116359",
+ "0x995783ce38fd5f6f9433027f122d4cf1e1ff3caf2d196ce591877f4a544ce9113ead60de2de1827eaff4dd31a20d79a8",
+ "0x8cf251d6f5229183b7f3fe2f607a90b4e4b6f020fb4ba2459d28eb8872426e7be8761a93d5413640a661d73e34a5b81f",
+ "0xb9232d99620652a3aa7880cad0876f153ff881c4ed4c0c2e7b4ea81d5d42b70daf1a56b869d752c3743c6d4c947e6641",
+ "0x849716f938f9d37250cccb1bf77f5f9fde53096cdfc6f2a25536a6187029a8f1331cdbed08909184b201f8d9f04b792f",
+ "0x80c7c4de098cbf9c6d17b14eba1805e433b5bc905f6096f8f63d34b94734f2e4ebf4bce8a177efd1186842a61204a062",
+ "0xb790f410cf06b9b8daadceeb4fd5ff40a2deda820c8df2537e0a7554613ae3948e149504e3e79aa84889df50c8678eeb",
+ "0x813aab8bd000299cd37485b73cd7cba06e205f8efb87f1efc0bae8b70f6db2bc7702eb39510ad734854fb65515fe9d0f",
+ "0x94f0ab7388ac71cdb67f6b85dfd5945748afb2e5abb622f0b5ad104be1d4d0062b651f134ba22385c9e32c2dfdcccce1",
+ "0xab6223dca8bd6a4f969e21ccd9f8106fc5251d321f9e90cc42cea2424b3a9c4e5060a47eeef6b23c7976109b548498e8",
+ "0x859c56b71343fce4d5c5b87814c47bf55d581c50fd1871a17e77b5e1742f5af639d0e94d19d909ec7dfe27919e954e0c",
+ "0xaae0d632b6191b8ad71b027791735f1578e1b89890b6c22e37de0e4a6074886126988fe8319ae228ac9ef3b3bcccb730",
+ "0x8ca9f32a27a024c3d595ecfaf96b0461de57befa3b331ab71dc110ec3be5824fed783d9516597537683e77a11d334338",
+ "0xa061df379fb3f4b24816c9f6cd8a94ecb89b4c6dc6cd81e4b8096fa9784b7f97ab3540259d1de9c02eb91d9945af4823",
+ "0x998603102ac63001d63eb7347a4bb2bf4cf33b28079bb48a169076a65c20d511ccd3ef696d159e54cc8e772fb5d65d50",
+ "0x94444d96d39450872ac69e44088c252c71f46be8333a608a475147752dbb99db0e36acfc5198f158509401959c12b709",
+ "0xac1b51b6c09fe055c1d7c9176eea9adc33f710818c83a1fbfa073c8dc3a7eb3513cbdd3f5960b7845e31e3e83181e6ba",
+ "0x803d530523fc9e1e0f11040d2412d02baef3f07eeb9b177fa9bfa396af42eea898a4276d56e1db998dc96ae47b644cb2",
+ "0x85a3c9fc7638f5bf2c3e15ba8c2fa1ae87eb1ceb44c6598c67a2948667a9dfa41e61f66d535b4e7fda62f013a5a8b885",
+ "0xa961cf5654c46a1a22c29baf7a4e77837a26b7f138f410e9d1883480ed5fa42411d522aba32040b577046c11f007388e",
+ "0xad1154142344f494e3061ef45a34fab1aaacf5fdf7d1b26adbb5fbc3d795655fa743444e39d9a4119b4a4f82a6f30441",
+ "0xb1d6c30771130c77806e7ab893b73d4deb590b2ff8f2f8b5e54c2040c1f3e060e2bd99afc668cf706a2df666a508bbf6",
+ "0xa00361fd440f9decabd98d96c575cd251dc94c60611025095d1201ef2dedde51cb4de7c2ece47732e5ed9b3526c2012c",
+ "0xa85c5ab4d17d328bda5e6d839a9a6adcc92ff844ec25f84981e4f44a0e8419247c081530f8d9aa629c7eb4ca21affba6",
+ "0xa4ddd3eab4527a2672cf9463db38bc29f61460e2a162f426b7852b7a7645fbd62084fd39a8e4d60e1958cce436dd8f57",
+ "0x811648140080fe55b8618f4cf17f3c5a250adb0cd53d885f2ddba835d2b4433188e41fc0661faac88e4ff910b16278c0",
+ "0xb85c7f1cfb0ed29addccf7546023a79249e8f15ac2d14a20accbfef4dd9dc11355d599815fa09d2b6b4e966e6ea8cff1",
+ "0xa10b5d8c260b159043b020d5dd62b3467df2671afea6d480ca9087b7e60ed170c82b121819d088315902842d66c8fb45",
+ "0x917e191df1bcf3f5715419c1e2191da6b8680543b1ba41fe84ed07ef570376e072c081beb67b375fca3565a2565bcabb",
+ "0x881fd967407390bfd7badc9ab494e8a287559a01eb07861f527207c127eadea626e9bcc5aa9cca2c5112fbac3b3f0e9c",
+ "0x959fd71149af82cc733619e0e5bf71760ca2650448c82984b3db74030d0e10f8ab1ce1609a6de6f470fe8b5bd90df5b3",
+ "0xa3370898a1c5f33d15adb4238df9a6c945f18b9ada4ce2624fc32a844f9ece4c916a64e9442225b6592afa06d2e015f2",
+ "0x817efb8a791435e4236f7d7b278181a5fa34587578c629dbc14fbf9a5c26772290611395eecd20222a4c58649fc256d8",
+ "0xa04c9876acf2cfdc8ef96de4879742709270fa1d03fe4c8511fbef2d59eb0aaf0336fa2c7dfe41a651157377fa217813",
+ "0x81e15875d7ea7f123e418edf14099f2e109d4f3a6ce0eb65f67fe9fb10d2f809a864a29f60ad3fc949f89e2596b21783",
+ "0xb49f529975c09e436e6bc202fdc16e3fdcbe056db45178016ad6fdece9faad4446343e83aed096209690b21a6910724f",
+ "0x879e8eda589e1a279f7f49f6dd0580788c040d973748ec4942dbe51ea8fbd05983cc919b78f0c6b92ef3292ae29db875",
+ "0x81a2b74b2118923f34139a102f3d95e7eee11c4c2929c2576dee200a5abfd364606158535a6c9e4178a6a83dbb65f3c4",
+ "0x8913f281d8927f2b45fc815d0f7104631cb7f5f7278a316f1327d670d15868daadd2a64e3eb98e1f53fe7e300338cc80",
+ "0xa6f815fba7ef9af7fbf45f93bc952e8b351f5de6568a27c7c47a00cb39a254c6b31753794f67940fc7d2e9cc581529f4",
+ "0xb3722a15c66a0014ce4d082de118def8d39190c15678a472b846225585f3a83756ae1b255b2e3f86a26168878e4773b2",
+ "0x817ae61ab3d0dd5b6e24846b5a5364b1a7dc2e77432d9fed587727520ae2f307264ea0948c91ad29f0aea3a11ff38624",
+ "0xb3db467464415fcad36dc1de2d6ba7686772a577cc2619242ac040d6734881a45d3b40ed4588db124e4289cfeec4bbf6",
+ "0xad66a14f5a54ac69603b16e5f1529851183da77d3cc60867f10aea41339dd5e06a5257982e9e90a352cdd32750f42ee4",
+ "0xadafa3681ef45d685555601a25a55cf23358319a17f61e2179e704f63df83a73bdd298d12cf6cef86db89bd17119e11d",
+ "0xa379dc44cb6dd3b9d378c07b2ec654fec7ca2f272de6ba895e3d00d20c9e4c5550498a843c8ac67e4221db2115bedc1c",
+ "0xb7bf81c267a78efc6b9e5a904574445a6487678d7ef70054e3e93ea6a23f966c2b68787f9164918e3b16d2175459ed92",
+ "0xb41d66a13a4afafd5760062b77f79de7e6ab8ccacde9c6c5116a6d886912fb491dc027af435b1b44aacc6af7b3c887f2",
+ "0x9904d23a7c1c1d2e4bab85d69f283eb0a8e26d46e8b7b30224438015c936729b2f0af7c7c54c03509bb0500acb42d8a4",
+ "0xae30d65e9e20c3bfd603994ae2b175ff691d51f3e24b2d058b3b8556d12ca4c75087809062dddd4aaac81c94d15d8a17",
+ "0x9245162fab42ac01527424f6013310c3eb462982518debef6c127f46ba8a06c705d7dc9f0a41e796ba8d35d60ae6cc64",
+ "0x87fab853638d7a29a20f3ba2b1a7919d023e9415bfa78ebb27973d8cbc7626f584dc5665d2e7ad71f1d760eba9700d88",
+ "0x85aac46ecd330608e5272430970e6081ff02a571e8ea444f1e11785ea798769634a22a142d0237f67b75369d3c484a8a",
+ "0x938c85ab14894cc5dfce3d80456f189a2e98eddbc8828f4ff6b1df1dcb7b42b17ca2ff40226a8a1390a95d63dca698dd",
+ "0xa18ce1f846e3e3c4d846822f60271eecf0f5d7d9f986385ac53c5ace9589dc7c0188910448c19b91341a1ef556652fa9",
+ "0x8611608a9d844f0e9d7584ad6ccf62a5087a64f764caf108db648a776b5390feb51e5120f0ef0e9e11301af3987dd7dc",
+ "0x8106333ba4b4de8d1ae43bc9735d3fea047392e88efd6a2fa6f7b924a18a7a265ca6123c3edc0f36307dd7fb7fe89257",
+ "0xa91426fa500951ff1b051a248c050b7139ca30dde8768690432d597d2b3c4357b11a577be6b455a1c5d145264dcf81fc",
+ "0xb7f9f90e0e450f37b081297f7f651bad0496a8b9afd2a4cf4120a2671aaaa8536dce1af301258bfbfdb122afa44c5048",
+ "0x84126da6435699b0c09fa4032dec73d1fca21d2d19f5214e8b0bea43267e9a8dd1fc44f8132d8315e734c8e2e04d7291",
+ "0xaff064708103884cb4f1a3c1718b3fc40a238d35cf0a7dc24bdf9823693b407c70da50df585bf5bc4e9c07d1c2d203e8",
+ "0xa8b40fc6533752983a5329c31d376c7a5c13ce6879cc7faee648200075d9cd273537001fb4c86e8576350eaac6ba60c2",
+ "0xa02db682bdc117a84dcb9312eb28fcbde12d49f4ce915cc92c610bb6965ec3cc38290f8c5b5ec70afe153956692cda95",
+ "0x86decd22b25d300508472c9ce75d3e465b737e7ce13bc0fcce32835e54646fe12322ba5bc457be18bfd926a1a6ca4a38",
+ "0xa18666ef65b8c2904fd598791f5627207165315a85ee01d5fb0e6b2e10bdd9b00babc447da5bd63445e3337de33b9b89",
+ "0x89bb0c06effadefdaf34ffe4b123e1678a90d4451ee856c863df1e752eef41fd984689ded8f0f878bf8916d5dd8e8024",
+ "0x97cfcba08ebec05d0073992a66b1d7d6fb9d95871f2cdc36db301f78bf8069294d1c259efef5c93d20dc937eedae3a1a",
+ "0xac2643b14ece79dcb2e289c96776a47e2bebd40dd6dc74fd035df5bb727b5596f40e3dd2d2202141e69b0993717ede09",
+ "0xa5e6fd88a2f9174d9bd4c6a55d9c30974be414992f22aa852f552c7648f722ed8077acf5aba030abd47939bb451b2c60",
+ "0x8ad40a612824a7994487731a40b311b7349038c841145865539c6ada75c56de6ac547a1c23df190e0caaafecddd80ccc",
+ "0x953a7cea1d857e09202c438c6108060961f195f88c32f0e012236d7a4b39d840c61b162ec86436e8c38567328bea0246",
+ "0x80d8b47a46dae1868a7b8ccfe7029445bbe1009dad4a6c31f9ef081be32e8e1ac1178c3c8fb68d3e536c84990cc035b1",
+ "0x81ecd99f22b3766ce0aca08a0a9191793f68c754fdec78b82a4c3bdc2db122bbb9ebfd02fc2dcc6e1567a7d42d0cc16a",
+ "0xb1dd0446bccc25846fb95d08c1c9cc52fb51c72c4c5d169ffde56ecfe800f108dc1106d65d5c5bd1087c656de3940b63",
+ "0xb87547f0931e164e96de5c550ca5aa81273648fe34f6e193cd9d69cf729cb432e17aa02e25b1c27a8a0d20a3b795e94e",
+ "0x820a94e69a927e077082aae66f6b292cfbe4589d932edf9e68e268c9bd3d71ef76cf7d169dd445b93967c25db11f58f1",
+ "0xb0d07ddf2595270c39adfa0c8cf2ab1322979b0546aa4d918f641be53cd97f36c879bb75d205e457c011aca3bbd9f731",
+ "0x8700b876b35b4b10a8a9372c5230acecd39539c1bb87515640293ad4464a9e02929d7d6a6a11112e8a29564815ac0de4",
+ "0xa61a601c5bb27dcb97e37c8e2b9ce479c6b192a5e04d9ed5e065833c5a1017ee5f237b77d1a17be5d48f8e7cc0bcacf6",
+ "0x92fb88fe774c1ba1d4a08cae3c0e05467ad610e7a3f1d2423fd47751759235fe0a3036db4095bd6404716aa03820f484",
+ "0xb274f140d77a3ce0796f5e09094b516537ccaf27ae1907099bff172e6368ba85e7c3ef8ea2a07457cac48ae334da95b3",
+ "0xb2292d9181f16581a9a9142490b2bdcdfb218ca6315d1effc8592100d792eb89d5356996c890441f04f2b4a95763503e",
+ "0x8897e73f576d86bc354baa3bd96e553107c48cf5889dcc23c5ba68ab8bcd4e81f27767be2233fdfa13d39f885087e668",
+ "0xa29eac6f0829791c728d71abc49569df95a4446ecbfc534b39f24f56c88fe70301838dfc1c19751e7f3c5c1b8c6af6a0",
+ "0x9346dc3720adc5df500a8df27fd9c75ef38dc5c8f4e8ed66983304750e66d502c3c59b8e955be781b670a0afc70a2167",
+ "0x9566d534e0e30a5c5f1428665590617e95fd05d45f573715f58157854ad596ece3a3cfec61356aee342308d623e029d5",
+ "0xa464fb8bffe6bd65f71938c1715c6e296cc6d0311a83858e4e7eb5873b7f2cf0c584d2101e3407b85b64ca78b2ac93ce",
+ "0xb54088f7217987c87e9498a747569ac5b2f8afd5348f9c45bf3fd9fbf713a20f495f49c8572d087efe778ac7313ad6d3",
+ "0x91fa9f5f8000fe050f5b224d90b59fcce13c77e903cbf98ded752e5b3db16adb2bc1f8c94be48b69f65f1f1ad81d6264",
+ "0x92d04a5b0ac5d8c8e313709b432c9434ecd3e73231f01e9b4e7952b87df60cbfa97b5dedd2200bd033b4b9ea8ba45cc1",
+ "0xa94b90ad3c3d6c4bbe169f8661a790c40645b40f0a9d1c7220f01cf7fc176e04d80bab0ced9323fcafb93643f12b2760",
+ "0x94d86149b9c8443b46196f7e5a3738206dd6f3be7762df488bcbb9f9ee285a64c997ed875b7b16b26604fa59020a8199",
+ "0x82efe4ae2c50a2d7645240c173a047f238536598c04a2c0b69c96e96bd18e075a99110f1206bc213f39edca42ba00cc1",
+ "0xab8667685f831bc14d4610f84a5da27b4ea5b133b4d991741a9e64dceb22cb64a3ce8f1b6e101d52af6296df7127c9ad",
+ "0x83ba433661c05dcc5d562f4a9a261c8110dac44b8d833ae1514b1fc60d8b4ee395b18804baea04cb10adb428faf713c3",
+ "0xb5748f6f660cc5277f1211d2b8649493ed8a11085b871cd33a5aea630abd960a740f08c08be5f9c21574600ac9bf5737",
+ "0xa5c8dd12af48fb710642ad65ebb97ca489e8206741807f7acfc334f8035d3c80593b1ff2090c9bb7bd138f0c48714ca8",
+ "0xa2b382fd5744e3babf454b1d806cc8783efeb4761bc42b6914ea48a46a2eae835efbe0a18262b6bc034379e03cf1262b",
+ "0xb3145ffaf603f69f15a64936d32e3219eea5ed49fdfd2f5bf40ea0dfd974b36fb6ff12164d4c2282d892db4cf3ff3ce1",
+ "0x87a316fb213f4c5e30c5e3face049db66be4f28821bd96034714ec23d3e97849d7b301930f90a4323c7ccf53de23050c",
+ "0xb9de09a919455070fed6220fc179c8b7a4c753062bcd27acf28f5b9947a659c0b364298daf7c85c4ca6fca7f945add1f",
+ "0x806fbd98d411b76979464c40ad88bc07a151628a27fcc1012ba1dfbaf5b5cc9d962fb9b3386008978a12515edce934bc",
+ "0xa15268877fae0d21610ae6a31061ed7c20814723385955fac09fdc9693a94c33dea11db98bb89fdfe68f933490f5c381",
+ "0x8d633fb0c4da86b2e0b37d8fad5972d62bff2ac663c5ec815d095cd4b7e1fe66ebef2a2590995b57eaf941983c7ad7a4",
+ "0x8139e5dd9cf405e8ef65f11164f0440827d98389ce1b418b0c9628be983a9ddd6cf4863036ccb1483b40b8a527acd9ed",
+ "0x88b15fa94a08eac291d2b94a2b30eb851ff24addf2cc30b678e72e32cfcb3424cf4b33aa395d741803f3e578ddf524de",
+ "0xb5eaf0c8506e101f1646bcf049ee38d99ea1c60169730da893fd6020fd00a289eb2f415947e44677af49e43454a7b1be",
+ "0x8489822ad0647a7e06aa2aa5595960811858ddd4542acca419dd2308a8c5477648f4dd969a6740bb78aa26db9bfcc555",
+ "0xb1e9a7b9f3423c220330d45f69e45fa03d7671897cf077f913c252e3e99c7b1b1cf6d30caad65e4228d5d7b80eb86e5e",
+ "0xb28fe9629592b9e6a55a1406903be76250b1c50c65296c10c5e48c64b539fb08fe11f68cf462a6edcbba71b0cee3feb2",
+ "0xa41acf96a02c96cd8744ff6577c244fc923810d17ade133587e4c223beb7b4d99fa56eae311a500d7151979267d0895c",
+ "0x880798938fe4ba70721be90e666dfb62fcab4f3556fdb7b0dc8ec5bc34f6b4513df965eae78527136eb391889fe2caf9",
+ "0x98d4d89d358e0fb7e212498c73447d94a83c1b66e98fc81427ab13acddb17a20f52308983f3a5a8e0aaacec432359604",
+ "0x81430b6d2998fc78ba937a1639c6020199c52da499f68109da227882dc26d005b73d54c5bdcac1a04e8356a8ca0f7017",
+ "0xa8d906a4786455eb74613aba4ce1c963c60095ffb8658d368df9266fdd01e30269ce10bf984e7465f34b4fd83beba26a",
+ "0xaf54167ac1f954d10131d44a8e0045df00d581dd9e93596a28d157543fbe5fb25d213806ed7fb3cba6b8f5b5423562db",
+ "0x8511e373a978a12d81266b9afbd55035d7bc736835cfa921903a92969eeba3624437d1346b55382e61415726ab84a448",
+ "0x8cf43eea93508ae586fa9a0f1354a1e16af659782479c2040874a46317f9e8d572a23238efa318fdfb87cc63932602b7",
+ "0xb0bdd3bacff077173d302e3a9678d1d37936188c7ecc34950185af6b462b7c679815176f3cce5db19aac8b282f2d60ad",
+ "0xa355e9b87f2f2672052f5d4d65b8c1c827d24d89b0d8594641fccfb69aef1b94009105f3242058bb31c8bf51caae5a41",
+ "0xb8baa9e4b950b72ff6b88a6509e8ed1304bc6fd955748b2e59a523a1e0c5e99f52aec3da7fa9ff407a7adf259652466c",
+ "0x840bc3dbb300ea6f27d1d6dd861f15680bd098be5174f45d6b75b094d0635aced539fa03ddbccb453879de77fb5d1fe9",
+ "0xb4bc7e7e30686303856472bae07e581a0c0bfc815657c479f9f5931cff208d5c12930d2fd1ff413ebd8424bcd7a9b571",
+ "0x89b5d514155d7999408334a50822508b9d689add55d44a240ff2bdde2eee419d117031f85e924e2a2c1ca77db9b91eea",
+ "0xa8604b6196f87a04e1350302e8aa745bba8dc162115d22657b37a1d1a98cb14876ddf7f65840b5dbd77e80cd22b4256c",
+ "0x83cb7acdb9e03247515bb2ce0227486ccf803426717a14510f0d59d45e998b245797d356f10abca94f7a14e1a2f0d552",
+ "0xaeb3266a9f16649210ab2df0e1908ac259f34ce1f01162c22b56cf1019096ee4ea5854c36e30bb2feb06c21a71e8a45c",
+ "0x89e72e86edf2aa032a0fc9acf4d876a40865fbb2c8f87cb7e4d88856295c4ac14583e874142fd0c314a49aba68c0aa3c",
+ "0x8c3576eba0583c2a7884976b4ed11fe1fda4f6c32f6385d96c47b0e776afa287503b397fa516a455b4b8c3afeedc76db",
+ "0xa31e5b633bda9ffa174654fee98b5d5930a691c3c42fcf55673d927dbc8d91c58c4e42e615353145431baa646e8bbb30",
+ "0x89f2f3f7a8da1544f24682f41c68114a8f78c86bd36b066e27da13acb70f18d9f548773a16bd8e24789420e17183f137",
+ "0xada27fa4e90a086240c9164544d2528621a415a5497badb79f8019dc3dce4d12eb6b599597e47ec6ac39c81efda43520",
+ "0x90dc1eb21bf21c0187f359566fc4bf5386abea52799306a0e5a1151c0817c5f5bc60c86e76b1929c092c0f3ff48cedd2",
+ "0xb702a53ebcc17ae35d2e735a347d2c700e9cbef8eadbece33cac83df483b2054c126593e1f462cfc00a3ce9d737e2af5",
+ "0x9891b06455ec925a6f8eafffba05af6a38cc5e193acaaf74ffbf199df912c5197106c5e06d72942bbb032ce277b6417f",
+ "0x8c0ee71eb01197b019275bcf96cae94e81d2cdc3115dbf2d8e3080074260318bc9303597e8f72b18f965ad601d31ec43",
+ "0x8aaf580aaf75c1b7a5f99ccf60503506e62058ef43b28b02f79b8536a96be3f019c9f71caf327b4e6730134730d1bef5",
+ "0xae6f9fc21dd7dfa672b25a87eb0a41644f7609fab5026d5cedb6e43a06dbbfd6d6e30322a2598c8dedde88c52eaed626",
+ "0x8159b953ffece5693edadb2e906ebf76ff080ee1ad22698950d2d3bfc36ac5ea78f58284b2ca180664452d55bd54716c",
+ "0xab7647c32ca5e9856ac283a2f86768d68de75ceeba9e58b74c5324f8298319e52183739aba4340be901699d66ac9eb3f",
+ "0xa4d85a5701d89bcfaf1572db83258d86a1a0717603d6f24ac2963ffcf80f1265e5ab376a4529ca504f4396498791253c",
+ "0x816080c0cdbfe61b4d726c305747a9eb58ac26d9a35f501dd32ba43c098082d20faf3ccd41aad24600aa73bfa453dfac",
+ "0x84f3afac024f576b0fd9acc6f2349c2fcefc3f77dbe5a2d4964d14b861b88e9b1810334b908cf3427d9b67a8aee74b18",
+ "0x94b390655557b1a09110018e9b5a14490681ade275bdc83510b6465a1218465260d9a7e2a6e4ec700f58c31dc3659962",
+ "0xa8c66826b1c04a2dd4c682543242e7a57acae37278bd09888a3d17747c5b5fec43548101e6f46d703638337e2fd3277b",
+ "0x86e6f4608a00007fa533c36a5b054c5768ccafe41ad52521d772dcae4c8a4bcaff8f7609be30d8fab62c5988cbbb6830",
+ "0x837da4cf09ae8aa0bceb16f8b3bfcc3b3367aecac9eed6b4b56d7b65f55981ef066490764fb4c108792623ecf8cad383",
+ "0x941ff3011462f9b5bf97d8cbdb0b6f5d37a1b1295b622f5485b7d69f2cb2bcabc83630dae427f0259d0d9539a77d8424",
+ "0xb99e5d6d82aa9cf7d5970e7f710f4039ac32c2077530e4c2779250c6b9b373bc380adb0a03b892b652f649720672fc8c",
+ "0xa791c78464b2d65a15440b699e1e30ebd08501d6f2720adbc8255d989a82fcded2f79819b5f8f201bed84a255211b141",
+ "0x84af7ad4a0e31fcbb3276ab1ad6171429cf39adcf78dc03750dc5deaa46536d15591e26d53e953dfb31e1622bc0743ab",
+ "0xa833e62fe97e1086fae1d4917fbaf09c345feb6bf1975b5cb863d8b66e8d621c7989ab3dbecda36bc9eaffc5eaa6fa66",
+ "0xb4ef79a46a2126f53e2ebe62770feb57fd94600be29459d70a77c5e9cc260fa892be06cd60f886bf48459e48eb50d063",
+ "0xb43b8f61919ea380bf151c294e54d3a3ff98e20d1ee5efbfe38aa2b66fafbc6a49739793bd5cb1c809f8b30466277c3a",
+ "0xab37735af2412d2550e62df9d8b3b5e6f467f20de3890bf56faf1abf2bf3bd1d98dc3fa0ad5e7ab3fce0fa20409eb392",
+ "0x82416b74b1551d484250d85bb151fabb67e29cce93d516125533df585bc80779ab057ea6992801a3d7d5c6dcff87a018",
+ "0x8145d0787f0e3b5325190ae10c1d6bee713e6765fb6a0e9214132c6f78f4582bb2771aaeae40d3dad4bafb56bf7e36d8",
+ "0xb6935886349ecbdd5774e12196f4275c97ec8279fdf28ccf940f6a022ebb6de8e97d6d2173c3fe402cbe9643bed3883b",
+ "0x87ef9b4d3dc71ac86369f8ed17e0dd3b91d16d14ae694bc21a35b5ae37211b043d0e36d8ff07dcc513fb9e6481a1f37f",
+ "0xae1d0ded32f7e6f1dc8fef495879c1d9e01826f449f903c1e5034aeeabc5479a9e323b162b688317d46d35a42d570d86",
+ "0xa40d16497004db4104c6794e2f4428d75bdf70352685944f3fbe17526df333e46a4ca6de55a4a48c02ecf0bde8ba03c0",
+ "0x8d45121efba8cc308a498e8ee39ea6fa5cae9fb2e4aab1c2ff9d448aa8494ccbec9a078f978a86fcd97b5d5e7be7522a",
+ "0xa8173865c64634ba4ac2fa432740f5c05056a9deaf6427cb9b4b8da94ca5ddbc8c0c5d3185a89b8b28878194de9cdfcd",
+ "0xb6ec06a74d690f6545f0f0efba236e63d1fdfba54639ca2617408e185177ece28901c457d02b849fd00f1a53ae319d0a",
+ "0xb69a12df293c014a40070e3e760169b6f3c627caf9e50b35a93f11ecf8df98b2bc481b410eecb7ab210bf213bbe944de",
+ "0x97e7dc121795a533d4224803e591eef3e9008bab16f12472210b73aaf77890cf6e3877e0139403a0d3003c12c8f45636",
+ "0xacdfa6fdd4a5acb7738cc8768f7cba84dbb95c639399b291ae8e4e63df37d2d4096900a84d2f0606bf534a9ccaa4993f",
+ "0x86ee253f3a9446a33e4d1169719b7d513c6b50730988415382faaf751988c10a421020609f7bcdef91be136704b906e2",
+ "0xaac9438382a856caf84c5a8a234282f71b5fc5f65219103b147e7e6cf565522285fbfd7417b513bdad8277a00f652ca1",
+ "0x83f3799d8e5772527930f5dc071a2e0a65471618993ec8990a96ccdeee65270e490bda9d26bb877612475268711ffd80",
+ "0x93f28a81ac8c0ec9450b9d762fae9c7f8feaace87a6ee6bd141ef1d2d0697ef1bbd159fe6e1de640dbdab2b0361fca8a",
+ "0xa0825c95ba69999b90eac3a31a3fd830ea4f4b2b7409bde5f202b61d741d6326852ce790f41de5cb0eccec7af4db30c1",
+ "0x83924b0e66233edd603c3b813d698daa05751fc34367120e3cf384ea7432e256ccee4d4daf13858950549d75a377107d",
+ "0x956fd9fa58345277e06ba2ec72f49ed230b8d3d4ff658555c52d6cddeb84dd4e36f1a614f5242d5ca0192e8daf0543c2",
+ "0x944869912476baae0b114cced4ff65c0e4c90136f73ece5656460626599051b78802df67d7201c55d52725a97f5f29fe",
+ "0x865cb25b64b4531fb6fe4814d7c8cd26b017a6c6b72232ff53defc18a80fe3b39511b23f9e4c6c7249d06e03b2282ed2",
+ "0x81e09ff55214960775e1e7f2758b9a6c4e4cd39edf7ec1adfaad51c52141182b79fe2176b23ddc7df9fd153e5f82d668",
+ "0xb31006896f02bc90641121083f43c3172b1039334501fbaf1672f7bf5d174ddd185f945adf1a9c6cf77be34c5501483d",
+ "0x88b92f6f42ae45e9f05b16e52852826e933efd0c68b0f2418ac90957fd018df661bc47c8d43c2a7d7bfcf669dab98c3c",
+ "0x92fc68f595853ee8683930751789b799f397135d002eda244fe63ecef2754e15849edde3ba2f0cc8b865c9777230b712",
+ "0x99ca06a49c5cd0bb097c447793fcdd809869b216a34c66c78c7e41e8c22f05d09168d46b8b1f3390db9452d91bc96dea",
+ "0xb48b9490a5d65296802431852d548d81047bbefc74fa7dc1d4e2a2878faacdfcb365ae59209cb0ade01901a283cbd15d",
+ "0xaff0fdbef7c188b120a02bc9085d7b808e88f73973773fef54707bf2cd772cd066740b1b6f4127b5c349f657bd97e738",
+ "0x966fd4463b4f43dd8ccba7ad50baa42292f9f8b2e70da23bb6780e14155d9346e275ef03ddaf79e47020dcf43f3738bd",
+ "0x9330c3e1fadd9e08ac85f4839121ae20bbeb0a5103d84fa5aadbd1213805bdcda67bf2fb75fc301349cbc851b5559d20",
+ "0x993bb99867bd9041a71a55ad5d397755cfa7ab6a4618fc526179bfc10b7dc8b26e4372fe9a9b4a15d64f2b63c1052dda",
+ "0xa29b59bcfab51f9b3c490a3b96f0bf1934265c315349b236012adbd64a56d7f6941b2c8cc272b412044bc7731f71e1dc",
+ "0xa65c9cefe1fc35d089fe8580c2e7671ebefdb43014ac291528ff4deefd4883fd4df274af83711dad610dad0d615f9d65",
+ "0x944c78c56fb227ae632805d448ca3884cd3d2a89181cead3d2b7835e63297e6d740aa79a112edb1d4727824991636df5",
+ "0xa73d782da1db7e4e65d7b26717a76e16dd9fab4df65063310b8e917dc0bc24e0d6755df5546c58504d04d9e68c3b474a",
+ "0xaf80f0b87811ae3124f68108b4ca1937009403f87928bbc53480e7c5408d072053ace5eeaf5a5aba814dab8a45502085",
+ "0x88aaf1acfc6e2e19b8387c97da707cb171c69812fefdd4650468e9b2c627bd5ccfb459f4d8e56bdfd84b09ddf87e128f",
+ "0x92c97276ff6f72bab6e9423d02ad6dc127962dbce15a0dd1e4a393b4510c555df6aa27be0f697c0d847033a9ca8b8dfd",
+ "0xa0e07d43d96e2d85b6276b3c60aadb48f0aedf2de8c415756dc597249ea64d2093731d8735231dadc961e5682ac59479",
+ "0xadc9e6718a8f9298957d1da3842a7751c5399bbdf56f8de6c1c4bc39428f4aee6f1ba6613d37bf46b9403345e9d6fc81",
+ "0x951da434da4b20d949b509ceeba02e24da7ed2da964c2fcdf426ec787779c696b385822c7dbea4df3e4a35921f1e912c",
+ "0xa04cbce0d2b2e87bbf038c798a12ec828423ca6aca08dc8d481cf6466e3c9c73d4d4a7fa47df9a7e2e15aae9e9f67208",
+ "0x8f855cca2e440d248121c0469de1f94c2a71b8ee2682bbad3a78243a9e03da31d1925e6760dbc48a1957e040fae9abe8",
+ "0xb642e5b17c1df4a4e101772d73851180b3a92e9e8b26c918050f51e6dd3592f102d20b0a1e96f0e25752c292f4c903ff",
+ "0xa92454c300781f8ae1766dbbb50a96192da7d48ef4cbdd72dd8cbb44c6eb5913c112cc38e9144615fdc03684deb99420",
+ "0x8b74f7e6c2304f8e780df4649ef8221795dfe85fdbdaa477a1542d135b75c8be45bf89adbbb6f3ddf54ca40f02e733e9",
+ "0x85cf66292cbb30cec5fd835ab10c9fcb3aea95e093aebf123e9a83c26f322d76ebc89c4e914524f6c5f6ee7d74fc917d",
+ "0xae0bfe0cdc97c09542a7431820015f2d16067b30dca56288013876025e81daa8c519e5e347268e19aa1a85fa1dc28793",
+ "0x921322fc6a47dc091afa0ad6df18ed14cde38e48c6e71550aa513918b056044983aee402de21051235eecf4ce8040fbe",
+ "0x96c030381e97050a45a318d307dcb3c8377b79b4dd5daf6337cded114de26eb725c14171b9b8e1b3c08fe1f5ea6b49e0",
+ "0x90c23b86b6111818c8baaf53a13eaee1c89203b50e7f9a994bf0edf851919b48edbac7ceef14ac9414cf70c486174a77",
+ "0x8bf6c301240d2d1c8d84c71d33a6dfc6d9e8f1cfae66d4d0f7a256d98ae12b0bcebfa94a667735ee89f810bcd7170cff",
+ "0xa41a4ffbbea0e36874d65c009ee4c3feffff322f6fc0e30d26ee4dbc1f46040d05e25d9d0ecb378cef0d24a7c2c4b850",
+ "0xa8d4cdd423986bb392a0a92c12a8bd4da3437eec6ef6af34cf5310944899287452a2eb92eb5386086d5063381189d10e",
+ "0xa81dd26ec057c4032a4ed7ad54d926165273ed51d09a1267b2e477535cf6966835a257c209e4e92d165d74fa75695fa3",
+ "0x8d7f708c3ee8449515d94fc26b547303b53d8dd55f177bc3b25d3da2768accd9bc8e9f09546090ebb7f15c66e6c9c723",
+ "0x839ba65cffcd24cfffa7ab3b21faabe3c66d4c06324f07b2729c92f15cad34e474b0f0ddb16cd652870b26a756b731d3",
+ "0x87f1a3968afec354d92d77e2726b702847c6afcabb8438634f9c6f7766de4c1504317dc4fa9a4a735acdbf985e119564",
+ "0x91a8a7fd6542f3e0673f07f510d850864b34ac087eb7eef8845a1d14b2b1b651cbdc27fa4049bdbf3fea54221c5c8549",
+ "0xaef3cf5f5e3a2385ead115728d7059e622146c3457d266c612e778324b6e06fbfb8f98e076624d2f3ce1035d65389a07",
+ "0x819915d6232e95ccd7693fdd78d00492299b1983bc8f96a08dcb50f9c0a813ed93ae53c0238345d5bea0beda2855a913",
+ "0x8e9ba68ded0e94935131b392b28218315a185f63bf5e3c1a9a9dd470944509ca0ba8f6122265f8da851b5cc2abce68f1",
+ "0xb28468e9b04ee9d69003399a3cf4457c9bf9d59f36ab6ceeb8e964672433d06b58beeea198fedc7edbaa1948577e9fa2",
+ "0xa633005e2c9f2fd94c8bce2dd5bb708fe946b25f1ec561ae65e54e15cdd88dc339f1a083e01f0d39610c8fe24151aaf0",
+ "0x841d0031e22723f9328dd993805abd13e0c99b0f59435d2426246996b08d00ce73ab906f66c4eab423473b409e972ce0",
+ "0x85758d1b084263992070ec8943f33073a2d9b86a8606672550c17545507a5b3c88d87382b41916a87ee96ff55a7aa535",
+ "0x8581b06b0fc41466ef94a76a1d9fb8ae0edca6d018063acf6a8ca5f4b02d76021902feba58972415691b4bdbc33ae3b4",
+ "0x83539597ff5e327357ee62bc6bf8c0bcaec2f227c55c7c385a4806f0d37fb461f1690bad5066b8a5370950af32fafbef",
+ "0xaee3557290d2dc10827e4791d00e0259006911f3f3fce4179ed3c514b779160613eca70f720bff7804752715a1266ffa",
+ "0xb48d2f0c4e90fc307d5995464e3f611a9b0ef5fe426a289071f4168ed5cc4f8770c9332960c2ca5c8c427f40e6bb389f",
+ "0x847af8973b4e300bb06be69b71b96183fd1a0b9d51b91701bef6fcfde465068f1eb2b1503b07afda380f18d69de5c9e1",
+ "0xa70a6a80ce407f07804c0051ac21dc24d794b387be94eb24e1db94b58a78e1bcfb48cd0006db8fc1f9bedaece7a44fbe",
+ "0xb40e942b8fa5336910ff0098347df716bff9d1fa236a1950c16eeb966b3bc1a50b8f7b0980469d42e75ae13ced53cead",
+ "0xb208fabaa742d7db3148515330eb7a3577487845abdb7bd9ed169d0e081db0a5816595c33d375e56aeac5b51e60e49d3",
+ "0xb7c8194b30d3d6ef5ab66ec88ad7ebbc732a3b8a41731b153e6f63759a93f3f4a537eab9ad369705bd730184bdbbdc34",
+ "0x9280096445fe7394d04aa1bc4620c8f9296e991cc4d6c131bd703cb1cc317510e6e5855ac763f4d958c5edfe7eebeed7",
+ "0xabc2aa4616a521400af1a12440dc544e3c821313d0ab936c86af28468ef8bbe534837e364598396a81cf8d06274ed5a6",
+ "0xb18ca8a3325adb0c8c18a666d4859535397a1c3fe08f95eebfac916a7a99bbd40b3c37b919e8a8ae91da38bc00fa56c0",
+ "0x8a40c33109ecea2a8b3558565877082f79121a432c45ec2c5a5e0ec4d1c203a6788e6b69cb37f1fd5b8c9a661bc5476d",
+ "0x88c47301dd30998e903c84e0b0f2c9af2e1ce6b9f187dab03528d44f834dc991e4c86d0c474a2c63468cf4020a1e24a0",
+ "0x920c832853e6ab4c851eecfa9c11d3acc7da37c823be7aa1ab15e14dfd8beb5d0b91d62a30cec94763bd8e4594b66600",
+ "0x98e1addbe2a6b8edc7f12ecb9be81c3250aeeca54a1c6a7225772ca66549827c15f3950d01b8eb44aecb56fe0fff901a",
+ "0x8cfb0fa1068be0ec088402f5950c4679a2eb9218c729da67050b0d1b2d7079f3ddf4bf0f57d95fe2a8db04bc6bcdb20c",
+ "0xb70f381aafe336b024120453813aeab70baac85b9c4c0f86918797b6aee206e6ed93244a49950f3d8ec9f81f4ac15808",
+ "0xa4c8edf4aa33b709a91e1062939512419711c1757084e46f8f4b7ed64f8e682f4e78b7135920c12f0eb0422fe9f87a6a",
+ "0xb4817e85fd0752d7ebb662d3a51a03367a84bac74ebddfba0e5af5e636a979500f72b148052d333b3dedf9edd2b4031b",
+ "0xa87430169c6195f5d3e314ff2d1c2f050e766fd5d2de88f5207d72dba4a7745bb86d0baca6e9ae156582d0d89e5838c7",
+ "0x991b00f8b104566b63a12af4826b61ce7aa40f4e5b8fff3085e7a99815bdb4471b6214da1e480214fac83f86a0b93cc5",
+ "0xb39966e3076482079de0678477df98578377a094054960ee518ef99504d6851f8bcd3203e8da5e1d4f6f96776e1fe6eb",
+ "0xa448846d9dc2ab7a0995fa44b8527e27f6b3b74c6e03e95edb64e6baa4f1b866103f0addb97c84bef1d72487b2e21796",
+ "0x894bec21a453ae84b592286e696c35bc30e820e9c2fd3e63dd4fbe629e07df16439c891056070faa490155f255bf7187",
+ "0xa9ec652a491b11f6a692064e955f3f3287e7d2764527e58938571469a1e29b5225b9415bd602a45074dfbfe9c131d6ca",
+ "0xb39d37822e6cbe28244b5f42ce467c65a23765bd16eb6447c5b3e942278069793763483dafd8c4dd864f8917aad357fe",
+ "0x88dba51133f2019cb266641c56101e3e5987d3b77647a2e608b5ff9113dfc5f85e2b7c365118723131fbc0c9ca833c9c",
+ "0xb566579d904b54ecf798018efcb824dccbebfc6753a0fd2128ac3b4bd3b038c2284a7c782b5ca6f310eb7ea4d26a3f0a",
+ "0xa97a55c0a492e53c047e7d6f9d5f3e86fb96f3dddc68389c0561515343b66b4bc02a9c0d5722dff1e3445308240b27f7",
+ "0xa044028ab4bcb9e1a2b9b4ca4efbf04c5da9e4bf2fff0e8bd57aa1fc12a71e897999c25d9117413faf2f45395dee0f13",
+ "0xa78dc461decbeaeed8ebd0909369b491a5e764d6a5645a7dac61d3140d7dc0062526f777b0eb866bff27608429ebbdde",
+ "0xb2c2a8991f94c39ca35fea59f01a92cb3393e0eccb2476dfbf57261d406a68bd34a6cff33ed80209991688c183609ef4",
+ "0x84189eefb521aff730a4fd3fd5b10ddfd29f0d365664caef63bb015d07e689989e54c33c2141dd64427805d37a7e546e",
+ "0x85ac80bd734a52235da288ff042dea9a62e085928954e8eacd2c751013f61904ed110e5b3afe1ab770a7e6485efb7b5e",
+ "0x9183a560393dcb22d0d5063e71182020d0fbabb39e32493eeffeb808df084aa243eb397027f150b55a247d1ed0c8513e",
+ "0x81c940944df7ecc58d3c43c34996852c3c7915ed185d7654627f7af62abae7e0048dd444a6c09961756455000bd96d09",
+ "0xaa8c34e164019743fd8284b84f06c3b449aae7996e892f419ee55d82ad548cb300fd651de329da0384243954c0ef6a60",
+ "0x89a7b7bdfc7e300d06a14d463e573d6296d8e66197491900cc9ae49504c4809ff6e61b758579e9091c61085ba1237b83",
+ "0x878d21809ba540f50bd11f4c4d9590fb6f3ab9de5692606e6e2ef4ed9d18520119e385be5e1f4b3f2e2b09c319f0e8fc",
+ "0x8eb248390193189cf0355365e630b782cd15751e672dc478b39d75dc681234dcd9309df0d11f4610dbb249c1e6be7ef9",
+ "0xa1d7fb3aecb896df3a52d6bd0943838b13f1bd039c936d76d03de2044c371d48865694b6f532393b27fd10a4cf642061",
+ "0xa34bca58a24979be442238cbb5ece5bee51ae8c0794dd3efb3983d4db713bc6f28a96e976ac3bd9a551d3ed9ba6b3e22",
+ "0x817c608fc8cacdd178665320b5a7587ca21df8bdd761833c3018b967575d25e3951cf3d498a63619a3cd2ad4406f5f28",
+ "0x86c95707db0495689afd0c2e39e97f445f7ca0edffad5c8b4cacd1421f2f3cc55049dfd504f728f91534e20383955582",
+ "0x99c3b0bb15942c301137765d4e19502f65806f3b126dc01a5b7820c87e8979bce6a37289a8f6a4c1e4637227ad5bf3bf",
+ "0x8aa1518a80ea8b074505a9b3f96829f5d4afa55a30efe7b4de4e5dbf666897fdd2cf31728ca45921e21a78a80f0e0f10",
+ "0x8d74f46361c79e15128ac399e958a91067ef4cec8983408775a87eca1eed5b7dcbf0ddf30e66f51780457413496c7f07",
+ "0xa41cde4a786b55387458a1db95171aca4fd146507b81c4da1e6d6e495527c3ec83fc42fad1dfe3d92744084a664fd431",
+ "0x8c352852c906fae99413a84ad11701f93f292fbf7bd14738814f4c4ceab32db02feb5eb70bc73898b0bc724a39d5d017",
+ "0xa5993046e8f23b71ba87b7caa7ace2d9023fb48ce4c51838813174880d918e9b4d2b0dc21a2b9c6f612338c31a289df8",
+ "0x83576d3324bf2d8afbfb6eaecdc5d767c8e22e7d25160414924f0645491df60541948a05e1f4202e612368e78675de8a",
+ "0xb43749b8df4b15bc9a3697e0f1c518e6b04114171739ef1a0c9c65185d8ec18e40e6954d125cbc14ebc652cf41ad3109",
+ "0xb4eebd5d80a7327a040cafb9ccdb12b2dfe1aa86e6bc6d3ac8a57fadfb95a5b1a7332c66318ff72ba459f525668af056",
+ "0x9198be7f1d413c5029b0e1c617bcbc082d21abe2c60ec8ce9b54ca1a85d3dba637b72fda39dae0c0ae40d047eab9f55a",
+ "0x8d96a0232832e24d45092653e781e7a9c9520766c3989e67bbe86b3a820c4bf621ea911e7cd5270a4bfea78b618411f6",
+ "0x8d7160d0ea98161a2d14d46ef01dff72d566c330cd4fabd27654d300e1bc7644c68dc8eabf2a20a59bfe7ba276545f9b",
+ "0xabb60fce29dec7ba37e3056e412e0ec3e05538a1fc0e2c68877378c867605966108bc5742585ab6a405ce0c962b285b6",
+ "0x8fabffa3ed792f05e414f5839386f6449fd9f7b41a47595c5d71074bd1bb3784cc7a1a7e1ad6b041b455035957e5b2dc",
+ "0x90ff017b4804c2d0533b72461436b10603ab13a55f86fd4ec11b06a70ef8166f958c110519ca1b4cc7beba440729fe2d",
+ "0xb340cfd120f6a4623e3a74cf8c32bfd7cd61a280b59dfd17b15ca8fae4d82f64a6f15fbde4c02f424debc72b7db5fe67",
+ "0x871311c9c7220c932e738d59f0ecc67a34356d1429fe570ca503d340c9996cb5ee2cd188fad0e3bd16e4c468ec1dbebd",
+ "0xa772470262186e7b94239ba921b29f2412c148d6f97c4412e96d21e55f3be73f992f1ad53c71008f0558ec3f84e2b5a7",
+ "0xb2a897dcb7ffd6257f3f2947ec966f2077d57d5191a88840b1d4f67effebe8c436641be85524d0a21be734c63ab5965d",
+ "0xa044f6eacc48a4a061fa149500d96b48cbf14853469aa4d045faf3dca973be1bd4b4ce01646d83e2f24f7c486d03205d",
+ "0x981af5dc2daa73f7fa9eae35a93d81eb6edba4a7f673b55d41f6ecd87a37685d31bb40ef4f1c469b3d72f2f18b925a17",
+ "0x912d2597a07864de9020ac77083eff2f15ceb07600f15755aba61251e8ce3c905a758453b417f04d9c38db040954eb65",
+ "0x9642b7f6f09394ba5e0805734ef6702c3eddf9eea187ba98c676d5bbaec0e360e3e51dc58433aaa1e2da6060c8659cb7",
+ "0x8ab3836e0a8ac492d5e707d056310c4c8e0489ca85eb771bff35ba1d658360084e836a6f51bb990f9e3d2d9aeb18fbb5",
+ "0x879e058e72b73bb1f4642c21ffdb90544b846868139c6511f299aafe59c2d0f0b944dffc7990491b7c4edcd6a9889250",
+ "0xb9e60b737023f61479a4a8fd253ed0d2a944ea6ba0439bbc0a0d3abf09b0ad1f18d75555e4a50405470ae4990626f390",
+ "0xb9c2535d362796dcd673640a9fa2ebdaec274e6f8b850b023153b0a7a30fffc87f96e0b72696f647ebe7ab63099a6963",
+ "0x94aeff145386a087b0e91e68a84a5ede01f978f9dd9fe7bebca78941938469495dc30a96bba9508c0d017873aeea9610",
+ "0x98b179f8a3d9f0d0a983c30682dd425a2ddc7803be59bd626c623c8951a5179117d1d2a68254c95c9952989877d0ee55",
+ "0x889ecf5f0ee56938273f74eb3e9ecfb5617f04fb58e83fe4c0e4aef51615cf345bc56f3f61b17f6eed3249d4afd54451",
+ "0xa0f2b2c39bcea4b50883e2587d16559e246248a66ecb4a4b7d9ab3b51fb39fe98d83765e087eee37a0f86b0ba4144c02",
+ "0xb2a61e247ed595e8a3830f7973b07079cbda510f28ad8c78c220b26cb6acde4fbb5ee90c14a665f329168ee951b08cf0",
+ "0x95bd0fcfb42f0d6d8a8e73d7458498a85bcddd2fb132fd7989265648d82ac2707d6d203fac045504977af4f0a2aca4b7",
+ "0x843e5a537c298666e6cf50fcc044f13506499ef83c802e719ff2c90e85003c132024e04711be7234c04d4b0125512d5d",
+ "0xa46d1797c5959dcd3a5cfc857488f4d96f74277c3d13b98b133620192f79944abcb3a361d939a100187f1b0856eae875",
+ "0xa1c7786736d6707a48515c38660615fcec67eb8a2598f46657855215f804fd72ab122d17f94fcffad8893f3be658dca7",
+ "0xb23dc9e610abc7d8bd21d147e22509a0fa49db5be6ea7057b51aae38e31654b3aa044df05b94b718153361371ba2f622",
+ "0xb00cc8f257d659c22d30e6d641f79166b1e752ea8606f558e4cad6fc01532e8319ea4ee12265ba4140ac45aa4613c004",
+ "0xac7019af65221b0cc736287b32d7f1a3561405715ba9a6a122342e04e51637ba911c41573de53e4781f2230fdcb2475f",
+ "0x81a630bc41b3da8b3eb4bf56cba10cd9f93153c3667f009dc332287baeb707d505fb537e6233c8e53d299ec0f013290c",
+ "0xa6b7aea5c545bb76df0f230548539db92bc26642572cb7dd3d5a30edca2b4c386f44fc8466f056b42de2a452b81aff5b",
+ "0x8271624ff736b7b238e43943c81de80a1612207d32036d820c11fc830c737972ccc9c60d3c2359922b06652311e3c994",
+ "0x8a684106458cb6f4db478170b9ad595d4b54c18bf63b9058f095a2fa1b928c15101472c70c648873d5887880059ed402",
+ "0xa5cc3c35228122f410184e4326cf61a37637206e589fcd245cb5d0cec91031f8f7586b80503070840fdfd8ce75d3c88b",
+ "0x9443fc631aed8866a7ed220890911057a1f56b0afe0ba15f0a0e295ab97f604b134b1ed9a4245e46ee5f9a93aa74f731",
+ "0x984b6f7d79835dffde9558c6bb912d992ca1180a2361757bdba4a7b69dc74b056e303adc69fe67414495dd9c2dd91e64",
+ "0xb15a5c8cba5de080224c274d31c68ed72d2a7126d347796569aef0c4e97ed084afe3da4d4b590b9dda1a07f0c2ff3dfb",
+ "0x991708fe9650a1f9a4e43938b91d45dc68c230e05ee999c95dbff3bf79b1c1b2bb0e7977de454237c355a73b8438b1d9",
+ "0xb4f7edc7468b176a4a7c0273700c444fa95c726af6697028bed4f77eee887e3400f9c42ee15b782c0ca861c4c3b8c98a",
+ "0x8c60dcc16c51087eb477c13e837031d6c6a3dc2b8bf8cb43c23f48006bc7173151807e866ead2234b460c2de93b31956",
+ "0x83ad63e9c910d1fc44bc114accfb0d4d333b7ebe032f73f62d25d3e172c029d5e34a1c9d547273bf6c0fead5c8801007",
+ "0x85de73213cc236f00777560756bdbf2b16841ba4b55902cf2cad9742ecaf5d28209b012ceb41f337456dfeca93010cd7",
+ "0xa7561f8827ccd75b6686ba5398bb8fc3083351c55a589b18984e186820af7e275af04bcd4c28e1dc11be1e8617a0610b",
+ "0x88c0a4febd4068850557f497ea888035c7fc9f404f6cc7794e7cc8722f048ad2f249e7dc62743e7a339eb7473ad3b0cd",
+ "0x932b22b1d3e6d5a6409c34980d176feb85ada1bf94332ef5c9fc4d42b907dabea608ceef9b5595ef3feee195151f18d8",
+ "0xa2867bb3f5ab88fbdae3a16c9143ab8a8f4f476a2643c505bb9f37e5b1fd34d216cab2204c9a017a5a67b7ad2dda10e8",
+ "0xb573d5f38e4e9e8a3a6fd82f0880dc049efa492a946d00283019bf1d5e5516464cf87039e80aef667cb86fdea5075904",
+ "0xb948f1b5ab755f3f5f36af27d94f503b070696d793b1240c1bdfd2e8e56890d69e6904688b5f8ff5a4bdf5a6abfe195f",
+ "0x917eae95ebc4109a2e99ddd8fec7881d2f7aaa0e25fda44dec7ce37458c2ee832f1829db7d2dcfa4ca0f06381c7fe91d",
+ "0x95751d17ed00a3030bce909333799bb7f4ab641acf585807f355b51d6976dceee410798026a1a004ef4dcdff7ec0f5b8",
+ "0xb9b7bd266f449a79bbfe075e429613e76c5a42ac61f01c8f0bbbd34669650682efe01ff9dbbc400a1e995616af6aa278",
+ "0xac1722d097ce9cd7617161f8ec8c23d68f1fb1c9ca533e2a8b4f78516c2fd8fb38f23f834e2b9a03bb06a9d655693ca9",
+ "0xa7ad9e96ffd98db2ecdb6340c5d592614f3c159abfd832fe27ee9293519d213a578e6246aae51672ee353e3296858873",
+ "0x989b8814d5de7937c4acafd000eec2b4cd58ba395d7b25f98cafd021e8efa37029b29ad8303a1f6867923f5852a220eb",
+ "0xa5bfe6282c771bc9e453e964042d44eff4098decacb89aecd3be662ea5b74506e1357ab26f3527110ba377711f3c9f41",
+ "0x8900a7470b656639721d2abbb7b06af0ac4222ab85a1976386e2a62eb4b88bfb5b72cf7921ddb3cf3a395d7eeb192a2e",
+ "0x95a71b55cd1f35a438cf5e75f8ff11c5ec6a2ebf2e4dba172f50bfad7d6d5dca5de1b1afc541662c81c858f7604c1163",
+ "0x82b5d62fea8db8d85c5bc3a76d68dedd25794cf14d4a7bc368938ffca9e09f7e598fdad2a5aac614e0e52f8112ae62b9",
+ "0x997173f07c729202afcde3028fa7f52cefc90fda2d0c8ac2b58154a5073140683e54c49ed1f254481070d119ce0ce02a",
+ "0xaeffb91ccc7a72bbd6ffe0f9b99c9e66e67d59cec2e02440465e9636a613ab3017278cfa72ea8bc4aba9a8dc728cb367",
+ "0x952743b06e8645894aeb6440fc7a5f62dd3acf96dab70a51e20176762c9751ea5f2ba0b9497ccf0114dc4892dc606031",
+ "0x874c63baeddc56fbbca2ff6031f8634b745f6e34ea6791d7c439201aee8f08ef5ee75f7778700a647f3b21068513fce6",
+ "0x85128fec9c750c1071edfb15586435cc2f317e3e9a175bb8a9697bcda1eb9375478cf25d01e7fed113483b28f625122d",
+ "0x85522c9576fd9763e32af8495ae3928ed7116fb70d4378448926bc9790e8a8d08f98cf47648d7da1b6e40d6a210c7924",
+ "0x97d0f37a13cfb723b848099ca1c14d83e9aaf2f7aeb71829180e664b7968632a08f6a85f557d74b55afe6242f2a36e7c",
+ "0xabaa472d6ad61a5fccd1a57c01aa1bc081253f95abbcba7f73923f1f11c4e79b904263890eeb66926de3e2652f5d1c70",
+ "0xb3c04945ba727a141e5e8aec2bf9aa3772b64d8fd0e2a2b07f3a91106a95cbcb249adcd074cbe498caf76fffac20d4ef",
+ "0x82c46781a3d730d9931bcabd7434a9171372dde57171b6180e5516d4e68db8b23495c8ac3ab96994c17ddb1cf249b9fb",
+ "0xa202d8b65613c42d01738ccd68ed8c2dbc021631f602d53f751966e04182743ebc8e0747d600b8a8676b1da9ae7f11ab",
+ "0xae73e7256e9459db04667a899e0d3ea5255211fb486d084e6550b6dd64ca44af6c6b2d59d7aa152de9f96ce9b58d940d",
+ "0xb67d87b176a9722945ec7593777ee461809861c6cfd1b945dde9ee4ff009ca4f19cf88f4bbb5c80c9cbab2fe25b23ac8",
+ "0x8f0b7a317a076758b0dac79959ee4a06c08b07d0f10538a4b53d3da2eda16e2af26922feb32c090330dc4d969cf69bd3",
+ "0x90b36bf56adbd8c4b6cb32febc3a8d5f714370c2ac3305c10fa6d168dffb2a026804517215f9a2d4ec8310cdb6bb459b",
+ "0xaa80c19b0682ead69934bf18cf476291a0beddd8ef4ed75975d0a472e2ab5c70f119722a8574ae4973aceb733d312e57",
+ "0xa3fc9abb12574e5c28dcb51750b4339b794b8e558675eef7d26126edf1de920c35e992333bcbffcbf6a5f5c0d383ce62",
+ "0xa1573ff23ab972acdcd08818853b111fc757fdd35aa070186d3e11e56b172fb49d840bf297ac0dd222e072fc09f26a81",
+ "0x98306f2be4caa92c2b4392212d0cbf430b409b19ff7d5b899986613bd0e762c909fc01999aa94be3bd529d67f0113d7f",
+ "0x8c1fc42482a0819074241746d17dc89c0304a2acdae8ed91b5009e9e3e70ff725ba063b4a3e68fdce05b74f5180c545e",
+ "0xa6c6113ebf72d8cf3163b2b8d7f3fa24303b13f55752522c660a98cd834d85d8c79214d900fa649499365e2e7641f77a",
+ "0xab95eea424f8a2cfd9fb1c78bb724e5b1d71a0d0d1e4217c5d0f98b0d8bbd3f8400a2002abc0a0e4576d1f93f46fefad",
+ "0x823c5a4fd8cf4a75fdc71d5f2dd511b6c0f189b82affeacd2b7cfcad8ad1a5551227dcc9bfdb2e34b2097eaa00efbb51",
+ "0xb97314dfff36d80c46b53d87a61b0e124dc94018a0bb680c32765b9a2d457f833a7c42bbc90b3b1520c33a182580398d",
+ "0xb17566ee3dcc6bb3b004afe4c0136dfe7dd27df9045ae896dca49fb36987501ae069eb745af81ba3fc19ff037e7b1406",
+ "0xb0bdc0f55cfd98d331e3a0c4fbb776a131936c3c47c6bffdc3aaf7d8c9fa6803fbc122c2fefbb532e634228687d52174",
+ "0xaa5d9e60cc9f0598559c28bb9bdd52aa46605ab4ffe3d192ba982398e72cec9a2a44c0d0d938ce69935693cabc0887ea",
+ "0x802b6459d2354fa1d56c592ac1346c428dadea6b6c0a87bf7d309bab55c94e1cf31dd98a7a86bd92a840dd51f218b91b",
+ "0xa526914efdc190381bf1a73dd33f392ecf01350b9d3f4ae96b1b1c3d1d064721c7d6eec5788162c933245a3943f5ee51",
+ "0xb3b8fcf637d8d6628620a1a99dbe619eabb3e5c7ce930d6efd2197e261bf394b74d4e5c26b96c4b8009c7e523ccfd082",
+ "0x8f7510c732502a93e095aba744535f3928f893f188adc5b16008385fb9e80f695d0435bfc5b91cdad4537e87e9d2551c",
+ "0x97b90beaa56aa936c3ca45698f79273a68dd3ccd0076eab48d2a4db01782665e63f33c25751c1f2e070f4d1a8525bf96",
+ "0xb9fb798324b1d1283fdc3e48288e3861a5449b2ab5e884b34ebb8f740225324af86e4711da6b5cc8361c1db15466602f",
+ "0xb6d52b53cea98f1d1d4c9a759c25bf9d8a50b604b144e4912acbdbdc32aab8b9dbb10d64a29aa33a4f502121a6fb481c",
+ "0x9174ffff0f2930fc228f0e539f5cfd82c9368d26b074467f39c07a774367ff6cccb5039ac63f107677d77706cd431680",
+ "0xa33b6250d4ac9e66ec51c063d1a6a31f253eb29bbaed12a0d67e2eccfffb0f3a52750fbf52a1c2aaba8c7692346426e7",
+ "0xa97025fd5cbcebe8ef865afc39cd3ea707b89d4e765ec817fd021d6438e02fa51e3544b1fd45470c58007a08efac6edd",
+ "0xb32a78480edd9ff6ba2f1eec4088db5d6ceb2d62d7e59e904ecaef7bb4a2e983a4588e51692b3be76e6ffbc0b5f911a5",
+ "0xb5ab590ef0bb77191f00495b33d11c53c65a819f7d0c1f9dc4a2caa147a69c77a4fff7366a602d743ee1f395ce934c1e",
+ "0xb3fb0842f9441fb1d0ee0293b6efbc70a8f58d12d6f769b12872db726b19e16f0f65efbc891cf27a28a248b0ef9c7e75",
+ "0x9372ad12856fefb928ccb0d34e198df99e2f8973b07e9d417a3134d5f69e12e79ff572c4e03ccd65415d70639bc7c73e",
+ "0xaa8d6e83d09ce216bfe2009a6b07d0110d98cf305364d5529c170a23e693aabb768b2016befb5ada8dabdd92b4d012bb",
+ "0xa954a75791eeb0ce41c85200c3763a508ed8214b5945a42c79bfdcfb1ec4f86ad1dd7b2862474a368d4ac31911a2b718",
+ "0x8e2081cfd1d062fe3ab4dab01f68062bac802795545fede9a188f6c9f802cb5f884e60dbe866710baadbf55dc77c11a4",
+ "0xa2f06003b9713e7dd5929501ed485436b49d43de80ea5b15170763fd6346badf8da6de8261828913ee0dacd8ff23c0e1",
+ "0x98eecc34b838e6ffd1931ca65eec27bcdb2fdcb61f33e7e5673a93028c5865e0d1bf6d3bec040c5e96f9bd08089a53a4",
+ "0x88cc16019741b341060b95498747db4377100d2a5bf0a5f516f7dec71b62bcb6e779de2c269c946d39040e03b3ae12b7",
+ "0xad1135ccbc3019d5b2faf59a688eef2500697642be8cfbdf211a1ab59abcc1f24483e50d653b55ff1834675ac7b4978f",
+ "0xa946f05ed9972f71dfde0020bbb086020fa35b482cce8a4cc36dd94355b2d10497d7f2580541bb3e81b71ac8bba3c49f",
+ "0xa83aeed488f9a19d8cfd743aa9aa1982ab3723560b1cd337fc2f91ad82f07afa412b3993afb845f68d47e91ba4869840",
+ "0x95eebe006bfc316810cb71da919e5d62c2cebb4ac99d8e8ef67be420302320465f8b69873470982de13a7c2e23516be9",
+ "0xa55f8961295a11e91d1e5deadc0c06c15dacbfc67f04ccba1d069cba89d72aa3b3d64045579c3ea8991b150ac29366ae",
+ "0xb321991d12f6ac07a5de3c492841d1a27b0d3446082fbce93e7e1f9e8d8fe3b45d41253556261c21b70f5e189e1a7a6f",
+ "0xa0b0822f15f652ce7962a4f130104b97bf9529797c13d6bd8e24701c213cc37f18157bd07f3d0f3eae6b7cd1cb40401f",
+ "0x96e2fa4da378aa782cc2d5e6e465fc9e49b5c805ed01d560e9b98abb5c0de8b74a2e7bec3aa5e2887d25cccb12c66f0c",
+ "0x97e4ab610d414f9210ed6f35300285eb3ccff5b0b6a95ed33425100d7725e159708ea78704497624ca0a2dcabce3a2f9",
+ "0x960a375b17bdb325761e01e88a3ea57026b2393e1d887b34b8fa5d2532928079ce88dc9fd06a728b26d2bb41b12b9032",
+ "0x8328a1647398e832aadc05bd717487a2b6fcdaa0d4850d2c4da230c6a2ed44c3e78ec4837b6094f3813f1ee99414713f",
+ "0xaa283834ebd18e6c99229ce4b401eda83f01d904f250fedd4e24f1006f8fa0712a6a89a7296a9bf2ce8de30e28d1408e",
+ "0xb29e097f2caadae3e0f0ae3473c072b0cd0206cf6d2e9b22c1a5ad3e07d433e32bd09ed1f4e4276a2da4268633357b7f",
+ "0x9539c5cbba14538b2fe077ecf67694ef240da5249950baaabea0340718b882a966f66d97f08556b08a4320ceb2cc2629",
+ "0xb4529f25e9b42ae8cf8338d2eface6ba5cd4b4d8da73af502d081388135c654c0b3afb3aa779ffc80b8c4c8f4425dd2b",
+ "0x95be0739c4330619fbe7ee2249c133c91d6c07eab846c18c5d6c85fc21ac5528c5d56dcb0145af68ed0c6a79f68f2ccd",
+ "0xac0c83ea802227bfc23814a24655c9ff13f729619bcffdb487ccbbf029b8eaee709f8bddb98232ef33cd70e30e45ca47",
+ "0xb503becb90acc93b1901e939059f93e671900ca52c6f64ae701d11ac891d3a050b505d89324ce267bc43ab8275da6ffe",
+ "0x98e3811b55b1bacb70aa409100abb1b870f67e6d059475d9f278c751b6e1e2e2d6f2e586c81a9fb6597fda06e7923274",
+ "0xb0b0f61a44053fa6c715dbb0731e35d48dba257d134f851ee1b81fd49a5c51a90ebf5459ec6e489fce25da4f184fbdb1",
+ "0xb1d2117fe811720bb997c7c93fe9e4260dc50fca8881b245b5e34f724aaf37ed970cdad4e8fcb68e05ac8cf55a274a53",
+ "0xa10f502051968f14b02895393271776dee7a06db9de14effa0b3471825ba94c3f805302bdddac4d397d08456f620999d",
+ "0xa3dbad2ef060ae0bb7b02eaa4a13594f3f900450faa1854fc09620b01ac94ab896321dfb1157cf2374c27e5718e8026a",
+ "0xb550fdec503195ecb9e079dcdf0cad559d64d3c30818ef369b4907e813e689da316a74ad2422e391b4a8c2a2bef25fc0",
+ "0xa25ba865e2ac8f28186cea497294c8649a201732ecb4620c4e77b8e887403119910423df061117e5f03fc5ba39042db1",
+ "0xb3f88174e03fdb443dd6addd01303cf88a4369352520187c739fc5ae6b22fa99629c63c985b4383219dab6acc5f6f532",
+ "0x97a7503248e31e81b10eb621ba8f5210c537ad11b539c96dfb7cf72b846c7fe81bd7532c5136095652a9618000b7f8d3",
+ "0xa8bcdc1ce5aa8bfa683a2fc65c1e79de8ff5446695dcb8620f7350c26d2972a23da22889f9e2b1cacb3f688c6a2953dc",
+ "0x8458c111df2a37f5dd91a9bee6c6f4b79f4f161c93fe78075b24a35f9817da8dde71763218d627917a9f1f0c4709c1ed",
+ "0xac5f061a0541152b876cbc10640f26f1cc923c9d4ae1b6621e4bb3bf2cec59bbf87363a4eb72fb0e5b6d4e1c269b52d5",
+ "0xa9a25ca87006e8a9203cbb78a93f50a36694aa4aad468b8d80d3feff9194455ca559fcc63838128a0ab75ad78c07c13a",
+ "0xa450b85f5dfffa8b34dfd8bc985f921318efacf8857cf7948f93884ba09fb831482ee90a44224b1a41e859e19b74962f",
+ "0x8ed91e7f92f5c6d7a71708b6132f157ac226ecaf8662af7d7468a4fa25627302efe31e4620ad28719318923e3a59bf82",
+ "0xab524165fd4c71b1fd395467a14272bd2b568592deafa039d8492e9ef36c6d3f96927c95c72d410a768dc0b6d1fbbc9b",
+ "0xb662144505aa8432c75ffb8d10318526b6d5777ac7af9ebfad87d9b0866c364f7905a6352743bd8fd79ffd9d5dd4f3e6",
+ "0xa48f1677550a5cd40663bb3ba8f84caaf8454f332d0ceb1d94dbea52d0412fe69c94997f7749929712fd3995298572f7",
+ "0x8391cd6e2f6b0c242de1117a612be99776c3dc95cb800b187685ea5bf7e2722275eddb79fd7dfc8be8e389c4524cdf70",
+ "0x875d3acb9af47833b72900bc0a2448999d638f153c5e97e8a14ec02d0c76f6264353a7e275e1f1a5855daced523d243b",
+ "0x91f1823657d30b59b2f627880a9a9cb530f5aca28a9fd217fe6f2f5133690dfe7ad5a897872e400512db2e788b3f7628",
+ "0xad3564332aa56cea84123fc7ca79ea70bb4fef2009fa131cb44e4b15e8613bd11ca1d83b9d9bf456e4b7fee9f2e8b017",
+ "0x8c530b84001936d5ab366c84c0b105241a26d1fb163669f17c8f2e94776895c2870edf3e1bc8ccd04d5e65531471f695",
+ "0x932d01fa174fdb0c366f1230cffde2571cc47485f37f23ba5a1825532190cc3b722aeb1f15aed62cf83ccae9403ba713",
+ "0x88b28c20585aca50d10752e84b901b5c2d58efef5131479fbbe53de7bce2029e1423a494c0298e1497669bd55be97a5d",
+ "0xb914148ca717721144ebb3d3bf3fcea2cd44c30c5f7051b89d8001502f3856fef30ec167174d5b76265b55d70f8716b5",
+ "0x81d0173821c6ddd2a068d70766d9103d1ee961c475156e0cbd67d54e668a796310474ef698c7ab55abe6f2cf76c14679",
+ "0x8f28e8d78e2fe7fa66340c53718e0db4b84823c8cfb159c76eac032a62fb53da0a5d7e24ca656cf9d2a890cb2a216542",
+ "0x8a26360335c73d1ab51cec3166c3cf23b9ea51e44a0ad631b0b0329ef55aaae555420348a544e18d5760969281759b61",
+ "0x94f326a32ed287545b0515be9e08149eb0a565025074796d72387cc3a237e87979776410d78339e23ef3172ca43b2544",
+ "0xa785d2961a2fa5e70bffa137858a92c48fe749fee91b02599a252b0cd50d311991a08efd7fa5e96b78d07e6e66ffe746",
+ "0x94af9030b5ac792dd1ce517eaadcec1482206848bea4e09e55cc7f40fd64d4c2b3e9197027c5636b70d6122c51d2235d",
+ "0x9722869f7d1a3992850fe7be405ec93aa17dc4d35e9e257d2e469f46d2c5a59dbd504056c85ab83d541ad8c13e8bcd54",
+ "0xb13c4088b61a06e2c03ac9813a75ff1f68ffdfee9df6a8f65095179a475e29cc49119cad2ce05862c3b1ac217f3aace9",
+ "0x8c64d51774753623666b10ca1b0fe63ae42f82ed6aa26b81dc1d48c86937c5772eb1402624c52a154b86031854e1fb9f",
+ "0xb47e4df18002b7dac3fee945bf9c0503159e1b8aafcce2138818e140753011b6d09ef1b20894e08ba3006b093559061b",
+ "0x93cb5970076522c5a0483693f6a35ffd4ea2aa7aaf3730c4eccd6af6d1bebfc1122fc4c67d53898ae13eb6db647be7e2",
+ "0xa68873ef80986795ea5ed1a597d1cd99ed978ec25e0abb57fdcc96e89ef0f50aeb779ff46e3dce21dc83ada3157a8498",
+ "0x8cab67f50949cc8eee6710e27358aea373aae3c92849f8f0b5531c080a6300cdf2c2094fe6fecfef6148de0d28446919",
+ "0x993e932bcb616dbaa7ad18a4439e0565211d31071ef1b85a0627db74a05d978c60d507695eaeea5c7bd9868a21d06923",
+ "0xacdadff26e3132d9478a818ef770e9fa0d2b56c6f5f48bd3bd674436ccce9bdfc34db884a73a30c04c5f5e9764cb2218",
+ "0xa0d3e64c9c71f84c0eef9d7a9cb4fa184224b969db5514d678e93e00f98b41595588ca802643ea225512a4a272f5f534",
+ "0x91c9140c9e1ba6e330cb08f6b2ce4809cd0d5a0f0516f70032bf30e912b0ed684d07b413b326ab531ee7e5b4668c799b",
+ "0x87bc2ee7a0c21ba8334cd098e35cb703f9af57f35e091b8151b9b63c3a5b0f89bd7701dbd44f644ea475901fa6d9ef08",
+ "0x9325ccbf64bf5d71b303e31ee85d486298f9802c5e55b2c3d75427097bf8f60fa2ab4fcaffa9b60bf922c3e24fbd4b19",
+ "0x95d0506e898318f3dc8d28d16dfd9f0038b54798838b3c9be2a2ae3c2bf204eb496166353fc042220b0bd4f6673b9285",
+ "0x811de529416331fe9c416726d45df9434c29dcd7e949045eb15740f47e97dde8f31489242200e19922cac2a8b7c6fd1f",
+ "0xade632d04a4c8bbab6ca7df370b2213cb9225023e7973f0e29f4f5e52e8aeaabc65171306bbdd12a67b195dfbb96d48f",
+ "0x88b7f029e079b6ae956042c0ea75d53088c5d0efd750dd018adaeacf46be21bf990897c58578c491f41afd3978d08073",
+ "0x91f477802de507ffd2be3f4319903119225b277ad24f74eb50f28b66c14d32fae53c7edb8c7590704741af7f7f3e3654",
+ "0x809838b32bb4f4d0237e98108320d4b079ee16ed80c567e7548bd37e4d7915b1192880f4812ac0e00476d246aec1dbc8",
+ "0x84183b5fc4a7997a8ae5afedb4d21dce69c480d5966b5cbdafd6dd10d29a9a6377f3b90ce44da0eb8b176ac3af0253bb",
+ "0x8508abbf6d3739a16b9165caf0f95afb3b3ac1b8c38d6d374cf0c91296e2c1809a99772492b539cda184510bce8a0271",
+ "0x8722054e59bab2062e6419a6e45fc803af77fde912ef2cd23055ad0484963de65a816a2debe1693d93c18218d2b8e81a",
+ "0x8e895f80e485a7c4f56827bf53d34b956281cdc74856c21eb3b51f6288c01cc3d08565a11cc6f3e2604775885490e8c5",
+ "0xafc92714771b7aa6e60f3aee12efd9c2595e9659797452f0c1e99519f67c8bc3ac567119c1ddfe82a3e961ee9defea9a",
+ "0x818ff0fd9cefd32db87b259e5fa32967201016fc02ef44116cdca3c63ce5e637756f60477a408709928444a8ad69c471",
+ "0x8251e29af4c61ae806fc5d032347fb332a94d472038149225298389495139ce5678fae739d02dfe53a231598a992e728",
+ "0xa0ea39574b26643f6f1f48f99f276a8a64b5481989cfb2936f9432a3f8ef5075abfe5c067dc5512143ce8bf933984097",
+ "0xaf67a73911b372bf04e57e21f289fc6c3dfac366c6a01409b6e76fea4769bdb07a6940e52e8d7d3078f235c6d2f632c6",
+ "0xb5291484ef336024dd2b9b4cf4d3a6b751133a40656d0a0825bcc6d41c21b1c79cb50b0e8f4693f90c29c8f4358641f9",
+ "0x8bc0d9754d70f2cb9c63f991902165a87c6535a763d5eece43143b5064ae0bcdce7c7a8f398f2c1c29167b2d5a3e6867",
+ "0x8d7faff53579ec8f6c92f661c399614cc35276971752ce0623270f88be937c414eddcb0997e14724a783905a026c8883",
+ "0x9310b5f6e675fdf60796f814dbaa5a6e7e9029a61c395761e330d9348a7efab992e4e115c8be3a43d08e90d21290c892",
+ "0xb5eb4f3eb646038ad2a020f0a42202532d4932e766da82b2c1002bf9c9c2e5336b54c8c0ffcc0e02d19dde2e6a35b6cc",
+ "0x91dabfd30a66710f1f37a891136c9be1e23af4abf8cb751f512a40c022a35f8e0a4fb05b17ec36d4208de02d56f0d53a",
+ "0xb3ded14e82d62ac7a5a036122a62f00ff8308498f3feae57d861babaff5a6628d43f0a0c5fc903f10936bcf4e2758ceb",
+ "0xa88e8348fed2b26acca6784d19ef27c75963450d99651d11a950ea81d4b93acd2c43e0ecce100eaf7e78508263d5baf3",
+ "0xb1f5bbf7c4756877b87bb42163ac570e08c6667c4528bf68b5976680e19beeff7c5effd17009b0718797077e2955457a",
+ "0xad2e7b516243f915d4d1415326e98b1a7390ae88897d0b03b66c2d9bd8c3fba283d7e8fe44ed3333296a736454cef6d8",
+ "0x8f82eae096d5b11f995de6724a9af895f5e1c58d593845ad16ce8fcae8507e0d8e2b2348a0f50a1f66a17fd6fac51a5c",
+ "0x890e4404d0657c6c1ee14e1aac132ecf7a568bb3e04137b85ac0f84f1d333bd94993e8750f88eee033a33fb00f85dcc7",
+ "0x82ac7d3385e035115f1d39a99fc73e5919de44f5e6424579776d118d711c8120b8e5916372c6f27bed4cc64cac170b6c",
+ "0x85ee16d8901c272cfbbe966e724b7a891c1bd5e68efd5d863043ad8520fc409080af61fd726adc680b3f1186fe0ac8b8",
+ "0x86dc564c9b545567483b43a38f24c41c6551a49cabeebb58ce86404662a12dbfafd0778d30d26e1c93ce222e547e3898",
+ "0xa29f5b4522db26d88f5f95f18d459f8feefab02e380c2edb65aa0617a82a3c1a89474727a951cef5f15050bcf7b380fb",
+ "0xa1ce039c8f6cac53352899edb0e3a72c76da143564ad1a44858bd7ee88552e2fe6858d1593bbd74aeee5a6f8034b9b9d",
+ "0x97f10d77983f088286bd7ef3e7fdd8fa275a56bec19919adf33cf939a90c8f2967d2b1b6fc51195cb45ad561202a3ed7",
+ "0xa25e2772e8c911aaf8712bdac1dd40ee061c84d3d224c466cfaae8e5c99604053f940cde259bd1c3b8b69595781dbfec",
+ "0xb31bb95a0388595149409c48781174c340960d59032ab2b47689911d03c68f77a2273576fbe0c2bf4553e330656058c7",
+ "0xb8b2e9287ad803fb185a13f0d7456b397d4e3c8ad5078f57f49e8beb2e85f661356a3392dbd7bcf6a900baa5582b86a1",
+ "0xa3d0893923455eb6e96cc414341cac33d2dbc88fba821ac672708cce131761d85a0e08286663a32828244febfcae6451",
+ "0x82310cb42f647d99a136014a9f881eb0b9791efd2e01fc1841907ad3fc8a9654d3d1dab6689c3607214b4dc2aca01cee",
+ "0x874022d99c16f60c22de1b094532a0bc6d4de700ad01a31798fac1d5088b9a42ad02bef8a7339af7ed9c0d4f16b186ee",
+ "0x94981369e120265aed40910eebc37eded481e90f4596b8d57c3bec790ab7f929784bd33ddd05b7870aad6c02e869603b",
+ "0xa4f1f50e1e2a73f07095e0dd31cb45154f24968dae967e38962341c1241bcd473102fff1ff668b20c6547e9732d11701",
+ "0xae2328f3b0ad79fcda807e69a1b5278145225083f150f67511dafc97e079f860c3392675f1752ae7e864c056e592205b",
+ "0x875d8c971e593ca79552c43d55c8c73b17cd20c81ff2c2fed1eb19b1b91e4a3a83d32df150dbfd5db1092d0aebde1e1f",
+ "0xadd2e80aa46aae95da73a11f130f4bda339db028e24c9b11e5316e75ba5e63bc991d2a1da172c7c8e8fee038baae3433",
+ "0xb46dbe1cb3424002aa7de51e82f600852248e251465c440695d52538d3f36828ff46c90ed77fc1d11534fe3c487df8ef",
+ "0xa5e5045d28b4e83d0055863c30c056628c58d4657e6176fd0536f5933f723d60e851bb726d5bf3c546b8ce4ac4a57ef8",
+ "0x91fec01e86dd1537e498fff7536ea3ca012058b145f29d9ada49370cd7b7193ac380e116989515df1b94b74a55c45df3",
+ "0xa7428176d6918cd916a310bdc75483c72de660df48cac4e6e7478eef03205f1827ea55afc0df5d5fa7567d14bbea7fc9",
+ "0x851d89bef45d9761fe5fdb62972209335193610015e16a675149519f9911373bac0919add226ef118d9f3669cfdf4734",
+ "0xb74acf5c149d0042021cb2422ea022be4c4f72a77855f42393e71ffd12ebb3eec16bdf16f812159b67b79a9706e7156d",
+ "0x99f35dce64ec99aa595e7894b55ce7b5a435851b396e79036ffb249c28206087db4c85379df666c4d95857db02e21ff9",
+ "0xb6b9a384f70db9e298415b8ab394ee625dafff04be2886476e59df8d052ca832d11ac68a9b93fba7ab055b7bc36948a4",
+ "0x898ee4aefa923ffec9e79f2219c7389663eb11eb5b49014e04ed4a336399f6ea1691051d86991f4c46ca65bcd4fdf359",
+ "0xb0f948217b0d65df7599a0ba4654a5e43c84db477936276e6f11c8981efc6eaf14c90d3650107ed4c09af4cc8ec11137",
+ "0xaa6286e27ac54f73e63dbf6f41865dd94d24bc0cf732262fcaff67319d162bb43af909f6f8ee27b1971939cfbba08141",
+ "0x8bca7cdf730cf56c7b2c8a2c4879d61361a6e1dba5a3681a1a16c17a56e168ace0e99cf0d15826a1f5e67e6b8a8a049a",
+ "0xa746d876e8b1ce225fcafca603b099b36504846961526589af977a88c60d31ba2cc56e66a3dec8a77b3f3531bf7524c9",
+ "0xa11e2e1927e6704cdb8874c75e4f1842cef84d7d43d7a38e339e61dc8ba90e61bbb20dd3c12e0b11d2471d58eed245be",
+ "0xa36395e22bc1d1ba8b0459a235203177737397da5643ce54ded3459d0869ff6d8d89f50c73cb62394bf66a959cde9b90",
+ "0x8b49f12ba2fdf9aca7e5f81d45c07d47f9302a2655610e7634d1e4bd16048381a45ef2c95a8dd5b0715e4b7cf42273af",
+ "0x91cffa2a17e64eb7f76bccbe4e87280ee1dd244e04a3c9eac12e15d2d04845d876eb24fe2ec6d6d266cce9efb281077f",
+ "0xa6b8afabf65f2dee01788114e33a2f3ce25376fb47a50b74da7c3c25ff1fdc8aa9f41307534abbf48acb6f7466068f69",
+ "0x8d13db896ccfea403bd6441191995c1a65365cab7d0b97fbe9526da3f45a877bd1f4ef2edef160e8a56838cd1586330e",
+ "0x98c717de9e01bef8842c162a5e757fe8552d53269c84862f4d451e7c656ae6f2ae473767b04290b134773f63be6fdb9d",
+ "0x8c2036ace1920bd13cf018e82848c49eb511fad65fd0ff51f4e4b50cf3bfc294afb63cba682c16f52fb595a98fa84970",
+ "0xa3520fdff05dbad9e12551b0896922e375f9e5589368bcb2cc303bde252743b74460cb5caf99629325d3620f13adc796",
+ "0x8d4f83a5bfec05caf5910e0ce538ee9816ee18d0bd44c1d0da2a87715a23cd2733ad4d47552c6dc0eb397687d611dd19",
+ "0xa7b39a0a6a02823452d376533f39d35029867b3c9a6ad6bca181f18c54132d675613a700f9db2440fb1b4fa13c8bf18a",
+ "0x80bcb114b2544b80f404a200fc36860ed5e1ad31fe551acd4661d09730c452831751baa9b19d7d311600d267086a70bc",
+ "0x90dcce03c6f88fc2b08f2b42771eedde90cc5330fe0336e46c1a7d1b5a6c1641e5fcc4e7b3d5db00bd8afca9ec66ed81",
+ "0xaec15f40805065c98e2965b1ae12a6c9020cfdb094c2d0549acfc7ea2401a5fb48d3ea7d41133cf37c4e096e7ff53eb9",
+ "0x80e129b735dba49fa627a615d6c273119acec8e219b2f2c4373a332b5f98d66cbbdd688dfbe72a8f8bfefaccc02c50c1",
+ "0xa9b596da3bdfe23e6799ece5f7975bf7a1979a75f4f546deeaf8b34dfe3e0d623217cb4cf4ccd504cfa3625b88cd53f1",
+ "0xabcbbb70b16f6e517c0ab4363ab76b46e4ff58576b5f8340e5c0e8cc0e02621b6e23d742d73b015822a238b17cfd7665",
+ "0xa046937cc6ea6a2e1adae543353a9fe929c1ae4ad655be1cc051378482cf88b041e28b1e9a577e6ccff2d3570f55e200",
+ "0x831279437282f315e65a60184ef158f0a3dddc15a648dc552bdc88b3e6fe8288d3cfe9f0031846d81350f5e7874b4b33",
+ "0x993d7916fa213c6d66e7c4cafafc1eaec9a2a86981f91c31eb8a69c5df076c789cbf498a24c84e0ee77af95b42145026",
+ "0x823907a3b6719f8d49b3a4b7c181bd9bb29fcf842d7c70660c4f351852a1e197ca46cf5e879b47fa55f616fa2b87ce5e",
+ "0x8d228244e26132b234930ee14c75d88df0943cdb9c276a8faf167d259b7efc1beec2a87c112a6c608ad1600a239e9aae",
+ "0xab6e55766e5bfb0cf0764ed909a8473ab5047d3388b4f46faeba2d1425c4754c55c6daf6ad4751e634c618b53e549529",
+ "0xab0cab6860e55a84c5ad2948a7e0989e2b4b1fd637605634b118361497332df32d9549cb854b2327ca54f2bcb85eed8f",
+ "0xb086b349ae03ef34f4b25a57bcaa5d1b29bd94f9ebf87e22be475adfe475c51a1230c1ebe13506cb72c4186192451658",
+ "0x8a0b49d8a254ca6d91500f449cbbfbb69bb516c6948ac06808c65595e46773e346f97a5ce0ef7e5a5e0de278af22709c",
+ "0xac49de11edaaf04302c73c578cc0824bdd165c0d6321be1c421c1950e68e4f3589aa3995448c9699e93c6ebae8803e27",
+ "0x884f02d841cb5d8f4c60d1402469216b114ab4e93550b5bc1431756e365c4f870a9853449285384a6fa49e12ce6dc654",
+ "0xb75f3a28fa2cc8d36b49130cb7448a23d73a7311d0185ba803ad55c8219741d451c110f48b786e96c728bc525903a54f",
+ "0x80ae04dbd41f4a35e33f9de413b6ad518af0919e5a30cb0fa1b061b260420780bb674f828d37fd3b52b5a31673cbd803",
+ "0xb9a8011eb5fcea766907029bf743b45262db3e49d24f84503687e838651ed11cb64c66281e20a0ae9f6aa51acc552263",
+ "0x90bfdd75e2dc9cf013e22a5d55d2d2b8a754c96103a17524488e01206e67f8b6d52b1be8c4e3d5307d4fe06d0e51f54c",
+ "0xb4af353a19b06203a815ec43e79a88578cc678c46f5a954b85bc5c53b84059dddba731f3d463c23bfd5273885c7c56a4",
+ "0xaa125e96d4553b64f7140e5453ff5d2330318b69d74d37d283e84c26ad672fa00e3f71e530eb7e28be1e94afb9c4612e",
+ "0xa18e060aee3d49cde2389b10888696436bb7949a79ca7d728be6456a356ea5541b55492b2138da90108bd1ce0e6f5524",
+ "0x93e55f92bdbccc2de655d14b1526836ea2e52dba65eb3f87823dd458a4cb5079bf22ce6ef625cb6d6bfdd0995ab9a874",
+ "0x89f5a683526b90c1c3ceebbb8dc824b21cff851ce3531b164f6626e326d98b27d3e1d50982e507d84a99b1e04e86a915",
+ "0x83d1c38800361633a3f742b1cb2bfc528129496e80232611682ddbe403e92c2ac5373aea0bca93ecb5128b0b2b7a719e",
+ "0x8ecba560ac94905e19ce8d9c7af217bf0a145d8c8bd38e2db82f5e94cc3f2f26f55819176376b51f154b4aab22056059",
+ "0xa7e2a4a002b60291924850642e703232994acb4cfb90f07c94d1e0ecd2257bb583443283c20fc6017c37e6bfe85b7366",
+ "0x93ed7316fa50b528f1636fc6507683a672f4f4403e55e94663f91221cc198199595bd02eef43d609f451acc9d9b36a24",
+ "0xa1220a8ebc5c50ceed76a74bc3b7e0aa77f6884c71b64b67c4310ac29ce5526cb8992d6abc13ef6c8413ce62486a6795",
+ "0xb2f6eac5c869ad7f4a25161d3347093e2f70e66cd925032747e901189355022fab3038bca4d610d2f68feb7e719c110b",
+ "0xb703fa11a4d511ca01c7462979a94acb40b5d933759199af42670eb48f83df202fa0c943f6ab3b4e1cc54673ea3aab1e",
+ "0xb5422912afbfcb901f84791b04f1ddb3c3fbdc76d961ee2a00c5c320e06d3cc5b5909c3bb805df66c5f10c47a292b13d",
+ "0xad0934368da823302e1ac08e3ede74b05dfdbfffca203e97ffb0282c226814b65c142e6e15ec1e754518f221f01b30f7",
+ "0xa1dd302a02e37df15bf2f1147efe0e3c06933a5a767d2d030e1132f5c3ce6b98e216b6145eb39e1e2f74e76a83165b8d",
+ "0xa346aab07564432f802ae44738049a36f7ca4056df2d8f110dbe7fef4a3e047684dea609b2d03dc6bf917c9c2a47608f",
+ "0xb96c5f682a5f5d02123568e50f5d0d186e4b2c4c9b956ec7aabac1b3e4a766d78d19bd111adb5176b898e916e49be2aa",
+ "0x8a96676d56876fc85538db2e806e1cba20fd01aeb9fa3cb43ca6ca94a2c102639f65660db330e5d74a029bb72d6a0b39",
+ "0xab0048336bd5c3def1a4064eadd49e66480c1f2abb4df46e03afbd8a3342c2c9d74ee35d79f08f4768c1646681440984",
+ "0x888427bdf76caec90814c57ee1c3210a97d107dd88f7256f14f883ad0f392334b82be11e36dd8bfec2b37935177c7831",
+ "0xb622b282becf0094a1916fa658429a5292ba30fb48a4c8066ce1ddcefb71037948262a01c95bab6929ed3a76ba5db9fe",
+ "0xb5b9e005c1f456b6a368a3097634fb455723abe95433a186e8278dceb79d4ca2fbe21f8002e80027b3c531e5bf494629",
+ "0xa3c6707117a1e48697ed41062897f55d8119403eea6c2ee88f60180f6526f45172664bfee96bf61d6ec0b7fbae6aa058",
+ "0xb02a9567386a4fbbdb772d8a27057b0be210447348efe6feb935ceec81f361ed2c0c211e54787dc617cdffed6b4a6652",
+ "0xa9b8364e40ef15c3b5902e5534998997b8493064fa2bea99600def58279bb0f64574c09ba11e9f6f669a8354dd79dc85",
+ "0x9998a2e553a9aa9a206518fae2bc8b90329ee59ab23005b10972712389f2ec0ee746033c733092ffe43d73d33abbb8ef",
+ "0x843a4b34d9039bf79df96d79f2d15e8d755affb4d83d61872daf540b68c0a3888cf8fc00d5b8b247b38524bcb3b5a856",
+ "0x84f7128920c1b0bb40eee95701d30e6fc3a83b7bb3709f16d97e72acbb6057004ee7ac8e8f575936ca9dcb7866ab45f7",
+ "0x918d3e2222e10e05edb34728162a899ad5ada0aaa491aeb7c81572a9c0d506e31d5390e1803a91ff3bd8e2bb15d47f31",
+ "0x9442d18e2489613a7d47bb1cb803c8d6f3259d088cd079460976d87f7905ee07dea8f371b2537f6e1d792d36d7e42723",
+ "0xb491976970fe091995b2ed86d629126523ccf3e9daf8145302faca71b5a71a5da92e0e05b62d7139d3efac5c4e367584",
+ "0xaa628006235dc77c14cef4c04a308d66b07ac92d377df3de1a2e6ecfe3144f2219ad6d7795e671e1cb37a3641910b940",
+ "0x99d386adaea5d4981d7306feecac9a555b74ffdc218c907c5aa7ac04abaead0ec2a8237300d42a3fbc464673e417ceed",
+ "0x8f78e8b1556f9d739648ea3cab9606f8328b52877fe72f9305545a73b74d49884044ba9c1f1c6db7d9b7c7b7c661caba",
+ "0x8fb357ae49932d0babdf74fc7aa7464a65d3b6a2b3acf4f550b99601d3c0215900cfd67f2b6651ef94cfc323bac79fae",
+ "0x9906f2fa25c0290775aa001fb6198113d53804262454ae8b83ef371b5271bde189c0460a645829cb6c59f9ee3a55ce4d",
+ "0x8f4379b3ebb50e052325b27655ca6a82e6f00b87bf0d2b680d205dd2c7afdc9ff32a9047ae71a1cdf0d0ce6b9474d878",
+ "0xa85534e88c2bd43c043792eaa75e50914b21741a566635e0e107ae857aed0412035f7576cf04488ade16fd3f35fdbb87",
+ "0xb4ce93199966d3c23251ca7f28ec5af7efea1763d376b0385352ffb2e0a462ef95c69940950278cf0e3dafd638b7bd36",
+ "0xb10cb3d0317dd570aa73129f4acf63c256816f007607c19b423fb42f65133ce21f2f517e0afb41a5378cccf893ae14d0",
+ "0xa9b231c9f739f7f914e5d943ed9bff7eba9e2c333fbd7c34eb1648a362ee01a01af6e2f7c35c9fe962b11152cddf35de",
+ "0x99ff6a899e156732937fb81c0cced80ae13d2d44c40ba99ac183aa246103b31ec084594b1b7feb96da58f4be2dd5c0ed",
+ "0x8748d15d18b75ff2596f50d6a9c4ce82f61ecbcee123a6ceae0e43cab3012a29b6f83cf67b48c22f6f9d757c6caf76b2",
+ "0xb88ab05e4248b7fb634cf640a4e6a945d13e331237410f7217d3d17e3e384ddd48897e7a91e4516f1b9cbd30f35f238b",
+ "0x8d826deaeeb84a3b2d2c04c2300ca592501f992810582d6ae993e0d52f6283a839dba66c6c72278cff5871802b71173b",
+ "0xb36fed027c2f05a5ef625ca00b0364b930901e9e4420975b111858d0941f60e205546474bb25d6bfa6928d37305ae95f",
+ "0xaf2fcfc6b87967567e8b8a13a4ed914478185705724e56ce68fb2df6d1576a0cf34a61e880997a0d35dc2c3276ff7501",
+ "0xac351b919cd1fbf106feb8af2c67692bfcddc84762d18cea681cfa7470a5644839caace27efee5f38c87d3df306f4211",
+ "0x8d6665fb1d4d8d1fa23bd9b8a86e043b8555663519caac214d1e3e3effbc6bee7f2bcf21e645f77de0ced279d69a8a8b",
+ "0xa9fc1c2061756b2a1a169c1b149f212ff7f0d2488acd1c5a0197eba793cffa593fc6d1d1b40718aa75ca3ec77eff10e1",
+ "0xaff64f0fa009c7a6cf0b8d7a22ddb2c8170c3cb3eec082e60d5aadb00b0040443be8936d728d99581e33c22178c41c87",
+ "0x82e0b181adc5e3b1c87ff8598447260e839d53debfae941ebea38265575546c3a74a14b4325a030833a62ff6c52d9365",
+ "0xb7ad43cbb22f6f892c2a1548a41dc120ab1f4e1b8dea0cb6272dd9cb02054c542ecabc582f7e16de709d48f5166cae86",
+ "0x985e0c61094281532c4afb788ecb2dfcba998e974b5d4257a22040a161883908cdd068fe80f8eb49b8953cfd11acf43a",
+ "0xae46895c6d67ea6d469b6c9c07b9e5d295d9ae73b22e30da4ba2c973ba83a130d7eef39717ec9d0f36e81d56bf742671",
+ "0x8600177ea1f7e7ef90514b38b219a37dedfc39cb83297e4c7a5b479817ef56479d48cf6314820960c751183f6edf8b0e",
+ "0xb9208ec1c1d7a1e99b59c62d3e4e61dfb706b0e940d09d3abfc3454c19749083260614d89cfd7e822596c3cdbcc6bb95",
+ "0xa1e94042c796c2b48bc724352d2e9f3a22291d9a34705993357ddb6adabd76da6fc25dac200a8cb0b5bbd99ecddb7af6",
+ "0xb29c3adedd0bcad8a930625bc4dfdc3552a9afd5ca6dd9c0d758f978068c7982b50b711aa0eb5b97f2b84ee784637835",
+ "0xaf0632a238bb1f413c7ea8e9b4c3d68f2827bd2e38cd56024391fba6446ac5d19a780d0cfd4a78fe497d537b766a591a",
+ "0xaaf6e7f7d54f8ef5e2e45dd59774ecbeecf8683aa70483b2a75be6a6071b5981bbaf1627512a65d212817acdfab2e428",
+ "0x8c751496065da2e927cf492aa5ca9013b24f861d5e6c24b30bbf52ec5aaf1905f40f9a28175faef283dd4ed4f2182a09",
+ "0x8952377d8e80a85cf67d6b45499f3bad5fd452ea7bcd99efc1b066c4720d8e5bff1214cea90fd1f972a7f0baac3d29be",
+ "0xa1946ee543d1a6e21f380453be4d446e4130950c5fc3d075794eb8260f6f52d0a795c1ff91d028a648dc1ce7d9ab6b47",
+ "0x89f3fefe37af31e0c17533d2ca1ce0884cc1dc97c15cbfab9c331b8debd94781c9396abef4bb2f163d09277a08d6adf0",
+ "0xa2753f1e6e1a154fb117100a5bd9052137add85961f8158830ac20541ab12227d83887d10acf7fd36dcaf7c2596d8d23",
+ "0x814955b4198933ee11c3883863b06ff98c7eceb21fc3e09df5f916107827ccf3323141983e74b025f46ae00284c9513b",
+ "0x8cc5c6bb429073bfef47cae7b3bfccb0ffa076514d91a1862c6bda4d581e0df87db53cc6c130bf8a7826304960f5a34e",
+ "0x909f22c1f1cdc87f7be7439c831a73484a49acbf8f23d47087d7cf867c64ef61da3bde85dc57d705682b4c3fc710d36e",
+ "0x8048fee7f276fcd504aed91284f28e73693615e0eb3858fa44bcf79d7285a9001c373b3ef71d9a3054817ba293ebe28c",
+ "0x94400e5cf5d2700ca608c5fe35ce14623f71cc24959f2bc27ca3684092850f76b67fb1f07ca9e5b2ca3062cf8ad17bd4",
+ "0x81c2ae7d4d1b17f8b6de6a0430acc0d58260993980fe48dc2129c4948269cdc74f9dbfbf9c26b19360823fd913083d48",
+ "0x8c41fe765128e63f6889d6a979f6a4342300327c8b245a8cfe3ecfbcac1e09c3da30e2a1045b24b78efc6d6d50c8c6ac",
+ "0xa5dd4ae51ae48c8be4b218c312ade226cffce671cf121cb77810f6c0990768d6dd767badecb5c69921d5574d5e8433d3",
+ "0xb7642e325f4ba97ae2a39c1c9d97b35aafd49d53dba36aed3f3cb0ca816480b3394079f46a48252d46596559c90f4d58",
+ "0xae87375b40f35519e7bd4b1b2f73cd0b329b0c2cb9d616629342a71c6c304338445eda069b78ea0fbe44087f3de91e09",
+ "0xb08918cb6f736855e11d3daca1ddfbdd61c9589b203b5493143227bf48e2c77c2e8c94b0d1aa2fab2226e0eae83f2681",
+ "0xac36b84a4ac2ebd4d6591923a449c564e3be8a664c46092c09e875c2998eba16b5d32bfd0882fd3851762868e669f0b1",
+ "0xa44800a3bb192066fa17a3f29029a23697240467053b5aa49b9839fb9b9b8b12bcdcbfc557f024b61f4f51a9aacdefcb",
+ "0x9064c688fec23441a274cdf2075e5a449caf5c7363cc5e8a5dc9747183d2e00a0c69f2e6b3f6a7057079c46014c93b3b",
+ "0xaa367b021469af9f5b764a79bb3afbe2d87fe1e51862221672d1a66f954b165778b7c27a705e0f93841fab4c8468344d",
+ "0xa1a8bfc593d4ab71f91640bc824de5c1380ab2591cfdafcbc78a14b32de3c0e15f9d1b461d85c504baa3d4232c16bb53",
+ "0x97df48da1799430f528184d30b6baa90c2a2f88f34cdfb342d715339c5ebd6d019aa693cea7c4993daafc9849063a3aa",
+ "0xabd923831fbb427e06e0dd335253178a9e5791395c84d0ab1433c07c53c1209161097e9582fb8736f8a60bde62d8693e",
+ "0x84cd1a43f1a438b43dc60ffc775f646937c4f6871438163905a3cebf1115f814ccd38a6ccb134130bff226306e412f32",
+ "0x91426065996b0743c5f689eb3ca68a9f7b9e4d01f6c5a2652b57fa9a03d8dc7cd4bdbdab0ca5a891fee1e97a7f00cf02",
+ "0xa4bee50249db3df7fd75162b28f04e57c678ba142ce4d3def2bc17bcb29e4670284a45f218dad3969af466c62a903757",
+ "0x83141ebcc94d4681404e8b67a12a46374fded6df92b506aff3490d875919631408b369823a08b271d006d5b93136f317",
+ "0xa0ea1c8883d58d5a784da3d8c8a880061adea796d7505c1f903d07c287c5467f71e4563fc0faafbc15b5a5538b0a7559",
+ "0x89d9d480574f201a87269d26fb114278ed2c446328df431dc3556e3500e80e4cd01fcac196a2459d8646361ebda840df",
+ "0x8bf302978973632dd464bec819bdb91304712a3ec859be071e662040620422c6e75eba6f864f764cffa2799272efec39",
+ "0x922f666bc0fd58b6d7d815c0ae4f66d193d32fc8382c631037f59eeaeae9a8ca6c72d08e72944cf9e800b8d639094e77",
+ "0x81ad8714f491cdff7fe4399f2eb20e32650cff2999dd45b9b3d996d54a4aba24cc6c451212e78c9e5550368a1a38fb3f",
+ "0xb58fcf4659d73edb73175bd9139d18254e94c3e32031b5d4b026f2ed37aa19dca17ec2eb54c14340231615277a9d347e",
+ "0xb365ac9c2bfe409b710928c646ea2fb15b28557e0f089d39878e365589b9d1c34baf5566d20bb28b33bb60fa133f6eff",
+ "0x8fcae1d75b53ab470be805f39630d204853ca1629a14158bac2f52632277d77458dec204ff84b7b2d77e641c2045be65",
+ "0xa03efa6bebe84f4f958a56e2d76b5ba4f95dd9ed7eb479edc7cc5e646c8d4792e5b0dfc66cc86aa4b4afe2f7a4850760",
+ "0xaf1c823930a3638975fb0cc5c59651771b2719119c3cd08404fbd4ce77a74d708cefbe3c56ea08c48f5f10e6907f338f",
+ "0x8260c8299b17898032c761c325ac9cabb4c5b7e735de81eacf244f647a45fb385012f4f8df743128888c29aefcaaad16",
+ "0xab2f37a573c82e96a8d46198691cd694dfa860615625f477e41f91b879bc58a745784fccd8ffa13065834ffd150d881d",
+ "0x986c746c9b4249352d8e5c629e8d7d05e716b3c7aab5e529ca969dd1e984a14b5be41528baef4c85d2369a42d7209216",
+ "0xb25e32da1a8adddf2a6080725818b75bc67240728ad1853d90738485d8924ea1e202df0a3034a60ffae6f965ec55cf63",
+ "0xa266e627afcebcefea6b6b44cbc50f5c508f7187e87d047b0450871c2a030042c9e376f3ede0afcf9d1952f089582f71",
+ "0x86c3bbca4c0300606071c0a80dbdec21ce1dd4d8d4309648151c420854032dff1241a1677d1cd5de4e4de4385efda986",
+ "0xb9a21a1fe2d1f3273a8e4a9185abf2ff86448cc98bfa435e3d68306a2b8b4a6a3ea33a155be3cb62a2170a86f77679a5",
+ "0xb117b1ea381adce87d8b342cba3a15d492ff2d644afa28f22424cb9cbc820d4f7693dfc1a4d1b3697046c300e1c9b4c8",
+ "0x9004c425a2e68870d6c69b658c344e3aa3a86a8914ee08d72b2f95c2e2d8a4c7bb0c6e7e271460c0e637cec11117bf8e",
+ "0x86a18aa4783b9ebd9131580c8b17994825f27f4ac427b0929a1e0236907732a1c8139e98112c605488ee95f48bbefbfc",
+ "0x84042243b955286482ab6f0b5df4c2d73571ada00716d2f737ca05a0d2e88c6349e8ee9e67934cfee4a1775dbf7f4800",
+ "0x92c2153a4733a62e4e1d5b60369f3c26777c7d01cd3c8679212660d572bd3bac9b8a8a64e1f10f7dbf5eaa7579c4e423",
+ "0x918454b6bb8e44a2afa144695ba8d48ae08d0cdfef4ad078f67709eddf3bb31191e8b006f04e82ea45a54715ef4d5817",
+ "0xacf0b54f6bf34cf6ed6c2b39cf43194a40d68de6bcf1e4b82c34c15a1343e9ac3737885e1a30b78d01fa3a5125463db8",
+ "0xa7d60dbe4b6a7b054f7afe9ee5cbbfeca0d05dc619e6041fa2296b549322529faddb8a11e949562309aecefb842ac380",
+ "0x91ffb53e6d7e5f11159eaf13e783d6dbdfdb1698ed1e6dbf3413c6ea23492bbb9e0932230a9e2caac8fe899a17682795",
+ "0xb6e8d7be5076ee3565d5765a710c5ecf17921dd3cf555c375d01e958a365ae087d4a88da492a5fb81838b7b92bf01143",
+ "0xa8c6b763de2d4b2ed42102ef64eccfef31e2fb2a8a2776241c82912fa50fc9f77f175b6d109a97ede331307c016a4b1a",
+ "0x99839f86cb700c297c58bc33e28d46b92931961548deac29ba8df91d3e11721b10ea956c8e16984f9e4acf1298a79b37",
+ "0x8c2e2c338f25ea5c25756b7131cde0d9a2b35abf5d90781180a00fe4b8e64e62590dc63fe10a57fba3a31c76d784eb01",
+ "0x9687d7df2f41319ca5469d91978fed0565a5f11f829ebadaa83db92b221755f76c6eacd7700735e75c91e257087512e3",
+ "0x8795fdfb7ff8439c58b9bf58ed53873d2780d3939b902b9ddaaa4c99447224ced9206c3039a23c2c44bcc461e2bb637f",
+ "0xa803697b744d2d087f4e2307218d48fa88620cf25529db9ce71e2e3bbcc65bac5e8bb9be04777ef7bfb5ed1a5b8e6170",
+ "0x80f3d3efbbb9346ddd413f0a8e36b269eb5d7ff6809d5525ff9a47c4bcab2c01b70018b117f6fe05253775612ff70c6b",
+ "0x9050e0e45bcc83930d4c505af35e5e4d7ca01cd8681cba92eb55821aececcebe32bb692ebe1a4daac4e7472975671067",
+ "0x8d206812aac42742dbaf233e0c080b3d1b30943b54b60283515da005de05ea5caa90f91fedcfcba72e922f64d7040189",
+ "0xa2d44faaeb2eff7915c83f32b13ca6f31a6847b1c1ce114ea240bac3595eded89f09b2313b7915ad882292e2b586d5b4",
+ "0x961776c8576030c39f214ea6e0a3e8b3d32f023d2600958c098c95c8a4e374deeb2b9dc522adfbd6bda5949bdc09e2a2",
+ "0x993fa7d8447407af0fbcd9e6d77f815fa5233ab00674efbcf74a1f51c37481445ae291cc7b76db7c178f9cb0e570e0fc",
+ "0xabd5b1c78e05f9d7c8cc99bdaef8b0b6a57f2daf0f02bf492bec48ea4a27a8f1e38b5854da96efff11973326ff980f92",
+ "0x8f15af4764bc275e6ccb892b3a4362cacb4e175b1526a9a99944e692fe6ccb1b4fc19abf312bb2a089cb1f344d91a779",
+ "0xa09b27ccd71855512aba1d0c30a79ffbe7f6707a55978f3ced50e674b511a79a446dbc6d7946add421ce111135a460af",
+ "0x94b2f98ce86a9271fbd4153e1fc37de48421fe3490fb3840c00f2d5a4d0ba8810c6a32880b002f6374b59e0a7952518b",
+ "0x8650ac644f93bbcb88a6a0f49fee2663297fd4bc6fd47b6a89b9d8038d32370438ab3a4775ec9b58cb10aea8a95ef7b6",
+ "0x95e5c2f2e84eed88c6980bbba5a1c0bb375d5a628bff006f7516d45bb7d723da676add4fdd45956f312e7bab0f052644",
+ "0xb3278a3fa377ac93af7cfc9453f8cb594aae04269bbc99d2e0e45472ff4b6a2f97a26c4c57bf675b9d86f5e77a5d55d1",
+ "0xb4bcbe6eb666a206e2ea2f877912c1d3b5bdbd08a989fc4490eb06013e1a69ad1ba08bcdac048bf29192312be399077b",
+ "0xa76d70b78c99fffcbf9bb9886eab40f1ea4f99a309710b660b64cbf86057cbcb644d243f6e341711bb7ef0fedf0435a7",
+ "0xb2093c1ee945dca7ac76ad5aed08eae23af31dd5a77c903fd7b6f051f4ab84425d33a03c3d45bf2907bc93c02d1f3ad8",
+ "0x904b1f7534e053a265b22d20be859912b9c9ccb303af9a8d6f1d8f6ccdc5c53eb4a45a1762b880d8444d9be0cd55e7f9",
+ "0x8f664a965d65bc730c9ef1ec7467be984d4b8eb46bd9b0d64e38e48f94e6e55dda19aeac82cbcf4e1473440e64c4ca18",
+ "0x8bcee65c4cc7a7799353d07b114c718a2aae0cd10a3f22b7eead5185d159dafd64852cb63924bf87627d176228878bce",
+ "0x8c78f2e3675096fef7ebaa898d2615cd50d39ca3d8f02b9bdfb07e67da648ae4be3da64838dffc5935fd72962c4b96c7",
+ "0x8c40afd3701629421fec1df1aac4e849384ef2e80472c0e28d36cb1327acdf2826f99b357f3d7afdbc58a6347fc40b3c",
+ "0xa197813b1c65a8ea5754ef782522a57d63433ef752215ecda1e7da76b0412ee619f58d904abd2e07e0c097048b6ae1dd",
+ "0xa670542629e4333884ad7410f9ea3bd6f988df4a8f8a424ca74b9add2312586900cf9ae8bd50411f9146e82626b4af56",
+ "0xa19875cc07ab84e569d98b8b67fb1dbbdfb59093c7b748fae008c8904a6fd931a63ca8d03ab5fea9bc8d263568125a9b",
+ "0xb57e7f68e4eb1bd04aafa917b1db1bdab759a02aa8a9cdb1cba34ba8852b5890f655645c9b4e15d5f19bf37e9f2ffe9f",
+ "0x8abe4e2a4f6462b6c64b3f10e45db2a53c2b0d3c5d5443d3f00a453e193df771eda635b098b6c8604ace3557514027af",
+ "0x8459e4fb378189b22b870a6ef20183deb816cefbf66eca1dc7e86d36a2e011537db893729f500dc154f14ce24633ba47",
+ "0x930851df4bc7913c0d8c0f7bd3b071a83668987ed7c397d3d042fdc0d9765945a39a3bae83da9c88cb6b686ed8aeeb26",
+ "0x8078c9e5cd05e1a8c932f8a1d835f61a248b6e7133fcbb3de406bf4ffc0e584f6f9f95062740ba6008d98348886cf76b",
+ "0xaddff62bb29430983fe578e3709b0949cdc0d47a13a29bc3f50371a2cb5c822ce53e2448cfaa01bcb6e0aa850d5a380e",
+ "0x9433add687b5a1e12066721789b1db2edf9b6558c3bdc0f452ba33b1da67426abe326e9a34d207bfb1c491c18811bde1",
+ "0x822beda3389963428cccc4a2918fa9a8a51cf0919640350293af70821967108cded5997adae86b33cb917780b097f1ca",
+ "0xa7a9f52bda45e4148ed56dd176df7bd672e9b5ed18888ccdb405f47920fdb0844355f8565cefb17010b38324edd8315f",
+ "0xb35c3a872e18e607b2555c51f9696a17fa18da1f924d503b163b4ec9fe22ed0c110925275cb6c93ce2d013e88f173d6a",
+ "0xadf34b002b2b26ab84fc1bf94e05bd8616a1d06664799ab149363c56a6e0c807fdc473327d25632416e952ea327fcd95",
+ "0xae4a6b9d22a4a3183fac29e2551e1124a8ce4a561a9a2afa9b23032b58d444e6155bb2b48f85c7b6d70393274e230db7",
+ "0xa2ea3be4fc17e9b7ce3110284038d46a09e88a247b6971167a7878d9dcf36925d613c382b400cfa4f37a3ebea3699897",
+ "0x8e5863786b641ce3140fbfe37124d7ad3925472e924f814ebfc45959aaf3f61dc554a597610b5defaecc85b59a99b50f",
+ "0xaefde3193d0f700d0f515ab2aaa43e2ef1d7831c4f7859f48e52693d57f97fa9e520090f3ed700e1c966f4b76048e57f",
+ "0x841a50f772956622798e5cd208dc7534d4e39eddee30d8ce133383d66e5f267e389254a0cdae01b770ecd0a9ca421929",
+ "0x8fbc2bfd28238c7d47d4c03b1b910946c0d94274a199575e5b23242619b1de3497784e646a92aa03e3e24123ae4fcaba",
+ "0x926999579c8eec1cc47d7330112586bdca20b4149c8b2d066f527c8b9f609e61ce27feb69db67eea382649c6905efcf9",
+ "0xb09f31f305efcc65589adf5d3690a76cf339efd67cd43a4e3ced7b839507466e4be72dd91f04e89e4bbef629d46e68c0",
+ "0xb917361f6b95f759642638e0b1d2b3a29c3bdef0b94faa30de562e6078c7e2d25976159df3edbacbf43614635c2640b4",
+ "0x8e7e8a1253bbda0e134d62bfe003a2669d471b47bd2b5cde0ff60d385d8e62279d54022f5ac12053b1e2d3aaa6910b4c",
+ "0xb69671a3c64e0a99d90b0ed108ce1912ff8ed983e4bddd75a370e9babde25ee1f5efb59ec707edddd46793207a8b1fe7",
+ "0x910b2f4ebd37b7ae94108922b233d0920b4aba0bd94202c70f1314418b548d11d8e9caa91f2cd95aff51b9432d122b7f",
+ "0x82f645c90dfb52d195c1020346287c43a80233d3538954548604d09fbab7421241cde8593dbc4acc4986e0ea39a27dd9",
+ "0x8fee895f0a140d88104ce442fed3966f58ff9d275e7373483f6b4249d64a25fb5374bbdc6bce6b5ab0270c2847066f83",
+ "0x84f5bd7aab27b2509397aeb86510dd5ac0a53f2c8f73799bf720f2f87a52277f8d6b0f77f17bc80739c6a7119b7eb062",
+ "0x9903ceced81099d7e146e661bcf01cbaccab5ba54366b85e2177f07e2d8621e19d9c9c3eee14b9266de6b3f9b6ea75ae",
+ "0xb9c16ea2a07afa32dd6c7c06df0dec39bca2067a9339e45475c98917f47e2320f6f235da353fd5e15b477de97ddc68dd",
+ "0x9820a9bbf8b826bec61ebf886de2c4f404c1ebdc8bab82ee1fea816d9de29127ce1852448ff717a3fe8bbfe9e92012e5",
+ "0x817224d9359f5da6f2158c2c7bf9165501424f063e67ba9859a07ab72ee2ee62eb00ca6da821cfa19065c3282ca72c74",
+ "0x94b95c465e6cb00da400558a3c60cfec4b79b27e602ca67cbc91aead08de4b6872d8ea096b0dc06dca4525c8992b8547",
+ "0xa2b539a5bccd43fa347ba9c15f249b417997c6a38c63517ca38394976baa08e20be384a360969ff54e7e721db536b3e5",
+ "0x96caf707e34f62811ee8d32ccf28d8d6ec579bc33e424d0473529af5315c456fd026aa910c1fed70c91982d51df7d3ca",
+ "0x8a77b73e890b644c6a142bdbac59b22d6a676f3b63ddafb52d914bb9d395b8bf5aedcbcc90429337df431ebd758a07a6",
+ "0x8857830a7351025617a08bc44caec28d2fae07ebf5ffc9f01d979ce2a53839a670e61ae2783e138313929129790a51a1",
+ "0xaa3e420321ed6f0aa326d28d1a10f13facec6f605b6218a6eb9cbc074801f3467bf013a456d1415a5536f12599efa3d3",
+ "0x824aed0951957b00ea2f3d423e30328a3527bf6714cf9abbae84cf27e58e5c35452ba89ccc011de7c68c75d6e021d8f1",
+ "0xa2e87cc06bf202e953fb1081933d8b4445527dde20e38ed1a4f440144fd8fa464a2b73e068b140562e9045e0f4bd3144",
+ "0xae3b8f06ad97d7ae3a5e5ca839efff3e4824dc238c0c03fc1a8d2fc8aa546cdfd165b784a31bb4dec7c77e9305b99a4b",
+ "0xb30c3e12395b1fb8b776f3ec9f87c70e35763a7b2ddc68f0f60a4982a84017f27c891a98561c830038deb033698ed7fc",
+ "0x874e507757cd1177d0dff0b0c62ce90130324442a33da3b2c8ee09dbca5d543e3ecfe707e9f1361e7c7db641c72794bb",
+ "0xb53012dd10b5e7460b57c092eaa06d6502720df9edbbe3e3f61a9998a272bf5baaac4a5a732ad4efe35d6fac6feca744",
+ "0x85e6509d711515534d394e6cacbed6c81da710074d16ef3f4950bf2f578d662a494d835674f79c4d6315bced4defc5f0",
+ "0xb6132b2a34b0905dcadc6119fd215419a7971fe545e52f48b768006944b4a9d7db1a74b149e2951ea48c083b752d0804",
+ "0x989867da6415036d19b4bacc926ce6f4df7a556f50a1ba5f3c48eea9cefbb1c09da81481c8009331ee83f0859185e164",
+ "0x960a6c36542876174d3fbc1505413e29f053ed87b8d38fef3af180491c7eff25200b45dd5fe5d4d8e63c7e8c9c00f4c8",
+ "0x9040b59bd739d9cc2e8f6e894683429e4e876a8106238689ff4c22770ae5fdae1f32d962b30301fa0634ee163b524f35",
+ "0xaf3fcd0a45fe9e8fe256dc7eab242ef7f582dd832d147444483c62787ac820fafc6ca55d639a73f76bfa5e7f5462ab8f",
+ "0xb934c799d0736953a73d91e761767fdb78454355c4b15c680ce08accb57ccf941b13a1236980001f9e6195801cffd692",
+ "0x8871e8e741157c2c326b22cf09551e78da3c1ec0fc0543136f581f1550f8bab03b0a7b80525c1e99812cdbf3a9698f96",
+ "0xa8a977f51473a91d178ee8cfa45ffef8d6fd93ab1d6e428f96a3c79816d9c6a93cd70f94d4deda0125fd6816e30f3bea",
+ "0xa7688b3b0a4fc1dd16e8ba6dc758d3cfe1b7cf401c31739484c7fa253cce0967df1b290769bcefc9d23d3e0cb19e6218",
+ "0x8ae84322662a57c6d729e6ff9d2737698cc2da2daeb1f39e506618750ed23442a6740955f299e4a15dda6db3e534d2c6",
+ "0xa04a961cdccfa4b7ef83ced17ab221d6a043b2c718a0d6cc8e6f798507a31f10bf70361f70a049bc8058303fa7f96864",
+ "0xb463e39732a7d9daec8a456fb58e54b30a6e160aa522a18b9a9e836488cce3342bcbb2e1deab0f5e6ec0a8796d77197d",
+ "0xb1434a11c6750f14018a2d3bcf94390e2948f4f187e93bb22070ca3e5393d339dc328cbfc3e48815f51929465ffe7d81",
+ "0x84ff81d73f3828340623d7e3345553610aa22a5432217ef0ebd193cbf4a24234b190c65ca0873c22d10ea7b63bd1fbed",
+ "0xb6fe2723f0c47757932c2ddde7a4f8434f665612f7b87b4009c2635d56b6e16b200859a8ade49276de0ef27a2b6c970a",
+ "0x9742884ed7cd52b4a4a068a43d3faa02551a424136c85a9313f7cb58ea54c04aa83b0728fd741d1fe39621e931e88f8f",
+ "0xb7d2d65ea4d1ad07a5dee39e40d6c03a61264a56b1585b4d76fc5b2a68d80a93a42a0181d432528582bf08d144c2d6a9",
+ "0x88c0f66bada89f8a43e5a6ead2915088173d106c76f724f4a97b0f6758aed6ae5c37c373c6b92cdd4aea8f6261f3a374",
+ "0x81f9c43582cb42db3900747eb49ec94edb2284999a499d1527f03315fd330e5a509afa3bff659853570e9886aab5b28b",
+ "0x821f9d27d6beb416abf9aa5c79afb65a50ed276dbda6060103bc808bcd34426b82da5f23e38e88a55e172f5c294b4d40",
+ "0x8ba307b9e7cb63a6c4f3851b321aebfdb6af34a5a4c3bd949ff7d96603e59b27ff4dc4970715d35f7758260ff942c9e9",
+ "0xb142eb6c5f846de33227d0bda61d445a7c33c98f0a8365fe6ab4c1fabdc130849be597ef734305894a424ea715372d08",
+ "0xa732730ae4512e86a741c8e4c87fee8a05ee840fec0e23b2e037d58dba8dde8d10a9bc5191d34d00598941becbbe467f",
+ "0xadce6f7c30fd221f6b10a0413cc76435c4bb36c2d60bca821e5c67409fe9dbb2f4c36ef85eb3d734695e4be4827e9fd3",
+ "0xa74f00e0f9b23aff7b2527ce69852f8906dab9d6abe62ecd497498ab21e57542e12af9918d4fd610bb09e10b0929c510",
+ "0xa593b6b0ef26448ce4eb3ab07e84238fc020b3cb10d542ff4b16d4e2be1bcde3797e45c9cf753b8dc3b0ffdb63984232",
+ "0xaed3913afccf1aa1ac0eb4980eb8426d0baccebd836d44651fd72af00d09fac488a870223c42aca3ceb39752070405ae",
+ "0xb2c44c66a5ea7fde626548ba4cef8c8710191343d3dadfd3bb653ce715c0e03056a5303a581d47dde66e70ea5a2d2779",
+ "0x8e5029b2ccf5128a12327b5103f7532db599846e422531869560ceaff392236434d87159f597937dbf4054f810c114f4",
+ "0x82beed1a2c4477e5eb39fc5b0e773b30cfec77ef2b1bf17eadaf60eb35b6d0dd9d8cf06315c48d3546badb3f21cd0cca",
+ "0x90077bd6cc0e4be5fff08e5d07a5a158d36cebd1d1363125bc4fae0866ffe825b26f933d4ee5427ba5cd0c33c19a7b06",
+ "0xa7ec0d8f079970e8e34f0ef3a53d3e0e45428ddcef9cc776ead5e542ef06f3c86981644f61c5a637e4faf001fb8c6b3e",
+ "0xae6d4add6d1a6f90b22792bc9d40723ee6850c27d0b97eefafd5b7fd98e424aa97868b5287cc41b4fbd7023bca6a322c",
+ "0x831aa917533d077da07c01417feaa1408846363ba2b8d22c6116bb858a95801547dd88b7d7fa1d2e3f0a02bdeb2e103d",
+ "0x96511b860b07c8a5ed773f36d4aa9d02fb5e7882753bf56303595bcb57e37ccc60288887eb83bef08c657ec261a021a2",
+ "0x921d2a3e7e9790f74068623de327443666b634c8443aba80120a45bba450df920b2374d96df1ce3fb1b06dd06f8cf6e3",
+ "0xaa74451d51fe82b4581ead8e506ec6cd881010f7e7dd51fc388eb9a557db5d3c6721f81c151d08ebd9c2591689fbc13e",
+ "0xa972bfbcf4033d5742d08716c927c442119bdae336bf5dff914523b285ccf31953da2733759aacaa246a9af9f698342c",
+ "0xad1fcd0cae0e76840194ce4150cb8a56ebed728ec9272035f52a799d480dfc85840a4d52d994a18b6edb31e79be6e8ad",
+ "0xa2c69fe1d36f235215432dad48d75887a44c99dfa0d78149acc74087da215a44bdb5f04e6eef88ff7eff80a5a7decc77",
+ "0xa94ab2af2b6ee1bc6e0d4e689ca45380d9fbd3c5a65b9bd249d266a4d4c07bf5d5f7ef2ae6000623aee64027892bf8fe",
+ "0x881ec1fc514e926cdc66480ac59e139148ff8a2a7895a49f0dff45910c90cdda97b66441a25f357d6dd2471cddd99bb3",
+ "0x884e6d3b894a914c8cef946a76d5a0c8351843b2bffa2d1e56c6b5b99c84104381dd1320c451d551c0b966f4086e60f9",
+ "0x817c6c10ce2677b9fc5223500322e2b880583254d0bb0d247d728f8716f5e05c9ff39f135854342a1afecd9fbdcf7c46",
+ "0xaaf4a9cb686a14619aa1fc1ac285dd3843ac3dd99f2b2331c711ec87b03491c02f49101046f3c5c538dc9f8dba2a0ac2",
+ "0x97ecea5ce53ca720b5d845227ae61d70269a2f53540089305c86af35f0898bfd57356e74a8a5e083fa6e1ea70080bd31",
+ "0xa22d811e1a20a75feac0157c418a4bfe745ccb5d29466ffa854dca03e395b6c3504a734341746b2846d76583a780b32e",
+ "0x940cbaa0d2b2db94ae96b6b9cf2deefbfd059e3e5745de9aec4a25f0991b9721e5cd37ef71c631575d1a0c280b01cd5b",
+ "0xae33cb4951191258a11044682de861bf8d92d90ce751b354932dd9f3913f542b6a0f8a4dc228b3cd9244ac32c4582832",
+ "0xa580df5e58c4274fe0f52ac2da1837e32f5c9db92be16c170187db4c358f43e5cfdda7c5911dcc79d77a5764e32325f5",
+ "0x81798178cb9d8affa424f8d3be67576ba94d108a28ccc01d330c51d5a63ca45bb8ca63a2f569b5c5fe1303cecd2d777f",
+ "0x89975b91b94c25c9c3660e4af4047a8bacf964783010820dbc91ff8281509379cb3b24c25080d5a01174dd9a049118d5",
+ "0xa7327fcb3710ed3273b048650bde40a32732ef40a7e58cf7f2f400979c177944c8bc54117ba6c80d5d4260801dddab79",
+ "0x92b475dc8cb5be4b90c482f122a51bcb3b6c70593817e7e2459c28ea54a7845c50272af38119406eaadb9bcb993368d0",
+ "0x9645173e9ecefc4f2eae8363504f7c0b81d85f8949a9f8a6c01f2d49e0a0764f4eacecf3e94016dd407fc14494fce9f9",
+ "0x9215fd8983d7de6ae94d35e6698226fc1454977ae58d42d294be9aad13ac821562ad37d5e7ee5cdfe6e87031d45cd197",
+ "0x810360a1c9b88a9e36f520ab5a1eb8bed93f52deefbe1312a69225c0a08edb10f87cc43b794aced9c74220cefcc57e7d",
+ "0xad7e810efd61ed4684aeda9ed8bb02fb9ae4b4b63fda8217d37012b94ff1b91c0087043bfa4e376f961fff030c729f3b",
+ "0x8b07c95c6a06db8738d10bb03ec11b89375c08e77f0cab7e672ce70b2685667ca19c7e1c8b092821d31108ea18dfd4c7",
+ "0x968825d025ded899ff7c57245250535c732836f7565eab1ae23ee7e513201d413c16e1ba3f5166e7ac6cf74de8ceef4f",
+ "0x908243370c5788200703ade8164943ad5f8c458219186432e74dbc9904a701ea307fd9b94976c866e6c58595fd891c4b",
+ "0x959969d16680bc535cdc6339e6186355d0d6c0d53d7bbfb411641b9bf4b770fd5f575beef5deec5c4fa4d192d455c350",
+ "0xad177f4f826a961adeac76da40e2d930748effff731756c797eddc4e5aa23c91f070fb69b19221748130b0961e68a6bb",
+ "0x82f8462bcc25448ef7e0739425378e9bb8a05e283ce54aae9dbebaf7a3469f57833c9171672ad43a79778366c72a5e37",
+ "0xa28fb275b1845706c2814d9638573e9bc32ff552ebaed761fe96fdbce70395891ca41c400ae438369264e31a2713b15f",
+ "0x8a9c613996b5e51dadb587a787253d6081ea446bf5c71096980bf6bd3c4b69905062a8e8a3792de2d2ece3b177a71089",
+ "0x8d5aefef9f60cb27c1db2c649221204dda48bb9bf8bf48f965741da051340e8e4cab88b9d15c69f3f84f4c854709f48a",
+ "0x93ebf2ca6ad85ab6deace6de1a458706285b31877b1b4d7dcb9d126b63047efaf8c06d580115ec9acee30c8a7212fa55",
+ "0xb3ee46ce189956ca298057fa8223b7fd1128cf52f39159a58bca03c71dd25161ac13f1472301f72aef3e1993fe1ab269",
+ "0xa24d7a8d066504fc3f5027ccb13120e2f22896860e02c45b5eba1dbd512d6a17c28f39155ea581619f9d33db43a96f92",
+ "0xae9ceacbfe12137db2c1a271e1b34b8f92e4816bad1b3b9b6feecc34df0f8b3b0f7ed0133acdf59c537d43d33fc8d429",
+ "0x83967e69bf2b361f86361bd705dce0e1ad26df06da6c52b48176fe8dfcbeb03c462c1a4c9e649eff8c654b18c876fdef",
+ "0x9148e6b814a7d779c19c31e33a068e97b597de1f8100513db3c581190513edc4d544801ce3dd2cf6b19e0cd6daedd28a",
+ "0x94ccdafc84920d320ed22de1e754adea072935d3c5f8c2d1378ebe53d140ea29853f056fb3fb1e375846061a038cc9bc",
+ "0xafb43348498c38b0fa5f971b8cdd3a62c844f0eb52bc33daf2f67850af0880fce84ecfb96201b308d9e6168a0d443ae3",
+ "0x86d5736520a83538d4cd058cc4b4e84213ed00ebd6e7af79ae787adc17a92ba5359e28ba6c91936d967b4b28d24c3070",
+ "0xb5210c1ff212c5b1e9ef9126e08fe120a41e386bb12c22266f7538c6d69c7fd8774f11c02b81fd4e88f9137b020801fe",
+ "0xb78cfd19f94d24e529d0f52e18ce6185cb238edc6bd43086270fd51dd99f664f43dd4c7d2fe506762fbd859028e13fcf",
+ "0xa6e7220598c554abdcc3fdc587b988617b32c7bb0f82c06205467dbedb58276cc07cae317a190f19d19078773f4c2bbb",
+ "0xb88862809487ee430368dccd85a5d72fa4d163ca4aad15c78800e19c1a95be2192719801e315d86cff7795e0544a77e4",
+ "0x87ecb13a03921296f8c42ceb252d04716f10e09c93962239fcaa0a7fef93f19ab3f2680bc406170108bc583e9ff2e721",
+ "0xa810cd473832b6581c36ec4cb403f2849357ba2d0b54df98ef3004b8a530c078032922a81d40158f5fb0043d56477f6e",
+ "0xa247b45dd85ca7fbb718b328f30a03f03c84aef2c583fbdc9fcc9eb8b52b34529e8c8f535505c10598b1b4dac3d7c647",
+ "0x96ee0b91313c68bac4aa9e065ce9e1d77e51ca4cff31d6a438718c58264dee87674bd97fc5c6b8008be709521e4fd008",
+ "0x837567ad073e42266951a9a54750919280a2ac835a73c158407c3a2b1904cf0d17b7195a393c71a18ad029cbd9cf79ee",
+ "0xa6a469c44b67ebf02196213e7a63ad0423aab9a6e54acc6fcbdbb915bc043586993454dc3cd9e4be8f27d67c1050879b",
+ "0x8712d380a843b08b7b294f1f06e2f11f4ad6bcc655fdde86a4d8bc739c23916f6fad2b902fe47d6212f03607907e9f0e",
+ "0x920adfb644b534789943cdae1bdd6e42828dda1696a440af2f54e6b97f4f97470a1c6ea9fa6a2705d8f04911d055acd1",
+ "0xa161c73adf584a0061e963b062f59d90faac65c9b3a936b837a10d817f02fcabfa748824607be45a183dd40f991fe83f",
+ "0x874f4ecd408c76e625ea50bc59c53c2d930ee25baf4b4eca2440bfbffb3b8bc294db579caa7c68629f4d9ec24187c1ba",
+ "0x8bff18087f112be7f4aa654e85c71fef70eee8ae480f61d0383ff6f5ab1a0508f966183bb3fc4d6f29cb7ca234aa50d3",
+ "0xb03b46a3ca3bc743a173cbc008f92ab1aedd7466b35a6d1ca11e894b9482ea9dc75f8d6db2ddd1add99bfbe7657518b7",
+ "0x8b4f3691403c3a8ad9e097f02d130769628feddfa8c2b3dfe8cff64e2bed7d6e5d192c1e2ba0ac348b8585e94acd5fa1",
+ "0xa0d9ca4a212301f97591bf65d5ef2b2664766b427c9dd342e23cb468426e6a56be66b1cb41fea1889ac5d11a8e3c50a5",
+ "0x8c93ed74188ca23b3df29e5396974b9cc135c91fdefdea6c0df694c8116410e93509559af55533a3776ac11b228d69b1",
+ "0x82dd331fb3f9e344ebdeeb557769b86a2cc8cc38f6c298d7572a33aea87c261afa9dbd898989139b9fc16bc1e880a099",
+ "0xa65faedf326bcfd8ef98a51410c78b021d39206704e8291cd1f09e096a66b9b0486be65ff185ca224c45918ac337ddeb",
+ "0xa188b37d363ac072a766fd5d6fa27df07363feff1342217b19e3c37385e42ffde55e4be8355aceaa2f267b6d66b4ac41",
+ "0x810fa3ba3e96d843e3bafd3f2995727f223d3567c8ba77d684c993ba1773c66551eb5009897c51b3fe9b37196984f5ec",
+ "0x87631537541852da323b4353af45a164f68b304d24c01183bf271782e11687f3fcf528394e1566c2a26cb527b3148e64",
+ "0xb721cb2b37b3c477a48e3cc0044167d51ff568a5fd2fb606e5aec7a267000f1ddc07d3db919926ae12761a8e017c767c",
+ "0x904dfad4ba2cc1f6e60d1b708438a70b1743b400164cd981f13c064b8328d5973987d4fb9cf894068f29d3deaf624dfb",
+ "0xa70491538893552c20939fae6be2f07bfa84d97e2534a6bbcc0f1729246b831103505e9f60e97a8fa7d2e6c1c2384579",
+ "0x8726cf1b26b41f443ff7485adcfddc39ace2e62f4d65dd0bb927d933e262b66f1a9b367ded5fbdd6f3b0932553ac1735",
+ "0xae8a11cfdf7aa54c08f80cb645e3339187ab3886babe9fae5239ba507bb3dd1c0d161ca474a2df081dcd3d63e8fe445e",
+ "0x92328719e97ce60e56110f30a00ac5d9c7a2baaf5f8d22355d53c1c77941e3a1fec7d1405e6fbf8959665fe2ba7a8cad",
+ "0x8d9d6255b65798d0018a8cccb0b6343efd41dc14ff2058d3eed9451ceaad681e4a0fa6af67b0a04318aa628024e5553d",
+ "0xb70209090055459296006742d946a513f0cba6d83a05249ee8e7a51052b29c0ca9722dc4af5f9816a1b7938a5dac7f79",
+ "0xaab7b766b9bf91786dfa801fcef6d575dc6f12b77ecc662eb4498f0312e54d0de9ea820e61508fc8aeee5ab5db529349",
+ "0xa8104b462337748b7f086a135d0c3f87f8e51b7165ca6611264b8fb639d9a2f519926cb311fa2055b5fadf03da70c678",
+ "0xb0d2460747d5d8b30fc6c6bd0a87cb343ddb05d90a51b465e8f67d499cfc5e3a9e365da05ae233bbee792cdf90ec67d5",
+ "0xaa55f5bf3815266b4a149f85ed18e451c93de9163575e3ec75dd610381cc0805bb0a4d7c4af5b1f94d10231255436d2c",
+ "0x8d4c6a1944ff94426151909eb5b99cfd92167b967dabe2bf3aa66bb3c26c449c13097de881b2cfc1bf052862c1ef7b03",
+ "0x8862296162451b9b6b77f03bf32e6df71325e8d7485cf3335d66fd48b74c2a8334c241db8263033724f26269ad95b395",
+ "0x901aa96deb26cda5d9321190ae6624d357a41729d72ef1abfd71bebf6139af6d690798daba53b7bc5923462115ff748a",
+ "0x96c195ec4992728a1eb38cdde42d89a7bce150db43adbc9e61e279ea839e538deec71326b618dd39c50d589f78fc0614",
+ "0xb6ff8b8aa0837b99a1a8b46fb37f20ad4aecc6a98381b1308697829a59b8442ffc748637a88cb30c9b1f0f28a926c4f6",
+ "0x8d807e3dca9e7bef277db1d2cfb372408dd587364e8048b304eff00eacde2c723bfc84be9b98553f83cba5c7b3cba248",
+ "0x8800c96adb0195c4fc5b24511450dee503c32bf47044f5e2e25bd6651f514d79a2dd9b01cd8c09f3c9d3859338490f57",
+ "0x89fe366096097e38ec28dd1148887112efa5306cc0c3da09562aafa56f4eb000bf46ff79bf0bdd270cbde6bf0e1c8957",
+ "0xaf409a90c2776e1e7e3760b2042507b8709e943424606e31e791d42f17873a2710797f5baaab4cc4a19998ef648556b0",
+ "0x8d761863c9b6edbd232d35ab853d944f5c950c2b643f84a1a1327ebb947290800710ff01dcfa26dc8e9828481240e8b1",
+ "0x90b95e9be1e55c463ed857c4e0617d6dc3674e99b6aa62ed33c8e79d6dfcf7d122f4f4cc2ee3e7c5a49170cb617d2e2e",
+ "0xb3ff381efefabc4db38cc4727432e0301949ae4f16f8d1dea9b4f4de611cf5a36d84290a0bef160dac4e1955e516b3b0",
+ "0xa8a84564b56a9003adcadb3565dc512239fc79572762cda7b5901a255bc82656bb9c01212ad33d6bef4fbbce18dacc87",
+ "0x90a081890364b222eef54bf0075417f85e340d2fec8b7375995f598aeb33f26b44143ebf56fca7d8b4ebb36b5747b0eb",
+ "0xade6ee49e1293224ddf2d8ab7f14bb5be6bc6284f60fd5b3a1e0cf147b73cff57cf19763b8a36c5083badc79c606b103",
+ "0xb2fa99806dd2fa3de09320b615a2570c416c9bcdb052e592b0aead748bbe407ec9475a3d932ae48b71c2627eb81986a6",
+ "0x91f3b7b73c8ccc9392542711c45fe6f236057e6efad587d661ad5cb4d6e88265f86b807bb1151736b1009ab74fd7acb4",
+ "0x8800e2a46af96696dfbdcbf2ca2918b3dcf28ad970170d2d1783b52b8d945a9167d052beeb55f56c126da7ffa7059baa",
+ "0x9862267a1311c385956b977c9aa08548c28d758d7ba82d43dbc3d0a0fd1b7a221d39e8399997fea9014ac509ff510ac4",
+ "0xb7d24f78886fd3e2d283e18d9ad5a25c1a904e7d9b9104bf47da469d74f34162e27e531380dbbe0a9d051e6ffd51d6e7",
+ "0xb0f445f9d143e28b9df36b0f2c052da87ee2ca374d9d0fbe2eff66ca6fe5fe0d2c1951b428d58f7314b7e74e45d445ea",
+ "0xb63fc4083eabb8437dafeb6a904120691dcb53ce2938b820bb553da0e1eecd476f72495aacb72600cf9cad18698fd3db",
+ "0xb9ffd8108eaebd582d665f8690fe8bb207fd85185e6dd9f0b355a09bac1bbff26e0fdb172bc0498df025414e88fe2eda",
+ "0x967ed453e1f1a4c5b7b6834cc9f75c13f6889edc0cc91dc445727e9f408487bbf05c337103f61397a10011dfbe25d61d",
+ "0x98ceb673aff36e1987d5521a3984a07079c3c6155974bb8b413e8ae1ce84095fe4f7862fba7aefa14753eb26f2a5805f",
+ "0x85f01d28603a8fdf6ce6a50cb5c44f8a36b95b91302e3f4cd95c108ce8f4d212e73aec1b8d936520d9226802a2bd9136",
+ "0x88118e9703200ca07910345fbb789e7a8f92bd80bbc79f0a9e040e8767d33df39f6eded403a9b636eabf9101e588482a",
+ "0x90833a51eef1b10ed74e8f9bbd6197e29c5292e469c854eed10b0da663e2bceb92539710b1858bbb21887bd538d28d89",
+ "0xb513b905ec19191167c6193067b5cfdf5a3d3828375360df1c7e2ced5815437dfd37f0c4c8f009d7fb29ff3c8793f560",
+ "0xb1b6d405d2d18f9554b8a358cc7e2d78a3b34269737d561992c8de83392ac9a2857be4bf15de5a6c74e0c9d0f31f393c",
+ "0xb828bd3e452b797323b798186607849f85d1fb20c616833c0619360dfd6b3e3aa000fd09dafe4b62d74abc41072ff1a9",
+ "0x8efde67d0cca56bb2c464731879c9ac46a52e75bac702a63200a5e192b4f81c641f855ca6747752b84fe469cb7113b6c",
+ "0xb2762ba1c89ac3c9a983c242e4d1c2610ff0528585ed5c0dfc8a2c0253551142af9b59f43158e8915a1da7cc26b9df67",
+ "0x8a3f1157fb820d1497ef6b25cd70b7e16bb8b961b0063ad340d82a79ee76eb2359ca9e15e6d42987ed7f154f5eeaa2da",
+ "0xa75e29f29d38f09c879f971c11beb5368affa084313474a5ecafa2896180b9e47ea1995c2733ec46f421e395a1d9cffe",
+ "0x8e8c3dd3e7196ef0b4996b531ec79e4a1f211db5d5635e48ceb80ff7568b2ff587e845f97ee703bb23a60945ad64314a",
+ "0x8e7f32f4a3e3c584af5e3d406924a0aa34024c42eca74ef6cc2a358fd3c9efaf25f1c03aa1e66bb94b023a2ee2a1cace",
+ "0xab7dce05d59c10a84feb524fcb62478906b3fa045135b23afbede3bb32e0c678d8ebe59feabccb5c8f3550ea76cae44b",
+ "0xb38bb4b44d827f6fd3bd34e31f9186c59e312dbfadd4a7a88e588da10146a78b1f8716c91ad8b806beb8da65cab80c4c",
+ "0x9490ce9442bbbd05438c7f5c4dea789f74a7e92b1886a730544b55ba377840740a3ae4f2f146ee73f47c9278b0e233bc",
+ "0x83c003fab22a7178eed1a668e0f65d4fe38ef3900044e9ec63070c23f2827d36a1e73e5c2b883ec6a2afe2450171b3b3",
+ "0x9982f02405978ddc4fca9063ebbdb152f524c84e79398955e66fe51bc7c1660ec1afc3a86ec49f58d7b7dde03505731c",
+ "0xab337bd83ccdd2322088ffa8d005f450ced6b35790f37ab4534313315ee84312adc25e99cce052863a8bedee991729ed",
+ "0x8312ce4bec94366d88f16127a17419ef64285cd5bf9e5eda010319b48085966ed1252ed2f5a9fd3e0259b91bb65f1827",
+ "0xa60d5a6327c4041b0c00a1aa2f0af056520f83c9ce9d9ccd03a0bd4d9e6a1511f26a422ea86bd858a1f77438adf07e6c",
+ "0xb84a0a0b030bdad83cf5202aa9afe58c9820e52483ab41f835f8c582c129ee3f34aa096d11c1cd922eda02ea1196a882",
+ "0x8077d105317f4a8a8f1aadeb05e0722bb55f11abcb490c36c0904401107eb3372875b0ac233144829e734f0c538d8c1d",
+ "0x9202503bd29a6ec198823a1e4e098f9cfe359ed51eb5174d1ca41368821bfeebcbd49debfd02952c41359d1c7c06d2b1",
+ "0xabc28c155e09365cb77ffead8dc8f602335ef93b2f44e4ef767ce8fc8ef9dd707400f3a722e92776c2e0b40192c06354",
+ "0xb0f6d1442533ca45c9399e0a63a11f85ff288d242cea6cb3b68c02e77bd7d158047cae2d25b3bcd9606f8f66d9b32855",
+ "0xb01c3d56a0db84dc94575f4b6ee2de4beca3230e86bed63e2066beb22768b0a8efb08ebaf8ac3dedb5fe46708b084807",
+ "0x8c8634b0432159f66feaabb165842d1c8ac378f79565b1b90c381aa8450eb4231c3dad11ec9317b9fc2b155c3a771e32",
+ "0x8e67f623d69ecd430c9ee0888520b6038f13a2b6140525b056dc0951f0cfed2822e62cf11d952a483107c5c5acac4826",
+ "0x9590bb1cba816dd6acd5ac5fba5142c0a19d53573e422c74005e0bcf34993a8138c83124cad35a3df65879dba6134edd",
+ "0x801cd96cde0749021a253027118d3ea135f3fcdbe895db08a6c145641f95ebd368dd6a1568d995e1d0084146aebe224a",
+ "0x848b5d196427f6fc1f762ee3d36e832b64a76ec1033cfedc8b985dea93932a7892b8ef1035c653fb9dcd9ab2d9a44ac8",
+ "0xa1017eb83d5c4e2477e7bd2241b2b98c4951a3b391081cae7d75965cadc1acaec755cf350f1f3d29741b0828e36fedea",
+ "0x8d6d2785e30f3c29aad17bd677914a752f831e96d46caf54446d967cb2432be2c849e26f0d193a60bee161ea5c6fe90a",
+ "0x935c0ba4290d4595428e034b5c8001cbd400040d89ab00861108e8f8f4af4258e41f34a7e6b93b04bc253d3b9ffc13bf",
+ "0xaac02257146246998477921cef2e9892228590d323b839f3e64ea893b991b463bc2f47e1e5092ddb47e70b2f5bce7622",
+ "0xb921fde9412970a5d4c9a908ae8ce65861d06c7679af577cf0ad0d5344c421166986bee471fd6a6cecb7d591f06ec985",
+ "0x8ef4c37487b139d6756003060600bb6ebac7ea810b9c4364fc978e842f13ac196d1264fbe5af60d76ff6d9203d8e7d3f",
+ "0x94b65e14022b5cf6a9b95f94be5ace2711957c96f4211c3f7bb36206bd39cfbd0ea82186cab5ad0577a23214a5c86e9e",
+ "0xa31c166d2a2ca1d5a75a5920fef7532681f62191a50d8555fdaa63ba4581c3391cc94a536fc09aac89f64eafceec3f90",
+ "0x919a8cc128de01e9e10f5d83b08b52293fdd41bde2b5ae070f3d95842d4a16e5331cf2f3d61c765570c8022403610fa4",
+ "0xb23d6f8331eef100152d60483cfa14232a85ee712c8538c9b6417a5a7c5b353c2ac401390c6c215cb101f5cee6b5f43e",
+ "0xab357160c08a18319510a571eafff154298ce1020de8e1dc6138a09fcb0fcbcdd8359f7e9386bda00b7b9cdea745ffdc",
+ "0xab55079aea34afa5c0bd1124b9cdfe01f325b402fdfa017301bf87812eaa811ea5798c3aaf818074d420d1c782b10ada",
+ "0xade616010dc5009e7fc4f8d8b00dc716686a5fa0a7816ad9e503e15839d3b909b69d9dd929b7575376434ffec0d2bea8",
+ "0x863997b97ed46898a8a014599508fa3079f414b1f4a0c4fdc6d74ae8b444afa350f327f8bfc2a85d27f9e2d049c50135",
+ "0x8d602ff596334efd4925549ed95f2aa762b0629189f0df6dbb162581657cf3ea6863cd2287b4d9c8ad52813d87fcd235",
+ "0xb70f68c596dcdeed92ad5c6c348578b26862a51eb5364237b1221e840c47a8702f0fbc56eb520a22c0eed99795d3903e",
+ "0x9628088f8e0853cefadee305a8bf47fa990c50fa96a82511bbe6e5dc81ef4b794e7918a109070f92fc8384d77ace226f",
+ "0x97e26a46e068b605ce96007197ecd943c9a23881862f4797a12a3e96ba2b8d07806ad9e2a0646796b1889c6b7d75188c",
+ "0xb1edf467c068cc163e2d6413cc22b16751e78b3312fe47b7ea82b08a1206d64415b2c8f2a677fa89171e82cc49797150",
+ "0xa44d15ef18745b251429703e3cab188420e2d974de07251501799b016617f9630643fcd06f895634d8ecdd579e1bf000",
+ "0xabd126df3917ba48c618ee4dbdf87df506193462f792874439043fa1b844466f6f4e0ff2e42516e63b5b23c0892b2695",
+ "0xa2a67f57c4aa3c2aa1eeddbfd5009a89c26c2ce8fa3c96a64626aba19514beb125f27df8559506f737de3eae0f1fc18f",
+ "0xa633e0132197e6038197304b296ab171f1d8e0d0f34dcf66fe9146ac385b0239232a8470b9205a4802ab432389f4836d",
+ "0xa914b3a28509a906c3821463b936455d58ff45dcbe158922f9efb2037f2eb0ce8e92532d29b5d5a3fcd0d23fa773f272",
+ "0xa0e1412ce4505daf1a2e59ce4f0fc0e0023e335b50d2b204422f57cd65744cc7a8ed35d5ef131a42c70b27111d3115b7",
+ "0xa2339e2f2b6072e88816224fdd612c04d64e7967a492b9f8829db15367f565745325d361fd0607b0def1be384d010d9e",
+ "0xa7309fc41203cb99382e8193a1dcf03ac190a7ce04835304eb7e341d78634e83ea47cb15b885601956736d04cdfcaa01",
+ "0x81f3ccd6c7f5b39e4e873365f8c37b214e8ab122d04a606fbb7339dc3298c427e922ec7418002561d4106505b5c399ee",
+ "0x92c121cf914ca549130e352eb297872a63200e99b148d88fbc9506ad882bec9d0203d65f280fb5b0ba92e336b7f932e8",
+ "0xa4b330cf3f064f5b131578626ad7043ce2a433b6f175feb0b52d36134a454ca219373fd30d5e5796410e005b69082e47",
+ "0x86fe5774112403ad83f9c55d58317eeb17ad8e1176d9f2f69c2afb7ed83bc718ed4e0245ceab4b377f5f062dcd4c00e7",
+ "0x809d152a7e2654c7fd175b57f7928365a521be92e1ed06c05188a95864ddb25f7cab4c71db7d61bbf4cae46f3a1d96ce",
+ "0xb82d663e55c2a5ada7e169e9b1a87bc1c0177baf1ec1c96559b4cb1c5214ce1ddf2ab8d345014cab6402f3774235cf5a",
+ "0x86580af86df1bd2c385adb8f9a079e925981b7184db66fc5fe5b14cddb82e7d836b06eaeef14924ac529487b23dae111",
+ "0xb5f5f4c5c94944ecc804df6ab8687d64e27d988cbfeae1ba7394e0f6adbf778c5881ead7cd8082dd7d68542b9bb4ecd5",
+ "0xa6016916146c2685c46e8fdd24186394e2d5496e77e08c0c6a709d4cd7dfa97f1efcef94922b89196819076a91ad37b5",
+ "0xb778e7367ded3b6eab53d5fc257f7a87e8faf74a593900f2f517220add2125be3f6142022660d8181df8d164ad9441ce",
+ "0x8581b2d36abe6f553add4d24be761bec1b8efaa2929519114346615380b3c55b59e6ad86990e312f7e234d0203bdf59b",
+ "0x9917e74fd45c3f71a829ff5498a7f6b5599b48c098dda2339bf04352bfc7f368ccf1a407f5835901240e76452ae807d7",
+ "0xafd196ce6f9335069138fd2e3d133134da253978b4ce373152c0f26affe77a336505787594022e610f8feb722f7cc1fb",
+ "0xa477491a1562e329764645e8f24d8e228e5ef28c9f74c6b5b3abc4b6a562c15ffb0f680d372aed04d9e1bf944dece7be",
+ "0x9767440d58c57d3077319d3a330e5322b9ba16981ec74a5a14d53462eab59ae7fd2b14025bfc63b268862094acb444e6",
+ "0x80986d921be3513ef69264423f351a61cb48390c1be8673aee0f089076086aaebea7ebe268fd0aa7182695606116f679",
+ "0xa9554c5c921c07b450ee04e34ec58e054ac1541b26ce2ce5a393367a97348ba0089f53db6660ad76b60278b66fd12e3e",
+ "0x95097e7d2999b3e84bf052c775581cf361325325f4a50192521d8f4693c830bed667d88f482dc1e3f833aa2bd22d2cbf",
+ "0x9014c91d0f85aefd28436b5228c12f6353c055a9326c7efbf5e071e089e2ee7c070fcbc84c5fafc336cbb8fa6fec1ca1",
+ "0x90f57ba36ee1066b55d37384942d8b57ae00f3cf9a3c1d6a3dfee1d1af42d4b5fa9baeb0cd7e46687d1d6d090ddb931d",
+ "0x8e4b1db12fd760a17214c9e47f1fce6e43c0dbb4589a827a13ac61aaae93759345697bb438a00edab92e0b7b62414683",
+ "0x8022a959a513cdc0e9c705e0fc04eafd05ff37c867ae0f31f6d01cddd5df86138a426cab2ff0ac8ff03a62e20f7e8f51",
+ "0x914e9a38829834c7360443b8ed86137e6f936389488eccf05b4b4db7c9425611705076ecb3f27105d24b85c852be7511",
+ "0x957fb10783e2bd0db1ba66b18e794df710bc3b2b05776be146fa5863c15b1ebdd39747b1a95d9564e1772cdfc4f37b8a",
+ "0xb6307028444daed8ed785ac9d0de76bc3fe23ff2cc7e48102553613bbfb5afe0ebe45e4212a27021c8eb870721e62a1f",
+ "0x8f76143597777d940b15a01b39c5e1b045464d146d9a30a6abe8b5d3907250e6c7f858ff2308f8591e8b0a7b3f3c568a",
+ "0x96163138ac0ce5fd00ae9a289648fd9300a0ca0f63a88481d703ecd281c06a52a3b5178e849e331f9c85ca4ba398f4cc",
+ "0xa63ef47c3e18245b0482596a09f488a716df3cbd0f9e5cfabed0d742843e65db8961c556f45f49762f3a6ac8b627b3ef",
+ "0x8cb595466552e7c4d42909f232d4063e0a663a8ef6f6c9b7ce3a0542b2459cde04e0e54c7623d404acb5b82775ac04f6",
+ "0xb47fe69960eb45f399368807cff16d941a5a4ebad1f5ec46e3dc8a2e4d598a7e6114d8f0ca791e9720fd786070524e2b",
+ "0x89eb5ff83eea9df490e5beca1a1fbbbbcf7184a37e2c8c91ede7a1e654c81e8cd41eceece4042ea7918a4f4646b67fd6",
+ "0xa84f5d155ed08b9054eecb15f689ba81e44589e6e7207a99790c598962837ca99ec12344105b16641ca91165672f7153",
+ "0xa6cc8f25c2d5b2d2f220ec359e6a37a52b95fa6af6e173c65e7cd55299eff4aa9e6d9e6f2769e6459313f1f2aecb0fab",
+ "0xafcde944411f017a9f7979755294981e941cc41f03df5e10522ef7c7505e5f1babdd67b3bf5258e8623150062eb41d9b",
+ "0x8fab39f39c0f40182fcd996ade2012643fe7731808afbc53f9b26900b4d4d1f0f5312d9d40b3df8baa4739970a49c732",
+ "0xae193af9726da0ebe7df1f9ee1c4846a5b2a7621403baf8e66c66b60f523e719c30c6b4f897bb14b27d3ff3da8392eeb",
+ "0x8ac5adb82d852eba255764029f42e6da92dcdd0e224d387d1ef94174038db9709ac558d90d7e7c57ad4ce7f89bbfc38c",
+ "0xa2066b3458fdf678ee487a55dd5bfb74fde03b54620cb0e25412a89ee28ad0d685e309a51e3e4694be2fa6f1593a344c",
+ "0x88d031745dd0ae07d61a15b594be5d4b2e2a29e715d081649ad63605e3404b0c3a5353f0fd9fad9c05c18e93ce674fa1",
+ "0x8283cfb0ef743a043f2b77ecaeba3005e2ca50435585b5dd24777ee6bce12332f85e21b446b536da38508807f0f07563",
+ "0xb376de22d5f6b0af0b59f7d9764561f4244cf8ffe22890ecd3dcf2ff1832130c9b821e068c9d8773136f4796721e5963",
+ "0xae3afc50c764f406353965363840bf28ee85e7064eb9d5f0bb3c31c64ab10f48c853e942ee2c9b51bae59651eaa08c2f",
+ "0x948b204d103917461a01a6c57a88f2d66b476eae5b00be20ec8c747650e864bc8a83aee0aff59cb7584b7a3387e0ee48",
+ "0x81ab098a082b07f896c5ffd1e4446cb7fb44804cbbf38d125208b233fc82f8ec9a6a8d8dd1c9a1162dc28ffeec0dde50",
+ "0xa149c6f1312821ced2969268789a3151bdda213451760b397139a028da609c4134ac083169feb0ee423a0acafd10eceb",
+ "0xb0ac9e27a5dadaf523010f730b28f0ebac01f460d3bbbe277dc9d44218abb5686f4fac89ae462682fef9edbba663520a",
+ "0x8d0e0073cca273daaaa61b6fc54bfe5a009bc3e20ae820f6c93ba77b19eca517d457e948a2de5e77678e4241807157cb",
+ "0xad61d3a2edf7c7533a04964b97499503fd8374ca64286dba80465e68fe932e96749b476f458c6fc57cb1a7ca85764d11",
+ "0x90eb5e121ae46bc01a30881eaa556f46bd8457a4e80787cf634aab355082de34ac57d7f497446468225f7721e68e2a47",
+ "0x8cdac557de7c42d1f3780e33dec1b81889f6352279be81c65566cdd4952d4c15d79e656cbd46035ab090b385e90245ef",
+ "0x82b67e61b88b84f4f4d4f65df37b3e3dcf8ec91ea1b5c008fdccd52da643adbe6468a1cfdb999e87d195afe2883a3b46",
+ "0x8503b467e8f5d6048a4a9b78496c58493a462852cab54a70594ae3fd064cfd0deb4b8f336a262155d9fedcaa67d2f6fd",
+ "0x8db56c5ac763a57b6ce6832930c57117058e3e5a81532b7d19346346205e2ec614eb1a2ee836ef621de50a7bc9b7f040",
+ "0xad344699198f3c6e8c0a3470f92aaffc805b76266734414c298e10b5b3797ca53578de7ccb2f458f5e0448203f55282b",
+ "0x80602032c43c9e2a09154cc88b83238343b7a139f566d64cb482d87436b288a98f1ea244fd3bff8da3c398686a900c14",
+ "0xa6385bd50ecd548cfb37174cdbb89e10025b5cadaf3cff164c95d7aef5a33e3d6a9bf0c681b9e11db9ef54ebeee2a0c1",
+ "0xabf2d95f4aa34b0581eb9257a0cc8462b2213941a5deb8ba014283293e8b36613951b61261cc67bbd09526a54cbbff76",
+ "0xa3d5de52f48df72c289ff713e445991f142390798cd42bd9d9dbefaee4af4f5faf09042d126b975cf6b98711c3072553",
+ "0x8e627302ff3d686cff8872a1b7c2a57b35f45bf2fc9aa42b049d8b4d6996a662b8e7cbac6597f0cb79b0cc4e29fbf133",
+ "0x8510702e101b39a1efbf4e504e6123540c34b5689645e70d0bac1ecc1baf47d86c05cef6c4317a4e99b4edaeb53f2d00",
+ "0xaa173f0ecbcc6088f878f8726d317748c81ebf501bba461f163b55d66099b191ec7c55f7702f351a9c8eb42cfa3280e2",
+ "0xb560a697eafab695bcef1416648a0a664a71e311ecbe5823ae903bd0ed2057b9d7574b9a86d3fe22aa3e6ddce38ea513",
+ "0x8df6304a3d9cf40100f3f687575419c998cd77e5cc27d579cf4f8e98642de3609af384a0337d145dd7c5635172d26a71",
+ "0x8105c7f3e4d30a29151849673853b457c1885c186c132d0a98e63096c3774bc9deb956cf957367e633d0913680bda307",
+ "0x95373fc22c0917c3c2044ac688c4f29a63ed858a45c0d6d2d0fe97afd6f532dcb648670594290c1c89010ecc69259bef",
+ "0x8c2fae9bcadab341f49b55230310df93cac46be42d4caa0d42e45104148a91e527af1b4209c0d972448162aed28fab64",
+ "0xb05a77baab70683f76209626eaefdda2d36a0b66c780a20142d23c55bd479ddd4ad95b24579384b6cf62c8eb4c92d021",
+ "0x8e6bc6a7ea2755b4aaa19c1c1dee93811fcde514f03485fdc3252f0ab7f032c315614f6336e57cea25dcfb8fb6084eeb",
+ "0xb656a27d06aade55eadae2ad2a1059198918ea6cc3fd22c0ed881294d34d5ac7b5e4700cc24350e27d76646263b223aa",
+ "0xa296469f24f6f56da92d713afcd4dd606e7da1f79dc4e434593c53695847eefc81c7c446486c4b3b8c8d00c90c166f14",
+ "0x87a326f57713ac2c9dffeb3af44b9f3c613a8f952676fc46343299122b47ee0f8d792abaa4b5db6451ced5dd153aabd0",
+ "0xb689e554ba9293b9c1f6344a3c8fcb6951d9f9eac4a2e2df13de021aade7c186be27500e81388e5b8bcab4c80f220a31",
+ "0x87ae0aa0aa48eac53d1ca5a7b93917de12db9e40ceabf8fdb40884ae771cfdf095411deef7c9f821af0b7070454a2608",
+ "0xa71ffa7eae8ace94e6c3581d4cb2ad25d48cbd27edc9ec45baa2c8eb932a4773c3272b2ffaf077b40f76942a1f3af7f2",
+ "0x94c218c91a9b73da6b7a495b3728f3028df8ad9133312fc0c03e8c5253b7ccb83ed14688fd4602e2fd41f29a0bc698bd",
+ "0xae1e77b90ca33728af07a4c03fb2ef71cd92e2618e7bf8ed4d785ce90097fc4866c29999eb84a6cf1819d75285a03af2",
+ "0xb7a5945b277dab9993cf761e838b0ac6eaa903d7111fca79f9fde3d4285af7a89bf6634a71909d095d7619d913972c9c",
+ "0x8c43b37be02f39b22029b20aca31bff661abce4471dca88aa3bddefd9c92304a088b2dfc8c4795acc301ca3160656af2",
+ "0xb32e5d0fba024554bd5fe8a793ebe8003335ddd7f585876df2048dcf759a01285fecb53daae4950ba57f3a282a4d8495",
+ "0x85ea7fd5e10c7b659df5289b2978b2c89e244f269e061b9a15fcab7983fc1962b63546e82d5731c97ec74b6804be63ef",
+ "0x96b89f39181141a7e32986ac02d7586088c5a9662cec39843f397f3178714d02f929af70630c12cbaba0268f8ba2d4fa",
+ "0x929ab1a2a009b1eb37a2817c89696a06426529ebe3f306c586ab717bd34c35a53eca2d7ddcdef36117872db660024af9",
+ "0xa696dccf439e9ca41511e16bf3042d7ec0e2f86c099e4fc8879d778a5ea79e33aa7ce96b23dc4332b7ba26859d8e674d",
+ "0xa8fe69a678f9a194b8670a41e941f0460f6e2dbc60470ab4d6ae2679cc9c6ce2c3a39df2303bee486dbfde6844e6b31a",
+ "0x95f58f5c82de2f2a927ca99bf63c9fc02e9030c7e46d0bf6b67fe83a448d0ae1c99541b59caf0e1ccab8326231af09a5",
+ "0xa57badb2c56ca2c45953bd569caf22968f76ed46b9bac389163d6fe22a715c83d5e94ae8759b0e6e8c2f27bff7748f3f",
+ "0x868726fd49963b24acb5333364dffea147e98f33aa19c7919dc9aca0fd26661cfaded74ede7418a5fadbe7f5ae67b67b",
+ "0xa8d8550dcc64d9f1dd7bcdab236c4122f2b65ea404bb483256d712c7518f08bb028ff8801f1da6aed6cbfc5c7062e33b",
+ "0x97e25a87dae23155809476232178538d4bc05d4ff0882916eb29ae515f2a62bfce73083466cc0010ca956aca200aeacc",
+ "0xb4ea26be3f4bd04aa82d7c4b0913b97bcdf5e88b76c57eb1a336cbd0a3eb29de751e1bc47c0e8258adec3f17426d0c71",
+ "0x99ee555a4d9b3cf2eb420b2af8e3bc99046880536116d0ce7193464ac40685ef14e0e3c442f604e32f8338cb0ef92558",
+ "0x8c64efa1da63cd08f319103c5c7a761221080e74227bbc58b8fb35d08aa42078810d7af3e60446cbaff160c319535648",
+ "0x8d9fd88040076c28420e3395cbdfea402e4077a3808a97b7939d49ecbcf1418fe50a0460e1c1b22ac3f6e7771d65169a",
+ "0xae3c19882d7a9875d439265a0c7003c8d410367627d21575a864b9cb4918de7dbdb58a364af40c5e045f3df40f95d337",
+ "0xb4f7bfacab7b2cafe393f1322d6dcc6f21ffe69cd31edc8db18c06f1a2b512c27bd0618091fd207ba8df1808e9d45914",
+ "0x94f134acd0007c623fb7934bcb65ef853313eb283a889a3ffa79a37a5c8f3665f3d5b4876bc66223610c21dc9b919d37",
+ "0xaa15f74051171daacdc1f1093d3f8e2d13da2833624b80a934afec86fc02208b8f55d24b7d66076444e7633f46375c6a",
+ "0xa32d6bb47ef9c836d9d2371807bafbbbbb1ae719530c19d6013f1d1f813c49a60e4fa51d83693586cba3a840b23c0404",
+ "0xb61b3599145ea8680011aa2366dc511a358b7d67672d5b0c5be6db03b0efb8ca5a8294cf220ea7409621f1664e00e631",
+ "0x859cafc3ee90b7ececa1ed8ef2b2fc17567126ff10ca712d5ffdd16aa411a5a7d8d32c9cab1fbf63e87dce1c6e2f5f53",
+ "0xa2fef1b0b2874387010e9ae425f3a9676d01a095d017493648bcdf3b31304b087ccddb5cf76abc4e1548b88919663b6b",
+ "0x939e18c73befc1ba2932a65ede34c70e4b91e74cc2129d57ace43ed2b3af2a9cc22a40fbf50d79a63681b6d98852866d",
+ "0xb3b4259d37b1b14aee5b676c9a0dd2d7f679ab95c120cb5f09f9fbf10b0a920cb613655ddb7b9e2ba5af4a221f31303c",
+ "0x997255fe51aaca6e5a9cb3359bcbf25b2bb9e30649bbd53a8a7c556df07e441c4e27328b38934f09c09d9500b5fabf66",
+ "0xabb91be2a2d860fd662ed4f1c6edeefd4da8dc10e79251cf87f06029906e7f0be9b486462718f0525d5e049472692cb7",
+ "0xb2398e593bf340a15f7801e1d1fbda69d93f2a32a889ec7c6ae5e8a37567ac3e5227213c1392ee86cfb3b56ec2787839",
+ "0x8ddf10ccdd72922bed36829a36073a460c2118fc7a56ff9c1ac72581c799b15c762cb56cb78e3d118bb9f6a7e56cb25e",
+ "0x93e6bc0a4708d16387cacd44cf59363b994dc67d7ada7b6d6dbd831c606d975247541b42b2a309f814c1bfe205681fc6",
+ "0xb93fc35c05998cffda2978e12e75812122831523041f10d52f810d34ff71944979054b04de0117e81ddf5b0b4b3e13c0",
+ "0x92221631c44d60d68c6bc7b287509f37ee44cbe5fdb6935cee36b58b17c7325098f98f7910d2c3ca5dc885ad1d6dabc7",
+ "0xa230124424a57fad3b1671f404a94d7c05f4c67b7a8fbacfccea28887b78d7c1ed40b92a58348e4d61328891cd2f6cee",
+ "0xa6a230edb8518a0f49d7231bc3e0bceb5c2ac427f045819f8584ba6f3ae3d63ed107a9a62aad543d7e1fcf1f20605706",
+ "0x845be1fe94223c7f1f97d74c49d682472585d8f772762baad8a9d341d9c3015534cc83d102113c51a9dea2ab10d8d27b",
+ "0xb44262515e34f2db597c8128c7614d33858740310a49cdbdf9c8677c5343884b42c1292759f55b8b4abc4c86e4728033",
+ "0x805592e4a3cd07c1844bc23783408310accfdb769cca882ad4d07d608e590a288b7370c2cb327f5336e72b7083a0e30f",
+ "0x95153e8b1140df34ee864f4ca601cb873cdd3efa634af0c4093fbaede36f51b55571ab271e6a133020cd34db8411241f",
+ "0x82878c1285cfa5ea1d32175c9401f3cc99f6bb224d622d3fd98cc7b0a27372f13f7ab463ce3a33ec96f9be38dbe2dfe3",
+ "0xb7588748f55783077c27fc47d33e20c5c0f5a53fc0ac10194c003aa09b9f055d08ec971effa4b7f760553997a56967b3",
+ "0xb36b4de6d1883b6951f59cfae381581f9c6352fcfcf1524fccdab1571a20f80441d9152dc6b48bcbbf00371337ca0bd5",
+ "0x89c5523f2574e1c340a955cbed9c2f7b5fbceb260cb1133160dabb7d41c2f613ec3f6e74bbfab3c4a0a6f0626dbe068f",
+ "0xa52f58cc39f968a9813b1a8ddc4e83f4219e4dd82c7aa1dd083bea7edf967151d635aa9597457f879771759b876774e4",
+ "0x8300a67c2e2e123f89704abfde095463045dbd97e20d4c1157bab35e9e1d3d18f1f4aaba9cbe6aa2d544e92578eaa1b6",
+ "0xac6a7f2918768eb6a43df9d3a8a04f8f72ee52f2e91c064c1c7d75cad1a3e83e5aba9fe55bb94f818099ac91ccf2e961",
+ "0x8d64a2b0991cf164e29835c8ddef6069993a71ec2a7de8157bbfa2e00f6367be646ed74cbaf524f0e9fe13fb09fa15fd",
+ "0x8b2ffe5a545f9f680b49d0a9797a4a11700a2e2e348c34a7a985fc278f0f12def6e06710f40f9d48e4b7fbb71e072229",
+ "0x8ab8f71cd337fa19178924e961958653abf7a598e3f022138b55c228440a2bac4176cea3aea393549c03cd38a13eb3fc",
+ "0x8419d28318c19ea4a179b7abb43669fe96347426ef3ac06b158d79c0acf777a09e8e770c2fb10e14b3a0421705990b23",
+ "0x8bacdac310e1e49660359d0a7a17fe3d334eb820e61ae25e84cb52f863a2f74cbe89c2e9fc3283745d93a99b79132354",
+ "0xb57ace3fa2b9f6b2db60c0d861ace7d7e657c5d35d992588aeed588c6ce3a80b6f0d49f8a26607f0b17167ab21b675e4",
+ "0x83e265cde477f2ecc164f49ddc7fb255bb05ff6adc347408353b7336dc3a14fdedc86d5a7fb23f36b8423248a7a67ed1",
+ "0xa60ada971f9f2d79d436de5d3d045f5ab05308cae3098acaf5521115134b2a40d664828bb89895840db7f7fb499edbc5",
+ "0xa63eea12efd89b62d3952bf0542a73890b104dd1d7ff360d4755ebfa148fd62de668edac9eeb20507967ea37fb220202",
+ "0xa0275767a270289adc991cc4571eff205b58ad6d3e93778ddbf95b75146d82517e8921bd0d0564e5b75fa0ccdab8e624",
+ "0xb9b03fd3bf07201ba3a039176a965d736b4ef7912dd9e9bf69fe1b57c330a6aa170e5521fe8be62505f3af81b41d7806",
+ "0xa95f640e26fb1106ced1729d6053e41a16e4896acac54992279ff873e5a969aad1dcfa10311e28b8f409ac1dab7f03bb",
+ "0xb144778921742418053cb3c70516c63162c187f00db2062193bb2c14031075dbe055d020cde761b26e8c58d0ea6df2c1",
+ "0x8432fbb799e0435ef428d4fefc309a05dd589bce74d7a87faf659823e8c9ed51d3e42603d878e80f439a38be4321c2fa",
+ "0xb08ddef14e42d4fd5d8bf39feb7485848f0060d43b51ed5bdda39c05fe154fb111d29719ee61a23c392141358c0cfcff",
+ "0x8ae3c5329a5e025b86b5370e06f5e61177df4bda075856fade20a17bfef79c92f54ed495f310130021ba94fb7c33632b",
+ "0x92b6d3c9444100b4d7391febfc1dddaa224651677c3695c47a289a40d7a96d200b83b64e6d9df51f534564f272a2c6c6",
+ "0xb432bc2a3f93d28b5e506d68527f1efeb2e2570f6be0794576e2a6ef9138926fdad8dd2eabfa979b79ab7266370e86bc",
+ "0x8bc315eacedbcfc462ece66a29662ca3dcd451f83de5c7626ef8712c196208fb3d8a0faf80b2e80384f0dd9772f61a23",
+ "0xa72375b797283f0f4266dec188678e2b2c060dfed5880fc6bb0c996b06e91a5343ea2b695adaab0a6fd183b040b46b56",
+ "0xa43445036fbaa414621918d6a897d3692fdae7b2961d87e2a03741360e45ebb19fcb1703d23f1e15bb1e2babcafc56ac",
+ "0xb9636b2ffe305e63a1a84bd44fb402442b1799bd5272638287aa87ca548649b23ce8ce7f67be077caed6aa2dbc454b78",
+ "0x99a30bf0921d854c282b83d438a79f615424f28c2f99d26a05201c93d10378ab2cd94a792b571ddae5d4e0c0013f4006",
+ "0x8648e3c2f93d70b392443be116b48a863e4b75991bab5db656a4ef3c1e7f645e8d536771dfe4e8d1ceda3be8d32978b0",
+ "0xab50dc9e6924c1d2e9d2e335b2d679fc7d1a7632e84964d3bac0c9fe57e85aa5906ec2e7b0399d98ddd022e9b19b5904",
+ "0xab729328d98d295f8f3272afaf5d8345ff54d58ff9884da14f17ecbdb7371857fdf2f3ef58080054e9874cc919b46224",
+ "0x83fa5da7592bd451cad3ad7702b4006332b3aae23beab4c4cb887fa6348317d234bf62a359e665b28818e5410c278a09",
+ "0x8bdbff566ae9d368f114858ef1f009439b3e9f4649f73efa946e678d6c781d52c69af195df0a68170f5f191b2eac286b",
+ "0x91245e59b4425fd4edb2a61d0d47c1ccc83d3ced8180de34887b9655b5dcda033d48cde0bdc3b7de846d246c053a02e8",
+ "0xa2cb00721e68f1cad8933947456f07144dc69653f96ceed845bd577d599521ba99cdc02421118971d56d7603ed118cbf",
+ "0xaf8cd66d303e808b22ec57860dd909ca64c27ec2c60e26ffecfdc1179d8762ffd2739d87b43959496e9fee4108df71df",
+ "0x9954136812dffcd5d3f167a500e7ab339c15cfc9b3398d83f64b0daa3dd5b9a851204f424a3493b4e326d3de81e50a62",
+ "0x93252254d12511955f1aa464883ad0da793f84d900fea83e1df8bca0f2f4cf5b5f9acbaec06a24160d33f908ab5fea38",
+ "0x997cb55c26996586ba436a95566bd535e9c22452ca5d2a0ded2bd175376557fa895f9f4def4519241ff386a063f2e526",
+ "0xa12c78ad451e0ac911260ade2927a768b50cb4125343025d43474e7f465cdc446e9f52a84609c5e7e87ae6c9b3f56cda",
+ "0xa789d4ca55cbba327086563831b34487d63d0980ba8cf55197c016702ed6da9b102b1f0709ce3da3c53ff925793a3d73",
+ "0xa5d76acbb76741ce85be0e655b99baa04f7f587347947c0a30d27f8a49ae78cce06e1cde770a8b618d3db402be1c0c4b",
+ "0x873c0366668c8faddb0eb7c86f485718d65f8c4734020f1a18efd5fa123d3ea8a990977fe13592cd01d17e60809cb5ff",
+ "0xb659b71fe70f37573ff7c5970cc095a1dc0da3973979778f80a71a347ef25ad5746b2b9608bad4ab9a4a53a4d7df42d7",
+ "0xa34cbe05888e5e5f024a2db14cb6dcdc401a9cbd13d73d3c37b348f68688f87c24ca790030b8f84fef9e74b4eab5e412",
+ "0x94ce8010f85875c045b0f014db93ef5ab9f1f6842e9a5743dce9e4cb872c94affd9e77c1f1d1ab8b8660b52345d9acb9",
+ "0xadefa9b27a62edc0c5b019ddd3ebf45e4de846165256cf6329331def2e088c5232456d3de470fdce3fa758bfdd387512",
+ "0xa6b83821ba7c1f83cc9e4529cf4903adb93b26108e3d1f20a753070db072ad5a3689643144bdd9c5ea06bb9a7a515cd0",
+ "0xa3a9ddedc2a1b183eb1d52de26718151744db6050f86f3580790c51d09226bf05f15111691926151ecdbef683baa992c",
+ "0xa64bac89e7686932cdc5670d07f0b50830e69bfb8c93791c87c7ffa4913f8da881a9d8a8ce8c1a9ce5b6079358c54136",
+ "0xa77b5a63452cb1320b61ab6c7c2ef9cfbcade5fd4727583751fb2bf3ea330b5ca67757ec1f517bf4d503ec924fe32fbd",
+ "0x8746fd8d8eb99639d8cd0ca34c0d9c3230ed5a312aab1d3d925953a17973ee5aeb66e68667e93caf9cb817c868ea8f3d",
+ "0x88a2462a26558fc1fbd6e31aa8abdc706190a17c27fdc4217ffd2297d1b1f3321016e5c4b2384c5454d5717dc732ed03",
+ "0xb78893a97e93d730c8201af2e0d3b31cb923d38dc594ffa98a714e627c473d42ea82e0c4d2eeb06862ee22a9b2c54588",
+ "0x920cc8b5f1297cf215a43f6fc843e379146b4229411c44c0231f6749793d40f07b9af7699fd5d21fd69400b97febe027",
+ "0xa0f0eafce1e098a6b58c7ad8945e297cd93aaf10bc55e32e2e32503f02e59fc1d5776936577d77c0b1162cb93b88518b",
+ "0x98480ba0064e97a2e7a6c4769b4d8c2a322cfc9a3b2ca2e67e9317e2ce04c6e1108169a20bd97692e1cb1f1423b14908",
+ "0x83dbbb2fda7e287288011764a00b8357753a6a44794cc8245a2275237f11affdc38977214e463ad67aec032f3dfa37e9",
+ "0x86442fff37598ce2b12015ff19b01bb8a780b40ad353d143a0f30a06f6d23afd5c2b0a1253716c855dbf445cc5dd6865",
+ "0xb8a4c60c5171189414887847b9ed9501bff4e4c107240f063e2d254820d2906b69ef70406c585918c4d24f1dd052142b",
+ "0x919f33a98e84015b2034b57b5ffe9340220926b2c6e45f86fd79ec879dbe06a148ae68b77b73bf7d01bd638a81165617",
+ "0x95c13e78d89474a47fbc0664f6f806744b75dede95a479bbf844db4a7f4c3ae410ec721cb6ffcd9fa9c323da5740d5ae",
+ "0xab7151acc41fffd8ec6e90387700bcd7e1cde291ea669567295bea1b9dd3f1df2e0f31f3588cd1a1c08af8120aca4921",
+ "0x80e74c5c47414bd6eeef24b6793fb1fa2d8fb397467045fcff887c52476741d5bc4ff8b6d3387cb53ad285485630537f",
+ "0xa296ad23995268276aa351a7764d36df3a5a3cffd7dbeddbcea6b1f77adc112629fdeffa0918b3242b3ccd5e7587e946",
+ "0x813d2506a28a2b01cb60f49d6bd5e63c9b056aa56946faf2f33bd4f28a8d947569cfead3ae53166fc65285740b210f86",
+ "0x924b265385e1646287d8c09f6c855b094daaee74b9e64a0dddcf9ad88c6979f8280ba30c8597b911ef58ddb6c67e9fe3",
+ "0x8d531513c70c2d3566039f7ca47cd2352fd2d55b25675a65250bdb8b06c3843db7b2d29c626eed6391c238fc651cf350",
+ "0x82b338181b62fdc81ceb558a6843df767b6a6e3ceedc5485664b4ea2f555904b1a45fbb35f6cf5d96f27da10df82a325",
+ "0x92e62faaedea83a37f314e1d3cb4faaa200178371d917938e59ac35090be1db4b4f4e0edb78b9c991de202efe4f313d8",
+ "0x99d645e1b642c2dc065bac9aaa0621bc648c9a8351efb6891559c3a41ba737bd155fb32d7731950514e3ecf4d75980e4",
+ "0xb34a13968b9e414172fb5d5ece9a39cf2eb656128c3f2f6cc7a9f0c69c6bae34f555ecc8f8837dc34b5e470e29055c78",
+ "0xa2a0bb7f3a0b23a2cbc6585d59f87cd7e56b2bbcb0ae48f828685edd9f7af0f5edb4c8e9718a0aaf6ef04553ba71f3b7",
+ "0x8e1a94bec053ed378e524b6685152d2b52d428266f2b6eadd4bcb7c4e162ed21ab3e1364879673442ee2162635b7a4d8",
+ "0x9944adaff14a85eab81c73f38f386701713b52513c4d4b838d58d4ffa1d17260a6d056b02334850ea9a31677c4b078bd",
+ "0xa450067c7eceb0854b3eca3db6cf38669d72cb7143c3a68787833cbca44f02c0be9bfbe082896f8a57debb13deb2afb1",
+ "0x8be4ad3ac9ef02f7df09254d569939757101ee2eda8586fefcd8c847adc1efe5bdcb963a0cafa17651befaafb376a531",
+ "0x90f6de91ea50255f148ac435e08cf2ac00c772a466e38155bd7e8acf9197af55662c7b5227f88589b71abe9dcf7ba343",
+ "0x86e5a24f0748b106dee2d4d54e14a3b0af45a96cbee69cac811a4196403ebbee17fd24946d7e7e1b962ac7f66dbaf610",
+ "0xafdd96fbcda7aa73bf9eeb2292e036c25753d249caee3b9c013009cc22e10d3ec29e2aa6ddbb21c4e949b0c0bccaa7f4",
+ "0xb5a4e7436d5473647c002120a2cb436b9b28e27ad4ebdd7c5f122b91597c507d256d0cbd889d65b3a908531936e53053",
+ "0xb632414c3da704d80ac2f3e5e0e9f18a3637cdc2ebeb613c29300745582427138819c4e7b0bec3099c1b8739dac1807b",
+ "0xa28df1464d3372ce9f37ef1db33cc010f752156afae6f76949d98cd799c0cf225c20228ae86a4da592d65f0cffe3951b",
+ "0x898b93d0a31f7d3f11f253cb7a102db54b669fd150da302d8354d8e02b1739a47cb9bd88015f3baf12b00b879442464e",
+ "0x96fb88d89a12049091070cb0048a381902965e67a8493e3991eaabe5d3b7ff7eecd5c94493a93b174df3d9b2c9511755",
+ "0xb899cb2176f59a5cfba3e3d346813da7a82b03417cad6342f19cc8f12f28985b03bf031e856a4743fd7ebe16324805b0",
+ "0xa60e2d31bc48e0c0579db15516718a03b73f5138f15037491f4dae336c904e312eda82d50862f4debd1622bb0e56d866",
+ "0x979fc8b987b5cef7d4f4b58b53a2c278bd25a5c0ea6f41c715142ea5ff224c707de38451b0ad3aa5e749aa219256650a",
+ "0xb2a75bff18e1a6b9cf2a4079572e41205741979f57e7631654a3c0fcec57c876c6df44733c9da3d863db8dff392b44a3",
+ "0xb7a0f0e811222c91e3df98ff7f286b750bc3b20d2083966d713a84a2281744199e664879401e77470d44e5a90f3e5181",
+ "0x82b74ba21c9d147fbc338730e8f1f8a6e7fc847c3110944eb17a48bea5e06eecded84595d485506d15a3e675fd0e5e62",
+ "0xa7f44eef817d5556f0d1abcf420301217d23c69dd2988f44d91ea1f1a16c322263cbacd0f190b9ba22b0f141b9267b4f",
+ "0xaadb68164ede84fc1cb3334b3194d84ba868d5a88e4c9a27519eef4923bc4abf81aab8114449496c073c2a6a0eb24114",
+ "0xb5378605fabe9a8c12a5dc55ef2b1de7f51aedb61960735c08767a565793cea1922a603a6983dc25f7cea738d0f7c40d",
+ "0xa97a4a5cd8d51302e5e670aee78fe6b5723f6cc892902bbb4f131e82ca1dfd5de820731e7e3367fb0c4c1922a02196e3",
+ "0x8bdfeb15c29244d4a28896f2b2cb211243cd6a1984a3f5e3b0ebe5341c419beeab3304b390a009ffb47588018034b0ea",
+ "0xa9af3022727f2aa2fca3b096968e97edad3f08edcbd0dbca107b892ae8f746a9c0485e0d6eb5f267999b23a845923ed0",
+ "0x8e7594034feef412f055590fbb15b6322dc4c6ab7a4baef4685bd13d71a83f7d682b5781bdfa0d1c659489ce9c2b8000",
+ "0x84977ca6c865ebee021c58106c1a4ad0c745949ecc5332948002fd09bd9b890524878d0c29da96fd11207621136421fe",
+ "0x8687551a79158e56b2375a271136756313122132a6670fa51f99a1b5c229ed8eea1655a734abae13228b3ebfd2a825dd",
+ "0xa0227d6708979d99edfc10f7d9d3719fd3fc68b0d815a7185b60307e4c9146ad2f9be2b8b4f242e320d4288ceeb9504c",
+ "0x89f75583a16735f9dd8b7782a130437805b34280ccea8dac6ecaee4b83fe96947e7b53598b06fecfffdf57ffc12cc445",
+ "0xa0056c3353227f6dd9cfc8e3399aa5a8f1d71edf25d3d64c982910f50786b1e395c508d3e3727ac360e3e040c64b5298",
+ "0xb070e61a6d813626144b312ded1788a6d0c7cec650a762b2f8df6e4743941dd82a2511cd956a3f141fc81e15f4e092da",
+ "0xb4e6db232e028a1f989bb5fc13416711f42d389f63564d60851f009dcffac01acfd54efa307aa6d4c0f932892d4e62b0",
+ "0x89b5991a67db90024ddd844e5e1a03ef9b943ad54194ae0a97df775dde1addf31561874f4e40fbc37a896630f3bbda58",
+ "0xad0e8442cb8c77d891df49cdb9efcf2b0d15ac93ec9be1ad5c3b3cca1f4647b675e79c075335c1f681d56f14dc250d76",
+ "0xb5d55a6ae65bb34dd8306806cb49b5ccb1c83a282ee47085cf26c4e648e19a52d9c422f65c1cd7e03ca63e926c5e92ea",
+ "0xb749501347e5ec07e13a79f0cb112f1b6534393458b3678a77f02ca89dca973fa7b30e55f0b25d8b92b97f6cb0120056",
+ "0x94144b4a3ffc5eec6ba35ce9c245c148b39372d19a928e236a60e27d7bc227d18a8cac9983851071935d8ffb64b3a34f",
+ "0x92bb4f9f85bc8c028a3391306603151c6896673135f8a7aefedd27acb322c04ef5dac982fc47b455d6740023e0dd3ea3",
+ "0xb9633a4a101461a782fc2aa092e9dbe4e2ad00987578f18cd7cf0021a909951d60fe79654eb7897806795f93c8ff4d1c",
+ "0x809f0196753024821b48a016eca5dbb449a7c55750f25981bb7a4b4c0e0846c09b8f6128137905055fc43a3f0deb4a74",
+ "0xa27dc9cdd1e78737a443570194a03d89285576d3d7f3a3cf15cc55b3013e42635d4723e2e8fe1d0b274428604b630db9",
+ "0x861f60f0462e04cd84924c36a28163def63e777318d00884ab8cb64c8df1df0bce5900342163edb60449296484a6c5bf",
+ "0xb7bc23fb4e14af4c4704a944253e760adefeca8caee0882b6bbd572c84434042236f39ae07a8f21a560f486b15d82819",
+ "0xb9a6eb492d6dd448654214bd01d6dc5ff12067a11537ab82023fc16167507ee25eed2c91693912f4155d1c07ed9650b3",
+ "0x97678af29c68f9a5e213bf0fb85c265303714482cfc4c2c00b4a1e8a76ed08834ee6af52357b143a1ca590fb0265ea5a",
+ "0x8a15b499e9eca5b6cac3070b5409e8296778222018ad8b53a5d1f6b70ad9bb10c68a015d105c941ed657bf3499299e33",
+ "0xb487fefede2e8091f2c7bfe85770db2edff1db83d4effe7f7d87bff5ab1ace35e9b823a71adfec6737fede8d67b3c467",
+ "0x8b51b916402aa2c437fce3bcad6dad3be8301a1a7eab9d163085b322ffb6c62abf28637636fe6114573950117fc92898",
+ "0xb06a2106d031a45a494adec0881cb2f82275dff9dcdd2bc16807e76f3bec28a6734edd3d54f0be8199799a78cd6228ad",
+ "0xaf0a185391bbe2315eb97feac98ad6dd2e5d931d012c621abd6e404a31cc188b286fef14871762190acf086482b2b5e2",
+ "0x8e78ee8206506dd06eb7729e32fceda3bebd8924a64e4d8621c72e36758fda3d0001af42443851d6c0aea58562870b43",
+ "0xa1ba52a569f0461aaf90b49b92be976c0e73ec4a2c884752ee52ffb62dd137770c985123d405dfb5de70692db454b54a",
+ "0x8d51b692fa1543c51f6b62b9acb8625ed94b746ef96c944ca02859a4133a5629da2e2ce84e111a7af8d9a5b836401c64",
+ "0xa7a20d45044cf6492e0531d0b8b26ffbae6232fa05a96ed7f06bdb64c2b0f5ca7ec59d5477038096a02579e633c7a3ff",
+ "0x84df867b98c53c1fcd4620fef133ee18849c78d3809d6aca0fb6f50ff993a053a455993f216c42ab6090fa5356b8d564",
+ "0xa7227c439f14c48e2577d5713c97a5205feb69acb0b449152842e278fa71e8046adfab468089c8b2288af1fc51fa945b",
+ "0x855189b3a105670779997690876dfaa512b4a25a24931a912c2f0f1936971d2882fb4d9f0b3d9daba77eaf660e9d05d5",
+ "0xb5696bd6706de51c502f40385f87f43040a5abf99df705d6aac74d88c913b8ecf7a99a63d7a37d9bdf3a941b9e432ff5",
+ "0xab997beb0d6df9c98d5b49864ef0b41a2a2f407e1687dfd6089959757ba30ed02228940b0e841afe6911990c74d536c4",
+ "0xb36b65f85546ebfdbe98823d5555144f96b4ab39279facd19c0de3b8919f105ba0315a0784dce4344b1bc62d8bb4a5a3",
+ "0xb8371f0e4450788720ac5e0f6cd3ecc5413d33895083b2c168d961ec2b5c3de411a4cc0712481cbe8df8c2fa1a7af006",
+ "0x98325d8026b810a8b7a114171ae59a57e8bbc9848e7c3df992efc523621729fd8c9f52114ce01d7730541a1ada6f1df1",
+ "0x8d0e76dbd37806259486cd9a31bc8b2306c2b95452dc395546a1042d1d17863ef7a74c636b782e214d3aa0e8d717f94a",
+ "0xa4e15ead76da0214d702c859fb4a8accdcdad75ed08b865842bd203391ec4cba2dcc916455e685f662923b96ee0c023f",
+ "0x8618190972086ebb0c4c1b4a6c94421a13f378bc961cc8267a301de7390c5e73c3333864b3b7696d81148f9d4843fd02",
+ "0x85369d6cc7342e1aa15b59141517d8db8baaaeb7ab9670f3ba3905353948d575923d283b7e5a05b13a30e7baf1208a86",
+ "0x87c51ef42233c24a6da901f28c9a075d9ba3c625687c387ad6757b72ca6b5a8885e6902a3082da7281611728b1e45f26",
+ "0xaa6348a4f71927a3106ad0ea8b02fc8d8c65531e4ab0bd0a17243e66f35afe252e40ab8eef9f13ae55a72566ffdaff5c",
+ "0x96a3bc976e9d03765cc3fee275fa05b4a84c94fed6b767e23ca689394501e96f56f7a97cffddc579a6abff632bf153be",
+ "0x97dbf96c6176379fdb2b888be4e757b2bca54e74124bd068d3fa1dbd82a011bbeb75079da38e0cd22a761fe208ecad9b",
+ "0xb70cf0a1d14089a4129ec4e295313863a59da8c7e26bf74cc0e704ed7f0ee4d7760090d0ddf7728180f1bf2c5ac64955",
+ "0x882d664714cc0ffe53cbc9bef21f23f3649824f423c4dbad1f893d22c4687ab29583688699efc4d5101aa08b0c3e267a",
+ "0x80ecb7cc963e677ccaddbe3320831dd6ee41209acf4ed41b16dc4817121a3d86a1aac9c4db3d8c08a55d28257088af32",
+ "0xa25ba667d832b145f9ce18c3f9b1bd00737aa36db020e1b99752c8ef7d27c6c448982bd8d352e1b6df266b8d8358a8d5",
+ "0x83734841c13dee12759d40bdd209b277e743b0d08cc0dd1e0b7afd2d65bfa640400eefcf6be4a52e463e5b3d885eeac6",
+ "0x848d16505b04804afc773aebabb51b36fd8aacfbb0e09b36c0d5d57df3c0a3b92f33e7d5ad0a7006ec46ebb91df42b8c",
+ "0x909a8d793f599e33bb9f1dc4792a507a97169c87cd5c087310bc05f30afcd247470b4b56dec59894c0fb1d48d39bb54e",
+ "0x8e558a8559df84a1ba8b244ece667f858095c50bb33a5381e60fcc6ba586b69693566d8819b4246a27287f16846c1dfa",
+ "0x84d6b69729f5aaa000cd710c2352087592cfbdf20d5e1166977e195818e593fa1a50d1e04566be23163a2523dc1612f1",
+ "0x9536d262b7a42125d89f4f32b407d737ba8d9242acfc99d965913ab3e043dcac9f7072a43708553562cac4cba841df30",
+ "0x9598548923ca119d6a15fd10861596601dd1dedbcccca97bb208cdc1153cf82991ea8cc17686fbaa867921065265970c",
+ "0xb87f2d4af6d026e4d2836bc3d390a4a18e98a6e386282ce96744603bab74974272e97ac2da281afa21885e2cbb3a8001",
+ "0x991ece62bf07d1a348dd22191868372904b9f8cf065ae7aa4e44fd24a53faf6d851842e35fb472895963aa1992894918",
+ "0xa8c53dea4c665b30e51d22ca6bc1bc78aaf172b0a48e64a1d4b93439b053877ec26cb5221c55efd64fa841bbf7d5aff4",
+ "0x93487ec939ed8e740f15335b58617c3f917f72d07b7a369befd479ae2554d04deb240d4a14394b26192efae4d2f4f35d",
+ "0xa44793ab4035443f8f2968a40e043b4555960193ffa3358d22112093aadfe2c136587e4139ffd46d91ed4107f61ea5e0",
+ "0xb13fe033da5f0d227c75927d3dacb06dbaf3e1322f9d5c7c009de75cdcba5e308232838785ab69a70f0bedea755e003f",
+ "0x970a29b075faccd0700fe60d1f726bdebf82d2cc8252f4a84543ebd3b16f91be42a75c9719a39c4096139f0f31393d58",
+ "0xa4c3eb1f7160f8216fc176fb244df53008ff32f2892363d85254002e66e2de21ccfe1f3b1047589abee50f29b9d507e3",
+ "0x8c552885eab04ba40922a8f0c3c38c96089c95ff1405258d3f1efe8d179e39e1295cbf67677894c607ae986e4e6b1fb0",
+ "0xb3671746fa7f848c4e2ae6946894defadd815230b906b419143523cc0597bc1d6c0a4c1e09d49b66b4a2c11cde3a4de3",
+ "0x937a249a95813a5e2ef428e355efd202e15a37d73e56cfb7e57ea9f943f2ce5ca8026f2f1fd25bf164ba89d07077d858",
+ "0x83646bdf6053a04aa9e2f112499769e5bd5d0d10f2e13db3ca89bd45c0b3b7a2d752b7d137fb3909f9c62b78166c9339",
+ "0xb4eac4b91e763666696811b7ed45e97fd78310377ebea1674b58a2250973f80492ac35110ed1240cd9bb2d17493d708c",
+ "0x82db43a99bc6573e9d92a3fd6635dbbb249ac66ba53099c3c0c8c8080b121dd8243cd5c6e36ba0a4d2525bae57f5c89c",
+ "0xa64d6a264a681b49d134c655d5fc7756127f1ee7c93d328820f32bca68869f53115c0d27fef35fe71f7bc4fdaed97348",
+ "0x8739b7a9e2b4bc1831e7f04517771bc7cde683a5e74e052542517f8375a2f64e53e0d5ac925ef722327e7bb195b4d1d9",
+ "0x8f337cdd29918a2493515ebb5cf702bbe8ecb23b53c6d18920cc22f519e276ca9b991d3313e2d38ae17ae8bdfa4f8b7e",
+ "0xb0edeab9850e193a61f138ef2739fc42ceec98f25e7e8403bfd5fa34a7bc956b9d0898250d18a69fa4625a9b3d6129da",
+ "0xa9920f26fe0a6d51044e623665d998745c9eca5bce12051198b88a77d728c8238f97d4196f26e43b24f8841500b998d0",
+ "0x86e655d61502b979eeeeb6f9a7e1d0074f936451d0a1b0d2fa4fb3225b439a3770767b649256fe481361f481a8dbc276",
+ "0x84d3b32fa62096831cc3bf013488a9f3f481dfe293ae209ed19585a03f7db8d961a7a9dd0db82bd7f62d612707575d9c",
+ "0x81c827826ec9346995ffccf62a241e3b2d32f7357acd1b1f8f7a7dbc97022d3eb51b8a1230e23ce0b401d2e535e8cd78",
+ "0x94a1e40c151191c5b055b21e86f32e69cbc751dcbdf759a48580951834b96a1eed75914c0d19a38aefd21fb6c8d43d0c",
+ "0xab890222b44bc21b71f7c75e15b6c6e16bb03371acce4f8d4353ff3b8fcd42a14026589c5ed19555a3e15e4d18bfc3a3",
+ "0xaccb0be851e93c6c8cc64724cdb86887eea284194b10e7a43c90528ed97e9ec71ca69c6fac13899530593756dd49eab2",
+ "0xb630220aa9e1829c233331413ee28c5efe94ea8ea08d0c6bfd781955078b43a4f92915257187d8526873e6c919c6a1de",
+ "0xadd389a4d358c585f1274b73f6c3c45b58ef8df11f9d11221f620e241bf3579fba07427b288c0c682885a700cc1fa28d",
+ "0xa9fe6ca8bf2961a3386e8b8dcecc29c0567b5c0b3bcf3b0f9169f88e372b80151af883871fc5229815f94f43a6f5b2b0",
+ "0xad839ae003b92b37ea431fa35998b46a0afc3f9c0dd54c3b3bf7a262467b13ff3c323ada1c1ae02ac7716528bdf39e3e",
+ "0x9356d3fd0edcbbb65713c0f2a214394f831b26f792124b08c5f26e7f734b8711a87b7c4623408da6a091c9aef1f6af3c",
+ "0x896b25b083c35ac67f0af3784a6a82435b0e27433d4d74cd6d1eafe11e6827827799490fb1c77c11de25f0d75f14e047",
+ "0x8bfa019391c9627e8e5f05c213db625f0f1e51ec68816455f876c7e55b8f17a4f13e5aae9e3fb9e1cf920b1402ee2b40",
+ "0x8ba3a6faa6a860a8f3ce1e884aa8769ceded86380a86520ab177ab83043d380a4f535fe13884346c5e51bee68da6ab41",
+ "0xa8292d0844084e4e3bb7af92b1989f841a46640288c5b220fecfad063ee94e86e13d3d08038ec2ac82f41c96a3bfe14d",
+ "0x8229bb030b2fc566e11fd33c7eab7a1bb7b49fed872ea1f815004f7398cb03b85ea14e310ec19e1f23e0bdaf60f8f76c",
+ "0x8cfbf869ade3ec551562ff7f63c2745cc3a1f4d4dc853a0cd42dd5f6fe54228f86195ea8fe217643b32e9f513f34a545",
+ "0xac52a3c8d3270ddfe1b5630159da9290a5ccf9ccbdef43b58fc0a191a6c03b8a5974cf6e2bbc7bd98d4a40a3581482d7",
+ "0xab13decb9e2669e33a7049b8eca3ca327c40dea15ad6e0e7fa63ed506db1d258bc36ac88b35f65cae0984e937eb6575d",
+ "0xb5e748eb1a7a1e274ff0cc56311c198f2c076fe4b7e73e5f80396fe85358549df906584e6bb2c8195b3e2be7736850a5",
+ "0xb5cb911325d8f963c41f691a60c37831c7d3bbd92736efa33d1f77a22b3fde7f283127256c2f47e197571e6fe0b46149",
+ "0x8a01dc6ed1b55f26427a014faa347130738b191a06b800e32042a46c13f60b49534520214359d68eb2e170c31e2b8672",
+ "0xa72fa874866e19b2efb8e069328362bf7921ec375e3bcd6b1619384c3f7ee980f6cf686f3544e9374ff54b4d17a1629c",
+ "0x8db21092f7c5f110fba63650b119e82f4b42a997095d65f08f8237b02dd66fdf959f788df2c35124db1dbd330a235671",
+ "0x8c65d50433d9954fe28a09fa7ba91a70a590fe7ba6b3060f5e4be0f6cef860b9897fa935fb4ebc42133524eb071dd169",
+ "0xb4614058e8fa21138fc5e4592623e78b8982ed72aa35ee4391b164f00c68d277fa9f9eba2eeefc890b4e86eba5124591",
+ "0xab2ad3a1bce2fbd55ca6b7c23786171fe1440a97d99d6df4d80d07dd56ac2d7203c294b32fc9e10a6c259381a73f24a1",
+ "0x812ae3315fdc18774a8da3713a4679e8ed10b9405edc548c00cacbe25a587d32040566676f135e4723c5dc25df5a22e9",
+ "0xa464b75f95d01e5655b54730334f443c8ff27c3cb79ec7af4b2f9da3c2039c609908cd128572e1fd0552eb597e8cef8d",
+ "0xa0db3172e93ca5138fe419e1c49a1925140999f6eff7c593e5681951ee0ec1c7e454c851782cbd2b8c9bc90d466e90e0",
+ "0x806db23ba7d00b87d544eed926b3443f5f9c60da6b41b1c489fba8f73593b6e3b46ebfcab671ee009396cd77d5e68aa1",
+ "0x8bfdf2c0044cc80260994e1c0374588b6653947b178e8b312be5c2a05e05767e98ea15077278506aee7df4fee1aaf89e",
+ "0x827f6558c16841b5592ff089c9c31e31eb03097623524394813a2e4093ad2d3f8f845504e2af92195aaa8a1679d8d692",
+ "0x925c4f8eab2531135cd71a4ec88e7035b5eea34ba9d799c5898856080256b4a15ed1a746e002552e2a86c9c157e22e83",
+ "0xa9f9a368f0e0b24d00a35b325964c85b69533013f9c2cfad9708be5fb87ff455210f8cb8d2ce3ba58ca3f27495552899",
+ "0x8ac0d3bebc1cae534024187e7c71f8927ba8fcc6a1926cb61c2b6c8f26bb7831019e635a376146c29872a506784a4aaa",
+ "0x97c577be2cbbfdb37ad754fae9df2ada5fc5889869efc7e18a13f8e502fbf3f4067a509efbd46fd990ab47ce9a70f5a8",
+ "0x935e7d82bca19f16614aa43b4a3474e4d20d064e4bfdf1cea2909e5c9ab72cfe3e54dc50030e41ee84f3588cebc524e9",
+ "0x941aafc08f7c0d94cebfbb1f0aad5202c02e6e37f2c12614f57e727efa275f3926348f567107ee6d8914dd71e6060271",
+ "0xaf0fbc1ba05b4b5b63399686df3619968be5d40073de0313cbf5f913d3d4b518d4c249cdd2176468ccaa36040a484f58",
+ "0xa0c414f23f46ca6d69ce74c6f8a00c036cb0edd098af0c1a7d39c802b52cfb2d5dbdf93fb0295453d4646e2af7954d45",
+ "0x909cf39e11b3875bb63b39687ae1b5d1f5a15445e39bf164a0b14691b4ddb39a8e4363f584ef42213616abc4785b5d66",
+ "0xa92bac085d1194fbd1c88299f07a061d0bdd3f980b663e81e6254dbb288bf11478c0ee880e28e01560f12c5ccb3c0103",
+ "0x841705cd5cd76b943e2b7c5e845b9dd3c8defe8ef67e93078d6d5e67ade33ad4b0fd413bc196f93b0a4073c855cd97d4",
+ "0x8e7eb8364f384a9161e81d3f1d52ceca9b65536ae49cc35b48c3e2236322ba4ae9973e0840802d9fa4f4d82ea833544f",
+ "0xaed3ab927548bc8bec31467ba80689c71a168e34f50dcb6892f19a33a099f5aa6b3f9cb79f5c0699e837b9a8c7f27efe",
+ "0xb8fbf7696210a36e20edabd77839f4dfdf50d6d015cdf81d587f90284a9bcef7d2a1ff520728d7cc69a4843d6c20dedd",
+ "0xa9d533769ce6830211c884ae50a82a7bf259b44ac71f9fb11f0296fdb3981e6b4c1753fe744647b247ebc433a5a61436",
+ "0x8b4bdf90d33360b7f428c71cde0a49fb733badba8c726876945f58c620ce7768ae0e98fc8c31fa59d8955a4823336bb1",
+ "0x808d42238e440e6571c59e52a35ae32547d502dc24fd1759d8ea70a7231a95859baf30b490a4ba55fa2f3aaa11204597",
+ "0x85594701f1d2fee6dc1956bc44c7b31db93bdeec2f3a7d622c1a08b26994760773e3d57521a44cfd7e407ac3fd430429",
+ "0xa66de045ce7173043a6825e9dc440ac957e2efb6df0a337f4f8003eb0c719d873a52e6eba3cb0d69d977ca37d9187674",
+ "0x87a1c6a1fdff993fa51efa5c3ba034c079c0928a7d599b906336af7c2dcab9721ceaf3108c646490af9dff9a754f54b3",
+ "0x926424223e462ceb75aed7c22ade8a7911a903b7e5dd4bc49746ddce8657f4616325cd12667d4393ac52cdd866396d0e",
+ "0xb5dc96106593b42b30f06f0b0a1e0c1aafc70432e31807252d3674f0b1ea5e58eac8424879d655c9488d85a879a3e572",
+ "0x997ca0987735cc716507cb0124b1d266d218b40c9d8e0ecbf26a1d65719c82a637ce7e8be4b4815d307df717bde7c72a",
+ "0x92994d3f57a569b7760324bb5ae4e8e14e1633d175dab06aa57b8e391540e05f662fdc08b8830f489a063f59b689a688",
+ "0xa8087fcc6aa4642cb998bea11facfe87eb33b90a9aa428ab86a4124ad032fc7d2e57795311a54ec9f55cc120ebe42df1",
+ "0xa9bd7d1de6c0706052ca0b362e2e70e8c8f70f1f026ea189b4f87a08ce810297ebfe781cc8004430776c54c1a05ae90c",
+ "0x856d33282e8a8e33a3d237fb0a0cbabaf77ba9edf2fa35a831fdafcadf620561846aa6cbb6bdc5e681118e1245834165",
+ "0x9524a7aa8e97a31a6958439c5f3339b19370f03e86b89b1d02d87e4887309dbbe9a3a8d2befd3b7ed5143c8da7e0a8ad",
+ "0x824fdf433e090f8acbd258ac7429b21f36f9f3b337c6d0b71d1416a5c88a767883e255b2888b7c906dd2e9560c4af24c",
+ "0x88c7fee662ca7844f42ed5527996b35723abffd0d22d4ca203b9452c639a5066031207a5ae763dbc0865b3299d19b1ec",
+ "0x919dca5c5595082c221d5ab3a5bc230f45da7f6dec4eb389371e142c1b9c6a2c919074842479c2844b72c0d806170c0c",
+ "0xb939be8175715e55a684578d8be3ceff3087f60fa875fff48e52a6e6e9979c955efef8ff67cfa2b79499ea23778e33b0",
+ "0x873b6db725e7397d11bc9bed9ac4468e36619135be686790a79bc6ed4249058f1387c9a802ea86499f692cf635851066",
+ "0xaeae06db3ec47e9e5647323fa02fac44e06e59b885ad8506bf71b184ab3895510c82f78b6b22a5d978e8218e7f761e9f",
+ "0xb99c0a8359c72ab88448bae45d4bf98797a26bca48b0d4460cd6cf65a4e8c3dd823970ac3eb774ae5d0cea4e7fadf33e",
+ "0x8f10c8ec41cdfb986a1647463076a533e6b0eec08520c1562401b36bb063ac972aa6b28a0b6ce717254e35940b900e3c",
+ "0xa106d9be199636d7add43b942290269351578500d8245d4aae4c083954e4f27f64740a3138a66230391f2d0e6043a8de",
+ "0xa469997908244578e8909ff57cffc070f1dbd86f0098df3cfeb46b7a085cfecc93dc69ee7cad90ff1dc5a34d50fe580c",
+ "0xa4ef087bea9c20eb0afc0ee4caba7a9d29dfa872137828c721391273e402fb6714afc80c40e98bbd8276d3836bffa080",
+ "0xb07a013f73cd5b98dae0d0f9c1c0f35bff8a9f019975c4e1499e9bee736ca6fcd504f9bc32df1655ff333062382cff04",
+ "0xb0a77188673e87cc83348c4cc5db1eecf6b5184e236220c8eeed7585e4b928db849944a76ec60ef7708ef6dac02d5592",
+ "0xb1284b37e59b529f0084c0dacf0af6c0b91fc0f387bf649a8c74819debf606f7b07fc3e572500016fb145ec2b24e9f17",
+ "0x97b20b5b4d6b9129da185adfbf0d3d0b0faeba5b9715f10299e48ea0521709a8296a9264ce77c275a59c012b50b6519a",
+ "0xb9d37e946fae5e4d65c1fbfacc8a62e445a1c9d0f882e60cca649125af303b3b23af53c81d7bac544fb7fcfc7a314665",
+ "0x8e5acaac379f4bb0127efbef26180f91ff60e4c525bc9b798fc50dfaf4fe8a5aa84f18f3d3cfb8baead7d1e0499af753",
+ "0xb0c0b8ab1235bf1cda43d4152e71efc1a06c548edb964eb4afceb201c8af24240bf8ab5cae30a08604e77432b0a5faf0",
+ "0x8cc28d75d5c8d062d649cbc218e31c4d327e067e6dbd737ec0a35c91db44fbbd0d40ec424f5ed79814add16947417572",
+ "0x95ae6219e9fd47efaa9cb088753df06bc101405ba50a179d7c9f7c85679e182d3033f35b00dbba71fdcd186cd775c52e",
+ "0xb5d28fa09f186ebc5aa37453c9b4d9474a7997b8ae92748ecb940c14868792292ac7d10ade01e2f8069242b308cf97e5",
+ "0x8c922a0faa14cc6b7221f302df3342f38fc8521ec6c653f2587890192732c6da289777a6cd310747ea7b7d104af95995",
+ "0xb9ad5f660b65230de54de535d4c0fcae5bc6b59db21dea5500fdc12eea4470fb8ea003690fdd16d052523418d5e01e8c",
+ "0xa39a9dd41a0ff78c82979483731f1cd68d3921c3e9965869662c22e02dde3877802e180ba93f06e7346f96d9fa9261d2",
+ "0x8b32875977ec372c583b24234c27ed73aef00cdff61eb3c3776e073afbdeade548de9497c32ec6d703ff8ad0a5cb7fe4",
+ "0x9644cbe755a5642fe9d26cfecf170d3164f1848c2c2e271d5b6574a01755f3980b3fc870b98cf8528fef6ecef4210c16",
+ "0x81ea9d1fdd9dd66d60f40ce0712764b99da9448ae0b300f8324e1c52f154e472a086dda840cb2e0b9813dc8ce8afd4b5",
+ "0x906aaa4a7a7cdf01909c5cfbc7ded2abc4b869213cbf7c922d4171a4f2e637e56f17020b852ad339d83b8ac92f111666",
+ "0x939b5f11acbdeff998f2a080393033c9b9d8d5c70912ea651c53815c572d36ee822a98d6dfffb2e339f29201264f2cf4",
+ "0xaba4898bf1ccea9b9e2df1ff19001e05891581659c1cbbde7ee76c349c7fc7857261d9785823c9463a8aea3f40e86b38",
+ "0x83ca1a56b8a0be4820bdb5a9346357c68f9772e43f0b887729a50d2eb2a326bbcede676c8bf2e51d7c89bbd8fdb778a6",
+ "0x94e86e9fe6addfe2c3ee3a547267ed921f4230d877a85bb4442c2d9350c2fa9a9c54e6fe662de82d1a2407e4ab1691c2",
+ "0xa0cc3bdef671a59d77c6984338b023fa2b431b32e9ed2abe80484d73edc6540979d6f10812ecc06d4d0c5d4eaca7183c",
+ "0xb5343413c1b5776b55ea3c7cdd1f3af1f6bd802ea95effe3f2b91a523817719d2ecc3f8d5f3cc2623ace7e35f99ca967",
+ "0x92085d1ed0ed28d8cabe3e7ff1905ed52c7ceb1eac5503760c52fb5ee3a726aba7c90b483c032acc3f166b083d7ec370",
+ "0x8ec679520455275cd957fca8122724d287db5df7d29f1702a322879b127bff215e5b71d9c191901465d19c86c8d8d404",
+ "0xb65eb2c63d8a30332eb24ee8a0c70156fc89325ebbb38bacac7cf3f8636ad8a472d81ccca80423772abc00192d886d8a",
+ "0xa9fe1c060b974bee4d590f2873b28635b61bfcf614e61ff88b1be3eee4320f4874e21e8d666d8ac8c9aba672efc6ecae",
+ "0xb3fe2a9a389c006a831dea7e777062df84b5c2803f9574d7fbe10b7e1c125817986af8b6454d6be9d931a5ac94cfe963",
+ "0x95418ad13b734b6f0d33822d9912c4c49b558f68d08c1b34a0127fcfa666bcae8e6fda8832d2c75bb9170794a20e4d7c",
+ "0xa9a7df761e7f18b79494bf429572140c8c6e9d456c4d4e336184f3f51525a65eb9582bea1e601bdb6ef8150b7ca736a5",
+ "0xa0de03b1e75edf7998c8c1ac69b4a1544a6fa675a1941950297917366682e5644a4bda9cdeedfaf9473d7fccd9080b0c",
+ "0xa61838af8d95c95edf32663a68f007d95167bf6e41b0c784a30b22d8300cfdd5703bd6d16e86396638f6db6ae7e42a85",
+ "0x8866d62084d905c145ff2d41025299d8b702ac1814a7dec4e277412c161bc9a62fed735536789cb43c88693c6b423882",
+ "0x91da22c378c81497fe363e7f695c0268443abee50f8a6625b8a41e865638a643f07b157ee566de09ba09846934b4e2d7",
+ "0x941d21dd57c9496aa68f0c0c05507405fdd413acb59bc668ce7e92e1936c68ec4b065c3c30123319884149e88228f0b2",
+ "0xa77af9b094bc26966ddf2bf9e1520c898194a5ccb694915950dadc204facbe3066d3d89f50972642d76b14884cfbaa21",
+ "0x8e76162932346869f4618bde744647f7ab52ab498ad654bdf2a4feeb986ac6e51370841e5acbb589e38b6e7142bb3049",
+ "0xb60979ace17d6937ece72e4f015da4657a443dd01cebc7143ef11c09e42d4aa8855999a65a79e2ea0067f31c9fc2ab0f",
+ "0xb3e2ffdd5ee6fd110b982fd4fad4b93d0fca65478f986d086eeccb0804960bfaa1919afa743c2239973ea65091fe57d2",
+ "0x8ce0ce05e7d7160d44574011da687454dbd3c8b8290aa671731b066e2c82f8cf2d63cb8e932d78c6122ec610e44660e6",
+ "0xab005dd8d297045c39e2f72fb1c48edb501ccf3575d3d04b9817b3afee3f0bb0f3f53f64bda37d1d9cde545aae999bae",
+ "0x95bd7edb4c4cd60e3cb8a72558845a3cce6bb7032ccdf33d5a49ebb6ddf203bc3c79e7b7e550735d2d75b04c8b2441e8",
+ "0x889953ee256206284094e4735dbbb17975bafc7c3cb94c9fbfee4c3e653857bfd49e818f64a47567f721b98411a3b454",
+ "0xb188423e707640ab0e75a061e0b62830cde8afab8e1ad3dae30db69ffae4e2fc005bababbdcbd7213b918ed4f70e0c14",
+ "0xa97e0fafe011abd70d4f99a0b36638b3d6e7354284588f17a88970ed48f348f88392779e9a038c6cbc9208d998485072",
+ "0x87db11014a91cb9b63e8dfaa82cdebca98272d89eb445ee1e3ff9dbaf2b3fad1a03b888cffc128e4fe208ed0dddece0f",
+ "0xaad2e40364edd905d66ea4ac9d51f9640d6fda9a54957d26ba233809851529b32c85660fa401dbee3679ec54fa6dd966",
+ "0x863e99336ca6edf03a5a259e59a2d0f308206e8a2fb320cfc0be06057366df8e0f94b33a28f574092736b3c5ada84270",
+ "0xb34bcc56a057589f34939a1adc51de4ff6a9f4fee9c7fa9aa131e28d0cf0759a0c871b640162acdfbf91f3f1b59a3703",
+ "0x935dd28f2896092995c5eff1618e5b6efe7a40178888d7826da9b0503c2d6e68a28e7fac1a334e166d0205f0695ef614",
+ "0xb842cd5f8f5de5ca6c68cb4a5c1d7b451984930eb4cc18fd0934d52fdc9c3d2d451b1c395594d73bc3451432bfba653f",
+ "0x9014537885ce2debad736bc1926b25fdab9f69b216bf024f589c49dc7e6478c71d595c3647c9f65ff980b14f4bb2283b",
+ "0x8e827ccca1dd4cd21707140d10703177d722be0bbe5cac578db26f1ef8ad2909103af3c601a53795435b27bf95d0c9ed",
+ "0x8a0b8ad4d466c09d4f1e9167410dbe2edc6e0e6229d4b3036d30f85eb6a333a18b1c968f6ca6d6889bb08fecde017ef4",
+ "0x9241ee66c0191b06266332dc9161dede384c4bb4e116dbd0890f3c3790ec5566da4568243665c4725b718ac0f6b5c179",
+ "0xaeb4d5fad81d2b505d47958a08262b6f1b1de9373c2c9ba6362594194dea3e002ab03b8cbb43f867be83065d3d370f19",
+ "0x8781bc83bb73f7760628629fe19e4714b494dbed444c4e4e4729b7f6a8d12ee347841a199888794c2234f51fa26fc2b9",
+ "0xb58864f0acd1c2afa29367e637cbde1968d18589245d9936c9a489c6c495f54f0113ecdcbe4680ac085dd3c397c4d0c3",
+ "0x94a24284afaeead61e70f3e30f87248d76e9726759445ca18cdb9360586c60cc9f0ec1c397f9675083e0b56459784e2e",
+ "0xaed358853f2b54dcbddf865e1816c2e89be12e940e1abfa661e2ee63ffc24a8c8096be2072fa83556482c0d89e975124",
+ "0xb95374e6b4fc0765708e370bc881e271abf2e35c08b056a03b847e089831ef4fe3124b9c5849d9c276eb2e35b3daf264",
+ "0xb834cdbcfb24c8f84bfa4c552e7fadc0028a140952fd69ed13a516e1314a4cd35d4b954a77d51a1b93e1f5d657d0315d",
+ "0x8fb6d09d23bfa90e7443753d45a918d91d75d8e12ec7d016c0dfe94e5c592ba6aaf483d2f16108d190822d955ad9cdc3",
+ "0xaa315cd3c60247a6ad4b04f26c5404c2713b95972843e4b87b5a36a89f201667d70f0adf20757ebe1de1b29ae27dda50",
+ "0xa116862dca409db8beff5b1ccd6301cdd0c92ca29a3d6d20eb8b87f25965f42699ca66974dd1a355200157476b998f3b",
+ "0xb4c2f5fe173c4dc8311b60d04a65ce1be87f070ac42e13cd19c6559a2931c6ee104859cc2520edebbc66a13dc7d30693",
+ "0x8d4a02bf99b2260c334e7d81775c5cf582b00b0c982ce7745e5a90624919028278f5e9b098573bad5515ce7fa92a80c8",
+ "0x8543493bf564ce6d97bd23be9bff1aba08bd5821ca834f311a26c9139c92a48f0c2d9dfe645afa95fec07d675d1fd53b",
+ "0x9344239d13fde08f98cb48f1f87d34cf6abe8faecd0b682955382a975e6eed64e863fa19043290c0736261622e00045c",
+ "0xaa49d0518f343005ca72b9e6c7dcaa97225ce6bb8b908ebbe7b1a22884ff8bfb090890364e325a0d414ad180b8f161d1",
+ "0x907d7fd3e009355ab326847c4a2431f688627faa698c13c03ffdd476ecf988678407f029b8543a475dcb3dafdf2e7a9c",
+ "0x845f1f10c6c5dad2adc7935f5cd2e2b32f169a99091d4f1b05babe7317b9b1cdce29b5e62f947dc621b9acbfe517a258",
+ "0x8f3be8e3b380ea6cdf9e9c237f5e88fd5a357e5ded80ea1fc2019810814de82501273b4da38916881125b6fa0cfd4459",
+ "0xb9c7f487c089bf1d20c822e579628db91ed9c82d6ca652983aa16d98b4270c4da19757f216a71b9c13ddee3e6e43705f",
+ "0x8ba2d8c88ad2b872db104ea8ddbb006ec2f3749fd0e19298a804bb3a5d94de19285cc7fb19fee58a66f7851d1a66c39f",
+ "0x9375ecd3ed16786fe161af5d5c908f56eeb467a144d3bbddfc767e90065b7c94fc53431adebecba2b6c9b5821184d36e",
+ "0xa49e069bfadb1e2e8bff6a4286872e2a9765d62f0eaa4fcb0e5af4bbbed8be3510fb19849125a40a8a81d1e33e81c3eb",
+ "0x9522cc66757b386aa6b88619525c8ce47a5c346d590bb3647d12f991e6c65c3ab3c0cfc28f0726b6756c892eae1672be",
+ "0xa9a0f1f51ff877406fa83a807aeb17b92a283879f447b8a2159653db577848cc451cbadd01f70441e351e9ed433c18bc",
+ "0x8ff7533dcff6be8714df573e33f82cf8e9f2bcaaa43e939c4759d52b754e502717950de4b4252fb904560fc31dce94a4",
+ "0x959724671e265a28d67c29d95210e97b894b360da55e4cf16e6682e7912491ed8ca14bfaa4dce9c25a25b16af580494f",
+ "0x92566730c3002f4046c737032487d0833c971e775de59fe02d9835c9858e2e3bc37f157424a69764596c625c482a2219",
+ "0xa84b47ceff13ed9c3e5e9cdf6739a66d3e7c2bd8a6ba318fefb1a9aecf653bb2981da6733ddb33c4b0a4523acc429d23",
+ "0xb4ddf571317e44f859386d6140828a42cf94994e2f1dcbcc9777f4eebbfc64fc1e160b49379acc27c4672b8e41835c5d",
+ "0x8ab95c94072b853d1603fdd0a43b30db617d13c1d1255b99075198e1947bfa5f59aed2b1147548a1b5e986cd9173d15c",
+ "0x89511f2eab33894fd4b3753d24249f410ff7263052c1fef6166fc63a79816656b0d24c529e45ccce6be28de6e375d916",
+ "0xa0866160ca63d4f2be1b4ea050dac6b59db554e2ebb4e5b592859d8df339b46fd7cb89aaed0951c3ee540aee982c238a",
+ "0x8fcc5cbba1b94970f5ff2eb1922322f5b0aa7d918d4b380c9e7abfd57afd8b247c346bff7b87af82efbce3052511cd1b",
+ "0x99aeb2a5e846b0a2874cca02c66ed40d5569eb65ab2495bc3f964a092e91e1517941f2688e79f8cca49cd3674c4e06dc",
+ "0xb7a096dc3bad5ca49bee94efd884aa3ff5615cf3825cf95fbe0ce132e35f46581d6482fa82666c7ef5f1643eaee8f1ca",
+ "0x94393b1da6eaac2ffd186b7725eca582f1ddc8cdd916004657f8a564a7c588175cb443fc6943b39029f5bbe0add3fad8",
+ "0x884b85fe012ccbcd849cb68c3ad832d83b3ef1c40c3954ffdc97f103b1ed582c801e1a41d9950f6bddc1d11f19d5ec76",
+ "0xb00061c00131eded8305a7ce76362163deb33596569afb46fe499a7c9d7a0734c084d336b38d168024c2bb42b58e7660",
+ "0xa439153ac8e6ca037381e3240e7ba08d056c83d7090f16ed538df25901835e09e27de2073646e7d7f3c65056af6e4ce7",
+ "0x830fc9ca099097d1f38b90e6843dc86f702be9d20bdacc3e52cae659dc41df5b8d2c970effa6f83a5229b0244a86fe22",
+ "0xb81ea2ffaaff2bb00dd59a9ab825ba5eed4db0d8ac9c8ed1a632ce8f086328a1cddd045fbe1ace289083c1325881b7e7",
+ "0xb51ea03c58daf2db32c99b9c4789b183365168cb5019c72c4cc91ac30b5fb7311d3db76e6fa41b7cd4a8c81e2f6cdc94",
+ "0xa4170b2c6d09ca5beb08318730419b6f19215ce6c631c854116f904be3bc30dd85a80c946a8ab054d3e307afaa3f8fbc",
+ "0x897cc42ff28971ff54d2a55dd6b35cfb8610ac902f3c06e3a5cea0e0a257e870c471236a8e84709211c742a09c5601a6",
+ "0xa18f2e98d389dace36641621488664ecbb422088ab03b74e67009b8b8acacaaa24fdcf42093935f355207d934adc52a8",
+ "0x92adcfb678cc2ba19c866f3f2b988fdcb4610567f3ab436cc0cb9acaf5a88414848d71133ebdbec1983e38e6190f1b5f",
+ "0xa86d43c2ce01b366330d3b36b3ca85f000c3548b8297e48478da1ee7d70d8576d4650cba7852ed125c0d7cb6109aa7f3",
+ "0x8ed31ceed9445437d7732dce78a762d72ff32a7636bfb3fd7974b7ae15db414d8184a1766915244355deb354fbc5803b",
+ "0x9268f70032584f416e92225d65af9ea18c466ebc7ae30952d56a4e36fd9ea811dde0a126da9220ba3c596ec54d8a335e",
+ "0x9433b99ee94f2d3fbdd63b163a2bdf440379334c52308bd24537f7defd807145a062ff255a50d119a7f29f4b85d250e3",
+ "0x90ce664f5e4628a02278f5cf5060d1a34f123854634b1870906e5723ac9afd044d48289be283b267d45fcbf3f4656aaf",
+ "0xaaf21c4d59378bb835d42ae5c5e5ab7a3c8c36a59e75997989313197752b79a472d866a23683b329ea69b048b87fa13e",
+ "0xb83c0589b304cec9ede549fde54f8a7c2a468c6657da8c02169a6351605261202610b2055c639b9ed2d5b8c401fb8f56",
+ "0x9370f326ea0f170c2c05fe2c5a49189f20aec93b6b18a5572a818cd4c2a6adb359e68975557b349fb54f065d572f4c92",
+ "0xac3232fa5ce6f03fca238bef1ce902432a90b8afce1c85457a6bee5571c033d4bceefafc863af04d4e85ac72a4d94d51",
+ "0x80d9ea168ff821b22c30e93e4c7960ce3ad3c1e6deeebedd342a36d01bd942419b187e2f382dbfd8caa34cca08d06a48",
+ "0xa387a3c61676fb3381eefa2a45d82625635a666e999aba30e3b037ec9e040f414f9e1ad9652abd3bcad63f95d85038db",
+ "0xa1b229fe32121e0b391b0f6e0180670b9dc89d79f7337de4c77ea7ad0073e9593846f06797c20e923092a08263204416",
+ "0x92164a9d841a2b828cedf2511213268b698520f8d1285852186644e9a0c97512cafa4bfbe29af892c929ebccd102e998",
+ "0x82ee2fa56308a67c7db4fd7ef539b5a9f26a1c2cc36da8c3206ba4b08258fbb3cec6fe5cdbd111433fb1ba2a1e275927",
+ "0x8c77bfe9e191f190a49d46f05600603fa42345592539b82923388d72392404e0b29a493a15e75e8b068dddcd444c2928",
+ "0x80b927f93ccf79dcf5c5b20bcf5a7d91d7a17bc0401bb7cc9b53a6797feac31026eb114257621f5a64a52876e4474cc1",
+ "0xb6b68b6501c37804d4833d5a063dd108a46310b1400549074e3cac84acc6d88f73948b7ad48d686de89c1ec043ae8c1a",
+ "0xab3da00f9bdc13e3f77624f58a3a18fc3728956f84b5b549d62f1033ae4b300538e53896e2d943f160618e05af265117",
+ "0xb6830e87233b8eace65327fdc764159645b75d2fd4024bf8f313b2dd5f45617d7ecfb4a0b53ccafb5429815a9a1adde6",
+ "0xb9251cfe32a6dc0440615aadcd98b6b1b46e3f4e44324e8f5142912b597ee3526bea2431e2b0282bb58f71be5b63f65e",
+ "0xaf8d70711e81cdddfb39e67a1b76643292652584c1ce7ce4feb1641431ad596e75c9120e85f1a341e7a4da920a9cdd94",
+ "0x98cd4e996594e89495c078bfd52a4586b932c50a449a7c8dfdd16043ca4cda94dafbaa8ad1b44249c99bbcc52152506e",
+ "0xb9fc6d1c24f48404a4a64fbe3e43342738797905db46e4132aee5f086aaa4c704918ad508aaefa455cfe1b36572e6242",
+ "0xa365e871d30ba9291cedaba1be7b04e968905d003e9e1af7e3b55c5eb048818ae5b913514fb08b24fb4fbdccbb35d0b8",
+ "0x93bf99510971ea9af9f1e364f1234c898380677c8e8de9b0dd24432760164e46c787bc9ec42a7ad450500706cf247b2d",
+ "0xb872f825a5b6e7b9c7a9ddfeded3516f0b1449acc9b4fd29fc6eba162051c17416a31e5be6d3563f424d28e65bab8b8f",
+ "0xb06b780e5a5e8eb4f4c9dc040f749cf9709c8a4c9ef15e925f442b696e41e5095db0778a6c73bcd329b265f2c6955c8b",
+ "0x848f1a981f5fc6cd9180cdddb8d032ad32cdfa614fc750d690dbae36cc0cd355cbf1574af9b3ffc8b878f1b2fafb9544",
+ "0xa03f48cbff3e9e8a3a655578051a5ae37567433093ac500ed0021c6250a51b767afac9bdb194ee1e3eac38a08c0eaf45",
+ "0xb5be78ce638ff8c4aa84352b536628231d3f7558c5be3bf010b28feac3022e64691fa672f358c8b663904aebe24a54ed",
+ "0xa9d4da70ff676fa55d1728ba6ab03b471fa38b08854d99e985d88c2d050102d8ccffbe1c90249a5607fa7520b15fe791",
+ "0x8fe9f7092ffb0b69862c8e972fb1ecf54308c96d41354ed0569638bb0364f1749838d6d32051fff1599112978c6e229c",
+ "0xae6083e95f37770ecae0df1e010456f165d96cfe9a7278c85c15cffd61034081ce5723e25e2bede719dc9341ec8ed481",
+ "0xa260891891103089a7afbd9081ea116cfd596fd1015f5b65e10b0961eb37fab7d09c69b7ce4be8bf35e4131848fb3fe4",
+ "0x8d729fa32f6eb9fd2f6a140bef34e8299a2f3111bffd0fe463aa8622c9d98bfd31a1df3f3e87cd5abc52a595f96b970e",
+ "0xa30ec6047ae4bc7da4daa7f4c28c93aedb1112cfe240e681d07e1a183782c9ff6783ac077c155af23c69643b712a533f",
+ "0xac830726544bfe7b5467339e5114c1a75f2a2a8d89453ce86115e6a789387e23551cd64620ead6283dfa4538eb313d86",
+ "0x8445c135b7a48068d8ed3e011c6d818cfe462b445095e2fbf940301e50ded23f272d799eea47683fc027430ce14613ef",
+ "0x95785411715c9ae9d8293ce16a693a2aa83e3cb1b4aa9f76333d0da2bf00c55f65e21e42e50e6c5772ce213dd7b4f7a0",
+ "0xb273b024fa18b7568c0d1c4d2f0c4e79ec509dafac8c5951f14192d63ddbcf2d8a7512c1c1b615cc38fa3e336618e0c5",
+ "0xa78b9d3ea4b6a90572eb27956f411f1d105fdb577ee2ffeec9f221da9b45db84bfe866af1f29597220c75e0c37a628d8",
+ "0xa4be2bf058c36699c41513c4d667681ce161a437c09d81383244fc55e1c44e8b1363439d0cce90a3e44581fb31d49493",
+ "0xb6eef13040f17dd4eba22aaf284d2f988a4a0c4605db44b8d2f4bf9567ac794550b543cc513c5f3e2820242dd704152e",
+ "0x87eb00489071fa95d008c5244b88e317a3454652dcb1c441213aa16b28cd3ecaa9b22fec0bdd483c1df71c37119100b1",
+ "0x92d388acdcb49793afca329cd06e645544d2269234e8b0b27d2818c809c21726bc9cf725651b951e358a63c83dedee24",
+ "0xae27e219277a73030da27ab5603c72c8bd81b6224b7e488d7193806a41343dff2456132274991a4722fdb0ef265d04cd",
+ "0x97583e08ecb82bbc27c0c8476d710389fa9ffbead5c43001bd36c1b018f29faa98de778644883e51870b69c5ffb558b5",
+ "0x90a799a8ce73387599babf6b7da12767c0591cadd36c20a7990e7c05ea1aa2b9645654ec65308ee008816623a2757a6a",
+ "0xa1b47841a0a2b06efd9ab8c111309cc5fc9e1d5896b3e42ed531f6057e5ade8977c29831ce08dbda40348386b1dcc06d",
+ "0xb92b8ef59bbddb50c9457691bc023d63dfcc54e0fd88bd5d27a09e0d98ac290fc90e6a8f6b88492043bf7c87fac8f3e4",
+ "0xa9d6240b07d62e22ec8ab9b1f6007c975a77b7320f02504fc7c468b4ee9cfcfd945456ff0128bc0ef2174d9e09333f8d",
+ "0x8e96534c94693226dc32bca79a595ca6de503af635f802e86442c67e77564829756961d9b701187fe91318da515bf0e6",
+ "0xb6ba290623cd8dd5c2f50931c0045d1cfb0c30877bc8fe58cbc3ff61ee8da100045a39153916efa1936f4aee0892b473",
+ "0xb43baa7717fac02d4294f5b3bb5e58a65b3557747e3188b482410388daac7a9c177f762d943fd5dcf871273921213da8",
+ "0xb9cf00f8fb5e2ef2b836659fece15e735060b2ea39b8e901d3dcbdcf612be8bf82d013833718c04cd46ffaa70b85f42e",
+ "0x8017d0c57419e414cbba504368723e751ef990cc6f05dad7b3c2de6360adc774ad95512875ab8337d110bf39a42026fa",
+ "0xae7401048b838c0dcd4b26bb6c56d79d51964a0daba780970b6c97daee4ea45854ea0ac0e4139b3fe60dac189f84df65",
+ "0x887b237b0cd0f816b749b21db0b40072f9145f7896c36916296973f9e6990ede110f14e5976c906d08987c9836cca57f",
+ "0xa88c3d5770148aee59930561ca1223aceb2c832fb5417e188dca935905301fc4c6c2c9270bc1dff7add490a125eb81c6",
+ "0xb6cf9b02c0cd91895ad209e38c54039523f137b5848b9d3ad33ae43af6c20c98434952db375fe378de7866f2d0e8b18a",
+ "0x84ef3d322ff580c8ad584b1fe4fe346c60866eb6a56e982ba2cf3b021ecb1fdb75ecc6c29747adda86d9264430b3f816",
+ "0xa0561c27224baf0927ad144cb71e31e54a064c598373fcf0d66aebf98ab7af1d8e2f343f77baefff69a6da750a219e11",
+ "0xaa5cc43f5b8162b016f5e1b61214c0c9d15b1078911c650b75e6cdfb49b85ee04c6739f5b1687d15908444f691f732de",
+ "0xad4ac099b935589c7b8fdfdf3db332b7b82bb948e13a5beb121ebd7db81a87d278024a1434bcf0115c54ca5109585c3d",
+ "0x8a00466abf3f109a1dcd19e643b603d3af23d42794ef8ca2514dd507ecea44a031ac6dbc18bd02f99701168b25c1791e",
+ "0xb00b5900dfad79645f8bee4e5adc7b84eb22e5b1e67df77ccb505b7fc044a6c08a8ea5faca662414eb945f874f884cea",
+ "0x950e204e5f17112250b22ea6bb8423baf522fc0af494366f18fe0f949f51d6e6812074a80875cf1ed9c8e7420058d541",
+ "0x91e5cbf8bb1a1d50c81608c9727b414d0dd2fb467ebc92f100882a3772e54f94979cfdf8e373fdef7c7fcdd60fec9e00",
+ "0xa093f6a857b8caaff80599c2e89c962b415ecbaa70d8fd973155fa976a284c6b29a855f5f7a3521134d00d2972755188",
+ "0xb4d55a3551b00da54cc010f80d99ddd2544bde9219a3173dfaadf3848edc7e4056ab532fb75ac26f5f7141e724267663",
+ "0xa03ea050fc9b011d1b04041b5765d6f6453a93a1819cd9bd6328637d0b428f08526466912895dcc2e3008ee58822e9a7",
+ "0x99b12b3665e473d01bc6985844f8994fb65cb15745024fb7af518398c4a37ff215da8f054e8fdf3286984ae36a73ca5e",
+ "0x9972c7e7a7fb12e15f78d55abcaf322c11249cd44a08f62c95288f34f66b51f146302bce750ff4d591707075d9123bd2",
+ "0xa64b4a6d72354e596d87cda213c4fc2814009461570ccb27d455bbe131f8d948421a71925425b546d8cf63d5458cd64b",
+ "0x91c215c73b195795ede2228b7ed1f6e37892e0c6b0f4a0b5a16c57aa1100c84df9239054a173b6110d6c2b7f4bf1ce52",
+ "0x88807198910ec1303480f76a3683870246a995e36adaeadc29c22f0bdba8152fe705bd070b75de657b04934f7d0ccf80",
+ "0xb37c0026c7b32eb02cacac5b55cb5fe784b8e48b2945c64d3037af83ece556a117f0ff053a5968c2f5fa230e291c1238",
+ "0x94c768384ce212bc2387e91ce8b45e4ff120987e42472888a317abc9dcdf3563b62e7a61c8e98d7cdcbe272167d91fc6",
+ "0xa10c2564936e967a390cb14ef6e8f8b04ea9ece5214a38837eda09e79e0c7970b1f83adf017c10efd6faa8b7ffa2c567",
+ "0xa5085eed3a95f9d4b1269182ea1e0d719b7809bf5009096557a0674bde4201b0ddc1f0f16a908fc468846b3721748ce3",
+ "0x87468eb620b79a0a455a259a6b4dfbc297d0d53336537b771254dd956b145dc816b195b7002647ea218552e345818a3f",
+ "0xace2b77ffb87366af0a9cb5d27d6fc4a14323dbbf1643f5f3c4559306330d86461bb008894054394cbfaefeaa0bc2745",
+ "0xb27f56e840a54fbd793f0b7a7631aa4cee64b5947e4382b2dfb5eb1790270288884c2a19afebe5dc0c6ef335d4531c1c",
+ "0x876e438633931f7f895062ee16c4b9d10428875f7bc79a8e156a64d379a77a2c45bf5430c5ab94330f03da352f1e9006",
+ "0xa2512a252587d200d2092b44c914df54e04ff8bcef36bf631f84bde0cf5a732e3dc7f00f662842cfd74b0b0f7f24180e",
+ "0x827f1bc8f54a35b7a4bd8154f79bcc055e45faed2e74adf7cf21cca95df44d96899e847bd70ead6bb27b9c0ed97bbd8b",
+ "0xa0c92cf5a9ed843714f3aea9fe7b880f622d0b4a3bf66de291d1b745279accf6ba35097849691370f41732ba64b5966b",
+ "0xa63f5c1e222775658421c487b1256b52626c6f79cb55a9b7deb2352622cedffb08502042d622eb3b02c97f9c09f9c957",
+ "0x8cc093d52651e65fb390e186db6cc4de559176af4624d1c44cb9b0e836832419dacac7b8db0627b96288977b738d785d",
+ "0xaa7b6a17dfcec146134562d32a12f7bd7fe9522e300859202a02939e69dbd345ed7ff164a184296268f9984f9312e8fc",
+ "0x8ac76721f0d2b679f023d06cbd28c85ae5f4b43c614867ccee88651d4101d4fd352dbdb65bf36bfc3ebc0109e4b0c6f9",
+ "0x8d350f7c05fc0dcd9a1170748846fb1f5d39453e4cb31e6d1457bed287d96fc393b2ecc53793ca729906a33e59c6834a",
+ "0xb9913510dfc5056d7ec5309f0b631d1ec53e3a776412ada9aefdaf033c90da9a49fdde6719e7c76340e86599b1f0eec2",
+ "0x94955626bf4ce87612c5cfffcf73bf1c46a4c11a736602b9ba066328dc52ad6d51e6d4f53453d4ed55a51e0aad810271",
+ "0xb0fcab384fd4016b2f1e53f1aafd160ae3b1a8865cd6c155d7073ecc1664e05b1d8bca1def39c158c7086c4e1103345e",
+ "0x827de3f03edfbde08570b72de6662c8bfa499b066a0a27ebad9b481c273097d17a5a0a67f01553da5392ec3f149b2a78",
+ "0xab7940384c25e9027c55c40df20bd2a0d479a165ced9b1046958353cd69015eeb1e44ed2fd64e407805ba42df10fc7bf",
+ "0x8ad456f6ff8cd58bd57567d931f923d0c99141978511b17e03cab7390a72b9f62498b2893e1b05c7c22dd274e9a31919",
+ "0xac75399e999effe564672db426faa17a839e57c5ef735985c70cd559a377adec23928382767b55ed5a52f7b11b54b756",
+ "0xb17f975a00b817299ac7af5f2024ea820351805df58b43724393bfb3920a8cd747a3bbd4b8286e795521489db3657168",
+ "0xa2bed800a6d95501674d9ee866e7314063407231491d794f8cf57d5be020452729c1c7cefd8c50dc1540181f5caab248",
+ "0x9743f5473171271ffdd3cc59a3ae50545901a7b45cd4bc3570db487865f3b73c0595bebabbfe79268809ee1862e86e4a",
+ "0xb7eab77c2d4687b60d9d7b04e842b3880c7940140012583898d39fcc22d9b9b0a9be2c2e3788b3e6f30319b39c338f09",
+ "0x8e2b8f797a436a1b661140e9569dcf3e1eea0a77c7ff2bc4ff0f3e49af04ed2de95e255df8765f1d0927fb456a9926b1",
+ "0x8aefea201d4a1f4ff98ffce94e540bb313f2d4dfe7e9db484a41f13fc316ed02b282e1acc9bc6f56cad2dc2e393a44c9",
+ "0xb950c17c0e5ca6607d182144aa7556bb0efe24c68f06d79d6413a973b493bfdf04fd147a4f1ab03033a32004cc3ea66f",
+ "0xb7b8dcbb179a07165f2dc6aa829fad09f582a71b05c3e3ea0396bf9e6fe73076f47035c031c2101e8e38e0d597eadd30",
+ "0xa9d77ed89c77ec1bf8335d08d41c3c94dcca9fd1c54f22837b4e54506b212aa38d7440126c80648ab7723ff18e65ed72",
+ "0xa819d6dfd4aef70e52b8402fe5d135f8082d40eb7d3bb5c4d7997395b621e2bb10682a1bad2c9caa33dd818550fc3ec6",
+ "0x8f6ee34128fac8bbf13ce2d68b2bb363eb4fd65b297075f88e1446ddeac242500eeb4ef0735e105882ff5ba8c44c139b",
+ "0xb4440e48255c1644bcecf3a1e9958f1ec4901cb5b1122ee5b56ffd02cad1c29c4266999dbb85aa2605c1b125490074d4",
+ "0xa43304a067bede5f347775d5811cf65a6380a8d552a652a0063580b5c5ef12a0867a39c7912fa219e184f4538eba1251",
+ "0xa891ad67a790089ffc9f6d53e6a3d63d3556f5f693e0cd8a7d0131db06fd4520e719cfcc3934f0a8f62a95f90840f1d4",
+ "0xaea6df8e9bb871081aa0fc5a9bafb00be7d54012c5baf653791907d5042a326aeee966fd9012a582cc16695f5baf7042",
+ "0x8ffa2660dc52ed1cd4eff67d6a84a8404f358a5f713d04328922269bee1e75e9d49afeec0c8ad751620f22352a438e25",
+ "0x87ec6108e2d63b06abed350f8b363b7489d642486f879a6c3aa90e5b0f335efc2ff2834eef9353951a42136f8e6a1b32",
+ "0x865619436076c2760d9e87ddc905023c6de0a8d56eef12c98a98c87837f2ca3f27fd26a2ad752252dbcbe2b9f1d5a032",
+ "0x980437dce55964293cb315c650c5586ffd97e7a944a83f6618af31c9d92c37b53ca7a21bb5bc557c151b9a9e217e7098",
+ "0x95d128fc369df4ad8316b72aea0ca363cbc7b0620d6d7bb18f7076a8717a6a46956ff140948b0cc4f6d2ce33b5c10054",
+ "0x8c7212d4a67b9ec70ebbca04358ad2d36494618d2859609163526d7b3acc2fc935ca98519380f55e6550f70a9bc76862",
+ "0x893a2968819401bf355e85eee0f0ed0406a6d4a7d7f172d0017420f71e00bb0ba984f6020999a3cdf874d3cd8ebcd371",
+ "0x9103c1af82dece25d87274e89ea0acd7e68c2921c4af3d8d7c82ab0ed9990a5811231b5b06113e7fa43a6bd492b4564f",
+ "0x99cfd87a94eab7d35466caa4ed7d7bb45e5c932b2ec094258fb14bf205659f83c209b83b2f2c9ccb175974b2a33e7746",
+ "0x874b6b93e4ee61be3f00c32dd84c897ccd6855c4b6251eb0953b4023634490ed17753cd3223472873cbc6095b2945075",
+ "0x84a32c0dc4ea60d33aac3e03e70d6d639cc9c4cc435c539eff915017be3b7bdaba33349562a87746291ebe9bc5671f24",
+ "0xa7057b24208928ad67914e653f5ac1792c417f413d9176ba635502c3f9c688f7e2ee81800d7e3dc0a340c464da2fd9c5",
+ "0xa03fb9ed8286aacfa69fbd5d953bec591c2ae4153400983d5dbb6cd9ea37fff46ca9e5cceb9d117f73e9992a6c055ad2",
+ "0x863b2de04e89936c9a4a2b40380f42f20aefbae18d03750fd816c658aee9c4a03df7b12121f795c85d01f415baaeaa59",
+ "0x8526eb9bd31790fe8292360d7a4c3eed23be23dd6b8b8f01d2309dbfdc0cfd33ad1568ddd7f8a610f3f85a9dfafc6a92",
+ "0xb46ab8c5091a493d6d4d60490c40aa27950574a338ea5bbc045be3a114af87bdcb160a8c80435a9b7ad815f3cb56a3f3",
+ "0xaeadc47b41a8d8b4176629557646202f868b1d728b2dda58a347d937e7ffc8303f20d26d6c00b34c851b8aeec547885d",
+ "0xaebb19fc424d72c1f1822aa7adc744cd0ef7e55727186f8df8771c784925058c248406ebeeaf3c1a9ee005a26e9a10c6",
+ "0x8ff96e81c1a4a2ab1b4476c21018fae0a67e92129ee36120cae8699f2d7e57e891f5c624902cb1b845b944926a605cc3",
+ "0x8251b8d2c43fadcaa049a9e7aff838dae4fb32884018d58d46403ac5f3beb5c518bfd45f03b8abb710369186075eb71c",
+ "0xa8b2a64f865f51a5e5e86a66455c093407933d9d255d6b61e1fd81ffafc9538d73caaf342338a66ba8ee166372a3d105",
+ "0xaad915f31c6ba7fdc04e2aaac62e84ef434b7ee76a325f07dc430d12c84081999720181067b87d792efd0117d7ee1eab",
+ "0xa13db3bb60389883fd41d565c54fb5180d9c47ce2fe7a169ae96e01d17495f7f4fa928d7e556e7c74319c4c25d653eb2",
+ "0xa4491b0198459b3f552855d680a59214eb74e6a4d6c5fa3b309887dc50ebea2ecf6d26c040550f7dc478b452481466fb",
+ "0x8f017f13d4b1e3f0c087843582b52d5f8d13240912254d826dd11f8703a99a2f3166dfbdfdffd9a3492979d77524276b",
+ "0x96c3d5dcd032660d50d7cd9db2914f117240a63439966162b10c8f1f3cf74bc83b0f15451a43b31dbd85e4a7ce0e4bb1",
+ "0xb479ec4bb79573d32e0ec93b92bdd7ec8c26ddb5a2d3865e7d4209d119fd3499eaac527615ffac78c440e60ef3867ae0",
+ "0xb2c49c4a33aa94b52b6410b599e81ff15490aafa7e43c8031c865a84e4676354a9c81eb4e7b8be6825fdcefd1e317d44",
+ "0x906dc51d6a90c089b6704b47592805578a6eed106608eeb276832f127e1b8e858b72e448edcbefb497d152447e0e68ff",
+ "0xb0e81c63b764d7dfbe3f3fddc9905aef50f3633e5d6a4af6b340495124abedcff5700dfd1577bbbed7b6bf97d02719cb",
+ "0x9304c64701e3b4ed6d146e48a881f7d83a17f58357cca0c073b2bb593afd2d94f6e2a7a1ec511d0a67ad6ff4c3be5937",
+ "0xb6fdbd12ba05aa598d80b83f70a15ef90e5cba7e6e75fa038540ee741b644cd1f408a6cecfd2a891ef8d902de586c6b5",
+ "0xb80557871a6521b1b3c74a1ba083ae055b575df607f1f7b04c867ba8c8c181ea68f8d90be6031f4d25002cca27c44da2",
+ "0xaa7285b8e9712e06b091f64163f1266926a36607f9d624af9996856ed2aaf03a580cb22ce407d1ade436c28b44ca173f",
+ "0x8148d72b975238b51e6ea389e5486940d22641b48637d7dfadfa603a605bfc6d74a016480023945d0b85935e396aea5d",
+ "0x8a014933a6aea2684b5762af43dcf4bdbb633cd0428d42d71167a2b6fc563ece5e618bff22f1db2ddb69b845b9a2db19",
+ "0x990d91740041db770d0e0eb9d9d97d826f09fd354b91c41e0716c29f8420e0e8aac0d575231efba12fe831091ec38d5a",
+ "0x9454d0d32e7e308ddec57cf2522fb1b67a2706e33fb3895e9e1f18284129ab4f4c0b7e51af25681d248d7832c05eb698",
+ "0xa5bd434e75bac105cb3e329665a35bce6a12f71dd90c15165777d64d4c13a82bceedb9b48e762bd24034e0fc9fbe45f4",
+ "0xb09e3b95e41800d4dc29c6ffdaab2cd611a0050347f6414f154a47ee20ee59bf8cf7181454169d479ebce1eb5c777c46",
+ "0xb193e341d6a047d15eea33766d656d807b89393665a783a316e9ba10518e5515c8e0ade3d6e15641d917a8a172a5a635",
+ "0xade435ec0671b3621dde69e07ead596014f6e1daa1152707a8c18877a8b067bde2895dd47444ffa69db2bbef1f1d8816",
+ "0xa7fd3d6d87522dfc56fb47aef9ce781a1597c56a8bbfd796baba907afdc872f753d732bfda1d3402aee6c4e0c189f52d",
+ "0xa298cb4f4218d0464b2fab393e512bbc477c3225aa449743299b2c3572f065bc3a42d07e29546167ed9e1b6b3b3a3af3",
+ "0xa9ee57540e1fd9c27f4f0430d194b91401d0c642456c18527127d1f95e2dba41c2c86d1990432eb38a692fda058fafde",
+ "0x81d6c1a5f93c04e6d8e5a7e0678c1fc89a1c47a5c920bcd36180125c49fcf7c114866b90e90a165823560b19898a7c16",
+ "0xa4b7a1ec9e93c899b9fd9aaf264c50e42c36c0788d68296a471f7a3447af4dbc81e4fa96070139941564083ec5b5b5a1",
+ "0xb3364e327d381f46940c0e11e29f9d994efc6978bf37a32586636c0070b03e4e23d00650c1440f448809e1018ef9f6d8",
+ "0x8056e0913a60155348300e3a62e28b5e30629a90f7dd4fe11289097076708110a1d70f7855601782a3cdc5bdb1ca9626",
+ "0xb4980fd3ea17bac0ba9ee1c470b17e575bb52e83ebdd7d40c93f4f87bebeaff1c8a679f9d3d09d635f068d37d5bd28bd",
+ "0x905a9299e7e1853648e398901dfcd437aa575c826551f83520df62984f5679cb5f0ea86aa45ed3e18b67ddc0dfafe809",
+ "0xab99553bf31a84f2e0264eb34a08e13d8d15e2484aa9352354becf9a15999c76cc568d68274b70a65e49703fc23540d0",
+ "0xa43681597bc574d2dae8964c9a8dc1a07613d7a1272bdcb818d98c85d44e16d744250c33f3b5e4d552d97396b55e601f",
+ "0xa54e5a31716fccb50245898c99865644405b8dc920ded7a11f3d19bdc255996054b268e16f2e40273f11480e7145f41e",
+ "0x8134f3ad5ef2ad4ba12a8a4e4d8508d91394d2bcdc38b7c8c8c0b0a820357ac9f79d286c65220f471eb1adca1d98fc68",
+ "0x94e2f755e60471578ab2c1adb9e9cea28d4eec9b0e92e0140770bca7002c365fcabfe1e5fb4fe6cfe79a0413712aa3ef",
+ "0xad48f8d0ce7eb3cc6e2a3086ad96f562e5bed98a360721492ae2e74dc158586e77ec8c35d5fd5927376301b7741bad2b",
+ "0x8614f0630bdd7fbad3a31f55afd9789f1c605dc85e7dc67e2edfd77f5105f878bb79beded6e9f0b109e38ea7da67e8d5",
+ "0x9804c284c4c5e77dabb73f655b12181534ca877c3e1e134aa3f47c23b7ec92277db34d2b0a5d38d2b69e5d1c3008a3e3",
+ "0xa51b99c3088e473afdaa9e0a9f7e75a373530d3b04e44e1148da0726b95e9f5f0c7e571b2da000310817c36f84b19f7f",
+ "0xac4ff909933b3b76c726b0a382157cdc74ab851a1ac6cef76953c6444441804cc43abb883363f416592e8f6cfbc4550b",
+ "0xae7d915eb9fc928b65a29d6edbc75682d08584d0014f7bcf17d59118421ae07d26a02137d1e4de6938bcd1ab8ef48fad",
+ "0x852f7e453b1af89b754df6d11a40d5d41ea057376e8ecacd705aacd2f917457f4a093d6b9a8801837fa0f62986ad7149",
+ "0x92c6bf5ada5d0c3d4dd8058483de36c215fa98edab9d75242f3eff9db07c734ad67337da6f0eefe23a487bf75a600dee",
+ "0xa2b42c09d0db615853763552a48d2e704542bbd786aae016eb58acbf6c0226c844f5fb31e428cb6450b9db855f8f2a6f",
+ "0x880cc07968266dbfdcfbc21815cd69e0eddfee239167ac693fb0413912d816f2578a74f7716eecd6deefa68c6eccd394",
+ "0xb885b3ace736cd373e8098bf75ba66fa1c6943ca1bc4408cd98ac7074775c4478594f91154b8a743d9c697e1b29f5840",
+ "0xa51ce78de512bd87bfa0835de819941dffbf18bec23221b61d8096fc9436af64e0693c335b54e7bfc763f287bdca2db6",
+ "0xa3c76166a3bdb9b06ef696e57603b58871bc72883ee9d45171a30fe6e1d50e30bc9c51b4a0f5a7270e19a77b89733850",
+ "0xacefc5c6f8a1e7c24d7b41e0fc7f6f3dc0ede6cf3115ffb9a6e54b1d954cbca9bda8ad7a084be9be245a1b8e9770d141",
+ "0xb420ed079941842510e31cfad117fa11fb6b4f97dfbc6298cb840f27ebaceba23eeaf3f513bcffbf5e4aae946310182d",
+ "0x95c3bb5ef26c5ed2f035aa5d389c6b3c15a6705b9818a3fefaed28922158b35642b2e8e5a1a620fdad07e75ad4b43af4",
+ "0x825149f9081ecf07a2a4e3e8b5d21bade86c1a882475d51c55ee909330b70c5a2ac63771c8600c6f38df716af61a3ea1",
+ "0x873b935aae16d9f08adbc25353cee18af2f1b8d5f26dec6538d6bbddc515f2217ed7d235dcfea59ae61b428798b28637",
+ "0x9294150843a2bedcedb3bb74c43eb28e759cf9499582c5430bccefb574a8ddd4f11f9929257ff4c153990f9970a2558f",
+ "0xb619563a811cc531da07f4f04e5c4c6423010ff9f8ed7e6ec9449162e3d501b269fb1c564c09c0429431879b0f45df02",
+ "0x91b509b87eb09f007d839627514658c7341bc76d468920fe8a740a8cb96a7e7e631e0ea584a7e3dc1172266f641d0f5c",
+ "0x8b8aceace9a7b9b4317f1f01308c3904d7663856946afbcea141a1c615e21ccad06b71217413e832166e9dd915fbe098",
+ "0x87b3b36e725833ea0b0f54753c3728c0dbc87c52d44d705ffc709f2d2394414c652d3283bab28dcce09799504996cee0",
+ "0xb2670aad5691cbf308e4a6a77a075c4422e6cbe86fdba24e9f84a313e90b0696afb6a067eebb42ba2d10340d6a2f6e51",
+ "0x876784a9aff3d54faa89b2bacd3ff5862f70195d0b2edc58e8d1068b3c9074c0da1cfa23671fe12f35e33b8a329c0ccd",
+ "0x8b48b9e758e8a8eae182f5cbec96f67d20cca6d3eee80a2d09208eb1d5d872e09ef23d0df8ebbb9b01c7449d0e3e3650",
+ "0xb79303453100654c04a487bdcadc9e3578bc80930c489a7069a52e8ca1dba36c492c8c899ce025f8364599899baa287d",
+ "0x961b35a6111da54ece6494f24dacd5ea46181f55775b5f03df0e370c34a5046ac2b4082925855325bb42bc2a2c98381d",
+ "0xa31feb1be3f5a0247a1f7d487987eb622e34fca817832904c6ee3ee60277e5847945a6f6ea1ac24542c72e47bdf647df",
+ "0xa12a2aa3e7327e457e1aae30e9612715dd2cfed32892c1cd6dcda4e9a18203af8a44afb46d03b2eed89f6b9c5a2c0c23",
+ "0xa08265a838e69a2ca2f80fead6ccf16f6366415b920c0b22ee359bcd8d4464ecf156f400a16a7918d52e6d733dd64211",
+ "0xb723d6344e938d801cca1a00032af200e541d4471fd6cbd38fb9130daa83f6a1dffbbe7e67fc20f9577f884acd7594b2",
+ "0xa6733d83ec78ba98e72ddd1e7ff79b7adb0e559e256760d0c590a986e742445e8cdf560d44b29439c26d87edd0b07c8c",
+ "0xa61c2c27d3f7b9ff4695a17afedf63818d4bfba390507e1f4d0d806ce8778d9418784430ce3d4199fd3bdbc2504d2af3",
+ "0x8332f3b63a6dc985376e8b1b25eeae68be6160fbe40053ba7bcf6f073204f682da72321786e422d3482fd60c9e5aa034",
+ "0xa280f44877583fbb6b860d500b1a3f572e3ee833ec8f06476b3d8002058e25964062feaa1e5bec1536d734a5cfa09145",
+ "0xa4026a52d277fcea512440d2204f53047718ebfcae7b48ac57ea7f6bfbc5de9d7304db9a9a6cbb273612281049ddaec5",
+ "0x95cdf69c831ab2fad6c2535ede9c07e663d2ddccc936b64e0843d2df2a7b1c31f1759c3c20f1e7a57b1c8f0dbb21b540",
+ "0x95c96cec88806469c277ab567863c5209027cecc06c7012358e5f555689c0d9a5ffb219a464f086b45817e8536b86d2f",
+ "0xafe38d4684132a0f03d806a4c8df556bf589b25271fbc6fe2e1ed16de7962b341c5003755da758d0959d2e6499b06c68",
+ "0xa9b77784fda64987f97c3a23c5e8f61b918be0f7c59ba285084116d60465c4a2aaafc8857eb16823282cc83143eb9126",
+ "0xa830f05881ad3ce532a55685877f529d32a5dbe56cea57ffad52c4128ee0fad0eeaf0da4362b55075e77eda7babe70e5",
+ "0x992b3ad190d6578033c13ed5abfee4ef49cbc492babb90061e3c51ee4b5790cdd4c8fc1abff1fa2c00183b6b64f0bbbe",
+ "0xb1015424d9364aeff75de191652dc66484fdbec3e98199a9eb9671ec57bec6a13ff4b38446e28e4d8aedb58dd619cd90",
+ "0xa745304604075d60c9db36cada4063ac7558e7ec2835d7da8485e58d8422e817457b8da069f56511b02601289fbb8981",
+ "0xa5ba4330bc5cb3dbe0486ddf995632a7260a46180a08f42ae51a2e47778142132463cc9f10021a9ad36986108fefa1a9",
+ "0xb419e9fd4babcaf8180d5479db188bb3da232ae77a1c4ed65687c306e6262f8083070a9ac32220cddb3af2ec73114092",
+ "0xa49e23dc5f3468f3bf3a0bb7e4a114a788b951ff6f23a3396ae9e12cbff0abd1240878a3d1892105413dbc38818e807c",
+ "0xb7ecc7b4831f650202987e85b86bc0053f40d983f252e9832ef503aea81c51221ce93279da4aa7466c026b2d2070e55d",
+ "0x96a8c35cb87f84fa84dcd6399cc2a0fd79cc9158ef4bdde4bae31a129616c8a9f2576cd19baa3f497ca34060979aed7d",
+ "0x8681b2c00aa62c2b519f664a95dcb8faef601a3b961bb4ce5d85a75030f40965e2983871d41ea394aee934e859581548",
+ "0x85c229a07efa54a713d0790963a392400f55fbb1a43995a535dc6c929f20d6a65cf4efb434e0ad1cb61f689b8011a3bc",
+ "0x90856f7f3444e5ad44651c28e24cc085a5db4d2ffe79aa53228c26718cf53a6e44615f3c5cda5aa752d5f762c4623c66",
+ "0x978999b7d8aa3f28a04076f74d11c41ef9c89fdfe514936c4238e0f13c38ec97e51a5c078ebc6409e517bfe7ccb42630",
+ "0xa099914dd7ed934d8e0d363a648e9038eb7c1ec03fa04dbcaa40f7721c618c3ef947afef7a16b4d7ac8c12aa46637f03",
+ "0xab2a104fed3c83d16f2cda06878fa5f30c8c9411de71bfb67fd2fc9aa454dcbcf3d299d72f8cc12e919466a50fcf7426",
+ "0xa4471d111db4418f56915689482f6144efc4664cfb0311727f36c864648d35734351becc48875df96f4abd3cfcf820f9",
+ "0x83be11727cd30ea94ccc8fa31b09b81c9d6a9a5d3a4686af9da99587332fe78c1f94282f9755854bafd6033549afec91",
+ "0x88020ff971dc1a01a9e993cd50a5d2131ffdcbb990c1a6aaa54b20d8f23f9546a70918ea57a21530dcc440c1509c24ad",
+ "0xae24547623465e87905eaffa1fa5d52bb7c453a8dbd89614fa8819a2abcedaf455c2345099b7324ae36eb0ad7c8ef977",
+ "0xb59b0c60997de1ee00b7c388bc7101d136c9803bf5437b1d589ba57c213f4f835a3e4125b54738e78abbc21b000f2016",
+ "0xa584c434dfe194546526691b68fa968c831c31da42303a1d735d960901c74011d522246f37f299555416b8cf25c5a548",
+ "0x80408ce3724f4837d4d52376d255e10f69eb8558399ae5ca6c11b78b98fe67d4b93157d2b9b639f1b5b64198bfe87713",
+ "0xabb941e8d406c2606e0ddc35c113604fdd9d249eacc51cb64e2991e551b8639ce44d288cc92afa7a1e7fc599cfc84b22",
+ "0xb223173f560cacb1c21dba0f1713839e348ad02cbfdef0626748604c86f89e0f4c919ed40b583343795bdd519ba952c8",
+ "0xaf1c70512ec3a19d98b8a1fc3ff7f7f5048a27d17d438d43f561974bbdd116fcd5d5c21040f3447af3f0266848d47a15",
+ "0x8a44809568ebe50405bede19b4d2607199159b26a1b33e03d180e6840c5cf59d991a4fb150d111443235d75ecad085b7",
+ "0xb06207cdca46b125a27b3221b5b50cf27af4c527dd7c80e2dbcebbb09778a96df3af67e50f07725239ce3583dad60660",
+ "0x993352d9278814ec89b26a11c4a7c4941bf8f0e6781ae79559d14749ee5def672259792db4587f85f0100c7bb812f933",
+ "0x9180b8a718b971fd27bc82c8582d19c4b4f012453e8c0ffeeeffe745581fc6c07875ab28be3af3fa3896d19f0c89ac5b",
+ "0x8b8e1263eb48d0fe304032dd5ea1f30e73f0121265f7458ba9054d3626894e8a5fef665340abd2ede9653045c2665938",
+ "0x99a2beee4a10b7941c24b2092192faf52b819afd033e4a2de050fd6c7f56d364d0cf5f99764c3357cf32399e60fc5d74",
+ "0x946a4aad7f8647ea60bee2c5fcdeb6f9a58fb2cfca70c4d10e458027a04846e13798c66506151be3df9454b1e417893f",
+ "0xa672a88847652d260b5472d6908d1d57e200f1e492d30dd1cecc441cdfc9b76e016d9bab560efd4d7f3c30801de884a9",
+ "0x9414e1959c156cde1eb24e628395744db75fc24b9df4595350aaad0bc38e0246c9b4148f6443ef68b8e253a4a6bcf11c",
+ "0x9316e9e4ec5fab4f80d6540df0e3a4774db52f1d759d2e5b5bcd3d7b53597bb007eb1887cb7dc61f62497d51ffc8d996",
+ "0x902d6d77bb49492c7a00bc4b70277bc28c8bf9888f4307bb017ac75a962decdedf3a4e2cf6c1ea9f9ba551f4610cbbd7",
+ "0xb07025a18b0e32dd5e12ec6a85781aa3554329ea12c4cd0d3b2c22e43d777ef6f89876dd90a9c8fb097ddf61cf18adc5",
+ "0xb355a849ad3227caa4476759137e813505ec523cbc2d4105bc7148a4630f9e81918d110479a2d5f5e4cd9ccec9d9d3e3",
+ "0xb49532cfdf02ee760109881ad030b89c48ee3bb7f219ccafc13c93aead754d29bdafe345be54c482e9d5672bd4505080",
+ "0x9477802410e263e4f938d57fa8f2a6cac7754c5d38505b73ee35ea3f057aad958cb9722ba6b7b3cfc4524e9ca93f9cdc",
+ "0x9148ea83b4436339580f3dbc9ba51509e9ab13c03063587a57e125432dd0915f5d2a8f456a68f8fff57d5f08c8f34d6e",
+ "0xb00b6b5392b1930b54352c02b1b3b4f6186d20bf21698689bbfc7d13e86538a4397b90e9d5c93fd2054640c4dbe52a4f",
+ "0x926a9702500441243cd446e7cbf15dde16400259726794694b1d9a40263a9fc9e12f7bcbf12a27cb9aaba9e2d5848ddc",
+ "0xa0c6155f42686cbe7684a1dc327100962e13bafcf3db97971fc116d9f5c0c8355377e3d70979cdbd58fd3ea52440901c",
+ "0xa277f899f99edb8791889d0817ea6a96c24a61acfda3ad8c3379e7c62b9d4facc4b965020b588651672fd261a77f1bfc",
+ "0x8f528cebb866b501f91afa50e995234bef5bf20bff13005de99cb51eaac7b4f0bf38580cfd0470de40f577ead5d9ba0f",
+ "0x963fc03a44e9d502cc1d23250efef44d299befd03b898d07ce63ca607bb474b5cf7c965a7b9b0f32198b04a8393821f7",
+ "0xab087438d0a51078c378bf4a93bd48ef933ff0f1fa68d02d4460820df564e6642a663b5e50a5fe509527d55cb510ae04",
+ "0xb0592e1f2c54746bb076be0fa480e1c4bebc4225e1236bcda3b299aa3853e3afb401233bdbcfc4a007b0523a720fbf62",
+ "0x851613517966de76c1c55a94dc4595f299398a9808f2d2f0a84330ba657ab1f357701d0895f658c18a44cb00547f6f57",
+ "0xa2fe9a1dd251e72b0fe4db27be508bb55208f8f1616b13d8be288363ec722826b1a1fd729fc561c3369bf13950bf1fd6",
+ "0xb896cb2bc2d0c77739853bc59b0f89b2e008ba1f701c9cbe3bef035f499e1baee8f0ff1e794854a48c320586a2dfc81a",
+ "0xa1b60f98e5e5106785a9b81a85423452ee9ef980fa7fa8464f4366e73f89c50435a0c37b2906052b8e58e212ebd366cf",
+ "0xa853b0ebd9609656636df2e6acd5d8839c0fda56f7bf9288a943b06f0b67901a32b95e016ca8bc99bd7b5eab31347e72",
+ "0xb290fa4c1346963bd5225235e6bdf7c542174dab4c908ab483d1745b9b3a6015525e398e1761c90e4b49968d05e30eea",
+ "0xb0f65a33ad18f154f1351f07879a183ad62e5144ad9f3241c2d06533dad09cbb2253949daff1bb02d24d16a3569f7ef0",
+ "0xa00db59b8d4218faf5aeafcd39231027324408f208ec1f54d55a1c41228b463b88304d909d16b718cfc784213917b71e",
+ "0xb8d695dd33dc2c3bc73d98248c535b2770ad7fa31aa726f0aa4b3299efb0295ba9b4a51c71d314a4a1bd5872307534d1",
+ "0xb848057cca2ca837ee49c42b88422303e58ea7d2fc76535260eb5bd609255e430514e927cc188324faa8e657396d63ec",
+ "0x92677836061364685c2aaf0313fa32322746074ed5666fd5f142a7e8f87135f45cd10e78a17557a4067a51dfde890371",
+ "0xa854b22c9056a3a24ab164a53e5c5cf388616c33e67d8ebb4590cb16b2e7d88b54b1393c93760d154208b5ca822dc68f",
+ "0x86fff174920388bfab841118fb076b2b0cdec3fdb6c3d9a476262f82689fb0ed3f1897f7be9dbf0932bb14d346815c63",
+ "0x99661cf4c94a74e182752bcc4b98a8c2218a8f2765642025048e12e88ba776f14f7be73a2d79bd21a61def757f47f904",
+ "0x8a8893144d771dca28760cba0f950a5d634195fd401ec8cf1145146286caffb0b1a6ba0c4c1828d0a5480ce49073c64c",
+ "0x938a59ae761359ee2688571e7b7d54692848eb5dde57ffc572b473001ea199786886f8c6346a226209484afb61d2e526",
+ "0x923f68a6aa6616714cf077cf548aeb845bfdd78f2f6851d8148cba9e33a374017f2f3da186c39b82d14785a093313222",
+ "0xac923a93d7da7013e73ce8b4a2b14b8fd0cc93dc29d5de941a70285bdd19be4740fedfe0c56b046689252a3696e9c5bc",
+ "0xb49b32c76d4ec1a2c68d4989285a920a805993bc6fcce6dacd3d2ddae73373050a5c44ba8422a3781050682fa0ef6ba2",
+ "0x8a367941c07c3bdca5712524a1411bad7945c7c48ffc7103b1d4dff2c25751b0624219d1ccde8c3f70c465f954be5445",
+ "0xb838f029df455efb6c530d0e370bbbf7d87d61a9aea3d2fe5474c5fe0a39cf235ceecf9693c5c6c5820b1ba8f820bd31",
+ "0xa8983b7c715eaac7f13a001d2abc462dfc1559dab4a6b554119c271aa8fe00ffcf6b6949a1121f324d6d26cb877bcbae",
+ "0xa2afb24ad95a6f14a6796315fbe0d8d7700d08f0cfaf7a2abe841f5f18d4fecf094406cbd54da7232a159f9c5b6e805e",
+ "0x87e8e95ad2d62f947b2766ff405a23f7a8afba14e7f718a691d95369c79955cdebe24c54662553c60a3f55e6322c0f6f",
+ "0x87c2cbcecb754e0cc96128e707e5c5005c9de07ffd899efa3437cadc23362f5a1d3fcdd30a1f5bdc72af3fb594398c2a",
+ "0x91afd6ee04f0496dc633db88b9370d41c428b04fd991002502da2e9a0ef051bcd7b760e860829a44fbe5539fa65f8525",
+ "0x8c50e5d1a24515a9dd624fe08b12223a75ca55196f769f24748686315329b337efadca1c63f88bee0ac292dd0a587440",
+ "0x8a07e8f912a38d94309f317c32068e87f68f51bdfa082d96026f5f5f8a2211621f8a3856dda8069386bf15fb2d28c18f",
+ "0x94ad1dbe341c44eeaf4dc133eed47d8dbfe752575e836c075745770a6679ff1f0e7883b6aa917462993a7f469d74cab5",
+ "0x8745f8bd86c2bb30efa7efb7725489f2654f3e1ac4ea95bd7ad0f3cfa223055d06c187a16192d9d7bdaea7b050c6a324",
+ "0x900d149c8d79418cda5955974c450a70845e02e5a4ecbcc584a3ca64d237df73987c303e3eeb79da1af83bf62d9e579f",
+ "0x8f652ab565f677fb1a7ba03b08004e3cda06b86c6f1b0b9ab932e0834acf1370abb2914c15b0d08327b5504e5990681c",
+ "0x9103097d088be1f75ab9d3da879106c2f597e2cc91ec31e73430647bdd5c33bcfd771530d5521e7e14df6acda44f38a6",
+ "0xb0fec7791cfb0f96e60601e1aeced9a92446b61fedab832539d1d1037558612d78419efa87ff5f6b7aab8fd697d4d9de",
+ "0xb9d2945bdb188b98958854ba287eb0480ef614199c4235ce5f15fc670b8c5ffe8eeb120c09c53ea8a543a022e6a321ac",
+ "0xa9461bb7d5490973ebaa51afc0bb4a5e42acdccb80e2f939e88b77ac28a98870e103e1042899750f8667a8cc9123bae9",
+ "0xa37fdf11d4bcb2aed74b9f460a30aa34afea93386fa4cdb690f0a71bc58f0b8df60bec56e7a24f225978b862626fa00e",
+ "0xa214420e183e03d531cf91661466ea2187d84b6e814b8b20b3730a9400a7d25cf23181bb85589ebc982cec414f5c2923",
+ "0xad09a45a698a6beb3e0915f540ef16e9af7087f53328972532d6b5dfe98ce4020555ece65c6cbad8bd6be8a4dfefe6fd",
+ "0xab6742800b02728c92d806976764cb027413d6f86edd08ad8bb5922a2969ee9836878cd39db70db0bd9a2646862acc4f",
+ "0x974ca9305bd5ea1dc1755dff3b63e8bfe9f744321046c1395659bcea2a987b528e64d5aa96ac7b015650b2253b37888d",
+ "0x84eee9d6bce039c52c2ebc4fccc0ad70e20c82f47c558098da4be2f386a493cbc76adc795b5488c8d11b6518c2c4fab8",
+ "0x875d7bda46efcb63944e1ccf760a20144df3b00d53282b781e95f12bfc8f8316dfe6492c2efbf796f1150e36e436e9df",
+ "0xb68a2208e0c587b5c31b5f6cb32d3e6058a9642e2d9855da4f85566e1412db528475892060bb932c55b3a80877ad7b4a",
+ "0xba006368ecab5febb6ab348644d9b63de202293085ed468df8bc24d992ae8ce468470aa37f36a73630c789fb9c819b30",
+ "0x90a196035150846cd2b482c7b17027471372a8ce7d914c4d82b6ea7fa705d8ed5817bd42d63886242585baf7d1397a1c",
+ "0xa223b4c85e0daa8434b015fd9170b5561fe676664b67064974a1e9325066ecf88fc81f97ab5011c59fad28cedd04b240",
+ "0x82e8ec43139cf15c6bbeed484b62e06cded8a39b5ce0389e4cbe9c9e9c02f2f0275d8d8d4e8dfec8f69a191bef220408",
+ "0x81a3fc07a7b68d92c6ee4b6d28f5653ee9ec85f7e2ee1c51c075c1b130a8c5097dc661cf10c5aff1c7114b1a6a19f11a",
+ "0x8ed2ef8331546d98819a5dd0e6c9f8cb2630d0847671314a28f277faf68da080b53891dd75c82cbcf7788b255490785d",
+ "0xacecabf84a6f9bbed6b2fc2e7e4b48f02ef2f15e597538a73aea8f98addc6badda15e4695a67ecdb505c1554e8f345ec",
+ "0xb8f51019b2aa575f8476e03dcadf86cc8391f007e5f922c2a36b2daa63f5a503646a468990cd5c65148d323942193051",
+ "0xaaa595a84b403ec65729bc1c8055a94f874bf9adddc6c507b3e1f24f79d3ad359595a672b93aab3394db4e2d4a7d8970",
+ "0x895144c55fcbd0f64d7dd69e6855cfb956e02b5658eadf0f026a70703f3643037268fdd673b0d21b288578a83c6338dd",
+ "0xa2e92ae6d0d237d1274259a8f99d4ea4912a299816350b876fba5ebc60b714490e198a916e1c38c6e020a792496fa23c",
+ "0xa45795fda3b5bb0ad1d3c628f6add5b2a4473a1414c1a232e80e70d1cfffd7f8a8d9861f8df2946999d7dbb56bf60113",
+ "0xb6659bf7f6f2fef61c39923e8c23b8c70e9c903028d8f62516d16755cd3fba2fe41c285aa9432dc75ab08f8a1d8a81fc",
+ "0xa735609a6bc5bfd85e58234fc439ff1f58f1ff1dd966c5921d8b649e21f006bf2b8642ad8a75063c159aaf6935789293",
+ "0xa3c622eb387c9d15e7bda2e3e84d007cb13a6d50d655c3f2f289758e49d3b37b9a35e4535d3cc53d8efd51f407281f19",
+ "0x8afe147b53ad99220f5ef9d763bfc91f9c20caecbcf823564236fb0e6ede49414c57d71eec4772c8715cc65a81af0047",
+ "0xb5f0203233cf71913951e9c9c4e10d9243e3e4a1f2cb235bf3f42009120ba96e04aa414c9938ea8873b63148478927e8",
+ "0x93c52493361b458d196172d7ba982a90a4f79f03aa8008edc322950de3ce6acf4c3977807a2ffa9e924047e02072b229",
+ "0xb9e72b805c8ac56503f4a86c82720afbd5c73654408a22a2ac0b2e5caccdfb0e20b59807433a6233bc97ae58cf14c70a",
+ "0xaf0475779b5cee278cca14c82da2a9f9c8ef222eb885e8c50cca2315fea420de6e04146590ed0dd5a29c0e0812964df5",
+ "0xb430ccab85690db02c2d0eb610f3197884ca12bc5f23c51e282bf3a6aa7e4a79222c3d8761454caf55d6c01a327595f9",
+ "0x830032937418b26ee6da9b5206f3e24dc76acd98589e37937e963a8333e5430abd6ce3dd93ef4b8997bd41440eed75d6",
+ "0x8820a6d73180f3fe255199f3f175c5eb770461ad5cfdde2fb11508041ed19b8c4ce66ad6ecebf7d7e836cc2318df47ca",
+ "0xaef1393e7d97278e77bbf52ef6e1c1d5db721ccf75fe753cf47a881fa034ca61eaa5098ee5a344c156d2b14ff9e284ad",
+ "0x8a4a26c07218948c1196c45d927ef4d2c42ade5e29fe7a91eaebe34a29900072ce5194cf28d51f746f4c4c649daf4396",
+ "0x84011dc150b7177abdcb715efbd8c201f9cb39c36e6069af5c50a096021768ba40cef45b659c70915af209f904ede3b6",
+ "0xb1bd90675411389bb66910b21a4bbb50edce5330850c5ab0b682393950124252766fc81f5ecfc72fb7184387238c402e",
+ "0x8dfdcd30583b696d2c7744655f79809f451a60c9ad5bf1226dc078b19f4585d7b3ef7fa9d54e1ac09520d95cbfd20928",
+ "0xb351b4dc6d98f75b8e5a48eb7c6f6e4b78451991c9ba630e5a1b9874c15ac450cd409c1a024713bf2cf82dc400e025ef",
+ "0xa462b8bc97ac668b97b28b3ae24b9f5de60e098d7b23ecb600d2194cd35827fb79f77c3e50d358f5bd72ee83fef18fa0",
+ "0xa183753265c5f7890270821880cce5f9b2965b115ba783c6dba9769536f57a04465d7da5049c7cf8b3fcf48146173c18",
+ "0xa8a771b81ed0d09e0da4d79f990e58eabcd2be3a2680419502dd592783fe52f657fe55125b385c41d0ba3b9b9cf54a83",
+ "0xa71ec577db46011689d073245e3b1c3222a9b1fe6aa5b83629adec5733dd48617ebea91346f0dd0e6cdaa86e4931b168",
+ "0xa334b8b244f0d598a02da6ae0f918a7857a54dce928376c4c85df15f3b0f2ba3ac321296b8b7c9dd47d770daf16c8f8c",
+ "0xa29037f8ef925c417c90c4df4f9fb27fb977d04e2b3dd5e8547d33e92ab72e7a00f5461de21e28835319eae5db145eb7",
+ "0xb91054108ae78b00e3298d667b913ebc44d8f26e531eae78a8fe26fdfb60271c97efb2dee5f47ef5a3c15c8228138927",
+ "0x926c13efbe90604f6244be9315a34f72a1f8d1aab7572df431998949c378cddbf2fe393502c930fff614ff06ae98a0ce",
+ "0x995c758fd5600e6537089b1baa4fbe0376ab274ff3e82a17768b40df6f91c2e443411de9cafa1e65ea88fb8b87d504f4",
+ "0x9245ba307a7a90847da75fca8d77ec03fdfc812c871e7a2529c56a0a79a6de16084258e7a9ac4ae8a3756f394336e21c",
+ "0x99e0cfa2bb57a7e624231317044c15e52196ecce020db567c8e8cb960354a0be9862ee0c128c60b44777e65ac315e59f",
+ "0xad4f6b3d27bbbb744126601053c3dc98c07ff0eb0b38a898bd80dce778372846d67e5ab8fb34fb3ad0ef3f235d77ba7f",
+ "0xa0f12cae3722bbbca2e539eb9cc7614632a2aefe51410430070a12b5bc5314ecec5857b7ff8f41e9980cac23064f7c56",
+ "0xb487f1bc59485848c98222fd3bc36c8c9bb3d2912e2911f4ceca32c840a7921477f9b1fe00877e05c96c75d3eecae061",
+ "0xa6033db53925654e18ecb3ce715715c36165d7035db9397087ac3a0585e587998a53973d011ac6d48af439493029cee6",
+ "0xa6b4d09cd01c70a3311fd131d3710ccf97bde3e7b80efd5a8c0eaeffeb48cca0f951ced905290267b115b06d46f2693b",
+ "0xa9dff1df0a8f4f218a98b6f818a693fb0d611fed0fc3143537cbd6578d479af13a653a8155e535548a2a0628ae24fa58",
+ "0xa58e469f65d366b519f9a394cacb7edaddac214463b7b6d62c2dbc1316e11c6c5184ce45c16de2d77f990dcdd8b55430",
+ "0x989e71734f8119103586dc9a3c5f5033ddc815a21018b34c1f876cdfc112efa868d5751bf6419323e4e59fa6a03ece1c",
+ "0xa2da00e05036c884369e04cf55f3de7d659cd5fa3f849092b2519dd263694efe0f051953d9d94b7e121f0aee8b6174d7",
+ "0x968f3c029f57ee31c4e1adea89a7f92e28483af9a74f30fbdb995dc2d40e8e657dff8f8d340d4a92bf65f54440f2859f",
+ "0x932778df6f60ac1639c1453ef0cbd2bf67592759dcccb3e96dcc743ff01679e4c7dd0ef2b0833dda548d32cb4eba49e2",
+ "0xa805a31139f8e0d6dae1ac87d454b23a3dc9fc653d4ca18d4f8ebab30fc189c16e73981c2cb7dd6f8c30454a5208109d",
+ "0xa9ba0991296caa2aaa4a1ceacfb205544c2a2ec97088eace1d84ee5e2767656a172f75d2f0c4e16a3640a0e0dec316e0",
+ "0xb1e49055c968dced47ec95ae934cf45023836d180702e20e2df57e0f62fb85d7ac60d657ba3ae13b8560b67210449459",
+ "0xa94e1da570a38809c71e37571066acabff7bf5632737c9ab6e4a32856924bf6211139ab3cedbf083850ff2d0e0c0fcfc",
+ "0x88ef1bb322000c5a5515b310c838c9af4c1cdbb32eab1c83ac3b2283191cd40e9573747d663763a28dad0d64adc13840",
+ "0xa987ce205f923100df0fbd5a85f22c9b99b9b9cbe6ddfa8dfda1b8fe95b4f71ff01d6c5b64ca02eb24edb2b255a14ef0",
+ "0x84fe8221a9e95d9178359918a108de4763ebfa7a6487facb9c963406882a08a9a93f492f8e77cf9e7ea41ae079c45993",
+ "0xaa1cf3dc7c5dcfa15bbbc811a4bb6dbac4fba4f97fb1ed344ab60264d7051f6eef19ea9773441d89929ee942ed089319",
+ "0x8f6a7d610d59d9f54689bbe6a41f92d9f6096cde919c1ab94c3c7fcecf0851423bc191e5612349e10f855121c0570f56",
+ "0xb5af1fa7894428a53ea520f260f3dc3726da245026b6d5d240625380bfb9c7c186df0204bb604efac5e613a70af5106e",
+ "0xa5bce6055ff812e72ce105f147147c7d48d7a2313884dd1f488b1240ee320f13e8a33f5441953a8e7a3209f65b673ce1",
+ "0xb9b55b4a1422677d95821e1d042ab81bbf0bf087496504021ec2e17e238c2ca6b44fb3b635a5c9eac0871a724b8d47c3",
+ "0x941c38e533ce4a673a3830845b56786585e5fe49c427f2e5c279fc6db08530c8f91db3e6c7822ec6bb4f956940052d18",
+ "0xa38e191d66c625f975313c7007bbe7431b5a06ed2da1290a7d5d0f2ec73770d476efd07b8e632de64597d47df175cbb0",
+ "0x94ba76b667abf055621db4c4145d18743a368d951565632ed4e743dd50dd3333507c0c34f286a5c5fdbf38191a2255cd",
+ "0xa5ca38c60be5602f2bfa6e00c687ac96ac36d517145018ddbee6f12eb0faa63dd57909b9eeed26085fe5ac44e55d10ab",
+ "0xb00fea3b825e60c1ed1c5deb4b551aa65a340e5af36b17d5262c9cd2c508711e4dc50dc2521a2c16c7c901902266e64a",
+ "0x971b86fc4033485e235ccb0997a236206ba25c6859075edbcdf3c943116a5030b7f75ebca9753d863a522ba21a215a90",
+ "0xb3b31f52370de246ee215400975b674f6da39b2f32514fe6bd54e747752eedca22bb840493b44a67df42a3639c5f901f",
+ "0xaffbbfac9c1ba7cbfa1839d2ae271dd6149869b75790bf103230637da41857fc326ef3552ff31c15bda0694080198143",
+ "0xa95d42aa7ef1962520845aa3688f2752d291926f7b0d73ea2ee24f0612c03b43f2b0fe3c9a9a99620ffc8d487b981bc2",
+ "0x914a266065caf64985e8c5b1cb2e3f4e3fe94d7d085a1881b1fefa435afef4e1b39a98551d096a62e4f5cc1a7f0fdc2e",
+ "0x81a0b4a96e2b75bc1bf2dbd165d58d55cfd259000a35504d1ffb18bc346a3e6f07602c683723864ffb980f840836fd8d",
+ "0x91c1556631cddd4c00b65b67962b39e4a33429029d311c8acf73a18600e362304fb68bccb56fde40f49e95b7829e0b87",
+ "0x8befbacc19e57f7c885d1b7a6028359eb3d80792fe13b92a8400df21ce48deb0bb60f2ddb50e3d74f39f85d7eab23adc",
+ "0x92f9458d674df6e990789690ec9ca73dacb67fc9255b58c417c555a8cc1208ace56e8e538f86ba0f3615573a0fbac00d",
+ "0xb4b1b3062512d6ae7417850c08c13f707d5838e43d48eb98dd4621baf62eee9e82348f80fe9b888a12874bfa538771f8",
+ "0xa13c4a3ac642ede37d9c883f5319e748d2b938f708c9d779714108a449b343f7b71a6e3ef4080fee125b416762920273",
+ "0xaf44983d5fc8cceee0551ef934e6e653f2d3efa385e5c8a27a272463a6f333e290378cc307c2b664eb923c78994e706e",
+ "0xa389fd6c59fe2b4031cc244e22d3991e541bd203dd5b5e73a6159e72df1ab41d49994961500dcde7989e945213184778",
+ "0x8d2141e4a17836c548de9598d7b298b03f0e6c73b7364979a411c464e0628e21cff6ac3d6decdba5d1c4909eff479761",
+ "0x980b22ef53b7bdf188a3f14bc51b0dbfdf9c758826daa3cbc1e3986022406a8aa9a6a79e400567120b88c67faa35ce5f",
+ "0xa28882f0a055f96df3711de5d0aa69473e71245f4f3e9aa944e9d1fb166e02caa50832e46da6d3a03b4801735fd01b29",
+ "0x8db106a37d7b88f5d995c126abb563934dd8de516af48e85695d02b1aea07f79217e3cdd03c6f5ca57421830186c772b",
+ "0xb5a7e50da0559a675c472f7dfaee456caab6695ab7870541b2be8c2b118c63752427184aad81f0e1afc61aef1f28c46f",
+ "0x9962118780e20fe291d10b64f28d09442a8e1b5cffd0f3dd68d980d0614050a626c616b44e9807fbee7accecae00686a",
+ "0xb38ddf33745e8d2ad6a991aefaf656a33c5f8cbe5d5b6b6fd03bd962153d8fd0e01b5f8f96d80ae53ab28d593ab1d4e7",
+ "0x857dc12c0544ff2c0c703761d901aba636415dee45618aba2e3454ff9cbc634a85c8b05565e88520ff9be2d097c8b2b1",
+ "0xa80d465c3f8cc63af6d74a6a5086b626c1cb4a8c0fee425964c3bd203d9d7094e299f81ce96d58afc20c8c9a029d9dae",
+ "0x89e1c8fbde8563763be483123a3ed702efac189c6d8ab4d16c85e74bbaf856048cc42d5d6e138633a38572ba5ec3f594",
+ "0x893a594cf495535f6d216508f8d03c317dcf03446668cba688da90f52d0111ac83d76ad09bf5ea47056846585ee5c791",
+ "0xaadbd8be0ae452f7f9450c7d2957598a20cbf10139a4023a78b4438172d62b18b0de39754dd2f8862dbd50a3a0815e53",
+ "0xae7d39670ecca3eb6db2095da2517a581b0e8853bdfef619b1fad9aacd443e7e6a40f18209fadd44038a55085c5fe8b2",
+ "0x866ef241520eacb6331593cfcb206f7409d2f33d04542e6e52cba5447934e02d44c471f6c9a45963f9307e9809ab91d9",
+ "0xb1a09911ad3864678f7be79a9c3c3eb5c84a0a45f8dcb52c67148f43439aeaaa9fd3ed3471276b7e588b49d6ebe3033a",
+ "0xadd07b7f0dbb34049cd8feeb3c18da5944bf706871cfd9f14ff72f6c59ad217ebb1f0258b13b167851929387e4e34cfe",
+ "0xae048892d5c328eefbdd4fba67d95901e3c14d974bfc0a1fc68155ca9f0d59e61d7ba17c6c9948b120cf35fd26e6fee9",
+ "0x9185b4f3b7da0ddb4e0d0f09b8a9e0d6943a4611e43f13c3e2a767ed8592d31e0ba3ebe1914026a3627680274291f6e5",
+ "0xa9c022d4e37b0802284ce3b7ee9258628ab4044f0db4de53d1c3efba9de19d15d65cc5e608dbe149c21c2af47d0b07b5",
+ "0xb24dbd5852f8f24921a4e27013b6c3fa8885b973266cb839b9c388efad95821d5d746348179dcc07542bd0d0aefad1ce",
+ "0xb5fb4f279300876a539a27a441348764908bc0051ebd66dc51739807305e73db3d2f6f0f294ffb91b508ab150eaf8527",
+ "0xace50841e718265b290c3483ed4b0fdd1175338c5f1f7530ae9a0e75d5f80216f4de37536adcbc8d8c95982e88808cd0",
+ "0xb19cadcde0f63bd1a9c24bd9c2806f53c14c0b9735bf351601498408ba503ddbd2037c891041cbba47f58b8c483f3b21",
+ "0xb6061e63558d312eb891b97b39aa552fa218568d79ee26fe6dd5b864aea9e3216d8f2e2f3b093503be274766dac41426",
+ "0x89730fdb2876ab6f0fe780d695f6e12090259027e789b819956d786e977518057e5d1d7f5ab24a3ae3d5d4c97773bd2b",
+ "0xb6fa841e81f9f2cad0163a02a63ae96dc341f7ae803b616efc6e1da2fbea551c1b96b11ad02c4afbdf6d0cc9f23da172",
+ "0x8fb66187182629c861ddb6896d7ed3caf2ad050c3dba8ab8eb0d7a2c924c3d44c48d1a148f9e33fb1f061b86972f8d21",
+ "0x86022ac339c1f84a7fa9e05358c1a5b316b4fc0b83dbe9c8c7225dc514f709d66490b539359b084ce776e301024345fa",
+ "0xb50b9c321468da950f01480bb62b6edafd42f83c0001d6e97f2bd523a1c49a0e8574fb66380ea28d23a7c4d54784f9f0",
+ "0xa31c05f7032f30d1dac06678be64d0250a071fd655e557400e4a7f4c152be4d5c7aa32529baf3e5be7c4bd49820054f6",
+ "0xb95ac0848cd322684772119f5b682d90a66bbf9dac411d9d86d2c34844bbd944dbaf8e47aa41380455abd51687931a78",
+ "0xae4a6a5ce9553b65a05f7935e61e496a4a0f6fd8203367a2c627394c9ce1e280750297b74cdc48fd1d9a31e93f97bef4",
+ "0xa22daf35f6e9b05e52e0b07f7bd1dbbebd2c263033fb0e1b2c804e2d964e2f11bc0ece6aca6af079dd3a9939c9c80674",
+ "0x902150e0cb1f16b9b59690db35281e28998ce275acb313900da8b2d8dfd29fa1795f8ca3ff820c31d0697de29df347c1",
+ "0xb17b5104a5dc665cdd7d47e476153d715eb78c6e5199303e4b5445c21a7fa7cf85fe7cfd08d7570f4e84e579b005428c",
+ "0xa03f49b81c15433f121680aa02d734bb9e363af2156654a62bcb5b2ba2218398ccb0ff61104ea5d7df5b16ea18623b1e",
+ "0x802101abd5d3c88876e75a27ffc2f9ddcce75e6b24f23dba03e5201281a7bd5cc7530b6a003be92d225093ca17d3c3bb",
+ "0xa4d183f63c1b4521a6b52226fc19106158fc8ea402461a5cccdaa35fee93669df6a8661f45c1750cd01308149b7bf08e",
+ "0x8d17c22e0c8403b69736364d460b3014775c591032604413d20a5096a94d4030d7c50b9fe3240e31d0311efcf9816a47",
+ "0x947225acfcce5992eab96276f668c3cbe5f298b90a59f2bb213be9997d8850919e8f496f182689b5cbd54084a7332482",
+ "0x8df6f4ed216fc8d1905e06163ba1c90d336ab991a18564b0169623eb39b84e627fa267397da15d3ed754d1f3423bff07",
+ "0x83480007a88f1a36dea464c32b849a3a999316044f12281e2e1c25f07d495f9b1710b4ba0d88e9560e72433addd50bc2",
+ "0xb3019d6e591cf5b33eb972e49e06c6d0a82a73a75d78d383dd6f6a4269838289e6e07c245f54fed67f5c9bb0fd5e1c5f",
+ "0x92e8ce05e94927a9fb02debadb99cf30a26172b2705003a2c0c47b3d8002bf1060edb0f6a5750aad827c98a656b19199",
+ "0xac2aff801448dbbfc13cca7d603fd9c69e82100d997faf11f465323b97255504f10c0c77401e4d1890339d8b224f5803",
+ "0xb0453d9903d08f508ee27e577445dc098baed6cde0ac984b42e0f0efed62760bd58d5816cf1e109d204607b7b175e30c",
+ "0xae68dc4ba5067e825d46d2c7c67f1009ceb49d68e8d3e4c57f4bcd299eb2de3575d42ea45e8722f8f28497a6e14a1cfe",
+ "0xb22486c2f5b51d72335ce819bbafb7fa25eb1c28a378a658f13f9fc79cd20083a7e573248d911231b45a5cf23b561ca7",
+ "0x89d1201d1dbd6921867341471488b4d2fd0fc773ae1d4d074c78ae2eb779a59b64c00452c2a0255826fca6b3d03be2b1",
+ "0xa2998977c91c7a53dc6104f5bc0a5b675e5350f835e2f0af69825db8af4aeb68435bdbcc795f3dd1f55e1dd50bc0507f",
+ "0xb0be4937a925b3c05056ed621910d535ccabf5ab99fd3b9335080b0e51d9607d0fd36cb5781ff340018f6acfca4a9736",
+ "0xaea145a0f6e0ba9df8e52e84bb9c9de2c2dc822f70d2724029b153eb68ee9c17de7d35063dcd6a39c37c59fdd12138f7",
+ "0x91cb4545d7165ee8ffbc74c874baceca11fdebbc7387908d1a25877ca3c57f2c5def424dab24148826832f1e880bede0",
+ "0xb3b579cb77573f19c571ad5eeeb21f65548d7dff9d298b8d7418c11f3e8cd3727c5b467f013cb87d6861cfaceee0d2e3",
+ "0xb98a1eeec2b19fecc8378c876d73645aa52fb99e4819903735b2c7a885b242787a30d1269a04bfb8573d72d9bbc5f0f0",
+ "0x940c1f01ed362bd588b950c27f8cc1d52276c71bb153d47f07ec85b038c11d9a8424b7904f424423e714454d5e80d1cd",
+ "0xaa343a8ecf09ce11599b8cf22f7279cf80f06dbf9f6d62cb05308dbbb39c46fd0a4a1240b032665fbb488a767379b91b",
+ "0x87c3ac72084aca5974599d3232e11d416348719e08443acaba2b328923af945031f86432e170dcdd103774ec92e988c9",
+ "0x91d6486eb5e61d2b9a9e742c20ec974a47627c6096b3da56209c2b4e4757f007e793ebb63b2b246857c9839b64dc0233",
+ "0xaebcd3257d295747dd6fc4ff910d839dd80c51c173ae59b8b2ec937747c2072fa85e3017f9060aa509af88dfc7529481",
+ "0xb3075ba6668ca04eff19efbfa3356b92f0ab12632dcda99cf8c655f35b7928c304218e0f9799d68ef9f809a1492ff7db",
+ "0x93ba7468bb325639ec2abd4d55179c69fd04eaaf39fc5340709227bbaa4ad0a54ea8b480a1a3c8d44684e3be0f8d1980",
+ "0xa6aef86c8c0d92839f38544d91b767c582568b391071228ff5a5a6b859c87bf4f81a7d926094a4ada1993ddbd677a920",
+ "0x91dcd6d14207aa569194aa224d1e5037b999b69ade52843315ca61ba26abe9a76412c9e88259bc5cf5d7b95b97d9c3bc",
+ "0xb3b483d31c88f78d49bd065893bc1e3d2aa637e27dedb46d9a7d60be7660ce7a10aaaa7deead362284a52e6d14021178",
+ "0x8e5730070acf8371461ef301cc4523e8e672aa0e3d945d438a0e0aa6bdf8cb9c685dcf38df429037b0c8aff3955c6f5b",
+ "0xb8c6d769890a8ee18dc4f9e917993315877c97549549b34785a92543cbeec96a08ae3a28d6e809c4aacd69de356c0012",
+ "0x95ca86cd384eaceaa7c077c5615736ca31f36824bd6451a16142a1edc129fa42b50724aeed7c738f08d7b157f78b569e",
+ "0x94df609c6d71e8eee7ab74226e371ccc77e01738fe0ef1a6424435b4570fe1e5d15797b66ed0f64eb88d4a3a37631f0e",
+ "0x89057b9783212add6a0690d6bb99097b182738deff2bd9e147d7fd7d6c8eacb4c219923633e6309ad993c24572289901",
+ "0x83a0f9f5f265c5a0e54defa87128240235e24498f20965009fef664f505a360b6fb4020f2742565dfc7746eb185bcec0",
+ "0x91170da5306128931349bc3ed50d7df0e48a68b8cc8420975170723ac79d8773e4fa13c5f14dc6e3fafcad78379050b1",
+ "0xb7178484d1b55f7e56a4cc250b6b2ec6040437d96bdfddfa7b35ed27435860f3855c2eb86c636f2911b012eb83b00db8",
+ "0xac0b00c4322d1e4208e09cd977b4e54d221133ff09551f75b32b0b55d0e2be80941dda26257b0e288c162e63c7e9cf68",
+ "0x9690ed9e7e53ed37ff362930e4096b878b12234c332fd19d5d064824084245952eda9f979e0098110d6963e468cf513e",
+ "0xb6fa547bb0bb83e5c5be0ed462a8783fba119041c136a250045c09d0d2af330c604331e7de960df976ff76d67f8000cd",
+ "0x814603907c21463bcf4e59cfb43066dfe1a50344ae04ef03c87c0f61b30836c3f4dea0851d6fa358c620045b7f9214c8",
+ "0x9495639e3939fad2a3df00a88603a5a180f3c3a0fe4d424c35060e2043e0921788003689887b1ed5be424d9a89bb18bb",
+ "0xaba4c02d8d57f2c92d5bc765885849e9ff8393d6554f5e5f3e907e5bfac041193a0d8716d7861104a4295d5a03c36b03",
+ "0x8ead0b56c1ca49723f94a998ba113b9058059321da72d9e395a667e6a63d5a9dac0f5717cec343f021695e8ced1f72af",
+ "0xb43037f7e3852c34ed918c5854cd74e9d5799eeddfe457d4f93bb494801a064735e326a76e1f5e50a339844a2f4a8ec9",
+ "0x99db8422bb7302199eb0ff3c3d08821f8c32f53a600c5b6fb43e41205d96adae72be5b460773d1280ad1acb806af9be8",
+ "0x8a9be08eae0086c0f020838925984df345c5512ff32e37120b644512b1d9d4fecf0fd30639ca90fc6cf334a86770d536",
+ "0x81b43614f1c28aa3713a309a88a782fb2bdfc4261dd52ddc204687791a40cf5fd6a263a8179388596582cccf0162efc2",
+ "0xa9f3a8b76912deb61d966c75daf5ddb868702ebec91bd4033471c8e533183df548742a81a2671de5be63a502d827437d",
+ "0x902e2415077f063e638207dc7e14109652e42ab47caccd6204e2870115791c9defac5425fd360b37ac0f7bd8fe7011f8",
+ "0xaa18e4fdc1381b59c18503ae6f6f2d6943445bd00dd7d4a2ad7e5adad7027f2263832690be30d456e6d772ad76f22350",
+ "0xa348b40ba3ba7d81c5d4631f038186ebd5e5f314f1ea737259151b07c3cc8cf0c6ed4201e71bcc1c22fefda81a20cde6",
+ "0xaa1306f7ac1acbfc47dc6f7a0cb6d03786cec8c8dc8060388ccda777bca24bdc634d03e53512c23dba79709ff64f8620",
+ "0x818ccfe46e700567b7f3eb400e5a35f6a5e39b3db3aa8bc07f58ace35d9ae5a242faf8dbccd08d9a9175bbce15612155",
+ "0xb7e3da2282b65dc8333592bb345a473f03bd6df69170055fec60222de9897184536bf22b9388b08160321144d0940279",
+ "0xa4d976be0f0568f4e57de1460a1729129252b44c552a69fceec44e5b97c96c711763360d11f9e5bf6d86b4976bf40d69",
+ "0x85d185f0397c24c2b875b09b6328a23b87982b84ee880f2677a22ff4c9a1ba9f0fea000bb3f7f66375a00d98ebafce17",
+ "0xb4ccbb8c3a2606bd9b87ce022704663af71d418351575f3b350d294f4efc68c26f9a2ce49ff81e6ff29c3b63d746294e",
+ "0x93ffd3265fddb63724dfde261d1f9e22f15ecf39df28e4d89e9fea03221e8e88b5dd9b77628bacaa783c6f91802d47cc",
+ "0xb1fd0f8d7a01378e693da98d03a2d2fda6b099d03454b6f2b1fa6472ff6bb092751ce6290059826b74ac0361eab00e1e",
+ "0xa89f440c71c561641589796994dd2769616b9088766e983c873fae0716b95c386c8483ab8a4f367b6a68b72b7456dd32",
+ "0xaf4fe92b01d42d03dd5d1e7fa55e96d4bbcb7bf7d4c8c197acd16b3e0f3455807199f683dcd263d74547ef9c244b35cc",
+ "0xa8227f6e0a344dfe76bfbe7a1861be32c4f4bed587ccce09f9ce2cf481b2dda8ae4f566154bc663d15f962f2d41761bd",
+ "0xa7b361663f7495939ed7f518ba45ea9ff576c4e628995b7aea026480c17a71d63fc2c922319f0502eb7ef8f14a406882",
+ "0x8ddcf382a9f39f75777160967c07012cfa89e67b19714a7191f0c68eaf263935e5504e1104aaabd0899348c972a8d3c6",
+ "0x98c95b9f6f5c91f805fb185eedd06c6fc4457d37dd248d0be45a6a168a70031715165ea20606245cbdf8815dc0ac697f",
+ "0x805b44f96e001e5909834f70c09be3efcd3b43632bcac5b6b66b6d227a03a758e4b1768ce2a723045681a1d34562aaeb",
+ "0xb0e81b07cdc45b3dca60882676d9badb99f25c461b7efe56e3043b80100bb62d29e1873ae25eb83087273160ece72a55",
+ "0xb0c53f0abe78ee86c7b78c82ae1f7c070bb0b9c45c563a8b3baa2c515d482d7507bb80771e60b38ac13f78b8af92b4a9",
+ "0xa7838ef6696a9e4d2e5dfd581f6c8d6a700467e8fd4e85adabb5f7a56f514785dd4ab64f6f1b48366f7d94728359441b",
+ "0x88c76f7700a1d23c30366a1d8612a796da57b2500f97f88fdf2d76b045a9d24e7426a8ffa2f4e86d3046937a841dad58",
+ "0xad8964baf98c1f02e088d1d9fcb3af6b1dfa44cdfe0ed2eae684e7187c33d3a3c28c38e8f4e015f9c04d451ed6f85ff6",
+ "0x90e9d00a098317ececaa9574da91fc149eda5b772dedb3e5a39636da6603aa007804fa86358550cfeff9be5a2cb7845e",
+ "0xa56ff4ddd73d9a6f5ab23bb77efa25977917df63571b269f6a999e1ad6681a88387fcc4ca3b26d57badf91b236503a29",
+ "0x97ad839a6302c410a47e245df84c01fb9c4dfef86751af3f9340e86ff8fc3cd52fa5ff0b9a0bd1d9f453e02ca80658a6",
+ "0xa4c8c44cbffa804129e123474854645107d1f0f463c45c30fd168848ebea94880f7c0c5a45183e9eb837f346270bdb35",
+ "0xa72e53d0a1586d736e86427a93569f52edd2f42b01e78aee7e1961c2b63522423877ae3ac1227a2cf1e69f8e1ff15bc3",
+ "0x8559f88a7ef13b4f09ac82ae458bbae6ab25671cfbf52dae7eac7280d6565dd3f0c3286aec1a56a8a16dc3b61d78ce47",
+ "0x8221503f4cdbed550876c5dc118a3f2f17800c04e8be000266633c83777b039a432d576f3a36c8a01e8fd18289ebc10b",
+ "0x99bfbe5f3e46d4d898a578ba86ed26de7ed23914bd3bcdf3c791c0bcd49398a52419077354a5ab75cea63b6c871c6e96",
+ "0xaa134416d8ff46f2acd866c1074af67566cfcf4e8be8d97329dfa0f603e1ff208488831ce5948ac8d75bfcba058ddcaa",
+ "0xb02609d65ebfe1fe8e52f21224a022ea4b5ea8c1bd6e7b9792eed8975fc387cdf9e3b419b8dd5bcce80703ab3a12a45f",
+ "0xa4f14798508698fa3852e5cac42a9db9797ecee7672a54988aa74037d334819aa7b2ac7b14efea6b81c509134a6b7ad2",
+ "0x884f01afecbcb987cb3e7c489c43155c416ed41340f61ecb651d8cba884fb9274f6d9e7e4a46dd220253ae561614e44c",
+ "0xa05523c9e71dce1fe5307cc71bd721feb3e1a0f57a7d17c7d1c9fb080d44527b7dbaa1f817b1af1c0b4322e37bc4bb1e",
+ "0x8560aec176a4242b39f39433dd5a02d554248c9e49d3179530815f5031fee78ba9c71a35ceeb2b9d1f04c3617c13d8f0",
+ "0x996aefd402748d8472477cae76d5a2b92e3f092fc834d5222ae50194dd884c9fb8b6ed8e5ccf8f6ed483ddbb4e80c747",
+ "0x8fd09900320000cbabc40e16893e2fcf08815d288ec19345ad7b6bb22f7d78a52b6575a3ca1ca2f8bc252d2eafc928ec",
+ "0x939e51f73022bc5dc6862a0adf8fb8a3246b7bfb9943cbb4b27c73743926cc20f615a036c7e5b90c80840e7f1bfee0e7",
+ "0xa0a6258700cadbb9e241f50766573bf9bdb7ad380b1079dc3afb4054363d838e177b869cad000314186936e40359b1f2",
+ "0x972699a4131c8ed27a2d0e2104d54a65a7ff1c450ad9da3a325c662ab26869c21b0a84d0700b98c8b5f6ce3b746873d7",
+ "0xa454c7fe870cb8aa6491eafbfb5f7872d6e696033f92e4991d057b59d70671f2acdabef533e229878b60c7fff8f748b1",
+ "0xa167969477214201f09c79027b10221e4707662e0c0fde81a0f628249f2f8a859ce3d30a7dcc03b8ecca8f7828ad85c7",
+ "0x8ff6b7265175beb8a63e1dbf18c9153fb2578c207c781282374f51b40d57a84fd2ef2ea2b9c6df4a54646788a62fd17f",
+ "0xa3d7ebeccde69d73d8b3e76af0da1a30884bb59729503ff0fb0c3bccf9221651b974a6e72ea33b7956fc3ae758226495",
+ "0xb71ef144c9a98ce5935620cb86c1590bd4f48e5a2815d25c0cdb008fde628cf628c31450d3d4f67abbfeb16178a74cfd",
+ "0xb5e0a16d115134f4e2503990e3f2035ed66b9ccf767063fe6747870d97d73b10bc76ed668550cb82eedc9a2ca6f75524",
+ "0xb30ffaaf94ee8cbc42aa2c413175b68afdb207dbf351fb20be3852cb7961b635c22838da97eaf43b103aff37e9e725cc",
+ "0x98aa7d52284f6c1f22e272fbddd8c8698cf8f5fbb702d5de96452141fafb559622815981e50b87a72c2b1190f59a7deb",
+ "0x81fbacda3905cfaf7780bb4850730c44166ed26a7c8d07197a5d4dcd969c09e94a0461638431476c16397dd7bdc449f9",
+ "0x95e47021c1726eac2e5853f570d6225332c6e48e04c9738690d53e07c6b979283ebae31e2af1fc9c9b3e59f87e5195b1",
+ "0xac024a661ba568426bb8fce21780406537f518075c066276197300841e811860696f7588188bc01d90bace7bc73d56e3",
+ "0xa4ebcaf668a888dd404988ab978594dee193dad2d0aec5cdc0ccaf4ec9a7a8228aa663db1da8ddc52ec8472178e40c32",
+ "0xa20421b8eaf2199d93b083f2aff37fb662670bd18689d046ae976d1db1fedd2c2ff897985ecc6277b396db7da68bcb27",
+ "0x8bc33d4b40197fd4d49d1de47489d10b90d9b346828f53a82256f3e9212b0cbc6930b895e879da9cec9fedf026aadb3e",
+ "0xaaafdd1bec8b757f55a0433eddc0a39f818591954fd4e982003437fcceb317423ad7ee74dbf17a2960380e7067a6b4e2",
+ "0xaad34277ebaed81a6ec154d16736866f95832803af28aa5625bf0461a71d02b1faba02d9d9e002be51c8356425a56867",
+ "0x976e9c8b150d08706079945bd0e84ab09a648ecc6f64ded9eb5329e57213149ae409ae93e8fbd8eda5b5c69f5212b883",
+ "0x8097fae1653247d2aed4111533bc378171d6b2c6d09cbc7baa9b52f188d150d645941f46d19f7f5e27b7f073c1ebd079",
+ "0x83905f93b250d3184eaba8ea7d727c4464b6bdb027e5cbe4f597d8b9dc741dcbea709630bd4fd59ce24023bec32fc0f3",
+ "0x8095030b7045cff28f34271386e4752f9a9a0312f8df75de4f424366d78534be2b8e1720a19cb1f9a2d21105d790a225",
+ "0xa7b7b73a6ae2ed1009c49960374b0790f93c74ee03b917642f33420498c188a169724945a975e5adec0a1e83e07fb1b2",
+ "0x856a41c54df393b6660b7f6354572a4e71c8bfca9cabaffb3d4ef2632c015e7ee2bc10056f3eccb3dbed1ad17d939178",
+ "0xa8f7a55cf04b38cd4e330394ee6589da3a07dc9673f74804fdf67b364e0b233f14aec42e783200a2e4666f7c5ff62490",
+ "0x82c529f4e543c6bca60016dc93232c115b359eaee2798a9cf669a654b800aafe6ab4ba58ea8b9cdda2b371c8d62fa845",
+ "0x8caab020c1baddce77a6794113ef1dfeafc5f5000f48e97f4351b588bf02f1f208101745463c480d37f588d5887e6d8c",
+ "0x8fa91b3cc400f48b77b6fd77f3b3fbfb3f10cdff408e1fd22d38f77e087b7683adad258804409ba099f1235b4b4d6fea",
+ "0x8aa02787663d6be9a35677d9d8188b725d5fcd770e61b11b64e3def8808ea5c71c0a9afd7f6630c48634546088fcd8e2",
+ "0xb5635b7b972e195cab878b97dea62237c7f77eb57298538582a330b1082f6207a359f2923864630136d8b1f27c41b9aa",
+ "0x8257bb14583551a65975946980c714ecd6e5b629672bb950b9caacd886fbd22704bc9e3ba7d30778adab65dc74f0203a",
+ "0xab5fe1cd12634bfa4e5c60d946e2005cbd38f1063ec9a5668994a2463c02449a0a185ef331bd86b68b6e23a8780cb3ba",
+ "0xa7d3487da56cda93570cc70215d438204f6a2709bfb5fda6c5df1e77e2efc80f4235c787e57fbf2c74aaff8cbb510a14",
+ "0xb61cff7b4c49d010e133319fb828eb900f8a7e55114fc86b39c261a339c74f630e1a7d7e1350244ada566a0ff3d46c4b",
+ "0x8d4d1d55d321d278db7a85522ccceca09510374ca81d4d73e3bb5249ace7674b73900c35a531ec4fa6448fabf7ad00dc",
+ "0x966492248aee24f0f56c8cfca3c8ec6ba3b19abb69ae642041d4c3be8523d22c65c4dafcab4c58989ccc4e0bd2f77919",
+ "0xb20c320a90cb220b86e1af651cdc1e21315cd215da69f6787e28157172f93fc8285dcd59b039c626ed8ca4633cba1a47",
+ "0xaae9e6b22f018ceb5c0950210bb8182cb8cb61014b7e14581a09d36ebd1bbfebdb2b82afb7fdb0cf75e58a293d9c456d",
+ "0x875547fb67951ad37b02466b79f0c9b985ccbc500cfb431b17823457dc79fb9597ec42cd9f198e15523fcd88652e63a4",
+ "0x92afce49773cb2e20fb21e4f86f18e0959ebb9c33361547ddb30454ee8e36b1e234019cbdca0e964cb292f7f77df6b90",
+ "0x8af85343dfe1821464c76ba11c216cbef697b5afc69c4d821342e55afdac047081ec2e3f7b09fc14b518d9a23b78c003",
+ "0xb7de4a1648fd63f3a918096ea669502af5357438e69dac77cb8102b6e6c15c76e033cfaa80dafc806e535ede5c1a20aa",
+ "0xac80e9b545e8bd762951d96c9ce87f629d01ffcde07efc2ef7879ca011f1d0d8a745abf26c9d452541008871304fac00",
+ "0xa4cf0f7ed724e481368016c38ea5816698a5f68eb21af4d3c422d2ba55f96a33e427c2aa40de1b56a7cfac7f7cf43ab0",
+ "0x899b0a678bb2db2cae1b44e75a661284844ebcdd87abf308fedeb2e4dbe5c5920c07db4db7284a7af806a2382e8b111a",
+ "0xaf0588a2a4afce2b1b13c1230816f59e8264177e774e4a341b289a101dcf6af813638fed14fb4d09cb45f35d5d032609",
+ "0xa4b8df79e2be76e9f5fc5845f06fe745a724cf37c82fcdb72719b77bdebea3c0e763f37909373e3a94480cc5e875cba0",
+ "0x83e42c46d88930c8f386b19fd999288f142d325e2ebc86a74907d6d77112cb0d449bc511c95422cc810574031a8cbba9",
+ "0xb5e39534070de1e5f6e27efbdd3dc917d966c2a9b8cf2d893f964256e95e954330f2442027dc148c776d63a95bcde955",
+ "0x958607569dc28c075e658cd4ae3927055c6bc456eef6212a6fea8205e48ed8777a8064f584cda38fe5639c371e2e7fba",
+ "0x812adf409fa63575113662966f5078a903212ffb65c9b0bbe62da0f13a133443a7062cb8fd70f5e5dd5559a32c26d2c8",
+ "0xa679f673e5ce6a3cce7fa31f22ee3785e96bcb55e5a776e2dd3467bef7440e3555d1a9b87cb215e86ee9ed13a090344b",
+ "0xafedbb34508b159eb25eb2248d7fe328f86ef8c7d84c62d5b5607d74aae27cc2cc45ee148eb22153b09898a835c58df4",
+ "0xb75505d4f6b67d31e665cfaf5e4acdb5838ae069166b7fbcd48937c0608a59e40a25302fcc1873d2e81c1782808c70f0",
+ "0xb62515d539ec21a155d94fc00ea3c6b7e5f6636937bce18ed5b618c12257fb82571886287fd5d1da495296c663ebc512",
+ "0xab8e1a9446bbdd588d1690243b1549d230e6149c28f59662b66a8391a138d37ab594df38e7720fae53217e5c3573b5be",
+ "0xb31e8abf4212e03c3287bb2c0a153065a7290a16764a0bac8f112a72e632185a654bb4e88fdd6053e6c7515d9719fadb",
+ "0xb55165477fe15b6abd2d0f4fddaa9c411710dcc4dd712daba3d30e303c9a3ee5415c256f9dc917ecf18c725b4dbab059",
+ "0xa0939d4f57cacaae549b78e87cc234de4ff6a35dc0d9cd5d7410abc30ebcd34c135e008651c756e5a9d2ca79c40ef42b",
+ "0x8cf10e50769f3443340844aad4d56ec790850fed5a41fcbd739abac4c3015f0a085a038fbe7fae9f5ad899cce5069f6b",
+ "0x924055e804d82a99ea4bb160041ea4dc14b568abf379010bc1922fde5d664718c31d103b8b807e3a1ae809390e708c73",
+ "0x8ec0f9d26f71b0f2e60a179e4fd1778452e2ffb129d50815e5d7c7cb9415fa69ae5890578086e8ef6bfde35ad2a74661",
+ "0x98c7f12b15ec4426b59f737f73bf5faea4572340f4550b7590dfb7f7ffedb2372e3e555977c63946d579544c53210ad0",
+ "0x8a935f7a955c78f69d66f18eee0092e5e833fa621781c9581058e219af4d7ceee48b84e472e159dda6199715fb2f9acf",
+ "0xb78d4219f95a2dbfaa7d0c8a610c57c358754f4f43c2af312ab0fe8f10a5f0177e475332fb8fd23604e474fc2abeb051",
+ "0x8d086a14803392b7318c28f1039a17e3cfdcece8abcaca3657ec3d0ac330842098a85c0212f889fabb296dfb133ce9aa",
+ "0xa53249f417aac82f2c2a50c244ce21d3e08a5e5a8bd33bec2a5ab0d6cd17793e34a17edfa3690899244ce201e2fb9986",
+ "0x8619b0264f9182867a1425be514dc4f1ababc1093138a728a28bd7e4ecc99b9faaff68c23792264bc6e4dce5f52a5c52",
+ "0x8c171edbbbde551ec19e31b2091eb6956107dd9b1f853e1df23bff3c10a3469ac77a58335eee2b79112502e8e163f3de",
+ "0xa9d19ec40f0ca07c238e9337c6d6a319190bdba2db76fb63902f3fb459aeeb50a1ac30db5b25ee1b4201f3ca7164a7f4",
+ "0xb9c6ec14b1581a03520b8d2c1fbbc31fb8ceaef2c0f1a0d0080b6b96e18442f1734bea7ef7b635d787c691de4765d469",
+ "0x8cb437beb4cfa013096f40ccc169a713dc17afee6daa229a398e45fd5c0645a9ad2795c3f0cd439531a7151945d7064d",
+ "0xa6e8740cc509126e146775157c2eb278003e5bb6c48465c160ed27888ca803fa12eee1f6a8dd7f444f571664ed87fdc1",
+ "0xb75c1fecc85b2732e96b3f23aefb491dbd0206a21d682aee0225838dc057d7ed3b576176353e8e90ae55663f79e986e4",
+ "0xad8d249b0aea9597b08358bce6c77c1fd552ef3fbc197d6a1cfe44e5e6f89b628b12a6fb04d5dcfcbacc51f46e4ae7bb",
+ "0xb998b2269932cbd58d04b8e898d373ac4bb1a62e8567484f4f83e224061bc0f212459f1daae95abdbc63816ae6486a55",
+ "0x827988ef6c1101cddc96b98f4a30365ff08eea2471dd949d2c0a9b35c3bbfa8c07054ad1f4c88c8fbf829b20bb5a9a4f",
+ "0x8692e638dd60babf7d9f2f2d2ce58e0ac689e1326d88311416357298c6a2bffbfebf55d5253563e7b3fbbf5072264146",
+ "0xa685d75b91aea04dbc14ab3c1b1588e6de96dae414c8e37b8388766029631b28dd860688079b12d09cd27f2c5af11adf",
+ "0xb57eced93eec3371c56679c259b34ac0992286be4f4ff9489d81cf9712403509932e47404ddd86f89d7c1c3b6391b28c",
+ "0xa1c8b4e42ebcbd8927669a97f1b72e236fb19249325659e72be7ddaaa1d9e81ca2abb643295d41a8c04a2c01f9c0efd7",
+ "0x877c33de20d4ed31674a671ba3e8f01a316581e32503136a70c9c15bf0b7cb7b1cba6cd4eb641fad165fb3c3c6c235fd",
+ "0xa2a469d84ec478da40838f775d11ad38f6596eb41caa139cc190d6a10b5108c09febae34ffdafac92271d2e73c143693",
+ "0x972f817caedb254055d52e963ed28c206848b6c4cfdb69dbc961c891f8458eaf582a6d4403ce1177d87bc2ea410ef60a",
+ "0xaccbd739e138007422f28536381decc54bb6bd71d93edf3890e54f9ef339f83d2821697d1a4ac1f5a98175f9a9ecb9b5",
+ "0x8940f8772e05389f823b62b3adc3ed541f91647f0318d7a0d3f293aeeb421013de0d0a3664ea53dd24e5fbe02d7efef6",
+ "0x8ecce20f3ef6212edef07ec4d6183fda8e0e8cad2c6ccd0b325e75c425ee1faba00b5c26b4d95204238931598d78f49d",
+ "0x97cc72c36335bd008afbed34a3b0c7225933faba87f7916d0a6d2161e6f82e0cdcda7959573a366f638ca75d30e9dab1",
+ "0x9105f5de8699b5bdb6bd3bb6cc1992d1eac23929c29837985f83b22efdda92af64d9c574aa9640475087201bbbe5fd73",
+ "0x8ffb33c4f6d05c413b9647eb6933526a350ed2e4278ca2ecc06b0e8026d8dbe829c476a40e45a6df63a633090a3f82ef",
+ "0x8bfc6421fdc9c2d2aaa68d2a69b1a2728c25b84944cc3e6a57ff0c94bfd210d1cbf4ff3f06702d2a8257024d8be7de63",
+ "0xa80e1dc1dddfb41a70220939b96dc6935e00b32fb8be5dff4eed1f1c650002ff95e4af481c43292e3827363b7ec4768a",
+ "0x96f714ebd54617198bd636ba7f7a7f8995a61db20962f2165078d9ed8ee764d5946ef3cbdc7ebf8435bb8d5dd4c1deac",
+ "0x8cdb0890e33144d66391d2ae73f5c71f5a861f72bc93bff6cc399fc25dd1f9e17d8772592b44593429718784802ac377",
+ "0x8ccf9a7f80800ee770b92add734ed45a73ecc31e2af0e04364eefc6056a8223834c7c0dc9dfc52495bdec6e74ce69994",
+ "0xaa0875f423bd68b5f10ba978ddb79d3b96ec093bfbac9ff366323193e339ed7c4578760fb60f60e93598bdf1e5cc4995",
+ "0xa9214f523957b59c7a4cb61a40251ad72aba0b57573163b0dc0f33e41d2df483fb9a1b85a5e7c080e9376c866790f8cb",
+ "0xb6224b605028c6673a536cc8ff9aeb94e7a22e686fda82cf16068d326469172f511219b68b2b3affb7933af0c1f80d07",
+ "0xb6d58968d8a017c6a34e24c2c09852f736515a2c50f37232ac6b43a38f8faa7572cc31dade543b594b61b5761c4781d0",
+ "0x8a97cefe5120020c38deeb861d394404e6c993c6cbd5989b6c9ebffe24f46ad11b4ba6348e2991cbf3949c28cfc3c99d",
+ "0x95bf046f8c3a9c0ce2634be4de3713024daec3fc4083e808903b25ce3ac971145af90686b451efcc72f6b22df0216667",
+ "0xa6a4e2f71b8fa28801f553231eff2794c0f10d12e7e414276995e21195abc9c2983a8997e41af41e78d19ff6fbb2680b",
+ "0x8e5e62a7ca9c2f58ebaab63db2ff1fb1ff0877ae94b7f5e2897f273f684ae639dff44cc65718f78a9c894787602ab26a",
+ "0x8542784383eec4f565fcb8b9fc2ad8d7a644267d8d7612a0f476fc8df3aff458897a38003d506d24142ad18f93554f2b",
+ "0xb7db68ba4616ea072b37925ec4fb39096358c2832cc6d35169e032326b2d6614479f765ae98913c267105b84afcb9bf2",
+ "0x8b31dbb9457d23d416c47542c786e07a489af35c4a87dadb8ee91bea5ac4a5315e65625d78dad2cf8f9561af31b45390",
+ "0xa8545a1d91ac17257732033d89e6b7111db8242e9c6ebb0213a88906d5ef407a2c6fdb444e29504b06368b6efb4f4839",
+ "0xb1bd85d29ebb28ccfb05779aad8674906b267c2bf8cdb1f9a0591dd621b53a4ee9f2942687ee3476740c0b4a7621a3ae",
+ "0xa2b54534e152e46c50d91fff03ae9cd019ff7cd9f4168b2fe7ac08ef8c3bbc134cadd3f9d6bd33d20ae476c2a8596c8a",
+ "0xb19b571ff4ae3e9f5d95acda133c455e72c9ea9973cae360732859836c0341c4c29ab039224dc5bc3deb824e031675d8",
+ "0x940b5f80478648bac025a30f3efeb47023ce20ee98be833948a248bca6979f206bb28fc0f17b90acf3bb4abd3d14d731",
+ "0x8f106b40588586ac11629b96d57808ad2808915d89539409c97414aded90b4ff23286a692608230a52bff696055ba5d6",
+ "0xae6bda03aa10da3d2abbc66d764ca6c8d0993e7304a1bdd413eb9622f3ca1913baa6da1e9f4f9e6cf847f14f44d6924d",
+ "0xa18e7796054a340ef826c4d6b5a117b80927afaf2ebd547794c400204ae2caf277692e2eabb55bc2f620763c9e9da66d",
+ "0x8d2d25180dc2c65a4844d3e66819ccfcf48858f0cc89e1c77553b463ec0f7feb9a4002ce26bc618d1142549b9850f232",
+ "0x863f413a394de42cc8166c1c75d513b91d545fff1de6b359037a742c70b008d34bf8e587afa2d62c844d0c6f0ea753e7",
+ "0x83cd0cf62d63475e7fcad18a2e74108499cdbf28af2113cfe005e3b5887794422da450b1944d0a986eb7e1f4c3b18f25",
+ "0xb4f8b350a6d88fea5ab2e44715a292efb12eb52df738c9b2393da3f1ddee68d0a75b476733ccf93642154bceb208f2b8",
+ "0xb3f52aaa4cd4221cb9fc45936cc67fd3864bf6d26bf3dd86aa85aa55ecfc05f5e392ecce5e7cf9406b4b1c4fce0398c8",
+ "0xb33137084422fb643123f40a6df2b498065e65230fc65dc31791c330e898c51c3a65ff738930f32c63d78f3c9315f85b",
+ "0x91452bfa75019363976bb7337fe3a73f1c10f01637428c135536b0cdc7da5ce558dae3dfc792aa55022292600814a8ef",
+ "0xad6ba94c787cd4361ca642c20793ea44f1f127d4de0bb4a77c7fbfebae0fcadbf28e2cb6f0c12c12a07324ec8c19761d",
+ "0x890aa6248b17f1501b0f869c556be7bf2b1d31a176f9978bb97ab7a6bd4138eed32467951c5ef1871944b7f620542f43",
+ "0x82111db2052194ee7dd22ff1eafffac0443cf969d3762cceae046c9a11561c0fdce9c0711f88ac01d1bed165f8a7cee3",
+ "0xb1527b71df2b42b55832f72e772a466e0fa05743aacc7814f4414e4bcc8d42a4010c9e0fd940e6f254cafedff3cd6543",
+ "0x922370fa49903679fc565f09c16a5917f8125e72acfeb060fcdbadbd1644eb9f4016229756019c93c6d609cda5d5d174",
+ "0xaa4c7d98a96cab138d2a53d4aee8ebff6ef903e3b629a92519608d88b3bbd94de5522291a1097e6acf830270e64c8ee1",
+ "0xb3dc21608a389a72d3a752883a382baaafc61ecc44083b832610a237f6a2363f24195acce529eb4aed4ef0e27a12b66e",
+ "0x94619f5de05e07b32291e1d7ab1d8b7337a2235e49d4fb5f3055f090a65e932e829efa95db886b32b153bdd05a53ec8c",
+ "0xade1e92722c2ffa85865d2426fb3d1654a16477d3abf580cfc45ea4b92d5668afc9d09275d3b79283e13e6b39e47424d",
+ "0xb7201589de7bed094911dd62fcd25c459a8e327ac447b69f541cdba30233063e5ddffad0b67e9c3e34adcffedfd0e13d",
+ "0x809d325310f862d6549e7cb40f7e5fc9b7544bd751dd28c4f363c724a0378c0e2adcb5e42ec8f912f5f49f18f3365c07",
+ "0xa79c20aa533de7a5d671c99eb9eb454803ba54dd4f2efa3c8fec1a38f8308e9905c71e9282955225f686146388506ff6",
+ "0xa85eeacb5e8fc9f3ed06a3fe2dc3108ab9f8c5877b148c73cf26e4e979bf5795edbe2e63a8d452565fd1176ed40402b2",
+ "0x97ef55662f8a1ec0842b22ee21391227540adf7708f491436044f3a2eb18c471525e78e1e14fa292507c99d74d7437c6",
+ "0x93110d64ed5886f3d16ce83b11425576a3a7a9bb831cd0de3f9a0b0f2270a730d68136b4ef7ff035ede004358f419b5c",
+ "0xac9ed0a071517f0ae4f61ce95916a90ba9a77a3f84b0ec50ef7298acdcd44d1b94525d191c39d6bd1bb68f4471428760",
+ "0x98abd6a02c7690f5a339adf292b8c9368dfc12e0f8069cf26a5e0ce54b4441638f5c66ea735142f3c28e00a0024267e6",
+ "0xb51efb73ba6d44146f047d69b19c0722227a7748b0e8f644d0fc9551324cf034c041a2378c56ce8b58d06038fb8a78de",
+ "0x8f115af274ef75c1662b588b0896b97d71f8d67986ae846792702c4742ab855952865ce236b27e2321967ce36ff93357",
+ "0xb3c4548f14d58b3ab03c222da09e4381a0afe47a72d18d50a94e0008797f78e39e99990e5b4757be62310d400746e35a",
+ "0xa9b1883bd5f31f909b8b1b6dcb48c1c60ed20aa7374b3ffa7f5b2ed036599b5bef33289d23c80a5e6420d191723b92f7",
+ "0x85d38dffd99487ae5bb41ab4a44d80a46157bbbe8ef9497e68f061721f74e4da513ccc3422936b059575975f6787c936",
+ "0xadf870fcb96e972c033ab7a35d28ae79ee795f82bc49c3bd69138f0e338103118d5529c53f2d72a9c0d947bf7d312af2",
+ "0xab4c7a44e2d9446c6ff303eb49aef0e367a58b22cc3bb27b4e69b55d1d9ee639c9234148d2ee95f9ca8079b1457d5a75",
+ "0xa386420b738aba2d7145eb4cba6d643d96bda3f2ca55bb11980b318d43b289d55a108f4bc23a9606fb0bccdeb3b3bb30",
+ "0x847020e0a440d9c4109773ecca5d8268b44d523389993b1f5e60e541187f7c597d79ebd6e318871815e26c96b4a4dbb1",
+ "0xa530aa7e5ca86fcd1bec4b072b55cc793781f38a666c2033b510a69e110eeabb54c7d8cbcb9c61fee531a6f635ffa972",
+ "0x87364a5ea1d270632a44269d686b2402da737948dac27f51b7a97af80b66728b0256547a5103d2227005541ca4b7ed04",
+ "0x8816fc6e16ea277de93a6d793d0eb5c15e9e93eb958c5ef30adaf8241805adeb4da8ce19c3c2167f971f61e0b361077d",
+ "0x8836a72d301c42510367181bb091e4be377777aed57b73c29ef2ce1d475feedd7e0f31676284d9a94f6db01cc4de81a2",
+ "0xb0d9d8b7116156d9dde138d28aa05a33e61f8a85839c1e9071ccd517b46a5b4b53acb32c2edd7150c15bc1b4bd8db9e3",
+ "0xae931b6eaeda790ba7f1cd674e53dc87f6306ff44951fa0df88d506316a5da240df9794ccbd7215a6470e6b31c5ea193",
+ "0x8c6d5bdf87bd7f645419d7c6444e244fe054d437ed1ba0c122fde7800603a5fadc061e5b836cb22a6cfb2b466f20f013",
+ "0x90d530c6d0cb654999fa771b8d11d723f54b8a8233d1052dc1e839ea6e314fbed3697084601f3e9bbb71d2b4eaa596df",
+ "0xb0d341a1422588c983f767b1ed36c18b141774f67ef6a43cff8e18b73a009da10fc12120938b8bba27f225bdfd3138f9",
+ "0xa131b56f9537f460d304e9a1dd75702ace8abd68cb45419695cb8dee76998139058336c87b7afd6239dc20d7f8f940cc",
+ "0xaa6c51fa28975f709329adee1bbd35d49c6b878041841a94465e8218338e4371f5cb6c17f44a63ac93644bf28f15d20f",
+ "0x88440fb584a99ebd7f9ea04aaf622f6e44e2b43bbb49fb5de548d24a238dc8f26c8da2ccf03dd43102bda9f16623f609",
+ "0x9777b8695b790e702159a4a750d5e7ff865425b95fa0a3c15495af385b91c90c00a6bd01d1b77bffe8c47d01baae846f",
+ "0x8b9d764ece7799079e63c7f01690c8eff00896a26a0d095773dea7a35967a8c40db7a6a74692f0118bf0460c26739af4",
+ "0x85808c65c485520609c9e61fa1bb67b28f4611d3608a9f7a5030ee61c3aa3c7e7dc17fff48af76b4aecee2cb0dbd22ac",
+ "0xad2783a76f5b3db008ef5f7e67391fda4e7e36abde6b3b089fc4835b5c339370287935af6bd53998bed4e399eda1136d",
+ "0x96f18ec03ae47c205cc4242ca58e2eff185c9dca86d5158817e2e5dc2207ab84aadda78725f8dc080a231efdc093b940",
+ "0x97de1ab6c6cc646ae60cf7b86df73b9cf56cc0cd1f31b966951ebf79fc153531af55ca643b20b773daa7cab784b832f7",
+ "0x870ba266a9bfa86ef644b1ef025a0f1b7609a60de170fe9508de8fd53170c0b48adb37f19397ee8019b041ce29a16576",
+ "0xad990e888d279ac4e8db90619d663d5ae027f994a3992c2fbc7d262b5990ae8a243e19157f3565671d1cb0de17fe6e55",
+ "0x8d9d5adcdd94c5ba3be4d9a7428133b42e485f040a28d16ee2384758e87d35528f7f9868de9bd23d1a42a594ce50a567",
+ "0x85a33ed75d514ece6ad78440e42f7fcdb59b6f4cff821188236d20edae9050b3a042ce9bc7d2054296e133d033e45022",
+ "0x92afd2f49a124aaba90de59be85ff269457f982b54c91b06650c1b8055f9b4b0640fd378df02a00e4fc91f7d226ab980",
+ "0x8c0ee09ec64bd831e544785e3d65418fe83ed9c920d9bb4d0bf6dd162c1264eb9d6652d2def0722e223915615931581c",
+ "0x8369bedfa17b24e9ad48ebd9c5afea4b66b3296d5770e09b00446c5b0a8a373d39d300780c01dcc1c6752792bccf5fd0",
+ "0x8b9e960782576a59b2eb2250d346030daa50bbbec114e95cdb9e4b1ba18c3d34525ae388f859708131984976ca439d94",
+ "0xb682bface862008fea2b5a07812ca6a28a58fd151a1d54c708fc2f8572916e0d678a9cb8dc1c10c0470025c8a605249e",
+ "0xa38d5e189bea540a824b36815fc41e3750760a52be0862c4cac68214febdc1a754fb194a7415a8fb7f96f6836196d82a",
+ "0xb9e7fbda650f18c7eb8b40e42cc42273a7298e65e8be524292369581861075c55299ce69309710e5b843cb884de171bd",
+ "0xb6657e5e31b3193874a1bace08f42faccbd3c502fb73ad87d15d18a1b6c2a146f1baa929e6f517db390a5a47b66c0acf",
+ "0xae15487312f84ed6265e4c28327d24a8a0f4d2d17d4a5b7c29b974139cf93223435aaebe3af918f5b4bb20911799715f",
+ "0x8bb4608beb06bc394e1a70739b872ce5a2a3ffc98c7547bf2698c893ca399d6c13686f6663f483894bccaabc3b9c56ad",
+ "0xb58ac36bc6847077584308d952c5f3663e3001af5ecf2e19cb162e1c58bd6c49510205d453cffc876ca1dc6b8e04a578",
+ "0x924f65ced61266a79a671ffb49b300f0ea44c50a0b4e3b02064faa99fcc3e4f6061ea8f38168ab118c5d47bd7804590e",
+ "0x8d67d43b8a06b0ff4fafd7f0483fa9ed1a9e3e658a03fb49d9d9b74e2e24858dc1bed065c12392037b467f255d4e5643",
+ "0xb4d4f87813125a6b355e4519a81657fa97c43a6115817b819a6caf4823f1d6a1169683fd68f8d025cdfa40ebf3069acb",
+ "0xa7fd4d2c8e7b59b8eed3d4332ae94b77a89a2616347402f880bc81bde072220131e6dbec8a605be3a1c760b775375879",
+ "0x8d4a7d8fa6f55a30df37bcf74952e2fa4fd6676a2e4606185cf154bdd84643fd01619f8fb8813a564f72e3f574f8ce30",
+ "0x8086fb88e6260e9a9c42e9560fde76315ff5e5680ec7140f2a18438f15bc2cc7d7d43bfb5880b180b738c20a834e6134",
+ "0x916c4c54721de03934fee6f43de50bb04c81f6f8dd4f6781e159e71c40c60408aa54251d457369d133d4ba3ed7c12cb4",
+ "0x902e5bf468f11ed9954e2a4a595c27e34abe512f1d6dc08bbca1c2441063f9af3dc5a8075ab910a10ff6c05c1c644a35",
+ "0xa1302953015e164bf4c15f7d4d35e3633425a78294406b861675667eec77765ff88472306531e5d3a4ec0a2ff0dd6a9e",
+ "0x87874461df3c9aa6c0fa91325576c0590f367075f2f0ecfeb34afe162c04c14f8ce9d608c37ac1adc8b9985bc036e366",
+ "0x84b50a8a61d3cc609bfb0417348133e698fe09a6d37357ce3358de189efcf35773d78c57635c2d26c3542b13cc371752",
+ "0xacaed2cff8633d12c1d12bb7270c54d65b0b0733ab084fd47f81d0a6e1e9b6f300e615e79538239e6160c566d8bb8d29",
+ "0x889e6a0e136372ca4bac90d1ab220d4e1cad425a710e8cdd48b400b73bb8137291ceb36a39440fa84305783b1d42c72f",
+ "0x90952e5becec45b2b73719c228429a2c364991cf1d5a9d6845ae5b38018c2626f4308daa322cab1c72e0f6c621bb2b35",
+ "0x8f5a97a801b6e9dcd66ccb80d337562c96f7914e7169e8ff0fda71534054c64bf2a9493bb830623d612cfe998789be65",
+ "0x84f3df8b9847dcf1d63ca470dc623154898f83c25a6983e9b78c6d2d90a97bf5e622445be835f32c1e55e6a0a562ea78",
+ "0x91d12095cd7a88e7f57f254f02fdb1a1ab18984871dead2f107404bcf8069fe68258c4e6f6ebd2477bddf738135400bb",
+ "0xb771a28bc04baef68604d4723791d3712f82b5e4fe316d7adc2fc01b935d8e644c06d59b83bcb542afc40ebafbee0683",
+ "0x872f6341476e387604a7e93ae6d6117e72d164e38ebc2b825bc6df4fcce815004d7516423c190c1575946b5de438c08d",
+ "0x90d6b4aa7d40a020cdcd04e8b016d041795961a8e532a0e1f4041252131089114a251791bf57794cadb7d636342f5d1c",
+ "0x899023ba6096a181448d927fed7a0fe858be4eac4082a42e30b3050ee065278d72fa9b9d5ce3bc1372d4cbd30a2f2976",
+ "0xa28f176571e1a9124f95973f414d5bdbf5794d41c3839d8b917100902ac4e2171eb940431236cec93928a60a77ede793",
+ "0x838dbe5bcd29c4e465d02350270fa0036cd46f8730b13d91e77afb7f5ed16525d0021d3b2ae173a76c378516a903e0cb",
+ "0x8e105d012dd3f5d20f0f1c4a7e7f09f0fdd74ce554c3032e48da8cce0a77260d7d47a454851387770f5c256fa29bcb88",
+ "0x8f4df0f9feeb7a487e1d138d13ea961459a6402fd8f8cabb226a92249a0d04ded5971f3242b9f90d08da5ff66da28af6",
+ "0xad1cfda4f2122a20935aa32fb17c536a3653a18617a65c6836700b5537122af5a8206befe9eaea781c1244c43778e7f1",
+ "0x832c6f01d6571964ea383292efc8c8fa11e61c0634a25fa180737cc7ab57bc77f25e614aac9a2a03d98f27b3c1c29de2",
+ "0x903f89cc13ec6685ac7728521898781fecb300e9094ef913d530bf875c18bcc3ceed7ed51e7b482d45619ab4b025c2e9",
+ "0xa03c474bb915aad94f171e8d96f46abb2a19c9470601f4c915512ec8b9e743c3938450a2a5b077b4618b9df8809e1dc1",
+ "0x83536c8456f306045a5f38ae4be2e350878fa7e164ea408d467f8c3bc4c2ee396bd5868008c089183868e4dfad7aa50b",
+ "0x88f26b4ea1b236cb326cd7ad7e2517ec8c4919598691474fe15d09cabcfc37a8d8b1b818f4d112432ee3a716b0f37871",
+ "0xa44324e3fe96e9c12b40ded4f0f3397c8c7ee8ff5e96441118d8a6bfad712d3ac990b2a6a23231a8f691491ac1fd480f",
+ "0xb0de4693b4b9f932191a21ee88629964878680152a82996c0019ffc39f8d9369bbe2fe5844b68d6d9589ace54af947e4",
+ "0x8e5d8ba948aea5fd26035351a960e87f0d23efddd8e13236cc8e4545a3dda2e9a85e6521efb8577e03772d3637d213d9",
+ "0x93efc82d2017e9c57834a1246463e64774e56183bb247c8fc9dd98c56817e878d97b05f5c8d900acf1fbbbca6f146556",
+ "0x8731176363ad7658a2862426ee47a5dce9434216cef60e6045fa57c40bb3ce1e78dac4510ae40f1f31db5967022ced32",
+ "0xb10c9a96745722c85bdb1a693100104d560433d45b9ac4add54c7646a7310d8e9b3ca9abd1039d473ae768a18e489845",
+ "0xa2ac374dfbb464bf850b4a2caf15b112634a6428e8395f9c9243baefd2452b4b4c61b0cb2836d8eae2d57d4900bf407e",
+ "0xb69fe3ded0c4f5d44a09a0e0f398221b6d1bf5dbb8bc4e338b93c64f1a3cac1e4b5f73c2b8117158030ec03787f4b452",
+ "0x8852cdbaf7d0447a8c6f211b4830711b3b5c105c0f316e3a6a18dcfbb9be08bd6f4e5c8ae0c3692da08a2dfa532f9d5c",
+ "0x93bbf6d7432a7d98ade3f94b57bf9f4da9bc221a180a370b113066dd42601bb9e09edd79e2e6e04e00423399339eebda",
+ "0xa80941c391f1eeafc1451c59e4775d6a383946ff22997aeaadf806542ba451d3b0f0c6864eeba954174a296efe2c1550",
+ "0xa045fe2bb011c2a2f71a0181a8f457a3078470fb74c628eab8b59aef69ffd0d649723bf74d6885af3f028bc5a104fb39",
+ "0xb9d8c35911009c4c8cad64692139bf3fc16b78f5a19980790cb6a7aea650a25df4231a4437ae0c351676a7e42c16134f",
+ "0x94c79501ded0cfcbab99e1841abe4a00a0252b3870e20774c3da16c982d74c501916ec28304e71194845be6e3113c7ab",
+ "0x900a66418b082a24c6348d8644ddb1817df5b25cb33044a519ef47cc8e1f7f1e38d2465b7b96d32ed472d2d17f8414c6",
+ "0xb26f45d393b8b2fcb29bdbb16323dc7f4b81c09618519ab3a39f8ee5bd148d0d9f3c0b5dfab55b5ce14a1cb9206d777b",
+ "0xaa1a87735fc493a80a96a9a57ca40a6d9c32702bfcaa9869ce1a116ae65d69cefe2f3e79a12454b4590353e96f8912b4",
+ "0xa922b188d3d0b69b4e4ea2a2aa076566962844637da12c0832105d7b31dea4a309eee15d12b7a336be3ea36fcbd3e3b7",
+ "0x8f3841fcf4105131d8c4d9885e6e11a46c448226401cf99356c291fadb864da9fa9d30f3a73c327f23f9fd99a11d633e",
+ "0x9791d1183fae270e226379af6c497e7da803ea854bb20afa74b253239b744c15f670ee808f708ede873e78d79a626c9a",
+ "0xa4cad52e3369491ada61bf28ada9e85de4516d21c882e5f1cd845bea9c06e0b2887b0c5527fcff6fc28acd3c04f0a796",
+ "0xb9ac86a900899603452bd11a7892a9bfed8054970bfcbeaa8c9d1930db891169e38d6977f5258c25734f96c8462eee3b",
+ "0xa3a154c28e5580656a859f4efc2f5ebfa7eaa84ca40e3f134fa7865e8581586db74992dbfa4036aa252fba103773ddde",
+ "0x95cc2a0c1885a029e094f5d737e3ecf4d26b99036453a8773c77e360101f9f98676ee246f6f732a377a996702d55691f",
+ "0x842651bbe99720438d8d4b0218feb60481280c05beb17750e9ca0d8c0599a60f873b7fbdcc7d8835ba9a6d57b16eec03",
+ "0x81ee54699da98f5620307893dcea8f64670609fa20e5622265d66283adeac122d458b3308c5898e6c57c298db2c8b24f",
+ "0xb97868b0b2bc98032d68352a535a1b341b9ff3c7af4e3a7f3ebc82d3419daa1b5859d6aedc39994939623c7cd878bd9b",
+ "0xb60325cd5d36461d07ef253d826f37f9ee6474a760f2fff80f9873d01fd2b57711543cdc8d7afa1c350aa753c2e33dea",
+ "0x8c205326c11d25a46717b780c639d89714c7736c974ae71287e3f4b02e6605ac2d9b4928967b1684f12be040b7bf2dd3",
+ "0x95a392d82db51e26ade6c2ccd3396d7e40aff68fa570b5951466580d6e56dda51775dce5cf3a74a7f28c3cb2eb551c4d",
+ "0x8f2cc8071eb56dffb70bda6dd433b556221dc8bba21c53353c865f00e7d4d86c9e39f119ea9a8a12ef583e9a55d9a6b6",
+ "0x9449a71af9672aaf8856896d7e3d788b22991a7103f75b08c0abbcc2bfe60fda4ed8ce502cea4511ff0ea52a93e81222",
+ "0x857090ab9fdb7d59632d068f3cc8cf27e61f0d8322d30e6b38e780a1f05227199b4cd746aac1311c36c659ef20931f28",
+ "0x98a891f4973e7d9aaf9ac70854608d4f7493dffc7e0987d7be9dd6029f6ea5636d24ef3a83205615ca1ff403750058e1",
+ "0xa486e1365bbc278dd66a2a25d258dc82f46b911103cb16aab3945b9c95ae87b386313a12b566df5b22322ede0afe25ad",
+ "0xa9a1eb399ed95d396dccd8d1ac718043446f8b979ec62bdce51c617c97a312f01376ab7fb87d27034e5f5570797b3c33",
+ "0xb7abc3858d7a74bb446218d2f5a037e0fae11871ed9caf44b29b69c500c1fa1dcfad64c9cdccc9d80d5e584f06213deb",
+ "0x8cfb09fe2e202faa4cebad932b1d35f5ca204e1c2a0c740a57812ac9a6792130d1312aabd9e9d4c58ca168bfebd4c177",
+ "0xa90a305c2cd0f184787c6be596fa67f436afd1f9b93f30e875f817ac2aae8bdd2e6e656f6be809467e6b3ad84adb86b1",
+ "0x80a9ef993c2b009ae172cc8f7ec036f5734cf4f4dfa06a7db4d54725e7fbfae5e3bc6f22687bdbb6961939d6f0c87537",
+ "0x848ade1901931e72b955d7db1893f07003e1708ff5d93174bac5930b9a732640f0578839203e9b77eb27965c700032d3",
+ "0x93fdf4697609c5ae9c33b9ca2f5f1af44abeb2b98dc4fdf732cf7388de086f410730dc384d9b7a7f447bb009653c8381",
+ "0x89ce3fb805aea618b5715c0d22a9f46da696b6fa86794f56fdf1d44155a33d42daf1920bcbe36cbacf3cf4c92df9cbc7",
+ "0x829ce2c342cf82aa469c65f724f308f7a750bd1494adc264609cd790c8718b8b25b5cab5858cf4ee2f8f651d569eea67",
+ "0xaf2f0cee7bf413204be8b9df59b9e4991bc9009e0d6dbe6815181df0ec2ca93ab8f4f3135b1c14d8f53d74bff0bd6f27",
+ "0xb87998cecf7b88cde93d1779f10a521edd5574a2fbd240102978639ec57433ba08cdb53849038a329cebbe74657268d2",
+ "0xa64542a1261a6ed3d720c2c3a802303aad8c4c110c95d0f12e05c1065e66f42da494792b6bfc5b9272363f3b1d457f58",
+ "0x86a6fd042e4f282fadf07a4bfee03fc96a3aea49f7a00f52bf249a20f1ec892326855410e61f37fbb27d9305eb2fc713",
+ "0x967ea5bc403b6db269682f7fd0df90659350d7e1aa66bc4fab4c9dfcd75ed0bba4b52f1cebc5f34dc8ba810793727629",
+ "0xa52990f9f3b8616ce3cdc2c74cd195029e6a969753dcf2d1630438700e7d6ebde36538532b3525ac516f5f2ce9dd27a3",
+ "0xa64f7ff870bab4a8bf0d4ef6f5c744e9bf1021ed08b4c80903c7ad318e80ba1817c3180cc45cb5a1cae1170f0241655f",
+ "0xb00f706fa4de1f663f021e8ad3d155e84ce6084a409374b6e6cd0f924a0a0b51bebaaaf1d228c77233a73b0a5a0df0e9",
+ "0x8b882cc3bff3e42babdb96df95fb780faded84887a0a9bab896bef371cdcf169d909f5658649e93006aa3c6e1146d62e",
+ "0x9332663ef1d1dcf805c3d0e4ce7a07d9863fb1731172e766b3cde030bf81682cc011e26b773fb9c68e0477b4ae2cfb79",
+ "0xa8aa8151348dbd4ef40aaeb699b71b4c4bfd3218560c120d85036d14f678f6736f0ec68e80ce1459d3d35feccc575164",
+ "0xa16cd8b729768f51881c213434aa28301fa78fcb554ddd5f9012ee1e4eae7b5cb3dd88d269d53146dea92d10790faf0b",
+ "0x86844f0ef9d37142faf3b1e196e44fbe280a3ba4189aa05c356778cb9e3b388a2bff95eed305ada8769935c9974e4c57",
+ "0xae2eec6b328fccf3b47bcdac32901ac2744a51beb410b04c81dea34dee4912b619466a4f5e2780d87ecefaebbe77b46d",
+ "0x915df4c38d301c8a4eb2dc5b1ba0ffaad67cbb177e0a80095614e9c711f4ef24a4cef133f9d982a63d2a943ba6c8669d",
+ "0xae6a2a4dedfc2d1811711a8946991fede972fdf2a389b282471280737536ffc0ac3a6d885b1f8bda0366eb0b229b9979",
+ "0xa9b628c63d08b8aba6b1317f6e91c34b2382a6c85376e8ef2410a463c6796740ae936fc4e9e0737cb9455d1daa287bd8",
+ "0x848e30bf7edf2546670b390d5cf9ab71f98fcb6add3c0b582cb34996c26a446dee5d1bde4fdcde4fc80c10936e117b29",
+ "0x907d6096c7c8c087d1808dd995d5d2b9169b3768c3f433475b50c2e2bd4b082f4d543afd8b0b0ddffa9c66222a72d51d",
+ "0xa59970a2493b07339124d763ac9d793c60a03354539ecbcf6035bc43d1ea6e35718202ae6d7060b7d388f483d971573c",
+ "0xb9cfef2af9681b2318f119d8611ff6d9485a68d8044581b1959ab1840cbca576dbb53eec17863d2149966e9feb21122f",
+ "0xad47271806161f61d3afa45cdfe2babceef5e90031a21779f83dc8562e6076680525b4970b2f11fe9b2b23c382768323",
+ "0x8e425a99b71677b04fe044625d338811fbb8ee32368a424f6ab2381c52e86ee7a6cecedf777dc97181519d41c351bc22",
+ "0x86b55b54d7adefc12954a9252ee23ae83efe8b5b4b9a7dc307904413e5d69868c7087a818b2833f9b004213d629be8ad",
+ "0xa14fda6b93923dd11e564ae4457a66f397741527166e0b16a8eb91c6701c244fd1c4b63f9dd3515193ec88fa6c266b35",
+ "0xa9b17c36ae6cd85a0ed7f6cabc5b47dc8f80ced605db327c47826476dc1fb8f8669aa7a7dc679fbd4ee3d8e8b4bd6a6f",
+ "0x82a0829469c1458d959c821148f15dacae9ea94bf56c59a6ab2d4dd8b3d16d73e313b5a3912a6c1f131d73a8f06730c4",
+ "0xb22d56d549a53eaef549595924bdb621ff807aa4513feedf3fdcbf7ba8b6b9cfa4481c2f67fc642db397a6b794a8b63a",
+ "0x974c59c24392e2cb9294006cbe3c52163e255f3bd0c2b457bdc68a6338e6d5b6f87f716854492f8d880a6b896ccf757c",
+ "0xb70d247ba7cad97c50b57f526c2ba915786e926a94e8f8c3eebc2e1be6f4255411b9670e382060049c8f4184302c40b2",
+ "0xad80201fe75ef21c3ddbd98cf23591e0d7a3ba1036dfe77785c32f44755a212c31f0ceb0a0b6f5ee9b6dc81f358d30c3",
+ "0x8c656e841f9bb90b9a42d425251f3fdbc022a604d75f5845f479ed4be23e02aaf9e6e56cde351dd7449c50574818a199",
+ "0x8b88dd3fa209d3063b7c5b058f7249ee9900fbc2287d16da61a0704a0a1d71e45d9c96e1cda7fdf9654534ec44558b22",
+ "0x961da00cc8750bd84d253c08f011970ae1b1158ad6778e8ed943d547bceaf52d6d5a212a7de3bf2706688c4389b827d2",
+ "0xa5dd379922549a956033e3d51a986a4b1508e575042b8eaa1df007aa77cf0b8c2ab23212f9c075702788fa9c53696133",
+ "0xac8fcfde3a349d1e93fc8cf450814e842005c545c4844c0401bc80e6b96cdb77f29285a14455e167c191d4f312e866cd",
+ "0xac63d79c799783a8466617030c59dd5a8f92ee6c5204676fd8d881ce5f7f8663bdbeb0379e480ea9b6340ab0dc88e574",
+ "0x805874fde19ce359041ae2bd52a39e2841acabfd31f965792f2737d7137f36d4e4722ede8340d8c95afa6af278af8acb",
+ "0x8d2f323a228aa8ba7b7dc1399138f9e6b41df1a16a7069003ab8104b8b68506a45141bc5fe66acf430e23e13a545190b",
+ "0xa1610c721a2d9af882bb6b39bea97cff1527a3aea041d25934de080214ae77c959e79957164440686d15ab301e897d4d",
+ "0xaba16d29a47fc36f12b654fde513896723e2c700c4190f11b26aa4011da57737ad717daa02794aa3246e4ae5f0b0cc3a",
+ "0xa406db2f15fdd135f346cc4846623c47edd195e80ba8c7cb447332095314d565e4040694ca924696bb5ee7f8996ea0ba",
+ "0x8b30e2cd9b47d75ba57b83630e40f832249af6c058d4f490416562af451993eec46f3e1f90bc4d389e4c06abd1b32a46",
+ "0xaacf9eb7036e248e209adbfc3dd7ce386569ea9b312caa4b240726549db3c68c4f1c8cbf8ed5ea9ea60c7e57c9df3b8e",
+ "0xb20fcac63bf6f5ee638a42d7f89be847f348c085ddcbec3fa318f4323592d136c230495f188ef2022aa355cc2b0da6f9",
+ "0x811eff750456a79ec1b1249d76d7c1547065b839d8d4aaad860f6d4528eb5b669473dcceeeea676cddbc3980b68461b7",
+ "0xb52d14ae33f4ab422f953392ae76a19c618cc31afc96290bd3fe2fb44c954b5c92c4789f3f16e8793f2c0c1691ade444",
+ "0xa7826dafeeba0db5b66c4dfcf2b17fd7b40507a5a53ac2e42942633a2cb30b95ba1739a6e9f3b7a0e0f1ec729bf274e2",
+ "0x8acfd83ddf7c60dd7c8b20c706a3b972c65d336b8f9b3d907bdd8926ced271430479448100050b1ef17578a49c8fa616",
+ "0xaf0c69f65184bb06868029ad46f8465d75c36814c621ac20a5c0b06a900d59305584f5a6709683d9c0e4b6cd08d650a6",
+ "0xb6cc8588191e00680ee6c3339bd0f0a17ad8fd7f4be57d5d7075bede0ea593a19e67f3d7c1a20114894ee5bfcab71063",
+ "0xa82fd4f58635129dbb6cc3eb9391cf2d28400018b105fc41500fbbd12bd890b918f97d3d359c29dd3b4c4e34391dfab0",
+ "0x92fc544ed65b4a3625cf03c41ddff7c039bc22d22c0d59dcc00efd5438401f2606adb125a1d5de294cca216ec8ac35a3",
+ "0x906f67e4a32582b71f15940523c0c7ce370336935e2646bdaea16a06995256d25e99df57297e39d6c39535e180456407",
+ "0x97510337ea5bbd5977287339197db55c60533b2ec35c94d0a460a416ae9f60e85cee39be82abeeacd5813cf54df05862",
+ "0x87e6894643815c0ea48cb96c607266c5ee4f1f82ba5fe352fb77f9b6ed14bfc2b8e09e80a99ac9047dfcf62b2ae26795",
+ "0xb6fd55dd156622ad7d5d51b7dde75e47bd052d4e542dd6449e72411f68275775c846dde301e84613312be8c7bce58b07",
+ "0xb98461ac71f554b2f03a94e429b255af89eec917e208a8e60edf5fc43b65f1d17a20de3f31d2ce9f0cb573c25f2f4d98",
+ "0x96f0dea40ca61cefbee41c4e1fe9a7d81fbe1f49bb153d083ab70f5d0488a1f717fd28cedcf6aa18d07cce2c62801898",
+ "0x8d7c3ab310184f7dc34b6ce4684e4d29a31e77b09940448ea4daac730b7eb308063125d4dd229046cf11bfd521b771e0",
+ "0x96f0564898fe96687918bbf0a6adead99cf72e3a35ea3347e124af9d006221f8e82e5a9d2fe80094d5e8d48e610f415e",
+ "0xad50fcb92c2675a398cf07d4c40a579e44bf8d35f27cc330b57e54d5ea59f7d898af0f75dccfe3726e5471133d70f92b",
+ "0x828beed62020361689ae7481dd8f116902b522fb0c6c122678e7f949fdef70ead011e0e6bffd25678e388744e17cdb69",
+ "0x8349decac1ca16599eee2efc95bcaabf67631107da1d34a2f917884bd70dfec9b4b08ab7bc4379d6c73b19c0b6e54fb8",
+ "0xb2a6a2e50230c05613ace9e58bb2e98d94127f196f02d9dddc53c43fc68c184549ca12d713cb1b025d8260a41e947155",
+ "0x94ff52181aadae832aed52fc3b7794536e2a31a21fc8be3ea312ca5c695750d37f08002f286b33f4023dba1e3253ecfa",
+ "0xa21d56153c7e5972ee9a319501be4faff199fdf09bb821ea9ce64aa815289676c00f105e6f00311b3a5b627091b0d0fc",
+ "0xa27a60d219f1f0c971db73a7f563b371b5c9fc3ed1f72883b2eac8a0df6698400c9954f4ca17d7e94e44bd4f95532afb",
+ "0xa2fc56fae99b1f18ba5e4fe838402164ce82f8a7f3193d0bbd360c2bac07c46f9330c4c7681ffb47074c6f81ee6e7ac6",
+ "0xb748e530cd3afb96d879b83e89c9f1a444f54e55372ab1dcd46a0872f95ce8f49cf2363fc61be82259e04f555937ed16",
+ "0x8bf8993e81080c7cbba1e14a798504af1e4950b2f186ab3335b771d6acaee4ffe92131ae9c53d74379d957cb6344d9cd",
+ "0x96774d0ef730d22d7ab6d9fb7f90b9ead44285219d076584a901960542756700a2a1603cdf72be4708b267200f6c36a9",
+ "0xb47703c2ab17be1e823cc7bf3460db1d6760c0e33862c90ca058845b2ff234b0f9834ddba2efb2ee1770eb261e7d8ffd",
+ "0x84319e67c37a9581f8b09b5e4d4ae88d0a7fb4cbb6908971ab5be28070c3830f040b1de83ee663c573e0f2f6198640e4",
+ "0x96811875fa83133e0b3c0e0290f9e0e28bca6178b77fdf5350eb19344d453dbd0d71e55a0ef749025a5a2ca0ad251e81",
+ "0x81a423423e9438343879f2bfd7ee9f1c74ebebe7ce3cfffc8a11da6f040cc4145c3b527bd3cf63f9137e714dbcb474ef",
+ "0xb8c3535701ddbeec2db08e17a4fa99ba6752d32ece5331a0b8743676f421fcb14798afc7c783815484f14693d2f70db8",
+ "0x81aee980c876949bf40782835eec8817d535f6f3f7e00bf402ddd61101fdcd60173961ae90a1cf7c5d060339a18c959d",
+ "0x87e67b928d97b62c49dac321ce6cb680233f3a394d4c9a899ac2e8db8ccd8e00418e66cdfd68691aa3cb8559723b580c",
+ "0x8eac204208d99a2b738648df96353bbb1b1065e33ee4f6bba174b540bbbd37d205855e1f1e69a6b7ff043ca377651126",
+ "0x848e6e7a54ad64d18009300b93ea6f459ce855971dddb419b101f5ac4c159215626fadc20cc3b9ab1701d8f6dfaddd8b",
+ "0x88aa123d9e0cf309d46dddb6acf634b1ade3b090a2826d6e5e78669fa1220d6df9a6697d7778cd9b627db17eea846126",
+ "0x9200c2a629b9144d88a61151b661b6c4256cc5dadfd1e59a8ce17a013c2d8f7e754aabe61663c3b30f1bc47784c1f8cf",
+ "0xb6e1a2827c3bdda91715b0e1b1f10dd363cef337e7c80cac1f34165fc0dea7c8b69747e310563db5818390146ce3e231",
+ "0x92c333e694f89f0d306d54105b2a5dcc912dbe7654d9e733edab12e8537350815be472b063e56cfde5286df8922fdecb",
+ "0xa6fac04b6d86091158ebb286586ccfec2a95c9786e14d91a9c743f5f05546073e5e3cc717635a0c602cad8334e922346",
+ "0xa581b4af77feebc1fb897d49b5b507c6ad513d8f09b273328efbb24ef0d91eb740d01b4d398f2738125dacfe550330cd",
+ "0x81c4860cccf76a34f8a2bc3f464b7bfd3e909e975cce0d28979f457738a56e60a4af8e68a3992cf273b5946e8d7f76e2",
+ "0x8d1eaa09a3180d8af1cbaee673db5223363cc7229a69565f592fa38ba0f9d582cedf91e15dabd06ebbf2862fc0feba54",
+ "0x9832f49b0147f4552402e54593cfa51f99540bffada12759b71fcb86734be8e500eea2d8b3d036710bdf04c901432de9",
+ "0x8bdb0e8ec93b11e5718e8c13cb4f5de545d24829fd76161216340108098dfe5148ed25e3b57a89a516f09fa79043734d",
+ "0xab96f06c4b9b0b2c0571740b24fca758e6976315053a7ecb20119150a9fa416db2d3a2e0f8168b390bb063f0c1caf785",
+ "0xab777f5c52acd62ecf4d1f168b9cc8e1a9b45d4ec6a8ff52c583e867c2239aba98d7d3af977289b367edce03d9c2dfb1",
+ "0xa09d3ce5e748da84802436951acc3d3ea5d8ec1d6933505ed724d6b4b0d69973ab0930daec9c6606960f6e541e4a3ce2",
+ "0x8ef94f7be4d85d5ad3d779a5cf4d7b2fc3e65c52fb8e1c3c112509a4af77a0b5be994f251e5e40fabeeb1f7d5615c22b",
+ "0xa7406a5bf5708d9e10922d3c5c45c03ef891b8d0d74ec9f28328a72be4cdc05b4f2703fa99366426659dfca25d007535",
+ "0xb7f52709669bf92a2e070bfe740f422f0b7127392c5589c7f0af71bb5a8428697c762d3c0d74532899da24ea7d8695c2",
+ "0xb9dfb0c8df84104dbf9239ccefa4672ef95ddabb8801b74997935d1b81a78a6a5669a3c553767ec19a1281f6e570f4ff",
+ "0xae4d5c872156061ce9195ac640190d8d71dd406055ee43ffa6f9893eb24b870075b74c94d65bc1d5a07a6573282b5520",
+ "0xafe6bd3eb72266d333f1807164900dcfa02a7eb5b1744bb3c86b34b3ee91e3f05e38fa52a50dc64eeb4bdb1dd62874b8",
+ "0x948043cf1bc2ef3c01105f6a78dc06487f57548a3e6ef30e6ebc51c94b71e4bf3ff6d0058c72b6f3ecc37efd7c7fa8c0",
+ "0xa22fd17c2f7ffe552bb0f23fa135584e8d2d8d75e3f742d94d04aded2a79e22a00dfe7acbb57d44e1cdb962fb22ae170",
+ "0x8cd0f4e9e4fb4a37c02c1bde0f69359c43ab012eb662d346487be0c3758293f1ca560122b059b091fddce626383c3a8f",
+ "0x90499e45f5b9c81426f3d735a52a564cafbed72711d9279fdd88de8038e953bc48c57b58cba85c3b2e4ce56f1ddb0e11",
+ "0x8c30e4c034c02958384564cac4f85022ef36ab5697a3d2feaf6bf105049675bbf23d01b4b6814711d3d9271abff04cac",
+ "0x81f7999e7eeea30f3e1075e6780bbf054f2fb6f27628a2afa4d41872a385b4216dd5f549da7ce6cf39049b2251f27fb7",
+ "0xb36a7191f82fc39c283ffe53fc1f5a9a00b4c64eee7792a8443475da9a4d226cf257f226ea9d66e329af15d8f04984ec",
+ "0xaad4da528fdbb4db504f3041c747455baff5fcd459a2efd78f15bdf3aea0bdb808343e49df88fe7a7c8620009b7964a3",
+ "0x99ebd8c6dd5dd299517fb6381cfc2a7f443e6e04a351440260dd7c2aee3f1d8ef06eb6c18820b394366ecdfd2a3ce264",
+ "0x8873725b81871db72e4ec3643084b1cdce3cbf80b40b834b092767728605825c19b6847ad3dcf328438607e8f88b4410",
+ "0xb008ee2f895daa6abd35bd39b6f7901ae4611a11a3271194e19da1cdcc7f1e1ea008fe5c5440e50d2c273784541ad9c5",
+ "0x9036feafb4218d1f576ef89d0e99124e45dacaa6d816988e34d80f454d10e96809791d5b78f7fd65f569e90d4d7238c5",
+ "0x92073c1d11b168e4fa50988b0288638b4868e48bbc668c5a6dddf5499875d53be23a285acb5e4bad60114f6cf6c556e9",
+ "0x88c87dfcb8ba6cbfe7e1be081ccfadbd589301db2cb7c99f9ee5d7db90aa297ed1538d5a867678a763f2deede5fd219a",
+ "0xb42a562805c661a50f5dea63108002c0f27c0da113da6a9864c9feb5552225417c0356c4209e8e012d9bcc9d182c7611",
+ "0x8e6317d00a504e3b79cd47feb4c60f9df186467fe9ca0f35b55c0364db30528f5ff071109dabb2fc80bb9cd4949f0c24",
+ "0xb7b1ea6a88694f8d2f539e52a47466695e39e43a5eb9c6f23bca15305fe52939d8755cc3ac9d6725e60f82f994a3772f",
+ "0xa3cd55161befe795af93a38d33290fb642b8d80da8b786c6e6fb02d393ea308fbe87f486994039cbd7c7b390414594b6",
+ "0xb416d2d45b44ead3b1424e92c73c2cf510801897b05d1724ff31cbd741920cd858282fb5d6040fe1f0aa97a65bc49424",
+ "0x950ee01291754feace97c2e933e4681e7ddfbc4fcd079eb6ff830b0e481d929c93d0c7fb479c9939c28ca1945c40da09",
+ "0x869bd916aee8d86efe362a49010382674825d49195b413b4b4018e88ce43fe091b475d0b863ff0ba2259400f280c2b23",
+ "0x9782f38cd9c9d3385ec286ebbc7cba5b718d2e65a5890b0a5906b10a89dc8ed80d417d71d7c213bf52f2af1a1f513ea7",
+ "0x91cd33bc2628d096269b23faf47ee15e14cb7fdc6a8e3a98b55e1031ea0b68d10ba30d97e660f7e967d24436d40fad73",
+ "0x8becc978129cc96737034c577ae7225372dd855da8811ae4e46328e020c803833b5bdbc4a20a93270e2b8bd1a2feae52",
+ "0xa36b1d8076783a9522476ce17f799d78008967728ce920531fdaf88303321bcaf97ecaa08e0c01f77bc32e53c5f09525",
+ "0xb4720e744943f70467983aa34499e76de6d59aa6fadf86f6b787fdce32a2f5b535b55db38fe2da95825c51002cfe142d",
+ "0x91ad21fc502eda3945f6de874d1b6bf9a9a7711f4d61354f9e5634fc73f9c06ada848de15ab0a75811d3250be862827d",
+ "0x84f78e2ebf5fc077d78635f981712daf17e2475e14c2a96d187913006ad69e234746184a51a06ef510c9455b38acb0d7",
+ "0x960aa7906e9a2f11db64a26b5892ac45f20d2ccb5480f4888d89973beb6fa0dfdc06d68d241ff5ffc7f1b82b1aac242d",
+ "0xa99365dcd1a00c66c9db6924b97c920f5c723380e823b250db85c07631b320ec4e92e586f7319e67a522a0578f7b6d6c",
+ "0xa25d92d7f70cf6a88ff317cfec071e13774516da664f5fac0d4ecaa65b8bf4eb87a64a4d5ef2bd97dfae98d388dbf5cc",
+ "0xa7af47cd0041295798f9779020a44653007444e8b4ef0712982b06d0dcdd434ec4e1f7c5f7a049326602cb605c9105b7",
+ "0xaefe172eac5568369a05980931cc476bebd9dea573ba276d59b9d8c4420784299df5a910033b7e324a6c2dfc62e3ef05",
+ "0xb69bc9d22ffa645baa55e3e02522e9892bb2daa7fff7c15846f13517d0799766883ee09ae0869df4139150c5b843ca8a",
+ "0x95a10856140e493354fdd12722c7fdded21b6a2ffbc78aa2697104af8ad0c8e2206f44b0bfee077ef3949d46bbf7c16b",
+ "0x891f2fcd2c47cbea36b7fa715968540c233313f05333f09d29aba23c193f462ed490dd4d00969656e89c53155fdfe710",
+ "0xa6c33e18115e64e385c843dde34e8a228222795c7ca90bc2cc085705d609025f3351d9be61822c69035a49fb3e48f2d5",
+ "0xb87fb12f12c0533b005adad0487f03393ff682e13575e3cb57280c3873b2c38ba96a63c49eef7a442753d26b7005230b",
+ "0xb905c02ba451bfd411c135036d92c27af3b0b1c9c2f1309d6948544a264b125f39dd41afeff4666b12146c545adc168a",
+ "0x8b29c513f43a78951cf742231cf5457a6d9d55edf45df5481a0f299a418d94effef561b15d2c1a01d1b8067e7153fda9",
+ "0xb9941cccd51dc645920d2781c81a317e5a33cb7cf76427b60396735912cb6d2ca9292bb4d36b6392467d390d2c58d9f3",
+ "0xa8546b627c76b6ef5c93c6a98538d8593dbe21cb7673fd383d5401b0c935eea0bdeeefeb1af6ad41bad8464fb87bbc48",
+ "0xaa286b27de2812de63108a1aec29d171775b69538dc6198640ac1e96767c2b83a50391f49259195957d457b493b667c9",
+ "0xa932fb229f641e9abbd8eb2bd874015d97b6658ab6d29769fc23b7db9e41dd4f850382d4c1f08af8f156c5937d524473",
+ "0xa1412840fcc86e2aeec175526f2fb36e8b3b8d21a78412b7266daf81e51b3f68584ed8bd42a66a43afdd8c297b320520",
+ "0x89c78be9efb624c97ebca4fe04c7704fa52311d183ffd87737f76b7dadc187c12c982bd8e9ed7cd8beb48cdaafd2fd01",
+ "0xa3f5ddec412a5bec0ce15e3bcb41c6214c2b05d4e9135a0d33c8e50a78eaba71e0a5a6ea8b45854dec5c2ed300971fc2",
+ "0x9721f9cec7a68b7758e3887548790de49fa6a442d0396739efa20c2f50352a7f91d300867556d11a703866def2d5f7b5",
+ "0xa23764e140a87e5991573521af039630dd28128bf56eed2edbed130fd4278e090b60cf5a1dca9de2910603d44b9f6d45",
+ "0xa1a6494a994215e48ab55c70efa8ffdddce6e92403c38ae7e8dd2f8288cad460c6c7db526bbdf578e96ca04d9fe12797",
+ "0xb1705ea4cb7e074efe0405fc7b8ee2ec789af0426142f3ec81241cacd4f7edcd88e39435e4e4d8e7b1df64f3880d6613",
+ "0x85595d061d677116089a6064418b93eb44ff79e68d12bd9625078d3bbc440a60d0b02944eff6054433ee34710ae6fbb4",
+ "0x9978d5e30bedb7526734f9a1febd973a70bfa20890490e7cc6f2f9328feab1e24f991285dbc3711d892514e2d7d005ad",
+ "0xaf30243c66ea43b9f87a061f947f7bce745f09194f6e95f379c7582b9fead920e5d6957eaf05c12ae1282ada4670652f",
+ "0xa1930efb473f88001e47aa0b2b2a7566848cccf295792e4544096ecd14ee5d7927c173a8576b405bfa2eec551cd67eb5",
+ "0xb0446d1c590ee5a45f7e22d269c044f3848c97aec1d226b44bfd0e94d9729c28a38bccddc3a1006cc5fe4e3c24f001f2",
+ "0xb8a8380172df3d84b06176df916cf557966d4f2f716d3e9437e415d75b646810f79f2b2b71d857181b7fc944018883a3",
+ "0xa563afec25b7817bfa26e19dc9908bc00aa8fc3d19be7d6de23648701659009d10e3e4486c28e9c6b13d48231ae29ac5",
+ "0xa5a8e80579de886fb7d6408f542791876885947b27ad6fa99a8a26e381f052598d7b4e647b0115d4b5c64297e00ce28e",
+ "0x8f87afcc7ad33c51ac719bade3cd92da671a37a82c14446b0a2073f4a0a23085e2c8d31913ed2d0be928f053297de8f6",
+ "0xa43c455ce377e0bc434386c53c752880687e017b2f5ae7f8a15c044895b242dffde4c92fb8f8bb50b18470b17351b156",
+ "0x8368f8b12a5bceb1dba25adb3a2e9c7dc9b1a77a1f328e5a693f5aec195cd1e06b0fe9476b554c1c25dac6c4a5b640a3",
+ "0x919878b27f3671fc78396f11531c032f3e2bd132d04cc234fa4858676b15fb1db3051c0b1db9b4fc49038216f11321ce",
+ "0xb48cd67fb7f1242696c1f877da4bdf188eac676cd0e561fbac1a537f7b8229aff5a043922441d603a26aae56a15faee4",
+ "0xa3e0fdfd4d29ea996517a16f0370b54787fefe543c2fe73bfc6f9e560c1fd30dad8409859e2d7fa2d44316f24746c712",
+ "0x8bb156ade8faf149df7bea02c140c7e392a4742ae6d0394d880a849127943e6f26312033336d3b9fdc0092d71b5efe87",
+ "0x8845e5d5cc555ca3e0523244300f2c8d7e4d02aaebcb5bd749d791208856c209a6f84dd99fd55968c9f0ab5f82916707",
+ "0xa3e90bb5c97b07789c2f32dff1aec61d0a2220928202f5ad5355ae71f8249237799d6c8a22602e32e572cb12eabe0c17",
+ "0xb150bcc391884c996149dc3779ce71f15dda63a759ee9cc05871f5a8379dcb62b047098922c0f26c7bd04deb394c33f9",
+ "0x95cd4ad88d51f0f2efcfd0c2df802fe252bb9704d1afbf9c26a248df22d55da87bdfaf41d7bc6e5df38bd848f0b13f42",
+ "0xa05a49a31e91dff6a52ac8b9c2cfdd646a43f0d488253f9e3cfbce52f26667166bbb9b608fc358763a65cbf066cd6d05",
+ "0xa59c3c1227fdd7c2e81f5e11ef5c406da44662987bac33caed72314081e2eed66055d38137e01b2268e58ec85dd986c0",
+ "0xb7020ec3bd73a99861f0f1d88cf5a19abab1cbe14b7de77c9868398c84bb8e18dbbe9831838a96b6d6ca06e82451c67b",
+ "0x98d1ff2525e9718ee59a21d8900621636fcd873d9a564b8dceb4be80a194a0148daf1232742730b3341514b2e5a5436c",
+ "0x886d97b635975fc638c1b6afc493e5998ca139edba131b75b65cfe5a8e814f11bb678e0eeee5e6e5cd913ad3f2fefdfc",
+ "0x8fb9fd928d38d5d813b671c924edd56601dd7163b686c13f158645c2f869d9250f3859aa5463a39258c90fef0f41190a",
+ "0xaac35e1cd655c94dec3580bb3800bd9c2946c4a9856f7d725af15fbea6a2d8ca51c8ad2772abed60ee0e3fb9cb24046b",
+ "0xb8d71fa0fa05ac9e443c9b4929df9e7f09a919be679692682e614d24227e04894bfc14a5c73a62fb927fedff4a0e4aa7",
+ "0xa45a19f11fbbb531a704badbb813ed8088ab827c884ee4e4ebf363fa1132ff7cfa9d28be9c85b143e4f7cdbc94e7cf1a",
+ "0x82b54703a4f295f5471b255ab59dce00f0fe90c9fb6e06b9ee48b15c91d43f4e2ef4a96c3118aeb03b08767be58181bb",
+ "0x8283264c8e6d2a36558f0d145c18576b6600ff45ff99cc93eca54b6c6422993cf392668633e5df396b9331e873d457e5",
+ "0x8c549c03131ead601bc30eb6b9537b5d3beb7472f5bb1bcbbfd1e9f3704477f7840ab3ab7f7dc13bbbbcdff886a462d4",
+ "0xafbb0c520ac1b5486513587700ad53e314cb74bfbc12e0b5fbdcfdaac36d342e8b59856196a0d84a25cff6e6e1d17e76",
+ "0x89e4c22ffb51f2829061b3c7c1983c5c750cad158e3a825d46f7cf875677da5d63f653d8a297022b5db5845c9271b32b",
+ "0xafb27a86c4c2373088c96b9adf4433f2ebfc78ac5c526e9f0510670b6e4e5e0057c0a4f75b185e1a30331b9e805c1c15",
+ "0xa18e16b57445f88730fc5d3567bf5a176861dc14c7a08ed2996fe80eed27a0e7628501bcb78a1727c5e9ac55f29c12c4",
+ "0x93d61bf88b192d6825cf4e1120af1c17aa0f994d158b405e25437eaeefae049f7b721a206e7cc8a04fdc29d3c42580a1",
+ "0xa99f2995a2e3ed2fd1228d64166112038de2f516410aa439f4c507044e2017ea388604e2d0f7121256fadf7fbe7023d1",
+ "0x914fd91cffc23c32f1c6d0e98bf660925090d873367d543034654389916f65f552e445b0300b71b61b721a72e9a5983c",
+ "0xb42a578a7787b71f924e7def425d849c1c777156b1d4170a8ee7709a4a914e816935131afd9a0412c4cb952957b20828",
+ "0x82fb30590e84b9e45db1ec475a39971cf554dc01bcc7050bc89265740725c02e2be5a972168c5170c86ae83e5b0ad2c0",
+ "0xb14f8d8e1e93a84976289e0cf0dfa6f3a1809e98da16ee5c4932d0e1ed6bf8a07697fdd4dd86a3df84fb0003353cdcc0",
+ "0x85d7a2f4bda31aa2cb208b771fe03291a4ebdaf6f1dc944c27775af5caec412584c1f45bc741fca2a6a85acb3f26ad7d",
+ "0xaf02e56ce886ff2253bc0a68faad76f25ead84b2144e5364f3fb9b648f03a50ee9dc0b2c33ebacf7c61e9e43201ef9ef",
+ "0x87e025558c8a0b0abd06dfc350016847ea5ced7af2d135a5c9eec9324a4858c4b21510fb0992ec52a73447f24945058e",
+ "0x80fff0bafcd058118f5e7a4d4f1ae0912efeb281d2cbe4d34ba8945cc3dbe5d8baf47fb077343b90b8d895c90b297aca",
+ "0xb6edcf3a40e7b1c3c0148f47a263cd819e585a51ef31c2e35a29ce6f04c53e413f743034c0d998d9c00a08ba00166f31",
+ "0xabb87ed86098c0c70a76e557262a494ff51a30fb193f1c1a32f8e35eafa34a43fcc07aa93a3b7a077d9e35afa07b1a3d",
+ "0xa280214cd3bb0fb7ecd2d8bcf518cbd9078417f2b91d2533ec2717563f090fb84f2a5fcfdbbeb2a2a1f8a71cc5aa5941",
+ "0xa63083ca7238ea2b57d15a475963cf1d4f550d8cd76db290014a0461b90351f1f26a67d674c837b0b773b330c7c3d534",
+ "0xa8fa39064cb585ece5263e2f42f430206476bf261bd50f18d2b694889bd79d04d56410664cecad62690e5c5a20b3f6ff",
+ "0x85ba52ce9d700a5dcf6c5b00559acbe599d671ce5512467ff4b6179d7fad550567ce2a9c126a50964e3096458ea87920",
+ "0xb913501e1008f076e5eac6d883105174f88b248e1c9801e568fefaffa1558e4909364fc6d9512aa4d125cbd7cc895f05",
+ "0x8eb33b5266c8f2ed4725a6ad147a322e44c9264cf261c933cbbe230a43d47fca0f29ec39756b20561dabafadd5796494",
+ "0x850ebc8b661a04318c9db5a0515066e6454fa73865aa4908767a837857ecd717387f614acb614a88e075d4edc53a2f5a",
+ "0xa08d6b92d866270f29f4ce23a3f5d99b36b1e241a01271ede02817c8ec3f552a5c562db400766c07b104a331835c0c64",
+ "0x8131804c89bb3e74e9718bfc4afa547c1005ff676bd4db9604335032b203390cfa54478d45c6c78d1fe31a436ed4be9f",
+ "0x9106d94f23cc1eacec8316f16d6f0a1cc160967c886f51981fdb9f3f12ee1182407d2bb24e5b873de58cb1a3ee915a6b",
+ "0xa13806bfc3eae7a7000c9d9f1bd25e10218d4e67f59ae798b145b098bca3edad2b1040e3fc1e6310e612fb8818f459ac",
+ "0x8c69fbca502046cb5f6db99900a47b34117aef3f4b241690cdb3b84ca2a2fc7833e149361995dc41fa78892525bce746",
+ "0x852c473150c91912d58ecb05769222fa18312800c3f56605ad29eec9e2d8667b0b81c379048d3d29100ed2773bb1f3c5",
+ "0xb1767f6074426a00e01095dbb1795beb4e4050c6411792cbad6537bc444c3165d1058bafd1487451f9c5ddd209e0ae7e",
+ "0x80c600a5fe99354ce59ff0f84c760923dc8ff66a30bf47dc0a086181785ceb01f9b951c4e66df800ea6d705e8bc47055",
+ "0xb5cf19002fbc88a0764865b82afcb4d64a50196ea361e5c71dff7de084f4dcbbc34ec94a45cc9e0247bd51da565981aa",
+ "0x93e67a254ea8ce25e112d93cc927fadaa814152a2c4ec7d9a56eaa1ed47aec99b7e9916b02e64452cc724a6641729bbb",
+ "0xace70b32491bda18eee4a4d041c3bc9effae9340fe7e6c2f5ad975ee0874c17f1a7da7c96bd85fccff9312c518fac6e9",
+ "0xab4cfa02065017dd7f1aadc66f2c92f78f0f11b8597c03a5d69d82cb2eaf95a4476a836ac102908f137662472c8d914b",
+ "0xa40b8cd8deb8ae503d20364d64cab7c2801b7728a9646ed19c65edea6a842756a2f636283494299584ad57f4bb12cd0b",
+ "0x8594e11d5fc2396bcd9dbf5509ce4816dbb2b7305168021c426171fb444d111da5a152d6835ad8034542277011c26c0e",
+ "0x8024de98c26b4c994a66628dc304bb737f4b6859c86ded552c5abb81fd4c6c2e19d5a30beed398a694b9b2fdea1dd06a",
+ "0x8843f5872f33f54df8d0e06166c1857d733995f67bc54abb8dfa94ad92407cf0179bc91b0a50bbb56cdc2b350d950329",
+ "0xb8bab44c7dd53ef9edf497dcb228e2a41282c90f00ba052fc52d57e87b5c8ab132d227af1fcdff9a12713d1f980bcaae",
+ "0x982b4d7b29aff22d527fd82d2a52601d95549bfb000429bb20789ed45e5abf1f4b7416c7b7c4b79431eb3574b29be658",
+ "0x8eb1f571b6a1878e11e8c1c757e0bc084bab5e82e897ca9be9b7f4b47b91679a8190bf0fc8f799d9b487da5442415857",
+ "0xa6e74b588e5af935c8b243e888582ef7718f8714569dd4992920740227518305eb35fab674d21a5551cca44b3e511ef2",
+ "0xa30fc2f3a4cb4f50566e82307de73cd7bd8fe2c1184e9293c136a9b9e926a018d57c6e4f308c95b9eb8299e94d90a2a1",
+ "0xa50c5869ca5d2b40722c056a32f918d47e0b65ca9d7863ca7d2fb4a7b64fe523fe9365cf0573733ceaadebf20b48fff8",
+ "0x83bbdd32c04d17581418cf360749c7a169b55d54f2427390defd9f751f100897b2d800ce6636c5bbc046c47508d60c8c",
+ "0xa82904bdf614de5d8deaff688c8a5e7ac5b3431687acbcda8fa53960b7c417a39c8b2e462d7af91ce6d79260f412db8e",
+ "0xa4362e31ff4b05d278b033cf5eebea20de01714ae16d4115d04c1da4754269873afc8171a6f56c5104bfd7b0db93c3e7",
+ "0xb5b8daa63a3735581e74a021b684a1038cea77168fdb7fdf83c670c2cfabcfc3ab2fc7359069b5f9048188351aef26b5",
+ "0xb48d723894b7782d96ac8433c48faca1bdfa5238019c451a7f47d958097cce3ae599b876cf274269236b9d6ff8b6d7ca",
+ "0x98ffff6a61a3a6205c7820a91ca2e7176fab5dba02bc194c4d14942ac421cb254183c705506ab279e4f8db066f941c6c",
+ "0xae7db24731da2eaa6efc4f7fcba2ecc26940ddd68038dce43acf2cee15b72dc4ef42a7bfdd32946d1ed78786dd7696b3",
+ "0xa656db14f1de9a7eb84f6301b4acb2fbf78bfe867f48a270e416c974ab92821eb4df1cb881b2d600cfed0034ac784641",
+ "0xaa315f8ecba85a5535e9a49e558b15f39520fce5d4bf43131bfbf2e2c9dfccc829074f9083e8d49f405fb221d0bc4c3c",
+ "0x90bffba5d9ff40a62f6c8e9fc402d5b95f6077ed58d030c93e321b8081b77d6b8dac3f63a92a7ddc01585cf2c127d66c",
+ "0xabdd733a36e0e0f05a570d0504e73801bf9b5a25ff2c78786f8b805704997acb2e6069af342538c581144d53149fa6d3",
+ "0xb4a723bb19e8c18a01bd449b1bb3440ddb2017f10bb153da27deb7a6a60e9bb37619d6d5435fbb1ba617687838e01dd0",
+ "0x870016b4678bab3375516db0187a2108b2e840bae4d264b9f4f27dbbc7cc9cac1d7dc582d7a04d6fd1ed588238e5e513",
+ "0x80d33d2e20e8fc170aa3cb4f69fffb72aeafb3b5bb4ea0bc79ab55da14142ca19b2d8b617a6b24d537366e3b49cb67c3",
+ "0xa7ee76aec273aaae03b3b87015789289551969fb175c11557da3ab77e39ab49d24634726f92affae9f4d24003050d974",
+ "0x8415ea4ab69d779ebd42d0fe0c6aef531d6a465a5739e429b1fcf433ec45aa8296c527e965a20f0ec9f340c9273ea3cf",
+ "0x8c7662520794e8b4405d0b33b5cac839784bc86a5868766c06cbc1fa306dbe334978177417b31baf90ce7b0052a29c56",
+ "0x902b2abecc053a3dbdea9897ee21e74821f3a1b98b2d560a514a35799f4680322550fd3a728d4f6d64e1de98033c32b8",
+ "0xa05e84ed9ecab8d508d670c39f2db61ad6e08d2795ec32a3c9d0d3737ef3801618f4fc2a95f90ec2f068606131e076c5",
+ "0x8b9208ff4d5af0c2e3f53c9375da666773ac57197dfabb0d25b1c8d0588ba7f3c15ee9661bb001297f322ea2fbf6928b",
+ "0xa3c827741b34a03254d4451b5ab74a96f2b9f7fb069e2f5adaf54fd97cc7a4d516d378db5ca07da87d8566d6eef13726",
+ "0x8509d8a3f4a0ed378e0a1e28ea02f6bf1d7f6c819c6c2f5297c7df54c895b848f841653e32ba2a2c22c2ff739571acb8",
+ "0xa0ce988b7d3c40b4e496aa83a09e4b5472a2d98679622f32bea23e6d607bc7de1a5374fb162bce0549a67dad948519be",
+ "0xaa8a3dd12bd60e3d2e05f9c683cdcb8eab17fc59134815f8d197681b1bcf65108cba63ac5c58ee632b1e5ed6bba5d474",
+ "0x8b955f1d894b3aefd883fb4b65f14cd37fc2b9db77db79273f1700bef9973bf3fd123897ea2b7989f50003733f8f7f21",
+ "0xac79c00ddac47f5daf8d9418d798d8af89fc6f1682e7e451f71ea3a405b0d36af35388dd2a332af790bc83ca7b819328",
+ "0xa0d44dd2a4438b809522b130d0938c3fe7c5c46379365dbd1810a170a9aa5818e1c783470dd5d0b6d4ac7edbb7330910",
+ "0xa30b69e39ad43dd540a43c521f05b51b5f1b9c4eed54b8162374ae11eac25da4f5756e7b70ce9f3c92c2eeceee7431ed",
+ "0xac43220b762c299c7951222ea19761ab938bf38e4972deef58ed84f4f9c68c230647cf7506d7cbfc08562fcca55f0485",
+ "0xb28233b46a8fb424cfa386a845a3b5399d8489ceb83c8f3e05c22c934798d639c93718b7b68ab3ce24c5358339e41cbb",
+ "0xac30d50ee8ce59a10d4b37a3a35e62cdb2273e5e52232e202ca7d7b8d09d28958ee667fae41a7bb6cdc6fe8f6e6c9c85",
+ "0xb199842d9141ad169f35cc7ff782b274cbaa645fdb727761e0a89edbf0d781a15f8218b4bf4eead326f2903dd88a9cc1",
+ "0x85e018c7ddcad34bb8285a737c578bf741ccd547e68c734bdb3808380e12c5d4ef60fc896b497a87d443ff9abd063b38",
+ "0x8c856e6ba4a815bdb891e1276f93545b7072f6cb1a9aa6aa5cf240976f29f4dee01878638500a6bf1daf677b96b54343",
+ "0xb8a47555fa8710534150e1a3f13eab33666017be6b41005397afa647ea49708565f2b86b77ad4964d140d9ced6b4d585",
+ "0x8cd1f1db1b2f4c85a3f46211599caf512d5439e2d8e184663d7d50166fd3008f0e9253272f898d81007988435f715881",
+ "0xb1f34b14612c973a3eceb716dc102b82ab18afef9de7630172c2780776679a7706a4874e1df3eaadf541fb009731807f",
+ "0xb25464af9cff883b55be2ff8daf610052c02df9a5e147a2cf4df6ce63edcdee6dc535c533590084cc177da85c5dc0baa",
+ "0x91c3c4b658b42d8d3448ae1415d4541d02379a40dc51e36a59bd6e7b9ba3ea51533f480c7c6e8405250ee9b96a466c29",
+ "0x86dc027b95deb74c36a58a1333a03e63cb5ae22d3b29d114cfd2271badb05268c9d0c819a977f5e0c6014b00c1512e3a",
+ "0xae0e6ff58eb5fa35da5107ebeacf222ab8f52a22bb1e13504247c1dfa65320f40d97b0e6b201cb6613476687cb2f0681",
+ "0x8f13415d960b9d7a1d93ef28afc2223e926639b63bdefce0f85e945dfc81670a55df288893a0d8b3abe13c5708f82f91",
+ "0x956f67ca49ad27c1e3a68c1faad5e7baf0160c459094bf6b7baf36b112de935fdfd79fa4a9ea87ea8de0ac07272969f4",
+ "0x835e45e4a67df9fb51b645d37840b3a15c171d571a10b03a406dd69d3c2f22df3aa9c5cbe1e73f8d767ce01c4914ea9a",
+ "0x919b938e56d4b32e2667469d0bdccb95d9dda3341aa907683ee70a14bbbe623035014511c261f4f59b318b610ac90aa3",
+ "0x96b48182121ccd9d689bf1dfdc228175564cd68dc904a99c808a7f0053a6f636c9d953e12198bdf2ea49ea92772f2e18",
+ "0xac5e5a941d567fa38fdbcfa8cf7f85bb304e3401c52d88752bcd516d1fa9bac4572534ea2205e38423c1df065990790f",
+ "0xac0bd594fb85a8d4fc26d6df0fa81f11919401f1ecf9168b891ec7f061a2d9368af99f7fd8d9b43b2ce361e7b8482159",
+ "0x83d92c69ca540d298fe80d8162a1c7af3fa9b49dfb69e85c1d136a3ec39fe419c9fa78e0bb6d96878771fbd37fe92e40",
+ "0xb35443ae8aa66c763c2db9273f908552fe458e96696b90e41dd509c17a5c04ee178e3490d9c6ba2dc0b8f793c433c134",
+ "0x923b2d25aa45b2e580ffd94cbb37dc8110f340f0f011217ee1bd81afb0714c0b1d5fb4db86006cdd2457563276f59c59",
+ "0x96c9125d38fca1a61ac21257b696f8ac3dae78def50285e44d90ea293d591d1c58f703540a7e4e99e070afe4646bbe15",
+ "0xb57946b2332077fbcdcb406b811779aefd54473b5559a163cd65cb8310679b7e2028aa55c12a1401fdcfcac0e6fae29a",
+ "0x845daedc5cf972883835d7e13c937b63753c2200324a3b8082a6c4abb4be06c5f7c629d4abe4bfaf1d80a1f073eb6ce6",
+ "0x91a55dfd0efefcd03dc6dacc64ec93b8d296cb83c0ee72400a36f27246e7f2a60e73b7b70ba65819e9cfb73edb7bd297",
+ "0x8874606b93266455fe8fdd25df9f8d2994e927460af06f2e97dd4d2d90db1e6b06d441b72c2e76504d753badca87fb37",
+ "0x8ee99e6d231274ff9252c0f4e84549da173041299ad1230929c3e3d32399731c4f20a502b4a307642cac9306ccd49d3c",
+ "0x8836497714a525118e20849d6933bb8535fb6f72b96337d49e3133d936999c90a398a740f42e772353b5f1c63581df6d",
+ "0xa6916945e10628f7497a6cdc5e2de113d25f7ade3e41e74d3de48ccd4fce9f2fa9ab69645275002e6f49399b798c40af",
+ "0x9597706983107eb23883e0812e1a2c58af7f3499d50c6e29b455946cb9812fde1aa323d9ed30d1c0ffd455abe32303cd",
+ "0xa24ee89f7f515cc33bdbdb822e7d5c1877d337f3b2162303cfc2dae028011c3a267c5cb4194afa63a4856a6e1c213448",
+ "0x8cd25315e4318801c2776824ae6e7d543cb85ed3bc2498ba5752df2e8142b37653cf9e60104d674be3aeb0a66912e97a",
+ "0xb5085ecbe793180b40dbeb879f4c976eaaccaca3a5246807dced5890e0ed24d35f3f86955e2460e14fb44ff5081c07ba",
+ "0x960188cc0b4f908633a6840963a6fa2205fc42c511c6c309685234911c5304ef4c304e3ae9c9c69daa2fb6a73560c256",
+ "0xa32d0a70bf15d569b4cda5aebe3e41e03c28bf99cdd34ffa6c5d58a097f322772acca904b3a47addb6c7492a7126ebac",
+ "0x977f72d06ad72d4aa4765e0f1f9f4a3231d9f030501f320fe7714cc5d329d08112789fa918c60dd7fdb5837d56bb7fc6",
+ "0x99fa038bb0470d45852bb871620d8d88520adb701712fcb1f278fed2882722b9e729e6cdce44c82caafad95e37d0e6f7",
+ "0xb855e8f4fc7634ada07e83b6c719a1e37acb06394bc8c7dcab7747a8c54e5df3943915f021364bd019fdea103864e55f",
+ "0x88bc2cd7458532e98c596ef59ea2cf640d7cc31b4c33cef9ed065c078d1d4eb49677a67de8e6229cc17ea48bace8ee5a",
+ "0xaaa78a3feaa836d944d987d813f9b9741afb076e6aca1ffa42682ab06d46d66e0c07b8f40b9dbd63e75e81efa1ef7b08",
+ "0xb7b080420cc4d808723b98b2a5b7b59c81e624ab568ecdfdeb8bf3aa151a581b6f56e983ef1b6f909661e25db40b0c69",
+ "0xabee85c462ac9a2c58e54f06c91b3e5cd8c5f9ab5b5deb602b53763c54826ed6deb0d6db315a8d7ad88733407e8d35e2",
+ "0x994d075c1527407547590df53e9d72dd31f037c763848d1662eebd4cefec93a24328c986802efa80e038cb760a5300f5",
+ "0xab8777640116dfb6678e8c7d5b36d01265dfb16321abbfc277da71556a34bb3be04bc4ae90124ed9c55386d2bfb3bda0",
+ "0x967e3a828bc59409144463bcf883a3a276b5f24bf3cbfdd7a42343348cba91e00b46ac285835a9b91eef171202974204",
+ "0x875a9f0c4ffe5bb1d8da5e3c8e41d0397aa6248422a628bd60bfae536a651417d4e8a7d2fb98e13f2dad3680f7bd86d3",
+ "0xacaa330c3e8f95d46b1880126572b238dbb6d04484d2cd4f257ab9642d8c9fc7b212188b9c7ac9e0fd135c520d46b1bf",
+ "0xaceb762edbb0f0c43dfcdb01ea7a1ac5918ca3882b1e7ebc4373521742f1ed5250d8966b498c00b2b0f4d13212e6dd0b",
+ "0x81d072b4ad258b3646f52f399bced97c613b22e7ad76373453d80b1650c0ca87edb291a041f8253b649b6e5429bb4cff",
+ "0x980a47d27416ac39c7c3a0ebe50c492f8c776ea1de44d5159ac7d889b6d554357f0a77f0e5d9d0ff41aae4369eba1fc2",
+ "0x8b4dfd5ef5573db1476d5e43aacfb5941e45d6297794508f29c454fe50ea622e6f068b28b3debe8635cf6036007de2e3",
+ "0xa60831559d6305839515b68f8c3bc7abbd8212cc4083502e19dd682d56ca37c9780fc3ce4ec2eae81ab23b221452dc57",
+ "0x951f6b2c1848ced9e8a2339c65918e00d3d22d3e59a0a660b1eca667d18f8430d737884e9805865ef3ed0fe1638a22d9",
+ "0xb02e38fe790b492aa5e89257c4986c9033a8b67010fa2add9787de857d53759170fdd67715ca658220b4e14b0ca48124",
+ "0xa51007e4346060746e6b0e4797fc08ef17f04a34fe24f307f6b6817edbb8ce2b176f40771d4ae8a60d6152cbebe62653",
+ "0xa510005b05c0b305075b27b243c9d64bcdce85146b6ed0e75a3178b5ff9608213f08c8c9246f2ca6035a0c3e31619860",
+ "0xaaff4ef27a7a23be3419d22197e13676d6e3810ceb06a9e920d38125745dc68a930f1741c9c2d9d5c875968e30f34ab5",
+ "0x864522a9af9857de9814e61383bebad1ba9a881696925a0ea6bfc6eff520d42c506bbe5685a9946ed710e889765be4a0",
+ "0xb63258c080d13f3b7d5b9f3ca9929f8982a6960bdb1b0f8676f4dca823971601672f15e653917bf5d3746bb220504913",
+ "0xb51ce0cb10869121ae310c7159ee1f3e3a9f8ad498827f72c3d56864808c1f21fa2881788f19ece884d3f705cd7bd0c5",
+ "0x95d9cecfc018c6ed510e441cf84c712d9909c778c16734706c93222257f64dcd2a9f1bd0b400ca271e22c9c487014274",
+ "0x8beff4d7d0140b86380ff4842a9bda94c2d2be638e20ac68a4912cb47dbe01a261857536375208040c0554929ced1ddc",
+ "0x891ff49258749e2b57c1e9b8e04b12c77d79c3308b1fb615a081f2aacdfb4b39e32d53e069ed136fdbd43c53b87418fa",
+ "0x9625cad224e163d387738825982d1e40eeff35fe816d10d7541d15fdc4d3eee48009090f3faef4024b249205b0b28f72",
+ "0x8f3947433d9bd01aa335895484b540a9025a19481a1c40b4f72dd676bfcf332713714fd4010bde936eaf9470fd239ed0",
+ "0xa00ec2d67789a7054b53f0e858a8a232706ccc29a9f3e389df7455f1a51a2e75801fd78469a13dbc25d28399ae4c6182",
+ "0xa3f65884506d4a62b8775a0ea0e3d78f5f46bc07910a93cd604022154eabdf1d73591e304d61edc869e91462951975e1",
+ "0xa14eef4fd5dfac311713f0faa9a60415e3d30b95a4590cbf95f2033dffb4d16c02e7ceff3dcd42148a4e3bc49cce2dd4",
+ "0x8afa11c0eef3c540e1e3460bc759bb2b6ea90743623f88e62950c94e370fe4fd01c22b6729beba4dcd4d581198d9358f",
+ "0xafb05548a69f0845ffcc5f5dc63e3cdb93cd270f5655173b9a950394b0583663f2b7164ba6df8d60c2e775c1d9f120af",
+ "0x97f179e01a947a906e1cbeafa083960bc9f1bade45742a3afee488dfb6011c1c6e2db09a355d77f5228a42ccaa7bdf8e",
+ "0x8447fca4d35f74b3efcbd96774f41874ca376bf85b79b6e66c92fa3f14bdd6e743a051f12a7fbfd87f319d1c6a5ce217",
+ "0xa57ca39c23617cd2cf32ff93b02161bd7baf52c4effb4679d9d5166406e103bc8f3c6b5209e17c37dbb02deb8bc72ddd",
+ "0x9667c7300ff80f0140be002b0e36caab07aaee7cce72679197c64d355e20d96196acaf54e06e1382167d081fe6f739c1",
+ "0x828126bb0559ce748809b622677267ca896fa2ee76360fd2c02990e6477e06a667241379ca7e65d61a5b64b96d7867de",
+ "0x8b8835dea6ba8cf61c91f01a4b3d2f8150b687a4ee09b45f2e5fc8f80f208ae5d142d8e3a18153f0722b90214e60c5a7",
+ "0xa98e8ff02049b4da386e3ee93db23bbb13dfeb72f1cfde72587c7e6d962780b7671c63e8ac3fbaeb1a6605e8d79e2f29",
+ "0x87a4892a0026d7e39ef3af632172b88337cb03669dea564bcdb70653b52d744730ebb5d642e20cb627acc9dbb547a26b",
+ "0x877352a22fc8052878a57effc159dac4d75fe08c84d3d5324c0bab6d564cdf868f33ceee515eee747e5856b62cfa0cc7",
+ "0x8b801ba8e2ff019ee62f64b8cb8a5f601fc35423eb0f9494b401050103e1307dc584e4e4b21249cd2c686e32475e96c3",
+ "0xa9e7338d6d4d9bfec91b2af28a8ed13b09415f57a3a00e5e777c93d768fdb3f8e4456ae48a2c6626b264226e911a0e28",
+ "0x99c05fedf40ac4726ed585d7c1544c6e79619a0d3fb6bda75a08c7f3c0008e8d5e19ed4da48de3216135f34a15eba17c",
+ "0xa61cce8a1a8b13a4a650fdbec0eeea8297c352a8238fb7cac95a0df18ed16ee02a3daa2de108fa122aca733bd8ad7855",
+ "0xb97f37da9005b440b4cb05870dd881bf8491fe735844f2d5c8281818583b38e02286e653d9f2e7fa5e74c3c3eb616540",
+ "0xa72164a8554da8e103f692ac5ebb4aece55d5194302b9f74b6f2a05335b6e39beede0bf7bf8c5bfd4d324a784c5fb08c",
+ "0xb87e8221c5341cd9cc8bb99c10fe730bc105550f25ed4b96c0d45e6142193a1b2e72f1b3857373a659b8c09be17b3d91",
+ "0xa41fb1f327ef91dcb7ac0787918376584890dd9a9675c297c45796e32d6e5985b12f9b80be47fc3a8596c245f419d395",
+ "0x90dafa3592bdbb3465c92e2a54c2531822ba0459d45d3e7a7092fa6b823f55af28357cb51896d4ec2d66029c82f08e26",
+ "0xa0a9adc872ebc396557f484f1dd21954d4f4a21c4aa5eec543f5fa386fe590839735c01f236574f7ff95407cd12de103",
+ "0xb8c5c940d58be7538acf8672852b5da3af34f82405ef2ce8e4c923f1362f97fc50921568d0fd2fe846edfb0823e62979",
+ "0x85aaf06a8b2d0dac89dafd00c28533f35dbd074978c2aaa5bef75db44a7b12aeb222e724f395513b9a535809a275e30b",
+ "0x81f3cbe82fbc7028c26a6c1808c604c63ba023a30c9f78a4c581340008dbda5ec07497ee849a2183fcd9124f7936af32",
+ "0xa11ac738de75fd60f15a34209d3825d5e23385796a4c7fc5931822f3f380af977dd0f7b59fbd58eed7777a071e21b680",
+ "0x85a279c493de03db6fa6c3e3c1b1b29adc9a8c4effc12400ae1128da8421954fa8b75ad19e5388fe4543b76fb0812813",
+ "0x83a217b395d59ab20db6c4adb1e9713fc9267f5f31a6c936042fe051ce8b541f579442f3dcf0fa16b9e6de9fd3518191",
+ "0x83a0b86e7d4ed8f9ccdc6dfc8ff1484509a6378fa6f09ed908e6ab9d1073f03011dc497e14304e4e3d181b57de06a5ab",
+ "0xa63ad69c9d25704ce1cc8e74f67818e5ed985f8f851afa8412248b2df5f833f83b95b27180e9e7273833ed0d07113d3b",
+ "0x99b1bc2021e63b561fe44ddd0af81fcc8627a91bfeecbbc989b642bc859abc0c8d636399701aad7bbaf6a385d5f27d61",
+ "0xb53434adb66f4a807a6ad917c6e856321753e559b1add70824e5c1e88191bf6993fccb9b8b911fc0f473fb11743acacd",
+ "0x97ed3b9e6fb99bf5f945d4a41f198161294866aa23f2327818cdd55cb5dc4c1a8eff29dd8b8d04902d6cd43a71835c82",
+ "0xb1e808260e368a18d9d10bdea5d60223ba1713b948c782285a27a99ae50cc5fc2c53d407de07155ecc16fb8a36d744a0",
+ "0xa3eb4665f18f71833fec43802730e56b3ee5a357ea30a888ad482725b169d6f1f6ade6e208ee081b2e2633079b82ba7d",
+ "0xab8beb2c8353fc9f571c18fdd02bdb977fc883313469e1277b0372fbbb33b80dcff354ca41de436d98d2ed710faa467e",
+ "0xaa9071cfa971e4a335a91ad634c98f2be51544cb21f040f2471d01bb97e1df2277ae1646e1ea8f55b7ba9f5c8c599b39",
+ "0x80b7dbfdcaf40f0678012acc634eba44ea51181475180d9deb2050dc4f2de395289edd0223018c81057ec79b04b04c49",
+ "0x89623d7f6cb17aa877af14de842c2d4ab7fd576d61ddd7518b5878620a01ded40b6010de0da3cdf31d837eecf30e9847",
+ "0xa773bb024ae74dd24761f266d4fb27d6fd366a8634febe8235376b1ae9065c2fe12c769f1d0407867dfbe9f5272c352f",
+ "0x8455a561c3aaa6ba64c881a5e13921c592b3a02e968f4fb24a2243c36202795d0366d9cc1a24e916f84d6e158b7aeac7",
+ "0x81d8bfc4b283cf702a40b87a2b96b275bdbf0def17e67d04842598610b67ea08c804d400c3e69fa09ea001eaf345b276",
+ "0xb8f8f82cb11fea1c99467013d7e167ff03deb0c65a677fab76ded58826d1ba29aa7cf9fcd7763615735ea3ad38e28719",
+ "0x89a6a04baf9cccc1db55179e1650b1a195dd91fb0aebc197a25143f0f393524d2589975e3fbfc2547126f0bced7fd6f2",
+ "0xb81b2162df045390f04df07cbd0962e6b6ca94275a63edded58001a2f28b2ae2af2c7a6cba4ecd753869684e77e7e799",
+ "0xa3757f722776e50de45c62d9c4a2ee0f5655a512344c4cbec542d8045332806568dd626a719ef21a4eb06792ca70f204",
+ "0x8c5590df96ec22179a4e8786de41beb44f987a1dcc508eb341eecbc0b39236fdfad47f108f852e87179ccf4e10091e59",
+ "0x87502f026ed4e10167419130b88c3737635c5b9074c364e1dd247cef5ef0fc064b4ae99b187e33301e438bbd2fe7d032",
+ "0xaf925a2165e980ced620ff12289129fe17670a90ae0f4db9d4b39bd887ccb1f5d2514ac9ecf910f6390a8fc66bd5be17",
+ "0x857fca899828cf5c65d26e3e8a6e658542782fc72762b3b9c73514919f83259e0f849a9d4838b40dc905fe43024d0d23",
+ "0x87ffebdbfb69a9e1007ebac4ffcb4090ff13705967b73937063719aa97908986effcb7262fdadc1ae0f95c3690e3245d",
+ "0xa9ff6c347ac6f4c6ab993b748802e96982eaf489dc69032269568412fc9a79e7c2850dfc991b28211b3522ee4454344b",
+ "0xa65b3159df4ec48bebb67cb3663cd744027ad98d970d620e05bf6c48f230fa45bf17527fe726fdf705419bb7a1bb913e",
+ "0x84b97b1e6408b6791831997b03cd91f027e7660fd492a93d95daafe61f02427371c0e237c75706412f442991dfdff989",
+ "0xab761c26527439b209af0ae6afccd9340bbed5fbe098734c3145b76c5d2cd7115d9227b2eb523882b7317fbb09180498",
+ "0xa0479a8da06d7a69c0b0fee60df4e691c19c551f5e7da286dab430bfbcabf31726508e20d26ea48c53365a7f00a3ad34",
+ "0xa732dfc9baa0f4f40b5756d2e8d8937742999623477458e0bc81431a7b633eefc6f53b3b7939fe0a020018549c954054",
+ "0x901502436a1169ba51dc479a5abe7c8d84e0943b16bc3c6a627b49b92cd46263c0005bc324c67509edd693f28e612af1",
+ "0xb627aee83474e7f84d1bab9b7f6b605e33b26297ac6bbf52d110d38ba10749032bd551641e73a383a303882367af429b",
+ "0x95108866745760baef4a46ef56f82da6de7e81c58b10126ebd2ba2cd13d339f91303bf2fb4dd104a6956aa3b13739503",
+ "0x899ed2ade37236cec90056f3569bc50f984f2247792defafcceb49ad0ca5f6f8a2f06573705300e07f0de0c759289ff5",
+ "0xa9f5eee196d608efe4bcef9bf71c646d27feb615e21252cf839a44a49fd89da8d26a758419e0085a05b1d59600e2dc42",
+ "0xb36c6f68fed6e6c85f1f4a162485f24817f2843ec5cbee45a1ebfa367d44892e464949c6669f7972dc7167af08d55d25",
+ "0xaaaede243a9a1b6162afbc8f571a52671a5a4519b4062e3f26777664e245ba873ed13b0492c5dbf0258c788c397a0e9e",
+ "0x972b4fb39c31cbe127bf9a32a5cc10d621ebdd9411df5e5da3d457f03b2ab2cd1f6372d8284a4a9400f0b06ecdbfd38e",
+ "0x8f6ca1e110e959a4b1d9a5ce5f212893cec21db40d64d5ac4d524f352d72198f923416a850bf845bc5a22a79c0ea2619",
+ "0xa0f3c93b22134f66f04b2553a53b738644d1665ceb196b8494b315a4c28236fb492017e4a0de4224827c78e42f9908b7",
+ "0x807fb5ee74f6c8735b0b5ca07e28506214fe4047dbeb00045d7c24f7849e98706aea79771241224939cb749cf1366c7d",
+ "0x915eb1ff034224c0b645442cdb7d669303fdc00ca464f91aaf0b6fde0b220a3a74ff0cb043c26c9f3a5667b3fdaa9420",
+ "0x8fda6cef56ed33fefffa9e6ac8e6f76b1af379f89761945c63dd448801f7bb8ca970504a7105fac2f74f652ccff32327",
+ "0x87380cffdcffb1d0820fa36b63cc081e72187f86d487315177d4d04da4533eb19a0e2ff6115ceab528887819c44a5164",
+ "0x8cd89e03411a18e7f16f968b89fb500c36d47d229f6487b99e62403a980058db5925ce249206743333538adfad168330",
+ "0x974451b1df33522ce7056de9f03e10c70bf302c44b0741a59df3d6877d53d61a7394dcee1dd46e013d7cb9d73419c092",
+ "0x98c35ddf645940260c490f384a49496a7352bb8e3f686feed815b1d38f59ded17b1ad6e84a209e773ed08f7b8ff1e4c2",
+ "0x963f386cf944bb9b2ddebb97171b64253ea0a2894ac40049bdd86cda392292315f3a3d490ca5d9628c890cfb669f0acb",
+ "0x8d507712152babd6d142ee682638da8495a6f3838136088df9424ef50d5ec28d815a198c9a4963610b22e49b4cdf95e9",
+ "0x83d4bc6b0be87c8a4f1e9c53f257719de0c73d85b490a41f7420e777311640937320557ff2f1d9bafd1daaa54f932356",
+ "0x82f5381c965b7a0718441131c4d13999f4cdce637698989a17ed97c8ea2e5bdb5d07719c5f7be8688edb081b23ede0f4",
+ "0xa6ebecab0b72a49dfd01d69fa37a7f74d34fb1d4fef0aa10e3d6fceb9eccd671225c230af89f6eb514250e41a5f91f52",
+ "0x846d185bdad6e11e604df7f753b7a08a28b643674221f0e750ebdb6b86ec584a29c869e131bca868972a507e61403f6a",
+ "0x85a98332292acb744bd1c0fd6fdcf1f889a78a2c9624d79413ffa194cc8dfa7821a4b60cde8081d4b5f71f51168dd67f",
+ "0x8f7d97c3b4597880d73200d074eb813d95432306e82dafc70b580b8e08cb8098b70f2d07b4b3ac6a4d77e92d57035031",
+ "0x8185439c8751e595825d7053518cbe121f191846a38d4dbcb558c3f9d7a3104f3153401adaaaf27843bbe2edb504bfe3",
+ "0xb3c00d8ece1518fca6b1215a139b0a0e26d9cba1b3a424f7ee59f30ce800a5db967279ed60958dd1f3ee69cf4dd1b204",
+ "0xa2e6cb6978e883f9719c3c0d44cfe8de0cc6f644b98f98858433bea8bbe7b612c8aca5952fccce4f195f9d54f9722dc2",
+ "0x99663087e3d5000abbec0fbda4e7342ec38846cc6a1505191fb3f1a337cb369455b7f8531a6eb8b0f7b2c4baf83cbe2b",
+ "0xab0836c6377a4dbc7ca6a4d6cf021d4cd60013877314dd05f351706b128d4af6337711ed3443cb6ca976f40d74070a9a",
+ "0x87abfd5126152fd3bac3c56230579b489436755ea89e0566aa349490b36a5d7b85028e9fb0710907042bcde6a6f5d7e3",
+ "0x974ba1033f75f60e0cf7c718a57ae1da3721cf9d0fb925714c46f027632bdd84cd9e6de4cf4d00bc55465b1c5ebb7384",
+ "0xa607b49d73689ac64f25cec71221d30d53e781e1100d19a2114a21da6507a60166166369d860bd314acb226596525670",
+ "0xa7c2b0b915d7beba94954f2aa7dd08ec075813661e2a3ecca5d28a0733e59583247fed9528eb28aba55b972cdbaf06eb",
+ "0xb8b3123e44128cc8efbe3270f2f94e50ca214a4294c71c3b851f8cbb70cb67fe9536cf07d04bf7fe380e5e3a29dd3c15",
+ "0xa59a07e343b62ad6445a0859a32b58c21a593f9ddbfe52049650f59628c93715aa1f4e1f45b109321756d0eeec8a5429",
+ "0x94f51f8a4ed18a6030d0aaa8899056744bd0e9dc9ac68f62b00355cddab11da5da16798db75f0bfbce0e5bdfe750c0b6",
+ "0x97460a97ca1e1fa5ce243b81425edc0ec19b7448e93f0b55bc9785eedeeafe194a3c8b33a61a5c72990edf375f122777",
+ "0x8fa859a089bc17d698a7ee381f37ce9beadf4e5b44fce5f6f29762bc04f96faff5d58c48c73631290325f05e9a1ecf49",
+ "0xabdf38f3b20fc95eff31de5aa9ef1031abfa48f1305ee57e4d507594570401503476d3bcc493838fc24d6967a3082c7f",
+ "0xb8914bfb82815abb86da35c64d39ab838581bc0bf08967192697d9663877825f2b9d6fbdcf9b410463482b3731361aef",
+ "0xa8187f9d22b193a5f578999954d6ec9aa9b32338ccadb8a3e1ce5bad5ea361d69016e1cdfac44e9d6c54e49dd88561b9",
+ "0xaac262cb7cba7fd62c14daa7b39677cabc1ef0947dd06dd89cac8570006a200f90d5f0353e84f5ff03179e3bebe14231",
+ "0xa630ef5ece9733b8c46c0a2df14a0f37647a85e69c63148e79ffdcc145707053f9f9d305c3f1cf3c7915cb46d33abd07",
+ "0xb102c237cb2e254588b6d53350dfda6901bd99493a3fbddb4121d45e0b475cf2663a40d7b9a75325eda83e4ba1e68cb3",
+ "0x86a930dd1ddcc16d1dfa00aa292cb6c2607d42c367e470aa920964b7c17ab6232a7108d1c2c11fc40fb7496547d0bbf8",
+ "0xa832fdc4500683e72a96cce61e62ac9ee812c37fe03527ad4cf893915ca1962cee80e72d4f82b20c8fc0b764376635a1",
+ "0x88ad985f448dabb04f8808efd90f273f11f5e6d0468b5489a1a6a3d77de342992a73eb842d419034968d733f101ff683",
+ "0x98a8538145f0d86f7fbf9a81c9140f6095c5bdd8960b1c6f3a1716428cd9cca1bf8322e6d0af24e6169abcf7df2b0ff6",
+ "0x9048c6eba5e062519011e177e955a200b2c00b3a0b8615bdecdebc217559d41058d3315f6d05617be531ef0f6aef0e51",
+ "0x833bf225ab6fc68cdcacf1ec1b50f9d05f5410e6cdcd8d56a3081dc2be8a8d07b81534d1ec93a25c2e270313dfb99e3b",
+ "0xa84bcd24c3da5e537e64a811b93c91bfc84d7729b9ead7f79078989a6eb76717d620c1fad17466a0519208651e92f5ff",
+ "0xb7cdd0a3fbd79aed93e1b5a44ca44a94e7af5ed911e4492f332e3a5ed146c7286bde01b52276a2fcc02780d2109874dd",
+ "0x8a19a09854e627cb95750d83c20c67442b66b35896a476358f993ba9ac114d32c59c1b3d0b8787ee3224cf3888b56c64",
+ "0xa9abd5afb8659ee52ada8fa5d57e7dd355f0a7350276f6160bec5fbf70d5f99234dd179eb221c913e22a49ec6d267846",
+ "0x8c13c4274c0d30d184e73eaf812200094bbbd57293780bdadbceb262e34dee5b453991e7f37c7333a654fc71c69d6445",
+ "0xa4320d73296ff8176ce0127ca1921c450e2a9c06eff936681ebaffb5a0b05b17fded24e548454de89aca2dcf6d7a9de4",
+ "0xb2b8b3e15c1f645f07783e5628aba614e60157889db41d8161d977606788842b67f83f361eae91815dc0abd84e09abd5",
+ "0xad26c3aa35ddfddc15719b8bb6c264aaec7065e88ac29ba820eb61f220fef451609a7bb037f3722d022e6c86e4f1dc88",
+ "0xb8615bf43e13ae5d7b8dd903ce37190800cd490f441c09b22aa29d7a29ed2c0417b7a08ead417868f1de2589deaadd80",
+ "0x8d3425e1482cd1e76750a76239d33c06b3554c3c3c87c15cb7ab58b1cee86a4c5c4178b44e23f36928365a1b484bde02",
+ "0x806893a62e38c941a7dd6f249c83af16596f69877cc737d8f73f6b8cd93cbc01177a7a276b2b8c6b0e5f2ad864db5994",
+ "0x86618f17fa4b0d65496b661bbb5ba3bc3a87129d30a4b7d4f515b904f4206ca5253a41f49fd52095861e5e065ec54f21",
+ "0x9551915da1304051e55717f4c31db761dcdcf3a1366c89a4af800a9e99aca93a357bf928307f098e62b44a02cb689a46",
+ "0x8f79c4ec0ec1146cb2a523b52fe33def90d7b5652a0cb9c2d1c8808a32293e00aec6969f5b1538e3a94cd1efa3937f86",
+ "0xa0c03e329a707300081780f1e310671315b4c6a4cedcb29697aedfabb07a9d5df83f27b20e9c44cf6b16e39d9ded5b98",
+ "0x86a7cfa7c8e7ce2c01dd0baec2139e97e8e090ad4e7b5f51518f83d564765003c65968f85481bbb97cb18f005ccc7d9f",
+ "0xa33811770c6dfda3f7f74e6ad0107a187fe622d61b444bbd84fd7ef6e03302e693b093df76f6ab39bb4e02afd84a575a",
+ "0x85480f5c10d4162a8e6702b5e04f801874d572a62a130be94b0c02b58c3c59bdcd48cd05f0a1c2839f88f06b6e3cd337",
+ "0x8e181011564b17f7d787fe0e7f3c87f6b62da9083c54c74fd6c357a1f464c123c1d3d8ade3cf72475000b464b14e2be3",
+ "0x8ee178937294b8c991337e0621ab37e9ffa4ca2bdb3284065c5e9c08aad6785d50cf156270ff9daf9a9127289710f55b",
+ "0x8bd1e8e2d37379d4b172f1aec96f2e41a6e1393158d7a3dbd9a95c8dd4f8e0b05336a42efc11a732e5f22b47fc5c271d",
+ "0x8f3da353cd487c13136a85677de8cedf306faae0edec733cf4f0046f82fa4639db4745b0095ff33a9766aba50de0cbcf",
+ "0x8d187c1e97638df0e4792b78e8c23967dac43d98ea268ca4aabea4e0fa06cb93183fd92d4c9df74118d7cc27bf54415e",
+ "0xa4c992f08c2f8bac0b74b3702fb0c75c9838d2ce90b28812019553d47613c14d8ce514d15443159d700b218c5a312c49",
+ "0xa6fd1874034a34c3ea962a316c018d9493d2b3719bb0ec4edbc7c56b240802b2228ab49bee6f04c8a3e9f6f24a48c1c2",
+ "0xb2efed8e799f8a15999020900dc2c58ece5a3641c90811b86a5198e593d7318b9d53b167818ccdfbe7df2414c9c34011",
+ "0x995ff7de6181ddf95e3ead746089c6148da3508e4e7a2323c81785718b754d356789b902e7e78e2edc6b0cbd4ff22c78",
+ "0x944073d24750a9068cbd020b834afc72d2dde87efac04482b3287b40678ad07588519a4176b10f2172a2c463d063a5cd",
+ "0x99db4b1bb76475a6fd75289986ef40367960279524378cc917525fb6ba02a145a218c1e9caeb99332332ab486a125ac0",
+ "0x89fce4ecd420f8e477af4353b16faabb39e063f3f3c98fde2858b1f2d1ef6eed46f0975a7c08f233b97899bf60ccd60a",
+ "0x8c09a4f07a02b80654798bc63aada39fd638d3e3c4236ccd8a5ca280350c31e4a89e5f4c9aafb34116e71da18c1226b8",
+ "0x85325cfa7ded346cc51a2894257eab56e7488dbff504f10f99f4cd2b630d913003761a50f175ed167e8073f1b6b63fb0",
+ "0xb678b4fbec09a8cc794dcbca185f133578f29e354e99c05f6d07ac323be20aecb11f781d12898168e86f2e0f09aca15e",
+ "0xa249cfcbca4d9ba0a13b5f6aac72bf9b899adf582f9746bb2ad043742b28915607467eb794fca3704278f9136f7642be",
+ "0x9438e036c836a990c5e17af3d78367a75b23c37f807228362b4d13e3ddcb9e431348a7b552d09d11a2e9680704a4514f",
+ "0x925ab70450af28c21a488bfb5d38ac994f784cf249d7fd9ad251bb7fd897a23e23d2528308c03415074d43330dc37ef4",
+ "0xa290563904d5a8c0058fc8330120365bdd2ba1fdbaef7a14bc65d4961bb4217acfaed11ab82669e359531f8bf589b8db",
+ "0xa7e07a7801b871fc9b981a71e195a3b4ba6b6313bc132b04796a125157e78fe5c11a3a46cf731a255ac2d78a4ae78cd0",
+ "0xb26cd2501ee72718b0eebab6fb24d955a71f363f36e0f6dff0ab1d2d7836dab88474c0cef43a2cc32701fca7e82f7df3",
+ "0xa1dc3b6c968f3de00f11275092290afab65b2200afbcfa8ddc70e751fa19dbbc300445d6d479a81bda3880729007e496",
+ "0xa9bc213e28b630889476a095947d323b9ac6461dea726f2dc9084473ae8e196d66fb792a21905ad4ec52a6d757863e7d",
+ "0xb25d178df8c2df8051e7c888e9fa677fde5922e602a95e966db9e4a3d6b23ce043d7dc48a5b375c6b7c78e966893e8c3",
+ "0xa1c8d88d72303692eaa7adf68ea41de4febec40cc14ae551bb4012afd786d7b6444a3196b5d9d5040655a3366d96b7cd",
+ "0xb22bd44f9235a47118a9bbe2ba5a2ba9ec62476061be2e8e57806c1a17a02f9a51403e849e2e589520b759abd0117683",
+ "0xb8add766050c0d69fe81d8d9ea73e1ed05f0135d093ff01debd7247e42dbb86ad950aceb3b50b9af6cdc14ab443b238f",
+ "0xaf2cf95f30ef478f018cf81d70d47d742120b09193d8bb77f0d41a5d2e1a80bfb467793d9e2471b4e0ad0cb2c3b42271",
+ "0x8af5ef2107ad284e246bb56e20fef2a255954f72de791cbdfd3be09f825298d8466064f3c98a50496c7277af32b5c0bc",
+ "0x85dc19558572844c2849e729395a0c125096476388bd1b14fa7f54a7c38008fc93e578da3aac6a52ff1504d6ca82db05",
+ "0xae8c9b43c49572e2e166d704caf5b4b621a3b47827bb2a3bcd71cdc599bba90396fd9a405261b13e831bb5d44c0827d7",
+ "0xa7ba7efede25f02e88f6f4cbf70643e76784a03d97e0fbd5d9437c2485283ad7ca3abb638a5f826cd9f6193e5dec0b6c",
+ "0x94a9d122f2f06ef709fd8016fd4b712d88052245a65a301f5f177ce22992f74ad05552b1f1af4e70d1eac62cef309752",
+ "0x82d999b3e7cf563833b8bc028ff63a6b26eb357dfdb3fd5f10e33a1f80a9b2cfa7814d871b32a7ebfbaa09e753e37c02",
+ "0xaec6edcde234df502a3268dd2c26f4a36a2e0db730afa83173f9c78fcb2b2f75510a02b80194327b792811caefda2725",
+ "0x94c0bfa66c9f91d462e9194144fdd12d96f9bbe745737e73bab8130607ee6ea9d740e2cfcbbd00a195746edb6369ee61",
+ "0xab7573dab8c9d46d339e3f491cb2826cabe8b49f85f1ede78d845fc3995537d1b4ab85140b7d0238d9c24daf0e5e2a7e",
+ "0x87e8b16832843251fe952dadfd01d41890ed4bb4b8fa0254550d92c8cced44368225eca83a6c3ad47a7f81ff8a80c984",
+ "0x9189d2d9a7c64791b19c0773ad4f0564ce6bea94aa275a917f78ad987f150fdb3e5e26e7fef9982ac184897ecc04683f",
+ "0xb3661bf19e2da41415396ae4dd051a9272e8a2580b06f1a1118f57b901fa237616a9f8075af1129af4eabfefedbe2f1c",
+ "0xaf43c86661fb15daf5d910a4e06837225e100fb5680bd3e4b10f79a2144c6ec48b1f8d6e6b98e067d36609a5d038889a",
+ "0x82ac0c7acaa83ddc86c5b4249aae12f28155989c7c6b91e5137a4ce05113c6cbc16f6c44948b0efd8665362d3162f16a",
+ "0x8f268d1195ab465beeeb112cd7ffd5d5548559a8bc01261106d3555533fc1971081b25558d884d552df0db1cddda89d8",
+ "0x8ef7caa5521f3e037586ce8ac872a4182ee20c7921c0065ed9986c047e3dda08294da1165f385d008b40d500f07d895f",
+ "0x8c2f98f6880550573fad46075d3eba26634b5b025ce25a0b4d6e0193352c8a1f0661064027a70fe8190b522405f9f4e3",
+ "0xb7653f353564feb164f0f89ec7949da475b8dad4a4d396d252fc2a884f6932d027b7eb2dc4d280702c74569319ed701a",
+ "0xa026904f4066333befd9b87a8fad791d014096af60cdd668ef919c24dbe295ff31f7a790e1e721ba40cf5105abca67f4",
+ "0x988f982004ada07a22dd345f2412a228d7a96b9cae2c487de42e392afe1e35c2655f829ce07a14629148ce7079a1f142",
+ "0x9616add009067ed135295fb74d5b223b006b312bf14663e547a0d306694ff3a8a7bb9cfc466986707192a26c0bce599f",
+ "0xad4c425de9855f6968a17ee9ae5b15e0a5b596411388cf976df62ecc6c847a6e2ddb2cea792a5f6e9113c2445dba3e5c",
+ "0xb698ac9d86afa3dc69ff8375061f88e3b0cff92ff6dfe747cebaf142e813c011851e7a2830c10993b715e7fd594604a9",
+ "0xa386fa189847bb3b798efca917461e38ead61a08b101948def0f82cd258b945ed4d45b53774b400af500670149e601b7",
+ "0x905c95abda2c68a6559d8a39b6db081c68cef1e1b4be63498004e1b2f408409be9350b5b5d86a30fd443e2b3e445640a",
+ "0x9116dade969e7ce8954afcdd43e5cab64dc15f6c1b8da9d2d69de3f02ba79e6c4f6c7f54d6bf586d30256ae405cd1e41",
+ "0xa3084d173eacd08c9b5084a196719b57e47a0179826fda73466758235d7ecdb87cbcf097bd6b510517d163a85a7c7edd",
+ "0x85bb00415ad3c9be99ff9ba83672cc59fdd24356b661ab93713a3c8eab34e125d8867f628a3c3891b8dc056e69cd0e83",
+ "0x8d58541f9f39ed2ee4478acce5d58d124031338ec11b0d55551f00a5a9a6351faa903a5d7c132dc5e4bb026e9cbd18e4",
+ "0xa622adf72dc250e54f672e14e128c700166168dbe0474cecb340da175346e89917c400677b1bc1c11fcc4cc26591d9db",
+ "0xb3f865014754b688ca8372e8448114fff87bf3ca99856ab9168894d0c4679782c1ced703f5b74e851b370630f5e6ee86",
+ "0xa7e490b2c40c2446fcd91861c020da9742c326a81180e38110558bb5d9f2341f1c1885e79b364e6419023d1cbdc47380",
+ "0xb3748d472b1062e54572badbb8e87ac36534407f74932e7fc5b8392d008e8e89758f1671d1e4d30ab0fa40551b13bb5e",
+ "0x89898a5c5ec4313aabc607b0049fd1ebad0e0c074920cf503c9275b564d91916c2c446d3096491c950b7af3ac5e4b0ed",
+ "0x8eb8c83fef2c9dd30ea44e286e9599ec5c20aba983f702e5438afe2e5b921884327ad8d1566c72395587efac79ca7d56",
+ "0xb92479599e806516ce21fb0bd422a1d1d925335ebe2b4a0a7e044dd275f30985a72b97292477053ac5f00e081430da80",
+ "0xa34ae450a324fe8a3c25a4d653a654f9580ed56bbea213b8096987bbad0f5701d809a17076435e18017fea4d69f414bc",
+ "0x81381afe6433d62faf62ea488f39675e0091835892ecc238e02acf1662669c6d3962a71a3db652f6fe3bc5f42a0e5dc5",
+ "0xa430d475bf8580c59111103316fe1aa79c523ea12f1d47a976bbfae76894717c20220e31cf259f08e84a693da6688d70",
+ "0xb842814c359754ece614deb7d184d679d05d16f18a14b288a401cef5dad2cf0d5ee90bad487b80923fc5573779d4e4e8",
+ "0x971d9a2627ff2a6d0dcf2af3d895dfbafca28b1c09610c466e4e2bff2746f8369de7f40d65b70aed135fe1d72564aa88",
+ "0x8f4ce1c59e22b1ce7a0664caaa7e53735b154cfba8d2c5cc4159f2385843de82ab58ed901be876c6f7fce69cb4130950",
+ "0x86cc9dc321b6264297987000d344fa297ef45bcc2a4df04e458fe2d907ad304c0ea2318e32c3179af639a9a56f3263cf",
+ "0x8229e0876dfe8f665c3fb19b250bd89d40f039bbf1b331468b403655be7be2e104c2fd07b9983580c742d5462ca39a43",
+ "0x99299d73066e8eb128f698e56a9f8506dfe4bd014931e86b6b487d6195d2198c6c5bf15cccb40ccf1f8ddb57e9da44a2",
+ "0xa3a3be37ac554c574b393b2f33d0a32a116c1a7cfeaf88c54299a4da2267149a5ecca71f94e6c0ef6e2f472b802f5189",
+ "0xa91700d1a00387502cdba98c90f75fbc4066fefe7cc221c8f0e660994c936badd7d2695893fde2260c8c11d5bdcdd951",
+ "0x8e03cae725b7f9562c5c5ab6361644b976a68bada3d7ca508abca8dfc80a469975689af1fba1abcf21bc2a190dab397d",
+ "0xb01461ad23b2a8fa8a6d241e1675855d23bc977dbf4714add8c4b4b7469ccf2375cec20e80cedfe49361d1a30414ac5b",
+ "0xa2673bf9bc621e3892c3d7dd4f1a9497f369add8cbaa3472409f4f86bd21ac67cfac357604828adfee6ada1835365029",
+ "0xa042dff4bf0dfc33c178ba1b335e798e6308915128de91b12e5dbbab7c4ac8d60a01f6aea028c3a6d87b9b01e4e74c01",
+ "0x86339e8a75293e4b3ae66b5630d375736b6e6b6b05c5cda5e73fbf7b2f2bd34c18a1d6cefede08625ce3046e77905cb8",
+ "0xaf2ebe1b7d073d03e3d98bc61af83bf26f7a8c130fd607aa92b75db22d14d016481b8aa231e2c9757695f55b7224a27f",
+ "0xa00ee882c9685e978041fd74a2c465f06e2a42ffd3db659053519925be5b454d6f401e3c12c746e49d910e4c5c9c5e8c",
+ "0x978a781c0e4e264e0dad57e438f1097d447d891a1e2aa0d5928f79a9d5c3faae6f258bc94fdc530b7b2fa6a9932bb193",
+ "0xaa4b7ce2e0c2c9e9655bf21e3e5651c8503bce27483017b0bf476be743ba06db10228b3a4c721219c0779747f11ca282",
+ "0xb003d1c459dacbcf1a715551311e45d7dbca83a185a65748ac74d1800bbeaba37765d9f5a1a221805c571910b34ebca8",
+ "0x95b6e531b38648049f0d19de09b881baa1f7ea3b2130816b006ad5703901a05da57467d1a3d9d2e7c73fb3f2e409363c",
+ "0xa6cf9c06593432d8eba23a4f131bb7f72b9bd51ab6b4b772a749fe03ed72b5ced835a349c6d9920dba2a39669cb7c684",
+ "0xaa3d59f6e2e96fbb66195bc58c8704e139fa76cd15e4d61035470bd6e305db9f98bcbf61ac1b95e95b69ba330454c1b3",
+ "0xb57f97959c208361de6d7e86dff2b873068adb0f158066e646f42ae90e650079798f165b5cd713141cd3a2a90a961d9a",
+ "0xa76ee8ed9052f6a7a8c69774bb2597be182942f08115baba03bf8faaeaee526feba86120039fe8ca7b9354c3b6e0a8e6",
+ "0x95689d78c867724823f564627d22d25010f278674c6d2d0cdb10329169a47580818995d1d727ce46c38a1e47943ebb89",
+ "0xab676d2256c6288a88e044b3d9ffd43eb9d5aaee00e8fc60ac921395fb835044c71a26ca948e557fed770f52d711e057",
+ "0x96351c72785c32e5d004b6f4a1259fb8153d631f0c93fed172f18e8ba438fbc5585c1618deeabd0d6d0b82173c2e6170",
+ "0x93dd8d3db576418e22536eba45ab7f56967c6c97c64260d6cddf38fb19c88f2ec5cd0e0156f50e70855eee8a2b879ffd",
+ "0xad6ff16f40f6de3d7a737f8e6cebd8416920c4ff89dbdcd75eabab414af9a6087f83ceb9aff7680aa86bff98bd09c8cc",
+ "0x84de53b11671abc9c38710e19540c5c403817562aeb22a88404cdaff792c1180f717dbdfe8f54940c062c4d032897429",
+ "0x872231b9efa1cdd447b312099a5c164c560440a9441d904e70f5abfc3b2a0d16be9a01aca5e0a2599a61e19407587e3d",
+ "0x88f44ac27094a2aa14e9dc40b099ee6d68f97385950f303969d889ee93d4635e34dff9239103bdf66a4b7cbba3e7eb7a",
+ "0xa59afebadf0260e832f6f44468443562f53fbaf7bcb5e46e1462d3f328ac437ce56edbca617659ac9883f9e13261fad7",
+ "0xb1990e42743a88de4deeacfd55fafeab3bc380cb95de43ed623d021a4f2353530bcab9594389c1844b1c5ea6634c4555",
+ "0x85051e841149a10e83f56764e042182208591396d0ce78c762c4a413e6836906df67f38c69793e158d64fef111407ba3",
+ "0x9778172bbd9b1f2ec6bbdd61829d7b39a7df494a818e31c654bf7f6a30139899c4822c1bf418dd4f923243067759ce63",
+ "0x9355005b4878c87804fc966e7d24f3e4b02bed35b4a77369d01f25a3dcbff7621b08306b1ac85b76fe7b4a3eb5f839b1",
+ "0x8f9dc6a54fac052e236f8f0e1f571ac4b5308a43acbe4cc8183bce26262ddaf7994e41cf3034a4cbeca2c505a151e3b1",
+ "0x8cc59c17307111723fe313046a09e0e32ea0cce62c13814ab7c6408c142d6a0311d801be4af53fc9240523f12045f9ef",
+ "0x8e6057975ed40a1932e47dd3ac778f72ee2a868d8540271301b1aa6858de1a5450f596466494a3e0488be4fbeb41c840",
+ "0x812145efbd6559ae13325d56a15940ca4253b17e72a9728986b563bb5acc13ec86453796506ac1a8f12bd6f9e4a288c3",
+ "0x911da0a6d6489eb3dab2ec4a16e36127e8a291ae68a6c2c9de33e97f3a9b1f00da57a94e270a0de79ecc5ecb45d19e83",
+ "0xb72ea85973f4b2a7e6e71962b0502024e979a73c18a9111130e158541fa47bbaaf53940c8f846913a517dc69982ba9e1",
+ "0xa7a56ad1dbdc55f177a7ad1d0af78447dc2673291e34e8ab74b26e2e2e7d8c5fe5dc89e7ef60f04a9508847b5b3a8188",
+ "0xb52503f6e5411db5d1e70f5fb72ccd6463fa0f197b3e51ca79c7b5a8ab2e894f0030476ada72534fa4eb4e06c3880f90",
+ "0xb51c7957a3d18c4e38f6358f2237b3904618d58b1de5dec53387d25a63772e675a5b714ad35a38185409931157d4b529",
+ "0xb86b4266e719d29c043d7ec091547aa6f65bbf2d8d831d1515957c5c06513b72aa82113e9645ad38a7bc3f5383504fa6",
+ "0xb95b547357e6601667b0f5f61f261800a44c2879cf94e879def6a105b1ad2bbf1795c3b98a90d588388e81789bd02681",
+ "0xa58fd4c5ae4673fa350da6777e13313d5d37ed1dafeeb8f4f171549765b84c895875d9d3ae6a9741f3d51006ef81d962",
+ "0x9398dc348d078a604aadc154e6eef2c0be1a93bb93ba7fe8976edc2840a3a318941338cc4d5f743310e539d9b46613d2",
+ "0x902c9f0095014c4a2f0dccaaab543debba6f4cc82c345a10aaf4e72511725dbed7a34cd393a5f4e48a3e5142b7be84ed",
+ "0xa7c0447849bb44d04a0393a680f6cd390093484a79a147dd238f5d878030d1c26646d88211108e59fe08b58ad20c6fbd",
+ "0x80db045535d6e67a422519f5c89699e37098449d249698a7cc173a26ccd06f60238ae6cc7242eb780a340705c906790c",
+ "0x8e52b451a299f30124505de2e74d5341e1b5597bdd13301cc39b05536c96e4380e7f1b5c7ef076f5b3005a868657f17c",
+ "0x824499e89701036037571761e977654d2760b8ce21f184f2879fda55d3cda1e7a95306b8abacf1caa79d3cc075b9d27f",
+ "0x9049b956b77f8453d2070607610b79db795588c0cec12943a0f5fe76f358dea81e4f57a4692112afda0e2c05c142b26f",
+ "0x81911647d818a4b5f4990bfd4bc13bf7be7b0059afcf1b6839333e8569cdb0172fd2945410d88879349f677abaed5eb3",
+ "0xad4048f19b8194ed45b6317d9492b71a89a66928353072659f5ce6c816d8f21e69b9d1817d793effe49ca1874daa1096",
+ "0x8d22f7b2ddb31458661abd34b65819a374a1f68c01fc6c9887edeba8b80c65bceadb8f57a3eb686374004b836261ef67",
+ "0x92637280c259bc6842884db3d6e32602a62252811ae9b019b3c1df664e8809ffe86db88cfdeb8af9f46435c9ee790267",
+ "0xa2f416379e52e3f5edc21641ea73dc76c99f7e29ea75b487e18bd233856f4c0183429f378d2bfc6cd736d29d6cadfa49",
+ "0x882cb6b76dbdc188615dcf1a8439eba05ffca637dd25197508156e03c930b17b9fed2938506fdd7b77567cb488f96222",
+ "0xb68b621bb198a763fb0634eddb93ed4b5156e59b96c88ca2246fd1aea3e6b77ed651e112ac41b30cd361fadc011d385e",
+ "0xa3cb22f6b675a29b2d1f827cacd30df14d463c93c3502ef965166f20d046af7f9ab7b2586a9c64f4eae4fad2d808a164",
+ "0x8302d9ce4403f48ca217079762ce42cee8bc30168686bb8d3a945fbd5acd53b39f028dce757b825eb63af2d5ae41169d",
+ "0xb2eef1fbd1a176f1f4cd10f2988c7329abe4eb16c7405099fb92baa724ab397bc98734ef7d4b24c0f53dd90f57520d04",
+ "0xa1bbef0bd684a3f0364a66bde9b29326bac7aa3dde4caed67f14fb84fed3de45c55e406702f1495a3e2864d4ee975030",
+ "0x976acdb0efb73e3a3b65633197692dedc2adaed674291ae3df76b827fc866d214e9cac9ca46baefc4405ff13f953d936",
+ "0xb9fbf71cc7b6690f601f0b1c74a19b7d14254183a2daaafec7dc3830cba5ae173d854bbfebeca985d1d908abe5ef0cda",
+ "0x90591d7b483598c94e38969c4dbb92710a1a894bcf147807f1bcbd8aa3ac210b9f2be65519aa829f8e1ccdc83ad9b8cf",
+ "0xa30568577c91866b9c40f0719d46b7b3b2e0b4a95e56196ac80898a2d89cc67880e1229933f2cd28ee3286f8d03414d7",
+ "0x97589a88c3850556b359ec5e891f0937f922a751ac7c95949d3bbc7058c172c387611c0f4cb06351ef02e5178b3dd9e4",
+ "0x98e7bbe27a1711f4545df742f17e3233fbcc63659d7419e1ca633f104cb02a32c84f2fac23ca2b84145c2672f68077ab",
+ "0xa7ddb91636e4506d8b7e92aa9f4720491bb71a72dadc47c7f4410e15f93e43d07d2b371951a0e6a18d1bd087aa96a5c4",
+ "0xa7c006692227a06db40bceac3d5b1daae60b5692dd9b54772bedb5fea0bcc91cbcdb530cac31900ffc70c5b3ffadc969",
+ "0x8d3ec6032778420dfa8be52066ba0e623467df33e4e1901dbadd586c5d750f4ccde499b5197e26b9ea43931214060f69",
+ "0x8d9a8410518ea64f89df319bfd1fc97a0971cdb9ad9b11d1f8fe834042ea7f8dce4db56eeaf179ff8dda93b6db93e5ce",
+ "0xa3c533e9b3aa04df20b9ff635cb1154ce303e045278fcf3f10f609064a5445552a1f93989c52ce852fd0bbd6e2b6c22e",
+ "0x81934f3a7f8c1ae60ec6e4f212986bcc316118c760a74155d06ce0a8c00a9b9669ec4e143ca214e1b995e41271774fd9",
+ "0xab8e2d01a71192093ef8fafa7485e795567cc9db95a93fb7cc4cf63a391ef89af5e2bfad4b827fffe02b89271300407f",
+ "0x83064a1eaa937a84e392226f1a60b7cfad4efaa802f66de5df7498962f7b2649924f63cd9962d47906380b97b9fe80e1",
+ "0xb4f5e64a15c6672e4b55417ee5dc292dcf93d7ea99965a888b1cc4f5474a11e5b6520eacbcf066840b343f4ceeb6bf33",
+ "0xa63d278b842456ef15c278b37a6ea0f27c7b3ffffefca77c7a66d2ea06c33c4631eb242bbb064d730e70a8262a7b848a",
+ "0x83a41a83dbcdf0d22dc049de082296204e848c453c5ab1ba75aa4067984e053acf6f8b6909a2e1f0009ed051a828a73b",
+ "0x819485b036b7958508f15f3c19436da069cbe635b0318ebe8c014cf1ef9ab2df038c81161b7027475bcfa6fff8dd9faf",
+ "0xaa40e38172806e1e045e167f3d1677ef12d5dcdc89b43639a170f68054bd196c4fae34c675c1644d198907a03f76ba57",
+ "0x969bae484883a9ed1fbed53b26b3d4ee4b0e39a6c93ece5b3a49daa01444a1c25727dabe62518546f36b047b311b177c",
+ "0x80a9e73a65da99664988b238096a090d313a0ee8e4235bc102fa79bb337b51bb08c4507814eb5baec22103ec512eaab0",
+ "0x86604379aec5bddda6cbe3ef99c0ac3a3c285b0b1a15b50451c7242cd42ae6b6c8acb717dcca7917838432df93a28502",
+ "0xa23407ee02a495bed06aa7e15f94cfb05c83e6d6fba64456a9bbabfa76b2b68c5c47de00ba169e710681f6a29bb41a22",
+ "0x98cff5ecc73b366c6a01b34ac9066cb34f7eeaf4f38a5429bad2d07e84a237047e2a065c7e8a0a6581017dadb4695deb",
+ "0x8de9f68a938f441f3b7ab84bb1f473c5f9e5c9e139e42b7ccee1d254bd57d0e99c2ccda0f3198f1fc5737f6023dd204e",
+ "0xb0ce48d815c2768fb472a315cad86aa033d0e9ca506f146656e2941829e0acb735590b4fbc713c2d18d3676db0a954ac",
+ "0x82f485cdefd5642a6af58ac6817991c49fac9c10ace60f90b27f1788cc026c2fe8afc83cf499b3444118f9f0103598a8",
+ "0x82c24550ed512a0d53fc56f64cc36b553823ae8766d75d772dacf038c460f16f108f87a39ceef7c66389790f799dbab3",
+ "0x859ffcf1fe9166388316149b9acc35694c0ea534d43f09dae9b86f4aa00a23b27144dda6a352e74b9516e8c8d6fc809c",
+ "0xb8f7f353eec45da77fb27742405e5ad08d95ec0f5b6842025be9def3d9892f85eb5dd0921b41e6eff373618dba215bca",
+ "0x8ccca4436f9017e426229290f5cd05eac3f16571a4713141a7461acfe8ae99cd5a95bf5b6df129148693c533966145da",
+ "0xa2c67ecc19c0178b2994846fea4c34c327a5d786ac4b09d1d13549d5be5996d8a89021d63d65cb814923388f47cc3a03",
+ "0xaa0ff87d676b418ec08f5cbf577ac7e744d1d0e9ebd14615b550eb86931eafd2a36d4732cc5d6fab1713fd7ab2f6f7c0",
+ "0x8aef4730bb65e44efd6bb9441c0ae897363a2f3054867590a2c2ecf4f0224e578c7a67f10b40f8453d9f492ac15a9b2d",
+ "0x86a187e13d8fba5addcfdd5b0410cedd352016c930f913addd769ee09faa6be5ca3e4b1bdb417a965c643a99bd92be42",
+ "0xa0a4e9632a7a094b14b29b78cd9c894218cdf6783e61671e0203865dc2a835350f465fbaf86168f28af7c478ca17bc89",
+ "0xa8c7b02d8deff2cd657d8447689a9c5e2cd74ef57c1314ac4d69084ac24a7471954d9ff43fe0907d875dcb65fd0d3ce5",
+ "0x97ded38760aa7be6b6960b5b50e83b618fe413cbf2bcc1da64c05140bcc32f5e0e709cd05bf8007949953fac5716bad9",
+ "0xb0d293835a24d64c2ae48ce26e550b71a8c94a0883103757fb6b07e30747f1a871707d23389ba2b2065fa6bafe220095",
+ "0x8f9e291bf849feaa575592e28e3c8d4b7283f733d41827262367ea1c40f298c7bcc16505255a906b62bf15d9f1ba85fb",
+ "0x998f4e2d12708b4fd85a61597ca2eddd750f73c9e0c9b3cf0825d8f8e01f1628fd19797dcaed3b16dc50331fc6b8b821",
+ "0xb30d1f8c115d0e63bf48f595dd10908416774c78b3bbb3194192995154d80ea042d2e94d858de5f8aa0261b093c401fd",
+ "0xb5d9c75bb41f964cbff3f00e96d9f1480c91df8913f139f0d385d27a19f57a820f838eb728e46823cbff00e21c660996",
+ "0xa6edec90b5d25350e2f5f0518777634f9e661ec9d30674cf5b156c4801746d62517751d90074830ac0f4b09911c262f1",
+ "0x82f98da1264b6b75b8fbeb6a4d96d6a05b25c24db0d57ba3a38efe3a82d0d4e331b9fc4237d6494ccfe4727206457519",
+ "0xb89511843453cf4ecd24669572d6371b1e529c8e284300c43e0d5bb6b3aaf35aeb634b3cb5c0a2868f0d5e959c1d0772",
+ "0xa82bf065676583e5c1d3b81987aaae5542f522ba39538263a944bb33ea5b514c649344a96c0205a3b197a3f930fcda6c",
+ "0xa37b47ea527b7e06c460776aa662d9a49ff4149d3993f1a974b0dd165f7171770d189b0e2ea54fd5fccb6a14b116e68a",
+ "0xa1017677f97dda818274d47556d09d0e4ccacb23a252f82a6cfe78c630ad46fb9806307445a59fb61262182de3a2b29c",
+ "0xb01e9fcac239ba270e6877b79273ddd768bf8a51d2ed8a051b1c11e18eff3de5920e2fcbfbd26f06d381eddd3b1f1e1b",
+ "0x82fcd53d803b1c8e4ed76adc339b7f3a5962d37042b9683aabac7513ac68775d4a566a9460183926a6a95dbe7d551a1f",
+ "0xa763e78995d55cd21cdb7ef75d9642d6e1c72453945e346ab6690c20a4e1eeec61bb848ef830ae4b56182535e3c71d8f",
+ "0xb769f4db602251d4b0a1186782799bdcef66de33c110999a5775c50b349666ffd83d4c89714c4e376f2efe021a5cfdb2",
+ "0xa59cbd1b785efcfa6e83fc3b1d8cf638820bc0c119726b5368f3fba9dce8e3414204fb1f1a88f6c1ff52e87961252f97",
+ "0x95c8c458fd01aa23ecf120481a9c6332ebec2e8bb70a308d0576926a858457021c277958cf79017ddd86a56cacc2d7db",
+ "0x82eb41390800287ae56e77f2e87709de5b871c8bdb67c10a80fc65f3acb9f7c29e8fa43047436e8933f27449ea61d94d",
+ "0xb3ec25e3545eb83aed2a1f3558d1a31c7edde4be145ecc13b33802654b77dc049b4f0065069dd9047b051e52ab11dcdd",
+ "0xb78a0c715738f56f0dc459ab99e252e3b579b208142836b3c416b704ca1de640ca082f29ebbcee648c8c127df06f6b1e",
+ "0xa4083149432eaaf9520188ebf4607d09cf664acd1f471d4fb654476e77a9eaae2251424ffda78d09b6cb880df35c1219",
+ "0x8c52857d68d6e9672df3db2df2dbf46b516a21a0e8a18eec09a6ae13c1ef8f369d03233320dd1c2c0bbe00abfc1ea18b",
+ "0x8c856089488803066bff3f8d8e09afb9baf20cecc33c8823c1c0836c3d45498c3de37e87c016b705207f60d2b00f8609",
+ "0x831a3df39be959047b2aead06b4dcd3012d7b29417f642b83c9e8ce8de24a3dbbd29c6fdf55e2db3f7ea04636c94e403",
+ "0xaed84d009f66544addabe404bf6d65af7779ce140dc561ff0c86a4078557b96b2053b7b8a43432ffb18cd814f143b9da",
+ "0x93282e4d72b0aa85212a77b336007d8ba071eea17492da19860f1ad16c1ea8867ccc27ef5c37c74b052465cc11ea4f52",
+ "0xa7b78b8c8d057194e8d68767f1488363f77c77bddd56c3da2bc70b6354c7aa76247c86d51f7371aa38a4aa7f7e3c0bb7",
+ "0xb1c77283d01dcd1bde649b5b044eac26befc98ff57cbee379fb5b8e420134a88f2fc7f0bf04d15e1fbd45d29e7590fe6",
+ "0xa4aa8de70330a73b2c6458f20a1067eed4b3474829b36970a8df125d53bbdda4f4a2c60063b7cccb0c80fc155527652f",
+ "0x948a6c79ba1b8ad7e0bed2fae2f0481c4e41b4d9bbdd9b58164e28e9065700e83f210c8d5351d0212e0b0b68b345b3a5",
+ "0x86a48c31dcbbf7b082c92d28e1f613a2378a910677d7db3a349dc089e4a1e24b12eee8e8206777a3a8c64748840b7387",
+ "0x976adb1af21e0fc34148917cf43d933d7bfd3fd12ed6c37039dcd5a4520e3c6cf5868539ba5bf082326430deb8a4458d",
+ "0xb93e1a4476f2c51864bb4037e7145f0635eb2827ab91732b98d49b6c07f6ac443111aa1f1da76d1888665cb897c3834e",
+ "0x8afd46fb23bf869999fa19784b18a432a1f252d09506b8dbb756af900518d3f5f244989b3d7c823d9029218c655d3dc6",
+ "0x83f1e59e3abeed18cdc632921672673f1cb6e330326e11c4e600e13e0d5bc11bdc970ae12952e15103a706fe720bf4d6",
+ "0x90ce4cc660714b0b673d48010641c09c00fc92a2c596208f65c46073d7f349dd8e6e077ba7dcef9403084971c3295b76",
+ "0x8b09b0f431a7c796561ecf1549b85048564de428dac0474522e9558b6065fede231886bc108539c104ce88ebd9b5d1b0",
+ "0x85d6e742e2fb16a7b0ba0df64bc2c0dbff9549be691f46a6669bca05e89c884af16822b85faefefb604ec48c8705a309",
+ "0xa87989ee231e468a712c66513746fcf03c14f103aadca0eac28e9732487deb56d7532e407953ab87a4bf8961588ef7b0",
+ "0xb00da10efe1c29ee03c9d37d5918e391ae30e48304e294696b81b434f65cf8c8b95b9d1758c64c25e534d045ba28696f",
+ "0x91c0e1fb49afe46c7056400baa06dbb5f6e479db78ee37e2d76c1f4e88994357e257b83b78624c4ef6091a6c0eb8254d",
+ "0x883fb797c498297ccbf9411a3e727c3614af4eccde41619b773dc7f3259950835ee79453debf178e11dec4d3ada687a0",
+ "0xa14703347e44eb5059070b2759297fcfcfc60e6893c0373eea069388eba3950aa06f1c57cd2c30984a2d6f9e9c92c79e",
+ "0xafebc7585b304ceba9a769634adff35940e89cd32682c78002822aab25eec3edc29342b7f5a42a56a1fec67821172ad5",
+ "0xaea3ff3822d09dba1425084ca95fd359718d856f6c133c5fabe2b2eed8303b6e0ba0d8698b48b93136a673baac174fd9",
+ "0xaf2456a09aa777d9e67aa6c7c49a1845ea5cdda2e39f4c935c34a5f8280d69d4eec570446998cbbe31ede69a91e90b06",
+ "0x82cada19fed16b891ef3442bafd49e1f07c00c2f57b2492dd4ee36af2bd6fd877d6cb41188a4d6ce9ec8d48e8133d697",
+ "0x82a21034c832287f616619a37c122cee265cc34ae75e881fcaea4ea7f689f3c2bc8150bbf7dbcfd123522bfb7f7b1d68",
+ "0x86877217105f5d0ec3eeff0289fc2a70d505c9fdf7862e8159553ef60908fb1a27bdaf899381356a4ef4649072a9796c",
+ "0x82b196e49c6e861089a427c0b4671d464e9d15555ffb90954cd0d630d7ae02eb3d98ceb529d00719c2526cd96481355a",
+ "0xa29b41d0d43d26ce76d4358e0db2b77df11f56e389f3b084d8af70a636218bd3ac86b36a9fe46ec9058c26a490f887f7",
+ "0xa4311c4c20c4d7dd943765099c50f2fd423e203ccfe98ff00087d205467a7873762510cac5fdce7a308913ed07991ed7",
+ "0xb1f040fc5cc51550cb2c25cf1fd418ecdd961635a11f365515f0cb4ffb31da71f48128c233e9cc7c0cf3978d757ec84e",
+ "0xa9ebae46f86d3bd543c5f207ed0d1aed94b8375dc991161d7a271f01592912072e083e2daf30c146430894e37325a1b9",
+ "0x826418c8e17ad902b5fe88736323a47e0ca7a44bce4cbe27846ec8fe81de1e8942455dda6d30e192cdcc73e11df31256",
+ "0x85199db563427c5edcbac21f3d39fec2357be91fb571982ddcdc4646b446ad5ced84410de008cb47b3477ee0d532daf8",
+ "0xb7eed9cd400b2ca12bf1d9ae008214b8561fb09c8ad9ff959e626ffde00fee5ff2f5b6612e231f2a1a9b1646fcc575e3",
+ "0x8b40bf12501dcbac78f5a314941326bfcddf7907c83d8d887d0bb149207f85d80cd4dfbd7935439ea7b14ea39a3fded7",
+ "0x83e3041af302485399ba6cd5120e17af61043977083887e8d26b15feec4a6b11171ac5c06e6ad0971d4b58a81ff12af3",
+ "0x8f5b9a0eecc589dbf8c35a65d5e996a659277ef6ea509739c0cb7b3e2da9895e8c8012de662e5b23c5fa85d4a8f48904",
+ "0x835d71ed5e919d89d8e6455f234f3ff215462c4e3720c371ac8c75e83b19dfe3ae15a81547e4dc1138e5f5997f413cc9",
+ "0x8b7d2e4614716b1db18e9370176ea483e6abe8acdcc3dcdf5fb1f4d22ca55d652feebdccc171c6de38398d9f7bfdec7a",
+ "0x93eace72036fe57d019676a02acf3d224cf376f166658c1bf705db4f24295881d477d6fdd7916efcfceff8c7a063deda",
+ "0xb1ac460b3d516879a84bc886c54f020a9d799e7c49af3e4d7de5bf0d2793c852254c5d8fe5616147e6659512e5ccb012",
+ "0xacd0947a35cb167a48bcd9667620464b54ac0e78f9316b4aa92dcaab5422d7a732087e52e1c827faa847c6b2fe6e7766",
+ "0x94ac33d21c3d12ff762d32557860e911cd94d666609ddcc42161b9c16f28d24a526e8b10bb03137257a92cec25ae637d",
+ "0x832e02058b6b994eadd8702921486241f9a19e68ed1406dad545e000a491ae510f525ccf9d10a4bba91c68f2c53a0f58",
+ "0x9471035d14f78ff8f463b9901dd476b587bb07225c351161915c2e9c6114c3c78a501379ab6fb4eb03194c457cbd22bf",
+ "0xab64593e034c6241d357fcbc32d8ea5593445a5e7c24cac81ad12bd2ef01843d477a36dc1ba21dbe63b440750d72096a",
+ "0x9850f3b30045e927ad3ec4123a32ed2eb4c911f572b6abb79121873f91016f0d80268de8b12e2093a4904f6e6cab7642",
+ "0x987212c36b4722fe2e54fa30c52b1e54474439f9f35ca6ad33c5130cd305b8b54b532dd80ffd2c274105f20ce6d79f6e",
+ "0x8b4d0c6abcb239b5ed47bef63bc17efe558a27462c8208fa652b056e9eae9665787cd1aee34fbb55beb045c8bfdb882b",
+ "0xa9f3483c6fee2fe41312d89dd4355d5b2193ac413258993805c5cbbf0a59221f879386d3e7a28e73014f10e65dd503d9",
+ "0xa2225da3119b9b7c83d514b9f3aeb9a6d9e32d9cbf9309cbb971fd53c4b2c001d10d880a8ad8a7c281b21d85ceca0b7c",
+ "0xa050be52e54e676c151f7a54453bbb707232f849beab4f3bf504b4d620f59ed214409d7c2bd3000f3ff13184ccda1c35",
+ "0xadbccf681e15b3edb6455a68d292b0a1d0f5a4cb135613f5e6db9943f02181341d5755875db6ee474e19ace1c0634a28",
+ "0x8b6eff675632a6fad0111ec72aacc61c7387380eb87933fd1d098856387d418bd38e77d897e65d6fe35951d0627c550b",
+ "0xaabe2328ddf90989b15e409b91ef055cb02757d34987849ae6d60bef2c902bf8251ed21ab30acf39e500d1d511e90845",
+ "0x92ba4eb1f796bc3d8b03515f65c045b66e2734c2da3fc507fdd9d6b5d1e19ab3893726816a32141db7a31099ca817d96",
+ "0x8a98b3cf353138a1810beb60e946183803ef1d39ac4ea92f5a1e03060d35a4774a6e52b14ead54f6794d5f4022b8685c",
+ "0x909f8a5c13ec4a59b649ed3bee9f5d13b21d7f3e2636fd2bb3413c0646573fdf9243d63083356f12f5147545339fcd55",
+ "0x9359d914d1267633141328ed0790d81c695fea3ddd2d406c0df3d81d0c64931cf316fe4d92f4353c99ff63e2aefc4e34",
+ "0xb88302031681b54415fe8fbfa161c032ea345c6af63d2fb8ad97615103fd4d4281c5a9cae5b0794c4657b97571a81d3b",
+ "0x992c80192a519038082446b1fb947323005b275e25f2c14c33cc7269e0ec038581cc43705894f94bad62ae33a8b7f965",
+ "0xa78253e3e3eece124bef84a0a8807ce76573509f6861d0b6f70d0aa35a30a123a9da5e01e84969708c40b0669eb70aa6",
+ "0x8d5724de45270ca91c94792e8584e676547d7ac1ac816a6bb9982ee854eb5df071d20545cdfd3771cd40f90e5ba04c8e",
+ "0x825a6f586726c68d45f00ad0f5a4436523317939a47713f78fd4fe81cd74236fdac1b04ecd97c2d0267d6f4981d7beb1"
+ ],
+ "g2_monomial": [
+ "0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8",
+ "0xb5bfd7dd8cdeb128843bc287230af38926187075cbfbefa81009a2ce615ac53d2914e5870cb452d2afaaab24f3499f72185cbfee53492714734429b7b38608e23926c911cceceac9a36851477ba4c60b087041de621000edc98edada20c1def2",
+ "0xb5337ba0ce5d37224290916e268e2060e5c14f3f9fc9e1ec3af5a958e7a0303122500ce18f1a4640bf66525bd10e763501fe986d86649d8d45143c08c3209db3411802c226e9fe9a55716ac4a0c14f9dcef9e70b2bb309553880dc5025eab3cc",
+ "0xb3c1dcdc1f62046c786f0b82242ef283e7ed8f5626f72542aa2c7a40f14d9094dd1ebdbd7457ffdcdac45fd7da7e16c51200b06d791e5e43e257e45efdf0bd5b06cd2333beca2a3a84354eb48662d83aef5ecf4e67658c851c10b13d8d87c874",
+ "0x954d91c7688983382609fca9e211e461f488a5971fd4e40d7e2892037268eacdfd495cfa0a7ed6eb0eb11ac3ae6f651716757e7526abe1e06c64649d80996fd3105c20c4c94bc2b22d97045356fe9d791f21ea6428ac48db6f9e68e30d875280",
+ "0x88a6b6bb26c51cf9812260795523973bb90ce80f6820b6c9048ab366f0fb96e48437a7f7cb62aedf64b11eb4dfefebb0147608793133d32003cb1f2dc47b13b5ff45f1bb1b2408ea45770a08dbfaec60961acb8119c47b139a13b8641e2c9487",
+ "0x85cd7be9728bd925d12f47fb04b32d9fad7cab88788b559f053e69ca18e463113ecc8bbb6dbfb024835f901b3a957d3108d6770fb26d4c8be0a9a619f6e3a4bf15cbfd48e61593490885f6cee30e4300c5f9cf5e1c08e60a2d5b023ee94fcad0",
+ "0x80477dba360f04399821a48ca388c0fa81102dd15687fea792ee8c1114e00d1bc4839ad37ac58900a118d863723acfbe08126ea883be87f50e4eabe3b5e72f5d9e041db8d9b186409fd4df4a7dde38c0e0a3b1ae29b098e5697e7f110b6b27e4",
+ "0xb7a6aec08715a9f8672a2b8c367e407be37e59514ac19dd4f0942a68007bba3923df22da48702c63c0d6b3efd3c2d04e0fe042d8b5a54d562f9f33afc4865dcbcc16e99029e25925580e87920c399e710d438ac1ce3a6dc9b0d76c064a01f6f7",
+ "0xac1b001edcea02c8258aeffbf9203114c1c874ad88dae1184fadd7d94cd09053649efd0ca413400e6e9b5fa4eac33261000af88b6bd0d2abf877a4f0355d2fb4d6007adb181695201c5432e50b850b51b3969f893bddf82126c5a71b042b7686",
+ "0x90043fda4de53fb364fab2c04be5296c215599105ecff0c12e4917c549257125775c29f2507124d15f56e30447f367db0596c33237242c02d83dfd058735f1e3c1ff99069af55773b6d51d32a68bf75763f59ec4ee7267932ae426522b8aaab6",
+ "0xa8660ce853e9dc08271bf882e29cd53397d63b739584dda5263da4c7cc1878d0cf6f3e403557885f557e184700575fee016ee8542dec22c97befe1d10f414d22e84560741cdb3e74c30dda9b42eeaaf53e27822de2ee06e24e912bf764a9a533",
+ "0x8fe3921a96d0d065e8aa8fce9aa42c8e1461ca0470688c137be89396dd05103606dab6cdd2a4591efd6addf72026c12e065da7be276dee27a7e30afa2bd81c18f1516e7f068f324d0bad9570b95f6bd02c727cd2343e26db0887c3e4e26dceda",
+ "0x8ae1ad97dcb9c192c9a3933541b40447d1dc4eebf380151440bbaae1e120cc5cdf1bcea55180b128d8e180e3af623815191d063cc0d7a47d55fb7687b9d87040bf7bc1a7546b07c61db5ccf1841372d7c2fe4a5431ffff829f3c2eb590b0b710",
+ "0x8c2fa96870a88150f7876c931e2d3cc2adeaaaf5c73ef5fa1cf9dfa0991ae4819f9321af7e916e5057d87338e630a2f21242c29d76963cf26035b548d2a63d8ad7bd6efefa01c1df502cbdfdfe0334fb21ceb9f686887440f713bf17a89b8081",
+ "0xb9aa98e2f02bb616e22ee5dd74c7d1049321ac9214d093a738159850a1dbcc7138cb8d26ce09d8296368fd5b291d74fa17ac7cc1b80840fdd4ee35e111501e3fa8485b508baecda7c1ab7bd703872b7d64a2a40b3210b6a70e8a6ffe0e5127e3",
+ "0x9292db67f8771cdc86854a3f614a73805bf3012b48f1541e704ea4015d2b6b9c9aaed36419769c87c49f9e3165f03edb159c23b3a49c4390951f78e1d9b0ad997129b17cdb57ea1a6638794c0cca7d239f229e589c5ae4f9fe6979f7f8cba1d7",
+ "0x91cd9e86550f230d128664f7312591fee6a84c34f5fc7aed557bcf986a409a6de722c4330453a305f06911d2728626e611acfdf81284f77f60a3a1595053a9479964fd713117e27c0222cc679674b03bc8001501aaf9b506196c56de29429b46",
+ "0xa9516b73f605cc31b89c68b7675dc451e6364595243d235339437f556cf22d745d4250c1376182273be2d99e02c10eee047410a43eff634d051aeb784e76cb3605d8e079b9eb6ad1957dfdf77e1cd32ce4a573c9dfcc207ca65af6eb187f6c3d",
+ "0xa9667271f7d191935cc8ad59ef3ec50229945faea85bfdfb0d582090f524436b348aaa0183b16a6231c00332fdac2826125b8c857a2ed9ec66821cfe02b3a2279be2412441bc2e369b255eb98614e4be8490799c4df22f18d47d24ec70bba5f7",
+ "0xa4371144d2aa44d70d3cb9789096d3aa411149a6f800cb46f506461ee8363c8724667974252f28aea61b6030c05930ac039c1ee64bb4bd56532a685cae182bf2ab935eee34718cffcb46cae214c77aaca11dbb1320faf23c47247db1da04d8dc",
+ "0x89a7eb441892260b7e81168c386899cd84ffc4a2c5cad2eae0d1ab9e8b5524662e6f660fe3f8bfe4c92f60b060811bc605b14c5631d16709266886d7885a5eb5930097127ec6fb2ebbaf2df65909cf48f253b3d5e22ae48d3e9a2fd2b01f447e",
+ "0x9648c42ca97665b5eccb49580d8532df05eb5a68db07f391a2340769b55119eaf4c52fe4f650c09250fa78a76c3a1e271799b8333cc2628e3d4b4a6a3e03da1f771ecf6516dd63236574a7864ff07e319a6f11f153406280d63af9e2b5713283",
+ "0x9663bf6dd446ea7a90658ee458578d4196dc0b175ef7fcfa75f44d41670850774c2e46c5a6be132a2c072a3c0180a24f0305d1acac49d2d79878e5cda80c57feda3d01a6af12e78b5874e2a4b3717f11c97503b41a4474e2e95b179113726199",
+ "0xb212aeb4814e0915b432711b317923ed2b09e076aaf558c3ae8ef83f9e15a83f9ea3f47805b2750ab9e8106cb4dc6ad003522c84b03dc02829978a097899c773f6fb31f7fe6b8f2d836d96580f216fec20158f1590c3e0d7850622e15194db05",
+ "0x925f005059bf07e9ceccbe66c711b048e236ade775720d0fe479aebe6e23e8af281225ad18e62458dc1b03b42ad4ca290d4aa176260604a7aad0d9791337006fbdebe23746f8060d42876f45e4c83c3643931392fde1cd13ff8bddf8111ef974",
+ "0x9553edb22b4330c568e156a59ef03b26f5c326424f830fe3e8c0b602f08c124730ffc40bc745bec1a22417adb22a1a960243a10565c2be3066bfdb841d1cd14c624cd06e0008f4beb83f972ce6182a303bee3fcbcabc6cfe48ec5ae4b7941bfc",
+ "0x935f5a404f0a78bdcce709899eda0631169b366a669e9b58eacbbd86d7b5016d044b8dfc59ce7ed8de743ae16c2343b50e2f925e88ba6319e33c3fc76b314043abad7813677b4615c8a97eb83cc79de4fedf6ccbcfa4d4cbf759a5a84e4d9742",
+ "0xa5b014ab936eb4be113204490e8b61cd38d71da0dec7215125bcd131bf3ab22d0a32ce645bca93e7b3637cf0c2db3d6601a0ddd330dc46f9fae82abe864ffc12d656c88eb50c20782e5bb6f75d18760666f43943abb644b881639083e122f557",
+ "0x935b7298ae52862fa22bf03bfc1795b34c70b181679ae27de08a9f5b4b884f824ef1b276b7600efa0d2f1d79e4a470d51692fd565c5cf8343dd80e5d3336968fc21c09ba9348590f6206d4424eb229e767547daefa98bc3aa9f421158dee3f2a",
+ "0x9830f92446e708a8f6b091cc3c38b653505414f8b6507504010a96ffda3bcf763d5331eb749301e2a1437f00e2415efb01b799ad4c03f4b02de077569626255ac1165f96ea408915d4cf7955047620da573e5c439671d1fa5c833fb11de7afe6",
+ "0x840dcc44f673fff3e387af2bb41e89640f2a70bcd2b92544876daa92143f67c7512faf5f90a04b7191de01f3e2b1bde00622a20dc62ca23bbbfaa6ad220613deff43908382642d4d6a86999f662efd64b1df448b68c847cfa87630a3ffd2ec76",
+ "0x92950c895ed54f7f876b2fda17ecc9c41b7accfbdd42c210cc5b475e0737a7279f558148531b5c916e310604a1de25a80940c94fe5389ae5d6a5e9c371be67bceea1877f5401725a6595bcf77ece60905151b6dfcb68b75ed2e708c73632f4fd",
+ "0x8010246bf8e94c25fd029b346b5fbadb404ef6f44a58fd9dd75acf62433d8cc6db66974f139a76e0c26dddc1f329a88214dbb63276516cf325c7869e855d07e0852d622c332ac55609ba1ec9258c45746a2aeb1af0800141ee011da80af175d4",
+ "0xb0f1bad257ebd187bdc3f37b23f33c6a5d6a8e1f2de586080d6ada19087b0e2bf23b79c1b6da1ee82271323f5bdf3e1b018586b54a5b92ab6a1a16bb3315190a3584a05e6c37d5ca1e05d702b9869e27f513472bcdd00f4d0502a107773097da",
+ "0x9636d24f1ede773ce919f309448dd7ce023f424afd6b4b69cb98c2a988d849a283646dc3e469879daa1b1edae91ae41f009887518e7eb5578f88469321117303cd3ac2d7aee4d9cb5f82ab9ae3458e796dfe7c24284b05815acfcaa270ff22e2",
+ "0xb373feb5d7012fd60578d7d00834c5c81df2a23d42794fed91aa9535a4771fde0341c4da882261785e0caca40bf83405143085e7f17e55b64f6c5c809680c20b050409bf3702c574769127c854d27388b144b05624a0e24a1cbcc4d08467005b",
+ "0xb15680648949ce69f82526e9b67d9b55ce5c537dc6ab7f3089091a9a19a6b90df7656794f6edc87fb387d21573ffc847062623685931c2790a508cbc8c6b231dd2c34f4d37d4706237b1407673605a604bcf6a50cc0b1a2db20485e22b02c17e",
+ "0x8817e46672d40c8f748081567b038a3165f87994788ec77ee8daea8587f5540df3422f9e120e94339be67f186f50952504cb44f61e30a5241f1827e501b2de53c4c64473bcc79ab887dd277f282fbfe47997a930dd140ac08b03efac88d81075",
+ "0xa6e4ef6c1d1098f95aae119905f87eb49b909d17f9c41bcfe51127aa25fee20782ea884a7fdf7d5e9c245b5a5b32230b07e0dbf7c6743bf52ee20e2acc0b269422bd6cf3c07115df4aa85b11b2c16630a07c974492d9cdd0ec325a3fabd95044",
+ "0x8634aa7c3d00e7f17150009698ce440d8e1b0f13042b624a722ace68ead870c3d2212fbee549a2c190e384d7d6ac37ce14ab962c299ea1218ef1b1489c98906c91323b94c587f1d205a6edd5e9d05b42d591c26494a6f6a029a2aadb5f8b6f67",
+ "0x821a58092900bdb73decf48e13e7a5012a3f88b06288a97b855ef51306406e7d867d613d9ec738ebacfa6db344b677d21509d93f3b55c2ebf3a2f2a6356f875150554c6fff52e62e3e46f7859be971bf7dd9d5b3e1d799749c8a97c2e04325df",
+ "0x8dba356577a3a388f782e90edb1a7f3619759f4de314ad5d95c7cc6e197211446819c4955f99c5fc67f79450d2934e3c09adefc91b724887e005c5190362245eec48ce117d0a94d6fa6db12eda4ba8dde608fbbd0051f54dcf3bb057adfb2493",
+ "0xa32a690dc95c23ed9fb46443d9b7d4c2e27053a7fcc216d2b0020a8cf279729c46114d2cda5772fd60a97016a07d6c5a0a7eb085a18307d34194596f5b541cdf01b2ceb31d62d6b55515acfd2b9eec92b27d082fbc4dc59fc63b551eccdb8468",
+ "0xa040f7f4be67eaf0a1d658a3175d65df21a7dbde99bfa893469b9b43b9d150fc2e333148b1cb88cfd0447d88fa1a501d126987e9fdccb2852ecf1ba907c2ca3d6f97b055e354a9789854a64ecc8c2e928382cf09dda9abde42bbdf92280cdd96",
+ "0x864baff97fa60164f91f334e0c9be00a152a416556b462f96d7c43b59fe1ebaff42f0471d0bf264976f8aa6431176eb905bd875024cf4f76c13a70bede51dc3e47e10b9d5652d30d2663b3af3f08d5d11b9709a0321aba371d2ef13174dcfcaf",
+ "0x95a46f32c994133ecc22db49bad2c36a281d6b574c83cfee6680b8c8100466ca034b815cfaedfbf54f4e75188e661df901abd089524e1e0eb0bf48d48caa9dd97482d2e8c1253e7e8ac250a32fd066d5b5cb08a8641bdd64ecfa48289dca83a3",
+ "0xa2cce2be4d12144138cb91066e0cd0542c80b478bf467867ebef9ddaf3bd64e918294043500bf5a9f45ee089a8d6ace917108d9ce9e4f41e7e860cbce19ac52e791db3b6dde1c4b0367377b581f999f340e1d6814d724edc94cb07f9c4730774",
+ "0xb145f203eee1ac0a1a1731113ffa7a8b0b694ef2312dabc4d431660f5e0645ef5838e3e624cfe1228cfa248d48b5760501f93e6ab13d3159fc241427116c4b90359599a4cb0a86d0bb9190aa7fabff482c812db966fd2ce0a1b48cb8ac8b3bca",
+ "0xadabe5d215c608696e03861cbd5f7401869c756b3a5aadc55f41745ad9478145d44393fec8bb6dfc4ad9236dc62b9ada0f7ca57fe2bae1b71565dbf9536d33a68b8e2090b233422313cc96afc7f1f7e0907dc7787806671541d6de8ce47c4cd0",
+ "0xae7845fa6b06db53201c1080e01e629781817f421f28956589c6df3091ec33754f8a4bd4647a6bb1c141ac22731e3c1014865d13f3ed538dcb0f7b7576435133d9d03be655f8fbb4c9f7d83e06d1210aedd45128c2b0c9bab45a9ddde1c862a5",
+ "0x9159eaa826a24adfa7adf6e8d2832120ebb6eccbeb3d0459ffdc338548813a2d239d22b26451fda98cc0c204d8e1ac69150b5498e0be3045300e789bcb4e210d5cd431da4bdd915a21f407ea296c20c96608ded0b70d07188e96e6c1a7b9b86b",
+ "0xa9fc6281e2d54b46458ef564ffaed6944bff71e389d0acc11fa35d3fcd8e10c1066e0dde5b9b6516f691bb478e81c6b20865281104dcb640e29dc116daae2e884f1fe6730d639dbe0e19a532be4fb337bf52ae8408446deb393d224eee7cfa50",
+ "0x84291a42f991bfb36358eedead3699d9176a38f6f63757742fdbb7f631f2c70178b1aedef4912fed7b6cf27e88ddc7eb0e2a6aa4b999f3eb4b662b93f386c8d78e9ac9929e21f4c5e63b12991fcde93aa64a735b75b535e730ff8dd2abb16e04",
+ "0xa1b7fcacae181495d91765dfddf26581e8e39421579c9cbd0dd27a40ea4c54af3444a36bf85a11dda2114246eaddbdd619397424bb1eb41b5a15004b902a590ede5742cd850cf312555be24d2df8becf48f5afba5a8cd087cb7be0a521728386",
+ "0x92feaaf540dbd84719a4889a87cdd125b7e995a6782911931fef26da9afcfbe6f86aaf5328fe1f77631491ce6239c5470f44c7791506c6ef1626803a5794e76d2be0af92f7052c29ac6264b7b9b51f267ad820afc6f881460521428496c6a5f1",
+ "0xa525c925bfae1b89320a5054acc1fa11820f73d0cf28d273092b305467b2831fab53b6daf75fb926f332782d50e2522a19edcd85be5eb72f1497193c952d8cd0bcc5d43b39363b206eae4cb1e61668bde28a3fb2fc1e0d3d113f6dfadb799717",
+ "0x98752bb6f5a44213f40eda6aa4ff124057c1b13b6529ab42fe575b9afa66e59b9c0ed563fb20dff62130c436c3e905ee17dd8433ba02c445b1d67182ab6504a90bbe12c26a754bbf734665c622f76c62fe2e11dd43ce04fd2b91a8463679058b",
+ "0xa9aa9a84729f7c44219ff9e00e651e50ddea3735ef2a73fdf8ed8cd271961d8ed7af5cd724b713a89a097a3fe65a3c0202f69458a8b4c157c62a85668b12fc0d3957774bc9b35f86c184dd03bfefd5c325da717d74192cc9751c2073fe9d170e",
+ "0xb221c1fd335a4362eff504cd95145f122bf93ea02ae162a3fb39c75583fc13a932d26050e164da97cff3e91f9a7f6ff80302c19dd1916f24acf6b93b62f36e9665a8785413b0c7d930c7f1668549910f849bca319b00e59dd01e5dec8d2edacc",
+ "0xa71e2b1e0b16d754b848f05eda90f67bedab37709550171551050c94efba0bfc282f72aeaaa1f0330041461f5e6aa4d11537237e955e1609a469d38ed17f5c2a35a1752f546db89bfeff9eab78ec944266f1cb94c1db3334ab48df716ce408ef",
+ "0xb990ae72768779ba0b2e66df4dd29b3dbd00f901c23b2b4a53419226ef9232acedeb498b0d0687c463e3f1eead58b20b09efcefa566fbfdfe1c6e48d32367936142d0a734143e5e63cdf86be7457723535b787a9cfcfa32fe1d61ad5a2617220",
+ "0x8d27e7fbff77d5b9b9bbc864d5231fecf817238a6433db668d5a62a2c1ee1e5694fdd90c3293c06cc0cb15f7cbeab44d0d42be632cb9ff41fc3f6628b4b62897797d7b56126d65b694dcf3e298e3561ac8813fbd7296593ced33850426df42db",
+ "0xa92039a08b5502d5b211a7744099c9f93fa8c90cedcb1d05e92f01886219dd464eb5fb0337496ad96ed09c987da4e5f019035c5b01cc09b2a18b8a8dd419bc5895388a07e26958f6bd26751929c25f89b8eb4a299d822e2d26fec9ef350e0d3c",
+ "0x92dcc5a1c8c3e1b28b1524e3dd6dbecd63017c9201da9dbe077f1b82adc08c50169f56fc7b5a3b28ec6b89254de3e2fd12838a761053437883c3e01ba616670cea843754548ef84bcc397de2369adcca2ab54cd73c55dc68d87aec3fc2fe4f10"
+ ]
+}
\ No newline at end of file
diff --git a/crypto/secp256k1/libsecp256k1/include/secp256k1.h b/crypto/secp256k1/libsecp256k1/include/secp256k1.h
index f268e309d0..76af839691 100644
--- a/crypto/secp256k1/libsecp256k1/include/secp256k1.h
+++ b/crypto/secp256k1/libsecp256k1/include/secp256k1.h
@@ -357,7 +357,7 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
/** Verify an ECDSA signature.
*
* Returns: 1: correct signature
- * 0: incorrect or unparseable signature
+ * 0: incorrect or unparsable signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
diff --git a/crypto/secp256k1/libsecp256k1/sage/group_prover.sage b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage
index ab580c5b23..68882e9365 100644
--- a/crypto/secp256k1/libsecp256k1/sage/group_prover.sage
+++ b/crypto/secp256k1/libsecp256k1/sage/group_prover.sage
@@ -17,7 +17,7 @@
# - A constraint describing the requirements of the law, called "require"
# * Implementations are transliterated into functions that operate as well on
# algebraic input points, and are called once per combination of branches
-# exectured. Each execution returns:
+# executed. Each execution returns:
# - A constraint describing the assumptions this implementation requires
# (such as Z1=1), called "assumeFormula"
# - A constraint describing the assumptions this specific branch requires,
diff --git a/crypto/sha3/LICENSE b/crypto/sha3/LICENSE
deleted file mode 100644
index 6a66aea5ea..0000000000
--- a/crypto/sha3/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/crypto/sha3/PATENTS b/crypto/sha3/PATENTS
deleted file mode 100644
index 733099041f..0000000000
--- a/crypto/sha3/PATENTS
+++ /dev/null
@@ -1,22 +0,0 @@
-Additional IP Rights Grant (Patents)
-
-"This implementation" means the copyrightable works distributed by
-Google as part of the Go project.
-
-Google hereby grants to You a perpetual, worldwide, non-exclusive,
-no-charge, royalty-free, irrevocable (except as stated in this section)
-patent license to make, have made, use, offer to sell, sell, import,
-transfer and otherwise run, modify and propagate the contents of this
-implementation of Go, where such license applies only to those patent
-claims, both currently owned or controlled by Google and acquired in
-the future, licensable by Google that are necessarily infringed by this
-implementation of Go. This grant does not include claims that would be
-infringed only as a consequence of further modification of this
-implementation. If you or your agent or exclusive licensee institute or
-order or agree to the institution of patent litigation against any
-entity (including a cross-claim or counterclaim in a lawsuit) alleging
-that this implementation of Go or any code incorporated within this
-implementation of Go constitutes direct or contributory patent
-infringement, or inducement of patent infringement, then any patent
-rights granted to you under this License for this implementation of Go
-shall terminate as of the date such litigation is filed.
diff --git a/crypto/sha3/doc.go b/crypto/sha3/doc.go
deleted file mode 100644
index 3dab530f86..0000000000
--- a/crypto/sha3/doc.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package sha3 implements the SHA-3 fixed-output-length hash functions and
-// the SHAKE variable-output-length hash functions defined by FIPS-202.
-//
-// Both types of hash function use the "sponge" construction and the Keccak
-// permutation. For a detailed specification see http://keccak.noekeon.org/
-//
-//
-// Guidance
-//
-// If you aren't sure what function you need, use SHAKE256 with at least 64
-// bytes of output. The SHAKE instances are faster than the SHA3 instances;
-// the latter have to allocate memory to conform to the hash.Hash interface.
-//
-// If you need a secret-key MAC (message authentication code), prepend the
-// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
-// output.
-//
-//
-// Security strengths
-//
-// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
-// strength against preimage attacks of x bits. Since they only produce "x"
-// bits of output, their collision-resistance is only "x/2" bits.
-//
-// The SHAKE-256 and -128 functions have a generic security strength of 256 and
-// 128 bits against all attacks, provided that at least 2x bits of their output
-// is used. Requesting more than 64 or 32 bytes of output, respectively, does
-// not increase the collision-resistance of the SHAKE functions.
-//
-//
-// The sponge construction
-//
-// A sponge builds a pseudo-random function from a public pseudo-random
-// permutation, by applying the permutation to a state of "rate + capacity"
-// bytes, but hiding "capacity" of the bytes.
-//
-// A sponge starts out with a zero state. To hash an input using a sponge, up
-// to "rate" bytes of the input are XORed into the sponge's state. The sponge
-// is then "full" and the permutation is applied to "empty" it. This process is
-// repeated until all the input has been "absorbed". The input is then padded.
-// The digest is "squeezed" from the sponge in the same way, except that output
-// output is copied out instead of input being XORed in.
-//
-// A sponge is parameterized by its generic security strength, which is equal
-// to half its capacity; capacity + rate is equal to the permutation's width.
-// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
-// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
-//
-//
-// Recommendations
-//
-// The SHAKE functions are recommended for most new uses. They can produce
-// output of arbitrary length. SHAKE256, with an output length of at least
-// 64 bytes, provides 256-bit security against all attacks. The Keccak team
-// recommends it for most applications upgrading from SHA2-512. (NIST chose a
-// much stronger, but much slower, sponge instance for SHA3-512.)
-//
-// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions.
-// They produce output of the same length, with the same security strengths
-// against all attacks. This means, in particular, that SHA3-256 only has
-// 128-bit collision resistance, because its output length is 32 bytes.
-package sha3
diff --git a/crypto/sha3/hashes.go b/crypto/sha3/hashes.go
deleted file mode 100644
index fa0d7b4362..0000000000
--- a/crypto/sha3/hashes.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-// This file provides functions for creating instances of the SHA-3
-// and SHAKE hash functions, as well as utility functions for hashing
-// bytes.
-
-import (
- "hash"
-)
-
-// NewKeccak256 creates a new Keccak-256 hash.
-func NewKeccak256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x01} }
-
-// NewKeccak512 creates a new Keccak-512 hash.
-func NewKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x01} }
-
-// New224 creates a new SHA3-224 hash.
-// Its generic security strength is 224 bits against preimage attacks,
-// and 112 bits against collision attacks.
-func New224() hash.Hash { return &state{rate: 144, outputLen: 28, dsbyte: 0x06} }
-
-// New256 creates a new SHA3-256 hash.
-// Its generic security strength is 256 bits against preimage attacks,
-// and 128 bits against collision attacks.
-func New256() hash.Hash { return &state{rate: 136, outputLen: 32, dsbyte: 0x06} }
-
-// New384 creates a new SHA3-384 hash.
-// Its generic security strength is 384 bits against preimage attacks,
-// and 192 bits against collision attacks.
-func New384() hash.Hash { return &state{rate: 104, outputLen: 48, dsbyte: 0x06} }
-
-// New512 creates a new SHA3-512 hash.
-// Its generic security strength is 512 bits against preimage attacks,
-// and 256 bits against collision attacks.
-func New512() hash.Hash { return &state{rate: 72, outputLen: 64, dsbyte: 0x06} }
-
-// Sum224 returns the SHA3-224 digest of the data.
-func Sum224(data []byte) (digest [28]byte) {
- h := New224()
- h.Write(data)
- h.Sum(digest[:0])
- return
-}
-
-// Sum256 returns the SHA3-256 digest of the data.
-func Sum256(data []byte) (digest [32]byte) {
- h := New256()
- h.Write(data)
- h.Sum(digest[:0])
- return
-}
-
-// Sum384 returns the SHA3-384 digest of the data.
-func Sum384(data []byte) (digest [48]byte) {
- h := New384()
- h.Write(data)
- h.Sum(digest[:0])
- return
-}
-
-// Sum512 returns the SHA3-512 digest of the data.
-func Sum512(data []byte) (digest [64]byte) {
- h := New512()
- h.Write(data)
- h.Sum(digest[:0])
- return
-}
diff --git a/crypto/sha3/keccakf.go b/crypto/sha3/keccakf.go
deleted file mode 100644
index 46d03ed385..0000000000
--- a/crypto/sha3/keccakf.go
+++ /dev/null
@@ -1,412 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !amd64 appengine gccgo
-
-package sha3
-
-// rc stores the round constants for use in the ι step.
-var rc = [24]uint64{
- 0x0000000000000001,
- 0x0000000000008082,
- 0x800000000000808A,
- 0x8000000080008000,
- 0x000000000000808B,
- 0x0000000080000001,
- 0x8000000080008081,
- 0x8000000000008009,
- 0x000000000000008A,
- 0x0000000000000088,
- 0x0000000080008009,
- 0x000000008000000A,
- 0x000000008000808B,
- 0x800000000000008B,
- 0x8000000000008089,
- 0x8000000000008003,
- 0x8000000000008002,
- 0x8000000000000080,
- 0x000000000000800A,
- 0x800000008000000A,
- 0x8000000080008081,
- 0x8000000000008080,
- 0x0000000080000001,
- 0x8000000080008008,
-}
-
-// keccakF1600 applies the Keccak permutation to a 1600b-wide
-// state represented as a slice of 25 uint64s.
-func keccakF1600(a *[25]uint64) {
- // Implementation translated from Keccak-inplace.c
- // in the keccak reference code.
- var t, bc0, bc1, bc2, bc3, bc4, d0, d1, d2, d3, d4 uint64
-
- for i := 0; i < 24; i += 4 {
- // Combines the 5 steps in each round into 2 steps.
- // Unrolls 4 rounds per loop and spreads some steps across rounds.
-
- // Round 1
- bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
- bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
- bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
- bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
- bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
- d0 = bc4 ^ (bc1<<1 | bc1>>63)
- d1 = bc0 ^ (bc2<<1 | bc2>>63)
- d2 = bc1 ^ (bc3<<1 | bc3>>63)
- d3 = bc2 ^ (bc4<<1 | bc4>>63)
- d4 = bc3 ^ (bc0<<1 | bc0>>63)
-
- bc0 = a[0] ^ d0
- t = a[6] ^ d1
- bc1 = t<<44 | t>>(64-44)
- t = a[12] ^ d2
- bc2 = t<<43 | t>>(64-43)
- t = a[18] ^ d3
- bc3 = t<<21 | t>>(64-21)
- t = a[24] ^ d4
- bc4 = t<<14 | t>>(64-14)
- a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i]
- a[6] = bc1 ^ (bc3 &^ bc2)
- a[12] = bc2 ^ (bc4 &^ bc3)
- a[18] = bc3 ^ (bc0 &^ bc4)
- a[24] = bc4 ^ (bc1 &^ bc0)
-
- t = a[10] ^ d0
- bc2 = t<<3 | t>>(64-3)
- t = a[16] ^ d1
- bc3 = t<<45 | t>>(64-45)
- t = a[22] ^ d2
- bc4 = t<<61 | t>>(64-61)
- t = a[3] ^ d3
- bc0 = t<<28 | t>>(64-28)
- t = a[9] ^ d4
- bc1 = t<<20 | t>>(64-20)
- a[10] = bc0 ^ (bc2 &^ bc1)
- a[16] = bc1 ^ (bc3 &^ bc2)
- a[22] = bc2 ^ (bc4 &^ bc3)
- a[3] = bc3 ^ (bc0 &^ bc4)
- a[9] = bc4 ^ (bc1 &^ bc0)
-
- t = a[20] ^ d0
- bc4 = t<<18 | t>>(64-18)
- t = a[1] ^ d1
- bc0 = t<<1 | t>>(64-1)
- t = a[7] ^ d2
- bc1 = t<<6 | t>>(64-6)
- t = a[13] ^ d3
- bc2 = t<<25 | t>>(64-25)
- t = a[19] ^ d4
- bc3 = t<<8 | t>>(64-8)
- a[20] = bc0 ^ (bc2 &^ bc1)
- a[1] = bc1 ^ (bc3 &^ bc2)
- a[7] = bc2 ^ (bc4 &^ bc3)
- a[13] = bc3 ^ (bc0 &^ bc4)
- a[19] = bc4 ^ (bc1 &^ bc0)
-
- t = a[5] ^ d0
- bc1 = t<<36 | t>>(64-36)
- t = a[11] ^ d1
- bc2 = t<<10 | t>>(64-10)
- t = a[17] ^ d2
- bc3 = t<<15 | t>>(64-15)
- t = a[23] ^ d3
- bc4 = t<<56 | t>>(64-56)
- t = a[4] ^ d4
- bc0 = t<<27 | t>>(64-27)
- a[5] = bc0 ^ (bc2 &^ bc1)
- a[11] = bc1 ^ (bc3 &^ bc2)
- a[17] = bc2 ^ (bc4 &^ bc3)
- a[23] = bc3 ^ (bc0 &^ bc4)
- a[4] = bc4 ^ (bc1 &^ bc0)
-
- t = a[15] ^ d0
- bc3 = t<<41 | t>>(64-41)
- t = a[21] ^ d1
- bc4 = t<<2 | t>>(64-2)
- t = a[2] ^ d2
- bc0 = t<<62 | t>>(64-62)
- t = a[8] ^ d3
- bc1 = t<<55 | t>>(64-55)
- t = a[14] ^ d4
- bc2 = t<<39 | t>>(64-39)
- a[15] = bc0 ^ (bc2 &^ bc1)
- a[21] = bc1 ^ (bc3 &^ bc2)
- a[2] = bc2 ^ (bc4 &^ bc3)
- a[8] = bc3 ^ (bc0 &^ bc4)
- a[14] = bc4 ^ (bc1 &^ bc0)
-
- // Round 2
- bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
- bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
- bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
- bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
- bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
- d0 = bc4 ^ (bc1<<1 | bc1>>63)
- d1 = bc0 ^ (bc2<<1 | bc2>>63)
- d2 = bc1 ^ (bc3<<1 | bc3>>63)
- d3 = bc2 ^ (bc4<<1 | bc4>>63)
- d4 = bc3 ^ (bc0<<1 | bc0>>63)
-
- bc0 = a[0] ^ d0
- t = a[16] ^ d1
- bc1 = t<<44 | t>>(64-44)
- t = a[7] ^ d2
- bc2 = t<<43 | t>>(64-43)
- t = a[23] ^ d3
- bc3 = t<<21 | t>>(64-21)
- t = a[14] ^ d4
- bc4 = t<<14 | t>>(64-14)
- a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1]
- a[16] = bc1 ^ (bc3 &^ bc2)
- a[7] = bc2 ^ (bc4 &^ bc3)
- a[23] = bc3 ^ (bc0 &^ bc4)
- a[14] = bc4 ^ (bc1 &^ bc0)
-
- t = a[20] ^ d0
- bc2 = t<<3 | t>>(64-3)
- t = a[11] ^ d1
- bc3 = t<<45 | t>>(64-45)
- t = a[2] ^ d2
- bc4 = t<<61 | t>>(64-61)
- t = a[18] ^ d3
- bc0 = t<<28 | t>>(64-28)
- t = a[9] ^ d4
- bc1 = t<<20 | t>>(64-20)
- a[20] = bc0 ^ (bc2 &^ bc1)
- a[11] = bc1 ^ (bc3 &^ bc2)
- a[2] = bc2 ^ (bc4 &^ bc3)
- a[18] = bc3 ^ (bc0 &^ bc4)
- a[9] = bc4 ^ (bc1 &^ bc0)
-
- t = a[15] ^ d0
- bc4 = t<<18 | t>>(64-18)
- t = a[6] ^ d1
- bc0 = t<<1 | t>>(64-1)
- t = a[22] ^ d2
- bc1 = t<<6 | t>>(64-6)
- t = a[13] ^ d3
- bc2 = t<<25 | t>>(64-25)
- t = a[4] ^ d4
- bc3 = t<<8 | t>>(64-8)
- a[15] = bc0 ^ (bc2 &^ bc1)
- a[6] = bc1 ^ (bc3 &^ bc2)
- a[22] = bc2 ^ (bc4 &^ bc3)
- a[13] = bc3 ^ (bc0 &^ bc4)
- a[4] = bc4 ^ (bc1 &^ bc0)
-
- t = a[10] ^ d0
- bc1 = t<<36 | t>>(64-36)
- t = a[1] ^ d1
- bc2 = t<<10 | t>>(64-10)
- t = a[17] ^ d2
- bc3 = t<<15 | t>>(64-15)
- t = a[8] ^ d3
- bc4 = t<<56 | t>>(64-56)
- t = a[24] ^ d4
- bc0 = t<<27 | t>>(64-27)
- a[10] = bc0 ^ (bc2 &^ bc1)
- a[1] = bc1 ^ (bc3 &^ bc2)
- a[17] = bc2 ^ (bc4 &^ bc3)
- a[8] = bc3 ^ (bc0 &^ bc4)
- a[24] = bc4 ^ (bc1 &^ bc0)
-
- t = a[5] ^ d0
- bc3 = t<<41 | t>>(64-41)
- t = a[21] ^ d1
- bc4 = t<<2 | t>>(64-2)
- t = a[12] ^ d2
- bc0 = t<<62 | t>>(64-62)
- t = a[3] ^ d3
- bc1 = t<<55 | t>>(64-55)
- t = a[19] ^ d4
- bc2 = t<<39 | t>>(64-39)
- a[5] = bc0 ^ (bc2 &^ bc1)
- a[21] = bc1 ^ (bc3 &^ bc2)
- a[12] = bc2 ^ (bc4 &^ bc3)
- a[3] = bc3 ^ (bc0 &^ bc4)
- a[19] = bc4 ^ (bc1 &^ bc0)
-
- // Round 3
- bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
- bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
- bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
- bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
- bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
- d0 = bc4 ^ (bc1<<1 | bc1>>63)
- d1 = bc0 ^ (bc2<<1 | bc2>>63)
- d2 = bc1 ^ (bc3<<1 | bc3>>63)
- d3 = bc2 ^ (bc4<<1 | bc4>>63)
- d4 = bc3 ^ (bc0<<1 | bc0>>63)
-
- bc0 = a[0] ^ d0
- t = a[11] ^ d1
- bc1 = t<<44 | t>>(64-44)
- t = a[22] ^ d2
- bc2 = t<<43 | t>>(64-43)
- t = a[8] ^ d3
- bc3 = t<<21 | t>>(64-21)
- t = a[19] ^ d4
- bc4 = t<<14 | t>>(64-14)
- a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2]
- a[11] = bc1 ^ (bc3 &^ bc2)
- a[22] = bc2 ^ (bc4 &^ bc3)
- a[8] = bc3 ^ (bc0 &^ bc4)
- a[19] = bc4 ^ (bc1 &^ bc0)
-
- t = a[15] ^ d0
- bc2 = t<<3 | t>>(64-3)
- t = a[1] ^ d1
- bc3 = t<<45 | t>>(64-45)
- t = a[12] ^ d2
- bc4 = t<<61 | t>>(64-61)
- t = a[23] ^ d3
- bc0 = t<<28 | t>>(64-28)
- t = a[9] ^ d4
- bc1 = t<<20 | t>>(64-20)
- a[15] = bc0 ^ (bc2 &^ bc1)
- a[1] = bc1 ^ (bc3 &^ bc2)
- a[12] = bc2 ^ (bc4 &^ bc3)
- a[23] = bc3 ^ (bc0 &^ bc4)
- a[9] = bc4 ^ (bc1 &^ bc0)
-
- t = a[5] ^ d0
- bc4 = t<<18 | t>>(64-18)
- t = a[16] ^ d1
- bc0 = t<<1 | t>>(64-1)
- t = a[2] ^ d2
- bc1 = t<<6 | t>>(64-6)
- t = a[13] ^ d3
- bc2 = t<<25 | t>>(64-25)
- t = a[24] ^ d4
- bc3 = t<<8 | t>>(64-8)
- a[5] = bc0 ^ (bc2 &^ bc1)
- a[16] = bc1 ^ (bc3 &^ bc2)
- a[2] = bc2 ^ (bc4 &^ bc3)
- a[13] = bc3 ^ (bc0 &^ bc4)
- a[24] = bc4 ^ (bc1 &^ bc0)
-
- t = a[20] ^ d0
- bc1 = t<<36 | t>>(64-36)
- t = a[6] ^ d1
- bc2 = t<<10 | t>>(64-10)
- t = a[17] ^ d2
- bc3 = t<<15 | t>>(64-15)
- t = a[3] ^ d3
- bc4 = t<<56 | t>>(64-56)
- t = a[14] ^ d4
- bc0 = t<<27 | t>>(64-27)
- a[20] = bc0 ^ (bc2 &^ bc1)
- a[6] = bc1 ^ (bc3 &^ bc2)
- a[17] = bc2 ^ (bc4 &^ bc3)
- a[3] = bc3 ^ (bc0 &^ bc4)
- a[14] = bc4 ^ (bc1 &^ bc0)
-
- t = a[10] ^ d0
- bc3 = t<<41 | t>>(64-41)
- t = a[21] ^ d1
- bc4 = t<<2 | t>>(64-2)
- t = a[7] ^ d2
- bc0 = t<<62 | t>>(64-62)
- t = a[18] ^ d3
- bc1 = t<<55 | t>>(64-55)
- t = a[4] ^ d4
- bc2 = t<<39 | t>>(64-39)
- a[10] = bc0 ^ (bc2 &^ bc1)
- a[21] = bc1 ^ (bc3 &^ bc2)
- a[7] = bc2 ^ (bc4 &^ bc3)
- a[18] = bc3 ^ (bc0 &^ bc4)
- a[4] = bc4 ^ (bc1 &^ bc0)
-
- // Round 4
- bc0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20]
- bc1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21]
- bc2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22]
- bc3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23]
- bc4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
- d0 = bc4 ^ (bc1<<1 | bc1>>63)
- d1 = bc0 ^ (bc2<<1 | bc2>>63)
- d2 = bc1 ^ (bc3<<1 | bc3>>63)
- d3 = bc2 ^ (bc4<<1 | bc4>>63)
- d4 = bc3 ^ (bc0<<1 | bc0>>63)
-
- bc0 = a[0] ^ d0
- t = a[1] ^ d1
- bc1 = t<<44 | t>>(64-44)
- t = a[2] ^ d2
- bc2 = t<<43 | t>>(64-43)
- t = a[3] ^ d3
- bc3 = t<<21 | t>>(64-21)
- t = a[4] ^ d4
- bc4 = t<<14 | t>>(64-14)
- a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3]
- a[1] = bc1 ^ (bc3 &^ bc2)
- a[2] = bc2 ^ (bc4 &^ bc3)
- a[3] = bc3 ^ (bc0 &^ bc4)
- a[4] = bc4 ^ (bc1 &^ bc0)
-
- t = a[5] ^ d0
- bc2 = t<<3 | t>>(64-3)
- t = a[6] ^ d1
- bc3 = t<<45 | t>>(64-45)
- t = a[7] ^ d2
- bc4 = t<<61 | t>>(64-61)
- t = a[8] ^ d3
- bc0 = t<<28 | t>>(64-28)
- t = a[9] ^ d4
- bc1 = t<<20 | t>>(64-20)
- a[5] = bc0 ^ (bc2 &^ bc1)
- a[6] = bc1 ^ (bc3 &^ bc2)
- a[7] = bc2 ^ (bc4 &^ bc3)
- a[8] = bc3 ^ (bc0 &^ bc4)
- a[9] = bc4 ^ (bc1 &^ bc0)
-
- t = a[10] ^ d0
- bc4 = t<<18 | t>>(64-18)
- t = a[11] ^ d1
- bc0 = t<<1 | t>>(64-1)
- t = a[12] ^ d2
- bc1 = t<<6 | t>>(64-6)
- t = a[13] ^ d3
- bc2 = t<<25 | t>>(64-25)
- t = a[14] ^ d4
- bc3 = t<<8 | t>>(64-8)
- a[10] = bc0 ^ (bc2 &^ bc1)
- a[11] = bc1 ^ (bc3 &^ bc2)
- a[12] = bc2 ^ (bc4 &^ bc3)
- a[13] = bc3 ^ (bc0 &^ bc4)
- a[14] = bc4 ^ (bc1 &^ bc0)
-
- t = a[15] ^ d0
- bc1 = t<<36 | t>>(64-36)
- t = a[16] ^ d1
- bc2 = t<<10 | t>>(64-10)
- t = a[17] ^ d2
- bc3 = t<<15 | t>>(64-15)
- t = a[18] ^ d3
- bc4 = t<<56 | t>>(64-56)
- t = a[19] ^ d4
- bc0 = t<<27 | t>>(64-27)
- a[15] = bc0 ^ (bc2 &^ bc1)
- a[16] = bc1 ^ (bc3 &^ bc2)
- a[17] = bc2 ^ (bc4 &^ bc3)
- a[18] = bc3 ^ (bc0 &^ bc4)
- a[19] = bc4 ^ (bc1 &^ bc0)
-
- t = a[20] ^ d0
- bc3 = t<<41 | t>>(64-41)
- t = a[21] ^ d1
- bc4 = t<<2 | t>>(64-2)
- t = a[22] ^ d2
- bc0 = t<<62 | t>>(64-62)
- t = a[23] ^ d3
- bc1 = t<<55 | t>>(64-55)
- t = a[24] ^ d4
- bc2 = t<<39 | t>>(64-39)
- a[20] = bc0 ^ (bc2 &^ bc1)
- a[21] = bc1 ^ (bc3 &^ bc2)
- a[22] = bc2 ^ (bc4 &^ bc3)
- a[23] = bc3 ^ (bc0 &^ bc4)
- a[24] = bc4 ^ (bc1 &^ bc0)
- }
-}
diff --git a/crypto/sha3/keccakf_amd64.go b/crypto/sha3/keccakf_amd64.go
deleted file mode 100644
index de035c550f..0000000000
--- a/crypto/sha3/keccakf_amd64.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build amd64,!appengine,!gccgo
-
-package sha3
-
-// This function is implemented in keccakf_amd64.s.
-
-//go:noescape
-
-func keccakF1600(state *[25]uint64)
diff --git a/crypto/sha3/keccakf_amd64.s b/crypto/sha3/keccakf_amd64.s
deleted file mode 100644
index f88533accd..0000000000
--- a/crypto/sha3/keccakf_amd64.s
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build amd64,!appengine,!gccgo
-
-// This code was translated into a form compatible with 6a from the public
-// domain sources at https://github.com/gvanas/KeccakCodePackage
-
-// Offsets in state
-#define _ba (0*8)
-#define _be (1*8)
-#define _bi (2*8)
-#define _bo (3*8)
-#define _bu (4*8)
-#define _ga (5*8)
-#define _ge (6*8)
-#define _gi (7*8)
-#define _go (8*8)
-#define _gu (9*8)
-#define _ka (10*8)
-#define _ke (11*8)
-#define _ki (12*8)
-#define _ko (13*8)
-#define _ku (14*8)
-#define _ma (15*8)
-#define _me (16*8)
-#define _mi (17*8)
-#define _mo (18*8)
-#define _mu (19*8)
-#define _sa (20*8)
-#define _se (21*8)
-#define _si (22*8)
-#define _so (23*8)
-#define _su (24*8)
-
-// Temporary registers
-#define rT1 AX
-
-// Round vars
-#define rpState DI
-#define rpStack SP
-
-#define rDa BX
-#define rDe CX
-#define rDi DX
-#define rDo R8
-#define rDu R9
-
-#define rBa R10
-#define rBe R11
-#define rBi R12
-#define rBo R13
-#define rBu R14
-
-#define rCa SI
-#define rCe BP
-#define rCi rBi
-#define rCo rBo
-#define rCu R15
-
-#define MOVQ_RBI_RCE MOVQ rBi, rCe
-#define XORQ_RT1_RCA XORQ rT1, rCa
-#define XORQ_RT1_RCE XORQ rT1, rCe
-#define XORQ_RBA_RCU XORQ rBa, rCu
-#define XORQ_RBE_RCU XORQ rBe, rCu
-#define XORQ_RDU_RCU XORQ rDu, rCu
-#define XORQ_RDA_RCA XORQ rDa, rCa
-#define XORQ_RDE_RCE XORQ rDe, rCe
-
-#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \
- /* Prepare round */ \
- MOVQ rCe, rDa; \
- ROLQ $1, rDa; \
- \
- MOVQ _bi(iState), rCi; \
- XORQ _gi(iState), rDi; \
- XORQ rCu, rDa; \
- XORQ _ki(iState), rCi; \
- XORQ _mi(iState), rDi; \
- XORQ rDi, rCi; \
- \
- MOVQ rCi, rDe; \
- ROLQ $1, rDe; \
- \
- MOVQ _bo(iState), rCo; \
- XORQ _go(iState), rDo; \
- XORQ rCa, rDe; \
- XORQ _ko(iState), rCo; \
- XORQ _mo(iState), rDo; \
- XORQ rDo, rCo; \
- \
- MOVQ rCo, rDi; \
- ROLQ $1, rDi; \
- \
- MOVQ rCu, rDo; \
- XORQ rCe, rDi; \
- ROLQ $1, rDo; \
- \
- MOVQ rCa, rDu; \
- XORQ rCi, rDo; \
- ROLQ $1, rDu; \
- \
- /* Result b */ \
- MOVQ _ba(iState), rBa; \
- MOVQ _ge(iState), rBe; \
- XORQ rCo, rDu; \
- MOVQ _ki(iState), rBi; \
- MOVQ _mo(iState), rBo; \
- MOVQ _su(iState), rBu; \
- XORQ rDe, rBe; \
- ROLQ $44, rBe; \
- XORQ rDi, rBi; \
- XORQ rDa, rBa; \
- ROLQ $43, rBi; \
- \
- MOVQ rBe, rCa; \
- MOVQ rc, rT1; \
- ORQ rBi, rCa; \
- XORQ rBa, rT1; \
- XORQ rT1, rCa; \
- MOVQ rCa, _ba(oState); \
- \
- XORQ rDu, rBu; \
- ROLQ $14, rBu; \
- MOVQ rBa, rCu; \
- ANDQ rBe, rCu; \
- XORQ rBu, rCu; \
- MOVQ rCu, _bu(oState); \
- \
- XORQ rDo, rBo; \
- ROLQ $21, rBo; \
- MOVQ rBo, rT1; \
- ANDQ rBu, rT1; \
- XORQ rBi, rT1; \
- MOVQ rT1, _bi(oState); \
- \
- NOTQ rBi; \
- ORQ rBa, rBu; \
- ORQ rBo, rBi; \
- XORQ rBo, rBu; \
- XORQ rBe, rBi; \
- MOVQ rBu, _bo(oState); \
- MOVQ rBi, _be(oState); \
- B_RBI_RCE; \
- \
- /* Result g */ \
- MOVQ _gu(iState), rBe; \
- XORQ rDu, rBe; \
- MOVQ _ka(iState), rBi; \
- ROLQ $20, rBe; \
- XORQ rDa, rBi; \
- ROLQ $3, rBi; \
- MOVQ _bo(iState), rBa; \
- MOVQ rBe, rT1; \
- ORQ rBi, rT1; \
- XORQ rDo, rBa; \
- MOVQ _me(iState), rBo; \
- MOVQ _si(iState), rBu; \
- ROLQ $28, rBa; \
- XORQ rBa, rT1; \
- MOVQ rT1, _ga(oState); \
- G_RT1_RCA; \
- \
- XORQ rDe, rBo; \
- ROLQ $45, rBo; \
- MOVQ rBi, rT1; \
- ANDQ rBo, rT1; \
- XORQ rBe, rT1; \
- MOVQ rT1, _ge(oState); \
- G_RT1_RCE; \
- \
- XORQ rDi, rBu; \
- ROLQ $61, rBu; \
- MOVQ rBu, rT1; \
- ORQ rBa, rT1; \
- XORQ rBo, rT1; \
- MOVQ rT1, _go(oState); \
- \
- ANDQ rBe, rBa; \
- XORQ rBu, rBa; \
- MOVQ rBa, _gu(oState); \
- NOTQ rBu; \
- G_RBA_RCU; \
- \
- ORQ rBu, rBo; \
- XORQ rBi, rBo; \
- MOVQ rBo, _gi(oState); \
- \
- /* Result k */ \
- MOVQ _be(iState), rBa; \
- MOVQ _gi(iState), rBe; \
- MOVQ _ko(iState), rBi; \
- MOVQ _mu(iState), rBo; \
- MOVQ _sa(iState), rBu; \
- XORQ rDi, rBe; \
- ROLQ $6, rBe; \
- XORQ rDo, rBi; \
- ROLQ $25, rBi; \
- MOVQ rBe, rT1; \
- ORQ rBi, rT1; \
- XORQ rDe, rBa; \
- ROLQ $1, rBa; \
- XORQ rBa, rT1; \
- MOVQ rT1, _ka(oState); \
- K_RT1_RCA; \
- \
- XORQ rDu, rBo; \
- ROLQ $8, rBo; \
- MOVQ rBi, rT1; \
- ANDQ rBo, rT1; \
- XORQ rBe, rT1; \
- MOVQ rT1, _ke(oState); \
- K_RT1_RCE; \
- \
- XORQ rDa, rBu; \
- ROLQ $18, rBu; \
- NOTQ rBo; \
- MOVQ rBo, rT1; \
- ANDQ rBu, rT1; \
- XORQ rBi, rT1; \
- MOVQ rT1, _ki(oState); \
- \
- MOVQ rBu, rT1; \
- ORQ rBa, rT1; \
- XORQ rBo, rT1; \
- MOVQ rT1, _ko(oState); \
- \
- ANDQ rBe, rBa; \
- XORQ rBu, rBa; \
- MOVQ rBa, _ku(oState); \
- K_RBA_RCU; \
- \
- /* Result m */ \
- MOVQ _ga(iState), rBe; \
- XORQ rDa, rBe; \
- MOVQ _ke(iState), rBi; \
- ROLQ $36, rBe; \
- XORQ rDe, rBi; \
- MOVQ _bu(iState), rBa; \
- ROLQ $10, rBi; \
- MOVQ rBe, rT1; \
- MOVQ _mi(iState), rBo; \
- ANDQ rBi, rT1; \
- XORQ rDu, rBa; \
- MOVQ _so(iState), rBu; \
- ROLQ $27, rBa; \
- XORQ rBa, rT1; \
- MOVQ rT1, _ma(oState); \
- M_RT1_RCA; \
- \
- XORQ rDi, rBo; \
- ROLQ $15, rBo; \
- MOVQ rBi, rT1; \
- ORQ rBo, rT1; \
- XORQ rBe, rT1; \
- MOVQ rT1, _me(oState); \
- M_RT1_RCE; \
- \
- XORQ rDo, rBu; \
- ROLQ $56, rBu; \
- NOTQ rBo; \
- MOVQ rBo, rT1; \
- ORQ rBu, rT1; \
- XORQ rBi, rT1; \
- MOVQ rT1, _mi(oState); \
- \
- ORQ rBa, rBe; \
- XORQ rBu, rBe; \
- MOVQ rBe, _mu(oState); \
- \
- ANDQ rBa, rBu; \
- XORQ rBo, rBu; \
- MOVQ rBu, _mo(oState); \
- M_RBE_RCU; \
- \
- /* Result s */ \
- MOVQ _bi(iState), rBa; \
- MOVQ _go(iState), rBe; \
- MOVQ _ku(iState), rBi; \
- XORQ rDi, rBa; \
- MOVQ _ma(iState), rBo; \
- ROLQ $62, rBa; \
- XORQ rDo, rBe; \
- MOVQ _se(iState), rBu; \
- ROLQ $55, rBe; \
- \
- XORQ rDu, rBi; \
- MOVQ rBa, rDu; \
- XORQ rDe, rBu; \
- ROLQ $2, rBu; \
- ANDQ rBe, rDu; \
- XORQ rBu, rDu; \
- MOVQ rDu, _su(oState); \
- \
- ROLQ $39, rBi; \
- S_RDU_RCU; \
- NOTQ rBe; \
- XORQ rDa, rBo; \
- MOVQ rBe, rDa; \
- ANDQ rBi, rDa; \
- XORQ rBa, rDa; \
- MOVQ rDa, _sa(oState); \
- S_RDA_RCA; \
- \
- ROLQ $41, rBo; \
- MOVQ rBi, rDe; \
- ORQ rBo, rDe; \
- XORQ rBe, rDe; \
- MOVQ rDe, _se(oState); \
- S_RDE_RCE; \
- \
- MOVQ rBo, rDi; \
- MOVQ rBu, rDo; \
- ANDQ rBu, rDi; \
- ORQ rBa, rDo; \
- XORQ rBi, rDi; \
- XORQ rBo, rDo; \
- MOVQ rDi, _si(oState); \
- MOVQ rDo, _so(oState) \
-
-// func keccakF1600(state *[25]uint64)
-TEXT ·keccakF1600(SB), 0, $200-8
- MOVQ state+0(FP), rpState
-
- // Convert the user state into an internal state
- NOTQ _be(rpState)
- NOTQ _bi(rpState)
- NOTQ _go(rpState)
- NOTQ _ki(rpState)
- NOTQ _mi(rpState)
- NOTQ _sa(rpState)
-
- // Execute the KeccakF permutation
- MOVQ _ba(rpState), rCa
- MOVQ _be(rpState), rCe
- MOVQ _bu(rpState), rCu
-
- XORQ _ga(rpState), rCa
- XORQ _ge(rpState), rCe
- XORQ _gu(rpState), rCu
-
- XORQ _ka(rpState), rCa
- XORQ _ke(rpState), rCe
- XORQ _ku(rpState), rCu
-
- XORQ _ma(rpState), rCa
- XORQ _me(rpState), rCe
- XORQ _mu(rpState), rCu
-
- XORQ _sa(rpState), rCa
- XORQ _se(rpState), rCe
- MOVQ _si(rpState), rDi
- MOVQ _so(rpState), rDo
- XORQ _su(rpState), rCu
-
- mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
- mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
-
- // Revert the internal state to the user state
- NOTQ _be(rpState)
- NOTQ _bi(rpState)
- NOTQ _go(rpState)
- NOTQ _ki(rpState)
- NOTQ _mi(rpState)
- NOTQ _sa(rpState)
-
- RET
diff --git a/crypto/sha3/register.go b/crypto/sha3/register.go
deleted file mode 100644
index 3cf6a22e09..0000000000
--- a/crypto/sha3/register.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build go1.4
-
-package sha3
-
-import (
- "crypto"
-)
-
-func init() {
- crypto.RegisterHash(crypto.SHA3_224, New224)
- crypto.RegisterHash(crypto.SHA3_256, New256)
- crypto.RegisterHash(crypto.SHA3_384, New384)
- crypto.RegisterHash(crypto.SHA3_512, New512)
-}
diff --git a/crypto/sha3/sha3.go b/crypto/sha3/sha3.go
deleted file mode 100644
index b12a35c87f..0000000000
--- a/crypto/sha3/sha3.go
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-// spongeDirection indicates the direction bytes are flowing through the sponge.
-type spongeDirection int
-
-const (
- // spongeAbsorbing indicates that the sponge is absorbing input.
- spongeAbsorbing spongeDirection = iota
- // spongeSqueezing indicates that the sponge is being squeezed.
- spongeSqueezing
-)
-
-const (
- // maxRate is the maximum size of the internal buffer. SHAKE-256
- // currently needs the largest buffer.
- maxRate = 168
-)
-
-type state struct {
- // Generic sponge components.
- a [25]uint64 // main state of the hash
- buf []byte // points into storage
- rate int // the number of bytes of state to use
-
- // dsbyte contains the "domain separation" bits and the first bit of
- // the padding. Sections 6.1 and 6.2 of [1] separate the outputs of the
- // SHA-3 and SHAKE functions by appending bitstrings to the message.
- // Using a little-endian bit-ordering convention, these are "01" for SHA-3
- // and "1111" for SHAKE, or 00000010b and 00001111b, respectively. Then the
- // padding rule from section 5.1 is applied to pad the message to a multiple
- // of the rate, which involves adding a "1" bit, zero or more "0" bits, and
- // a final "1" bit. We merge the first "1" bit from the padding into dsbyte,
- // giving 00000110b (0x06) and 00011111b (0x1f).
- // [1] http://csrc.nist.gov/publications/drafts/fips-202/fips_202_draft.pdf
- // "Draft FIPS 202: SHA-3 Standard: Permutation-Based Hash and
- // Extendable-Output Functions (May 2014)"
- dsbyte byte
- storage [maxRate]byte
-
- // Specific to SHA-3 and SHAKE.
- outputLen int // the default output size in bytes
- state spongeDirection // whether the sponge is absorbing or squeezing
-}
-
-// BlockSize returns the rate of sponge underlying this hash function.
-func (d *state) BlockSize() int { return d.rate }
-
-// Size returns the output size of the hash function in bytes.
-func (d *state) Size() int { return d.outputLen }
-
-// Reset clears the internal state by zeroing the sponge state and
-// the byte buffer, and setting Sponge.state to absorbing.
-func (d *state) Reset() {
- // Zero the permutation's state.
- for i := range d.a {
- d.a[i] = 0
- }
- d.state = spongeAbsorbing
- d.buf = d.storage[:0]
-}
-
-func (d *state) clone() *state {
- ret := *d
- if ret.state == spongeAbsorbing {
- ret.buf = ret.storage[:len(ret.buf)]
- } else {
- ret.buf = ret.storage[d.rate-cap(d.buf) : d.rate]
- }
-
- return &ret
-}
-
-// permute applies the KeccakF-1600 permutation. It handles
-// any input-output buffering.
-func (d *state) permute() {
- switch d.state {
- case spongeAbsorbing:
- // If we're absorbing, we need to xor the input into the state
- // before applying the permutation.
- xorIn(d, d.buf)
- d.buf = d.storage[:0]
- keccakF1600(&d.a)
- case spongeSqueezing:
- // If we're squeezing, we need to apply the permutatin before
- // copying more output.
- keccakF1600(&d.a)
- d.buf = d.storage[:d.rate]
- copyOut(d, d.buf)
- }
-}
-
-// pads appends the domain separation bits in dsbyte, applies
-// the multi-bitrate 10..1 padding rule, and permutes the state.
-func (d *state) padAndPermute(dsbyte byte) {
- if d.buf == nil {
- d.buf = d.storage[:0]
- }
- // Pad with this instance's domain-separator bits. We know that there's
- // at least one byte of space in d.buf because, if it were full,
- // permute would have been called to empty it. dsbyte also contains the
- // first one bit for the padding. See the comment in the state struct.
- d.buf = append(d.buf, dsbyte)
- zerosStart := len(d.buf)
- d.buf = d.storage[:d.rate]
- for i := zerosStart; i < d.rate; i++ {
- d.buf[i] = 0
- }
- // This adds the final one bit for the padding. Because of the way that
- // bits are numbered from the LSB upwards, the final bit is the MSB of
- // the last byte.
- d.buf[d.rate-1] ^= 0x80
- // Apply the permutation
- d.permute()
- d.state = spongeSqueezing
- d.buf = d.storage[:d.rate]
- copyOut(d, d.buf)
-}
-
-// Write absorbs more data into the hash's state. It produces an error
-// if more data is written to the ShakeHash after writing
-func (d *state) Write(p []byte) (written int, err error) {
- if d.state != spongeAbsorbing {
- panic("sha3: write to sponge after read")
- }
- if d.buf == nil {
- d.buf = d.storage[:0]
- }
- written = len(p)
-
- for len(p) > 0 {
- if len(d.buf) == 0 && len(p) >= d.rate {
- // The fast path; absorb a full "rate" bytes of input and apply the permutation.
- xorIn(d, p[:d.rate])
- p = p[d.rate:]
- keccakF1600(&d.a)
- } else {
- // The slow path; buffer the input until we can fill the sponge, and then xor it in.
- todo := d.rate - len(d.buf)
- if todo > len(p) {
- todo = len(p)
- }
- d.buf = append(d.buf, p[:todo]...)
- p = p[todo:]
-
- // If the sponge is full, apply the permutation.
- if len(d.buf) == d.rate {
- d.permute()
- }
- }
- }
-
- return
-}
-
-// Read squeezes an arbitrary number of bytes from the sponge.
-func (d *state) Read(out []byte) (n int, err error) {
- // If we're still absorbing, pad and apply the permutation.
- if d.state == spongeAbsorbing {
- d.padAndPermute(d.dsbyte)
- }
-
- n = len(out)
-
- // Now, do the squeezing.
- for len(out) > 0 {
- n := copy(out, d.buf)
- d.buf = d.buf[n:]
- out = out[n:]
-
- // Apply the permutation if we've squeezed the sponge dry.
- if len(d.buf) == 0 {
- d.permute()
- }
- }
-
- return
-}
-
-// Sum applies padding to the hash state and then squeezes out the desired
-// number of output bytes.
-func (d *state) Sum(in []byte) []byte {
- // Make a copy of the original hash so that caller can keep writing
- // and summing.
- dup := d.clone()
- hash := make([]byte, dup.outputLen)
- dup.Read(hash)
- return append(in, hash...)
-}
diff --git a/crypto/sha3/sha3_test.go b/crypto/sha3/sha3_test.go
deleted file mode 100644
index 0e33676ce6..0000000000
--- a/crypto/sha3/sha3_test.go
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-// Tests include all the ShortMsgKATs provided by the Keccak team at
-// https://github.com/gvanas/KeccakCodePackage
-//
-// They only include the zero-bit case of the bitwise testvectors
-// published by NIST in the draft of FIPS-202.
-
-import (
- "bytes"
- "compress/flate"
- "encoding/hex"
- "encoding/json"
- "hash"
- "os"
- "strings"
- "testing"
-)
-
-const (
- testString = "brekeccakkeccak koax koax"
- katFilename = "testdata/keccakKats.json.deflate"
-)
-
-// Internal-use instances of SHAKE used to test against KATs.
-func newHashShake128() hash.Hash {
- return &state{rate: 168, dsbyte: 0x1f, outputLen: 512}
-}
-func newHashShake256() hash.Hash {
- return &state{rate: 136, dsbyte: 0x1f, outputLen: 512}
-}
-
-// testDigests contains functions returning hash.Hash instances
-// with output-length equal to the KAT length for both SHA-3 and
-// SHAKE instances.
-var testDigests = map[string]func() hash.Hash{
- "SHA3-224": New224,
- "SHA3-256": New256,
- "SHA3-384": New384,
- "SHA3-512": New512,
- "SHAKE128": newHashShake128,
- "SHAKE256": newHashShake256,
-}
-
-// testShakes contains functions that return ShakeHash instances for
-// testing the ShakeHash-specific interface.
-var testShakes = map[string]func() ShakeHash{
- "SHAKE128": NewShake128,
- "SHAKE256": NewShake256,
-}
-
-// structs used to marshal JSON test-cases.
-type KeccakKats struct {
- Kats map[string][]struct {
- Digest string `json:"digest"`
- Length int64 `json:"length"`
- Message string `json:"message"`
- }
-}
-
-func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) {
- xorInOrig, copyOutOrig := xorIn, copyOut
- xorIn, copyOut = xorInGeneric, copyOutGeneric
- testf("generic")
- if xorImplementationUnaligned != "generic" {
- xorIn, copyOut = xorInUnaligned, copyOutUnaligned
- testf("unaligned")
- }
- xorIn, copyOut = xorInOrig, copyOutOrig
-}
-
-// TestKeccakKats tests the SHA-3 and Shake implementations against all the
-// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage
-// (The testvectors are stored in keccakKats.json.deflate due to their length.)
-func TestKeccakKats(t *testing.T) {
- testUnalignedAndGeneric(t, func(impl string) {
- // Read the KATs.
- deflated, err := os.Open(katFilename)
- if err != nil {
- t.Errorf("error opening %s: %s", katFilename, err)
- }
- file := flate.NewReader(deflated)
- dec := json.NewDecoder(file)
- var katSet KeccakKats
- err = dec.Decode(&katSet)
- if err != nil {
- t.Errorf("error decoding KATs: %s", err)
- }
-
- // Do the KATs.
- for functionName, kats := range katSet.Kats {
- d := testDigests[functionName]()
- for _, kat := range kats {
- d.Reset()
- in, err := hex.DecodeString(kat.Message)
- if err != nil {
- t.Errorf("error decoding KAT: %s", err)
- }
- d.Write(in[:kat.Length/8])
- got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
- if got != kat.Digest {
- t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
- functionName, impl, kat.Length, kat.Message, got, kat.Digest)
- t.Logf("wanted %+v", kat)
- t.FailNow()
- }
- continue
- }
- }
- })
-}
-
-// TestUnalignedWrite tests that writing data in an arbitrary pattern with
-// small input buffers.
-func TestUnalignedWrite(t *testing.T) {
- testUnalignedAndGeneric(t, func(impl string) {
- buf := sequentialBytes(0x10000)
- for alg, df := range testDigests {
- d := df()
- d.Reset()
- d.Write(buf)
- want := d.Sum(nil)
- d.Reset()
- for i := 0; i < len(buf); {
- // Cycle through offsets which make a 137 byte sequence.
- // Because 137 is prime this sequence should exercise all corner cases.
- offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
- for _, j := range offsets {
- if v := len(buf) - i; v < j {
- j = v
- }
- d.Write(buf[i : i+j])
- i += j
- }
- }
- got := d.Sum(nil)
- if !bytes.Equal(got, want) {
- t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
- }
- }
- })
-}
-
-// TestAppend checks that appending works when reallocation is necessary.
-func TestAppend(t *testing.T) {
- testUnalignedAndGeneric(t, func(impl string) {
- d := New224()
-
- for capacity := 2; capacity <= 66; capacity += 64 {
- // The first time around the loop, Sum will have to reallocate.
- // The second time, it will not.
- buf := make([]byte, 2, capacity)
- d.Reset()
- d.Write([]byte{0xcc})
- buf = d.Sum(buf)
- expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
- if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
- t.Errorf("got %s, want %s", got, expected)
- }
- }
- })
-}
-
-// TestAppendNoRealloc tests that appending works when no reallocation is necessary.
-func TestAppendNoRealloc(t *testing.T) {
- testUnalignedAndGeneric(t, func(impl string) {
- buf := make([]byte, 1, 200)
- d := New224()
- d.Write([]byte{0xcc})
- buf = d.Sum(buf)
- expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
- if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
- t.Errorf("%s: got %s, want %s", impl, got, expected)
- }
- })
-}
-
-// TestSqueezing checks that squeezing the full output a single time produces
-// the same output as repeatedly squeezing the instance.
-func TestSqueezing(t *testing.T) {
- testUnalignedAndGeneric(t, func(impl string) {
- for functionName, newShakeHash := range testShakes {
- d0 := newShakeHash()
- d0.Write([]byte(testString))
- ref := make([]byte, 32)
- d0.Read(ref)
-
- d1 := newShakeHash()
- d1.Write([]byte(testString))
- var multiple []byte
- for range ref {
- one := make([]byte, 1)
- d1.Read(one)
- multiple = append(multiple, one...)
- }
- if !bytes.Equal(ref, multiple) {
- t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref))
- }
- }
- })
-}
-
-// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
-func sequentialBytes(size int) []byte {
- result := make([]byte, size)
- for i := range result {
- result[i] = byte(i)
- }
- return result
-}
-
-// BenchmarkPermutationFunction measures the speed of the permutation function
-// with no input data.
-func BenchmarkPermutationFunction(b *testing.B) {
- b.SetBytes(int64(200))
- var lanes [25]uint64
- for i := 0; i < b.N; i++ {
- keccakF1600(&lanes)
- }
-}
-
-// benchmarkHash tests the speed to hash num buffers of buflen each.
-func benchmarkHash(b *testing.B, h hash.Hash, size, num int) {
- b.StopTimer()
- h.Reset()
- data := sequentialBytes(size)
- b.SetBytes(int64(size * num))
- b.StartTimer()
-
- var state []byte
- for i := 0; i < b.N; i++ {
- for j := 0; j < num; j++ {
- h.Write(data)
- }
- state = h.Sum(state[:0])
- }
- b.StopTimer()
- h.Reset()
-}
-
-// benchmarkShake is specialized to the Shake instances, which don't
-// require a copy on reading output.
-func benchmarkShake(b *testing.B, h ShakeHash, size, num int) {
- b.StopTimer()
- h.Reset()
- data := sequentialBytes(size)
- d := make([]byte, 32)
-
- b.SetBytes(int64(size * num))
- b.StartTimer()
-
- for i := 0; i < b.N; i++ {
- h.Reset()
- for j := 0; j < num; j++ {
- h.Write(data)
- }
- h.Read(d)
- }
-}
-
-func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) }
-func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) }
-func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) }
-func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) }
-
-func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) }
-func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) }
-func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) }
-func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) }
-
-func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) }
-
-func Example_sum() {
- buf := []byte("some data to hash")
- // A hash needs to be 64 bytes long to have 256-bit collision resistance.
- h := make([]byte, 64)
- // Compute a 64-byte hash of buf and put it in h.
- ShakeSum256(h, buf)
-}
-
-func Example_mac() {
- k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long")
- buf := []byte("and this is some data to authenticate")
- // A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key.
- h := make([]byte, 32)
- d := NewShake256()
- // Write the key into the hash.
- d.Write(k)
- // Now write the data.
- d.Write(buf)
- // Read 32 bytes of output from the hash into h.
- d.Read(h)
-}
diff --git a/crypto/sha3/shake.go b/crypto/sha3/shake.go
deleted file mode 100644
index 841f9860f0..0000000000
--- a/crypto/sha3/shake.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-// This file defines the ShakeHash interface, and provides
-// functions for creating SHAKE instances, as well as utility
-// functions for hashing bytes to arbitrary-length output.
-
-import (
- "io"
-)
-
-// ShakeHash defines the interface to hash functions that
-// support arbitrary-length output.
-type ShakeHash interface {
- // Write absorbs more data into the hash's state. It panics if input is
- // written to it after output has been read from it.
- io.Writer
-
- // Read reads more output from the hash; reading affects the hash's
- // state. (ShakeHash.Read is thus very different from Hash.Sum)
- // It never returns an error.
- io.Reader
-
- // Clone returns a copy of the ShakeHash in its current state.
- Clone() ShakeHash
-
- // Reset resets the ShakeHash to its initial state.
- Reset()
-}
-
-func (d *state) Clone() ShakeHash {
- return d.clone()
-}
-
-// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
-// Its generic security strength is 128 bits against all attacks if at
-// least 32 bytes of its output are used.
-func NewShake128() ShakeHash { return &state{rate: 168, dsbyte: 0x1f} }
-
-// NewShake256 creates a new SHAKE128 variable-output-length ShakeHash.
-// Its generic security strength is 256 bits against all attacks if
-// at least 64 bytes of its output are used.
-func NewShake256() ShakeHash { return &state{rate: 136, dsbyte: 0x1f} }
-
-// ShakeSum128 writes an arbitrary-length digest of data into hash.
-func ShakeSum128(hash, data []byte) {
- h := NewShake128()
- h.Write(data)
- h.Read(hash)
-}
-
-// ShakeSum256 writes an arbitrary-length digest of data into hash.
-func ShakeSum256(hash, data []byte) {
- h := NewShake256()
- h.Write(data)
- h.Read(hash)
-}
diff --git a/crypto/sha3/testdata/keccakKats.json.deflate b/crypto/sha3/testdata/keccakKats.json.deflate
deleted file mode 100644
index 62e85ae242..0000000000
Binary files a/crypto/sha3/testdata/keccakKats.json.deflate and /dev/null differ
diff --git a/crypto/sha3/xor.go b/crypto/sha3/xor.go
deleted file mode 100644
index 46a0d63a6d..0000000000
--- a/crypto/sha3/xor.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !amd64,!386,!ppc64le appengine
-
-package sha3
-
-var (
- xorIn = xorInGeneric
- copyOut = copyOutGeneric
- xorInUnaligned = xorInGeneric
- copyOutUnaligned = copyOutGeneric
-)
-
-const xorImplementationUnaligned = "generic"
diff --git a/crypto/sha3/xor_generic.go b/crypto/sha3/xor_generic.go
deleted file mode 100644
index fd35f02ef6..0000000000
--- a/crypto/sha3/xor_generic.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package sha3
-
-import "encoding/binary"
-
-// xorInGeneric xors the bytes in buf into the state; it
-// makes no non-portable assumptions about memory layout
-// or alignment.
-func xorInGeneric(d *state, buf []byte) {
- n := len(buf) / 8
-
- for i := 0; i < n; i++ {
- a := binary.LittleEndian.Uint64(buf)
- d.a[i] ^= a
- buf = buf[8:]
- }
-}
-
-// copyOutGeneric copies ulint64s to a byte buffer.
-func copyOutGeneric(d *state, b []byte) {
- for i := 0; len(b) >= 8; i++ {
- binary.LittleEndian.PutUint64(b, d.a[i])
- b = b[8:]
- }
-}
diff --git a/crypto/sha3/xor_unaligned.go b/crypto/sha3/xor_unaligned.go
deleted file mode 100644
index 929a486a79..0000000000
--- a/crypto/sha3/xor_unaligned.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build amd64 386 ppc64le
-// +build !appengine
-
-package sha3
-
-import "unsafe"
-
-func xorInUnaligned(d *state, buf []byte) {
- bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))
- n := len(buf)
- if n >= 72 {
- d.a[0] ^= bw[0]
- d.a[1] ^= bw[1]
- d.a[2] ^= bw[2]
- d.a[3] ^= bw[3]
- d.a[4] ^= bw[4]
- d.a[5] ^= bw[5]
- d.a[6] ^= bw[6]
- d.a[7] ^= bw[7]
- d.a[8] ^= bw[8]
- }
- if n >= 104 {
- d.a[9] ^= bw[9]
- d.a[10] ^= bw[10]
- d.a[11] ^= bw[11]
- d.a[12] ^= bw[12]
- }
- if n >= 136 {
- d.a[13] ^= bw[13]
- d.a[14] ^= bw[14]
- d.a[15] ^= bw[15]
- d.a[16] ^= bw[16]
- }
- if n >= 144 {
- d.a[17] ^= bw[17]
- }
- if n >= 168 {
- d.a[18] ^= bw[18]
- d.a[19] ^= bw[19]
- d.a[20] ^= bw[20]
- }
-}
-
-func copyOutUnaligned(d *state, buf []byte) {
- ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
- copy(buf, ab[:])
-}
-
-var (
- xorIn = xorInUnaligned
- copyOut = copyOutUnaligned
-)
-
-const xorImplementationUnaligned = "unaligned"
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index 665a20429a..6811657503 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.go
@@ -14,13 +14,15 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build !nacl,!js,!nocgo
+//go:build !nacl && !js && cgo && !gofuzz
+// +build !nacl,!js,cgo,!gofuzz
package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
+ "errors"
"fmt"
"github.com/XinFinOrg/XDPoSChain/common/math"
@@ -47,31 +49,31 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be chosen by an adversery. Common
+// be aware that the given digest cannot be chosen by an adversary. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
-func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
- if len(hash) != 32 {
- return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
+func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
+ if len(digestHash) != DigestLength {
+ return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
}
seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
defer zeroBytes(seckey)
- return secp256k1.Sign(hash, seckey)
+ return secp256k1.Sign(digestHash, seckey)
}
-// VerifySignature checks that the given public key created signature over hash.
+// VerifySignature checks that the given public key created signature over digest.
// The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format.
// The signature should have the 64 byte [R || S] format.
-func VerifySignature(pubkey, hash, signature []byte) bool {
- return secp256k1.VerifySignature(pubkey, hash, signature)
+func VerifySignature(pubkey, digestHash, signature []byte) bool {
+ return secp256k1.VerifySignature(pubkey, digestHash, signature)
}
// DecompressPubkey parses a public key in the 33-byte compressed format.
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
x, y := secp256k1.DecompressPubkey(pubkey)
if x == nil {
- return nil, fmt.Errorf("invalid public key")
+ return nil, errors.New("invalid public key")
}
return &ecdsa.PublicKey{X: x, Y: y, Curve: S256()}, nil
}
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
index f636b23772..6d628d758d 100644
--- a/crypto/signature_nocgo.go
+++ b/crypto/signature_nocgo.go
@@ -14,7 +14,8 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build nacl js nocgo
+//go:build nacl || js || !cgo || gofuzz
+// +build nacl js !cgo gofuzz
package crypto
@@ -23,37 +24,48 @@ import (
"crypto/elliptic"
"errors"
"fmt"
- "math/big"
- "github.com/btcsuite/btcd/btcec"
+ "github.com/btcsuite/btcd/btcec/v2"
+ btc_ecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
)
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
- pub, err := SigToPub(hash, sig)
+ pub, err := sigToPub(hash, sig)
if err != nil {
return nil, err
}
- bytes := (*btcec.PublicKey)(pub).SerializeUncompressed()
+ bytes := pub.SerializeUncompressed()
return bytes, err
}
+func sigToPub(hash, sig []byte) (*btcec.PublicKey, error) {
+ if len(sig) != SignatureLength {
+ return nil, errors.New("invalid signature")
+ }
+ // Convert to btcec input format with 'recovery id' v at the beginning.
+ btcsig := make([]byte, SignatureLength)
+ btcsig[0] = sig[RecoveryIDOffset] + 27
+ copy(btcsig[1:], sig)
+
+ pub, _, err := btc_ecdsa.RecoverCompact(btcsig, hash)
+ return pub, err
+}
+
// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
- // Convert to btcec input format with 'recovery id' v at the beginning.
- btcsig := make([]byte, 65)
- btcsig[0] = sig[64] + 27
- copy(btcsig[1:], sig)
-
- pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash)
- return (*ecdsa.PublicKey)(pub), err
+ pub, err := sigToPub(hash, sig)
+ if err != nil {
+ return nil, err
+ }
+ return pub.ToECDSA(), nil
}
// Sign calculates an ECDSA signature.
//
// This function is susceptible to chosen plaintext attacks that can leak
// information about the private key that is used for signing. Callers must
-// be aware that the given hash cannot be chosen by an adversery. Common
+// be aware that the given hash cannot be chosen by an adversary. Common
// solution is to hash any input before calculating the signature.
//
// The produced signature is in the [R || S || V] format where V is 0 or 1.
@@ -62,16 +74,22 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) {
return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash))
}
if prv.Curve != btcec.S256() {
- return nil, fmt.Errorf("private key curve is not secp256k1")
+ return nil, errors.New("private key curve is not secp256k1")
}
- sig, err := btcec.SignCompact(btcec.S256(), (*btcec.PrivateKey)(prv), hash, false)
+ // ecdsa.PrivateKey -> btcec.PrivateKey
+ var priv btcec.PrivateKey
+ if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() {
+ return nil, errors.New("invalid private key")
+ }
+ defer priv.Zero()
+ sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey
if err != nil {
return nil, err
}
// Convert to Ethereum signature format with 'recovery id' v at the end.
v := sig[0] - 27
copy(sig, sig[1:])
- sig[64] = v
+ sig[RecoveryIDOffset] = v
return sig, nil
}
@@ -82,13 +100,20 @@ func VerifySignature(pubkey, hash, signature []byte) bool {
if len(signature) != 64 {
return false
}
- sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
- key, err := btcec.ParsePubKey(pubkey, btcec.S256())
+ var r, s btcec.ModNScalar
+ if r.SetByteSlice(signature[:32]) {
+ return false // overflow
+ }
+ if s.SetByteSlice(signature[32:]) {
+ return false
+ }
+ sig := btc_ecdsa.NewSignature(&r, &s)
+ key, err := btcec.ParsePubKey(pubkey)
if err != nil {
return false
}
// Reject malleable signatures. libsecp256k1 does this check but btcec doesn't.
- if sig.S.Cmp(secp256k1_halfN) > 0 {
+ if s.IsOverHalfOrder() {
return false
}
return sig.Verify(hash, key)
@@ -99,16 +124,26 @@ func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
if len(pubkey) != 33 {
return nil, errors.New("invalid compressed public key length")
}
- key, err := btcec.ParsePubKey(pubkey, btcec.S256())
+ key, err := btcec.ParsePubKey(pubkey)
if err != nil {
return nil, err
}
return key.ToECDSA(), nil
}
-// CompressPubkey encodes a public key to the 33-byte compressed format.
+// CompressPubkey encodes a public key to the 33-byte compressed format. The
+// provided PublicKey must be valid. Namely, the coordinates must not be larger
+// than 32 bytes each, they must be less than the field prime, and it must be a
+// point on the secp256k1 curve. This is the case for a PublicKey constructed by
+// elliptic.Unmarshal (see UnmarshalPubkey), or by ToECDSA and ecdsa.GenerateKey
+// when constructing a PrivateKey.
func CompressPubkey(pubkey *ecdsa.PublicKey) []byte {
- return (*btcec.PublicKey)(pubkey).SerializeCompressed()
+ // NOTE: the coordinates may be validated with
+ // btcec.ParsePubKey(FromECDSAPub(pubkey))
+ var x, y btcec.FieldVal
+ x.SetByteSlice(pubkey.X.Bytes())
+ y.SetByteSlice(pubkey.Y.Bytes())
+ return btcec.NewPublicKey(&x, &y).SerializeCompressed()
}
// S256 returns an instance of the secp256k1 curve.
diff --git a/docker/XDPoSChain/entrypoint.sh b/docker/XDPoSChain/entrypoint.sh
index 3ce0db757c..d080d1fb79 100755
--- a/docker/XDPoSChain/entrypoint.sh
+++ b/docker/XDPoSChain/entrypoint.sh
@@ -56,7 +56,7 @@ if [[ ! -z $NETWORK_ID ]]; then
;;
89 )
genesisPath="testnet.json"
- params="$params --apothem --gcmode archive --rpcapi db,eth,net,web3,debug,XDPoS"
+ params="$params --apothem --gcmode archive --http-api db,eth,net,web3,debug,XDPoS"
;;
90 )
genesisPath="devnet.json"
@@ -158,7 +158,7 @@ fi
# debug mode
if [[ ! -z $DEBUG_MODE ]]; then
- params="$params --gcmode archive --rpcapi db,eth,net,web3,debug,XDPoS"
+ params="$params --gcmode archive --http-api db,eth,net,web3,debug,XDPoS"
fi
# maxpeers
@@ -179,18 +179,18 @@ exec XDC $params \
--maxpeers $MAXPEERS \
--password ./password \
--port 30303 \
- --txpool.globalqueue 5000 \
- --txpool.globalslots 5000 \
- --rpc \
- --rpccorsdomain "*" \
- --rpcaddr 0.0.0.0 \
- --rpcport 8545 \
- --rpcvhosts "*" \
+ --txpool-globalqueue 5000 \
+ --txpool-globalslots 5000 \
+ --http \
+ --http-corsdomain "*" \
+ --http-addr 0.0.0.0 \
+ --http-port 8545 \
+ --http-vhosts "*" \
--ws \
- --wsaddr 0.0.0.0 \
- --wsport 8546 \
- --wsorigins "*" \
+ --ws-addr 0.0.0.0 \
+ --ws-port 8546 \
+ --ws-origins "*" \
--mine \
--gasprice "250000000" \
- --targetgaslimit "84000000" \
+ --miner-gaslimit "84000000" \
"$@"
diff --git a/eth/api_backend.go b/eth/api_backend.go
index f5e2ead83d..183a0f1f44 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -24,24 +24,20 @@ import (
"os"
"path/filepath"
+ "github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
- "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
-
- "github.com/XinFinOrg/XDPoSChain/XDCx"
-
- "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
-
"github.com/XinFinOrg/XDPoSChain/accounts"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/contracts"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
- stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
@@ -241,7 +237,7 @@ func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t
}
func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
- return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil
+ return b.eth.blockchain.GetReceiptsByHash(blockHash), nil
}
func (b *EthApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
@@ -258,8 +254,9 @@ func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
vmConfig = b.eth.blockchain.GetVMConfig()
}
state.SetBalance(msg.From(), math.MaxBig256)
- context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
- return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil
+ txContext := core.NewEVMTxContext(msg)
+ context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
+ return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), vmError, nil
}
func (b *EthApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
@@ -301,10 +298,7 @@ func (b *EthApiBackend) SendLendingTx(ctx context.Context, signedTx *types.Lendi
}
func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
- pending, err := b.eth.txPool.Pending()
- if err != nil {
- return nil, err
- }
+ pending := b.eth.txPool.Pending(false)
var txs types.Transactions
for _, batch := range pending {
txs = append(txs, batch...)
@@ -328,6 +322,10 @@ func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.TxPool().Content()
}
+func (b *EthApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ return b.eth.TxPool().ContentFrom(addr)
+}
+
func (b *EthApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) {
return b.eth.OrderPool().Content()
}
@@ -347,8 +345,12 @@ func (b *EthApiBackend) ProtocolVersion() int {
return b.eth.EthVersion()
}
-func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
- return b.gpo.SuggestPrice(ctx)
+func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ return b.gpo.SuggestTipCap(ctx)
+}
+
+func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
+ return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
}
func (b *EthApiBackend) ChainDb() ethdb.Database {
@@ -396,6 +398,10 @@ func (b *EthApiBackend) GetEngine() consensus.Engine {
return b.eth.engine
}
+func (b *EthApiBackend) CurrentHeader() *types.Header {
+ return b.eth.blockchain.CurrentHeader()
+}
+
func (b *EthApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
return b.eth.stateAtBlock(block, reexec, base, checkLive)
}
@@ -497,7 +503,7 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm
var voterResults map[common.Address]*big.Int
for signer, calcReward := range rewardSigners {
if signer == masternodeAddr {
- err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, state, masternodeAddr, calcReward, number)
+ rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, state, masternodeAddr, calcReward, number)
if err != nil {
log.Crit("Fail to calculate reward for holders.", "error", err)
return nil
@@ -515,20 +521,20 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm
func (b *EthApiBackend) GetVotersCap(checkpoint *big.Int, masterAddr common.Address, voters []common.Address) map[common.Address]*big.Int {
chain := b.eth.blockchain
checkpointBlock := chain.GetBlockByNumber(checkpoint.Uint64())
- state, err := chain.StateAt(checkpointBlock.Root())
+ statedb, err := chain.StateAt(checkpointBlock.Root())
if err != nil {
log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint, "err", err)
return nil
}
- if state != nil {
+ if statedb != nil {
log.Error("fail to get state in GetVotersCap", "checkpoint", checkpoint)
return nil
}
voterCaps := make(map[common.Address]*big.Int)
for _, voteAddr := range voters {
- voterCap := stateDatabase.GetVoterCap(state, masterAddr, voteAddr)
+ voterCap := state.GetVoterCap(statedb, masterAddr, voteAddr)
voterCaps[voteAddr] = voterCap
}
return voterCaps
@@ -551,21 +557,21 @@ func (b *EthApiBackend) GetEpochDuration() *big.Int {
// GetMasternodesCap return a cap of all masternode at a checkpoint
func (b *EthApiBackend) GetMasternodesCap(checkpoint uint64) map[common.Address]*big.Int {
checkpointBlock := b.eth.blockchain.GetBlockByNumber(checkpoint)
- state, err := b.eth.blockchain.StateAt(checkpointBlock.Root())
+ statedb, err := b.eth.blockchain.StateAt(checkpointBlock.Root())
if err != nil {
log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint, "err", err)
return nil
}
- if state == nil {
+ if statedb == nil {
log.Error("fail to get state in GetMasternodesCap", "checkpoint", checkpoint)
return nil
}
- candicates := stateDatabase.GetCandidates(state)
+ candicates := state.GetCandidates(statedb)
masternodesCap := map[common.Address]*big.Int{}
for _, candicate := range candicates {
- masternodesCap[candicate] = stateDatabase.GetCandidateCap(state, candicate)
+ masternodesCap[candicate] = state.GetCandidateCap(statedb, candicate)
}
return masternodesCap
diff --git a/eth/api_test.go b/eth/api_test.go
index 37a16e8f79..b7a6929b6a 100644
--- a/eth/api_test.go
+++ b/eth/api_test.go
@@ -17,10 +17,11 @@
package eth
import (
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"reflect"
"testing"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/davecgh/go-spew/spew"
@@ -84,7 +85,7 @@ func TestStorageRangeAt(t *testing.T) {
t.Error(err)
}
if !reflect.DeepEqual(result, test.want) {
- t.Fatalf("wrong result for range 0x%x.., limit %d:\ngot %s\nwant %s",
+ t.Fatalf("wrong result for range %#x.., limit %d:\ngot %s\nwant %s",
test.start, test.limit, dumper.Sdump(result), dumper.Sdump(&test.want))
}
}
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index d0f5e8f14b..35297da7f8 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -28,7 +28,6 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
-
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -232,24 +231,24 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
// Fetch and execute the next block trace tasks
for task := range tasks {
signer := types.MakeSigner(api.config, task.block.Number())
+ blockCtx := core.NewEVMBlockContext(task.block.Header(), api.eth.blockchain, nil)
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
- var balacne *big.Int
+ var balance *big.Int
if tx.To() != nil {
if value, ok := feeCapacity[*tx.To()]; ok {
- balacne = value
+ balance = value
}
}
- msg, _ := tx.AsMessage(signer, balacne, task.block.Number())
+ header := task.block.Header()
+ msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee)
txctx := &tracers.Context{
BlockHash: task.block.Hash(),
TxIndex: i,
TxHash: tx.Hash(),
}
- vmctx := core.NewEVMContext(msg, task.block.Header(), api.eth.blockchain, nil)
-
- res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config)
+ res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@@ -473,29 +472,28 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
threads = len(txs)
}
blockHash := block.Hash()
+ blockCtx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil)
for th := 0; th < threads; th++ {
pend.Add(1)
go func() {
defer pend.Done()
-
// Fetch and execute the next transaction trace tasks
for task := range jobs {
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
- var balacne *big.Int
+ var balance *big.Int
if txs[task.index].To() != nil {
if value, ok := feeCapacity[*txs[task.index].To()]; ok {
- balacne = value
+ balance = value
}
}
- msg, _ := txs[task.index].AsMessage(signer, balacne, block.Number())
+ header := block.Header()
+ msg, _ := txs[task.index].AsMessage(signer, balance, header.Number, header.BaseFee)
txctx := &tracers.Context{
BlockHash: blockHash,
TxIndex: task.index,
TxHash: txs[task.index].Hash(),
}
- vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
-
- res, err := api.traceTx(ctx, msg, txctx, vmctx, task.statedb, config)
+ res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
if err != nil {
results[task.index] = &txTraceResult{Error: err.Error()}
continue
@@ -510,24 +508,25 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
for i, tx := range txs {
// Send the trace task over for execution
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
- var balacne *big.Int
+ var balance *big.Int
if tx.To() != nil {
// Bypass the validation for trading and lending transactions as their nonce are not incremented
if tx.IsSkipNonceTransaction() {
continue
}
if value, ok := feeCapacity[*tx.To()]; ok {
- balacne = value
+ balance = value
}
}
// Generate the next state snapshot fast without tracing
- msg, _ := tx.AsMessage(signer, balacne, block.Number())
- statedb.Prepare(tx.Hash(), block.Hash(), i)
- vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
+ header := block.Header()
+ msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee)
+ txContext := core.NewEVMTxContext(msg)
+ statedb.SetTxContext(tx.Hash(), i)
- vmenv := vm.NewEVM(vmctx, statedb, XDCxState, api.config, vm.Config{})
+ vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{})
owner := common.Address{}
- if _, _, _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil {
+ if _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil {
failed = err
break
}
@@ -604,7 +603,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
}
root := statedb.IntermediateRoot(true)
if root != block.Root() {
- return nil, nil, fmt.Errorf("invalid merkle root (number :%d got : %x expect: %x)", block.NumberU64(), root.Hex(), block.Root())
+ return nil, nil, fmt.Errorf("invalid merkle root (number %d : got : %x expect: %x)", block.NumberU64(), root.Hex(), block.Root())
}
// Finalize the state so any modifications are written to the trie
root, err = statedb.Commit(true)
@@ -692,8 +691,13 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti
}
}
// Execute the trace
- msg := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap())
- vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
+ // TODO: replace block.BaseFee() with vmctx.BaseFee
+ // reference: https://github.com/ethereum/go-ethereum/pull/29051
+ msg, err := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap(), block.BaseFee())
+ if err != nil {
+ return nil, err
+ }
+ vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil)
var traceConfig *TraceConfig
if config != nil {
traceConfig = &config.TraceConfig
@@ -704,11 +708,12 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
-func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.Context, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
+func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, txctx *tracers.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
- tracer vm.EVMLogger
- err error
+ tracer vm.EVMLogger
+ err error
+ txContext = core.NewEVMTxContext(message)
)
switch {
case config == nil:
@@ -739,13 +744,13 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
tracer = vm.NewStructLogger(config.LogConfig)
}
// Run the transaction with tracing enabled.
- vmenv := vm.NewEVM(vmctx, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer})
+ vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
- // Call Prepare to clear out the statedb access list
- statedb.Prepare(txctx.TxHash, txctx.BlockHash, txctx.TxIndex)
+ // Call SetTxContext to clear out the statedb access list
+ statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
owner := common.Address{}
- ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
+ result, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
}
@@ -753,9 +758,9 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
switch tracer := tracer.(type) {
case *vm.StructLogger:
return ðapi.ExecutionResult{
- Gas: gas,
- Failed: failed,
- ReturnValue: fmt.Sprintf("%x", ret),
+ Gas: result.UsedGas,
+ Failed: result.Failed(),
+ ReturnValue: fmt.Sprintf("%x", result.Return()),
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
@@ -768,19 +773,19 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
}
// computeTxEnv returns the execution environment of a certain transaction.
-func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) {
+func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) {
// Create the parent state database
block := api.eth.blockchain.GetBlockByHash(blockHash)
if block == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("block %x not found", blockHash)
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("block %x not found", blockHash)
}
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %x not found", block.ParentHash())
}
statedb, XDCxState, err := api.computeStateDB(parent, reexec)
if err != nil {
- return nil, vm.Context{}, nil, err
+ return nil, vm.BlockContext{}, nil, err
}
// Recompute transactions up to the target index.
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
@@ -794,7 +799,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
usedGas := new(uint64)
// Iterate over and process the individual transactions
for idx, tx := range block.Transactions() {
- statedb.Prepare(tx.Hash(), block.Hash(), idx)
+ statedb.SetTxContext(tx.Hash(), idx)
if idx == txIndex {
var balanceFee *big.Int
if tx.To() != nil {
@@ -802,16 +807,17 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
balanceFee = value
}
}
- msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number())
+ header := block.Header()
+ msg, err := tx.AsMessage(types.MakeSigner(api.config, header.Number), balanceFee, header.Number, header.BaseFee)
if err != nil {
- return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
}
- context := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
+ context := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil)
return msg, context, statedb, nil
}
_, gas, err, tokenFeeUsed := core.ApplyTransaction(api.config, feeCapacity, api.eth.blockchain, nil, gp, statedb, XDCxState, block.Header(), tx, usedGas, vm.Config{})
if err != nil {
- return nil, vm.Context{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
}
if tokenFeeUsed {
@@ -822,5 +828,5 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
}
}
statedb.DeleteSuicides()
- return nil, vm.Context{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
}
diff --git a/eth/backend.go b/eth/backend.go
index 45e1bd95ec..b9cef04b11 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -37,6 +37,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/contracts"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
@@ -72,9 +73,9 @@ type Ethereum struct {
shutdownChan chan bool // Channel for shutting down the ethereum
// Handlers
- txPool *core.TxPool
- orderPool *core.OrderPool
- lendingPool *core.LendingPool
+ txPool *txpool.TxPool
+ orderPool *txpool.OrderPool
+ lendingPool *txpool.LendingPool
blockchain *core.BlockChain
protocolManager *ProtocolManager
lesServer LesServer
@@ -103,9 +104,9 @@ type Ethereum struct {
Lending *XDCxlending.Lending
}
-func (s *Ethereum) AddLesServer(ls LesServer) {
- s.lesServer = ls
- ls.SetBloomBitsIndexer(s.bloomIndexer)
+func (e *Ethereum) AddLesServer(ls LesServer) {
+ e.lesServer = ls
+ ls.SetBloomBitsIndexer(e.bloomIndexer)
}
// New creates a new Ethereum object (including the
@@ -154,7 +155,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
if !config.SkipBcVersionCheck {
bcVersion := core.GetBlockChainVersion(chainDb)
if bcVersion != core.BlockChainVersion && bcVersion != 0 {
- return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d). Run geth upgradedb.\n", bcVersion, core.BlockChainVersion)
+ return nil, fmt.Errorf("blockchain DB version mismatch (%d / %d). Run geth upgradedb", bcVersion, core.BlockChainVersion)
}
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
}
@@ -186,9 +187,9 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
if config.TxPool.Journal != "" {
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
}
- eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
- eth.orderPool = core.NewOrderPool(eth.chainConfig, eth.blockchain)
- eth.lendingPool = core.NewLendingPool(eth.chainConfig, eth.blockchain)
+ eth.txPool = txpool.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
+ eth.orderPool = txpool.NewOrderPool(eth.chainConfig, eth.blockchain)
+ eth.lendingPool = txpool.NewLendingPool(eth.chainConfig, eth.blockchain)
if common.RollbackHash != (common.Hash{}) {
curBlock := eth.blockchain.CurrentBlock()
if curBlock == nil {
@@ -224,11 +225,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
} else {
eth.ApiBackend = &EthApiBackend{eth, nil, nil}
}
- gpoParams := config.GPO
- if gpoParams.Default == nil {
- gpoParams.Default = config.GasPrice
- }
- eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
+ eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, config.GPO, config.GasPrice)
// Set global ipc endpoint.
eth.blockchain.IPCEndpoint = ctx.GetConfig().IPCEndpoint()
@@ -247,7 +244,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
}
if block.NumberU64()%common.MergeSignRange == 0 || !eth.chainConfig.IsTIP2019(block.Number()) {
if err := contracts.CreateTransactionSign(chainConfig, eth.txPool, eth.accountManager, block, chainDb, eb); err != nil {
- return fmt.Errorf("Fail to create tx sign for importing block: %v", err)
+ return fmt.Errorf("fail to create tx sign for importing block: %v", err)
}
}
return nil
@@ -371,80 +368,80 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai
// APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
-func (s *Ethereum) APIs() []rpc.API {
- apis := ethapi.GetAPIs(s.ApiBackend, s.BlockChain())
+func (e *Ethereum) APIs() []rpc.API {
+ apis := ethapi.GetAPIs(e.ApiBackend, e.BlockChain())
// Append any APIs exposed explicitly by the consensus engine
- apis = append(apis, s.engine.APIs(s.BlockChain())...)
+ apis = append(apis, e.engine.APIs(e.BlockChain())...)
// Append all the local APIs and return
return append(apis, []rpc.API{
{
Namespace: "eth",
Version: "1.0",
- Service: NewPublicEthereumAPI(s),
+ Service: NewPublicEthereumAPI(e),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
- Service: NewPublicMinerAPI(s),
+ Service: NewPublicMinerAPI(e),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
- Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux),
+ Service: downloader.NewPublicDownloaderAPI(e.protocolManager.downloader, e.eventMux),
Public: true,
}, {
Namespace: "miner",
Version: "1.0",
- Service: NewPrivateMinerAPI(s),
+ Service: NewPrivateMinerAPI(e),
Public: false,
}, {
Namespace: "eth",
Version: "1.0",
- Service: filters.NewFilterAPI(filters.NewFilterSystem(s.ApiBackend, filters.Config{LogCacheSize: s.config.FilterLogCacheSize}), false),
+ Service: filters.NewFilterAPI(filters.NewFilterSystem(e.ApiBackend, filters.Config{LogCacheSize: e.config.FilterLogCacheSize}), false),
Public: true,
}, {
Namespace: "admin",
Version: "1.0",
- Service: NewPrivateAdminAPI(s),
+ Service: NewPrivateAdminAPI(e),
}, {
Namespace: "debug",
Version: "1.0",
- Service: NewPublicDebugAPI(s),
+ Service: NewPublicDebugAPI(e),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
- Service: NewPrivateDebugAPI(s.chainConfig, s),
+ Service: NewPrivateDebugAPI(e.chainConfig, e),
}, {
Namespace: "net",
Version: "1.0",
- Service: s.netRPCService,
+ Service: e.netRPCService,
Public: true,
},
}...)
}
-func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
- s.blockchain.ResetWithGenesisBlock(gb)
+func (e *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
+ e.blockchain.ResetWithGenesisBlock(gb)
}
-func (s *Ethereum) Etherbase() (eb common.Address, err error) {
- s.lock.RLock()
- etherbase := s.etherbase
- s.lock.RUnlock()
+func (e *Ethereum) Etherbase() (eb common.Address, err error) {
+ e.lock.RLock()
+ etherbase := e.etherbase
+ e.lock.RUnlock()
if etherbase != (common.Address{}) {
return etherbase, nil
}
- if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
+ if wallets := e.AccountManager().Wallets(); len(wallets) > 0 {
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
etherbase := accounts[0].Address
- s.lock.Lock()
- s.etherbase = etherbase
- s.lock.Unlock()
+ e.lock.Lock()
+ e.etherbase = etherbase
+ e.lock.Unlock()
log.Info("Etherbase automatically configured", "address", etherbase)
return etherbase, nil
@@ -454,43 +451,43 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
}
// set in js console via admin interface or wrapper from cli flags
-func (self *Ethereum) SetEtherbase(etherbase common.Address) {
- self.lock.Lock()
- self.etherbase = etherbase
- self.lock.Unlock()
+func (e *Ethereum) SetEtherbase(etherbase common.Address) {
+ e.lock.Lock()
+ e.etherbase = etherbase
+ e.lock.Unlock()
- self.miner.SetEtherbase(etherbase)
+ e.miner.SetEtherbase(etherbase)
}
// ValidateMasternode checks if node's address is in set of masternodes
-func (s *Ethereum) ValidateMasternode() (bool, error) {
- eb, err := s.Etherbase()
+func (e *Ethereum) ValidateMasternode() (bool, error) {
+ eb, err := e.Etherbase()
if err != nil {
return false, err
}
- if s.chainConfig.XDPoS != nil {
+ if e.chainConfig.XDPoS != nil {
//check if miner's wallet is in set of validators
- c := s.engine.(*XDPoS.XDPoS)
+ c := e.engine.(*XDPoS.XDPoS)
- authorized := c.IsAuthorisedAddress(s.blockchain, s.blockchain.CurrentHeader(), eb)
+ authorized := c.IsAuthorisedAddress(e.blockchain, e.blockchain.CurrentHeader(), eb)
if !authorized {
//This miner doesn't belong to set of validators
return false, nil
}
} else {
- return false, errors.New("Only verify masternode permission in XDPoS protocol")
+ return false, errors.New("only verify masternode permission in XDPoS protocol")
}
return true, nil
}
-func (s *Ethereum) StartStaking(local bool) error {
- eb, err := s.Etherbase()
+func (e *Ethereum) StartStaking(local bool) error {
+ eb, err := e.Etherbase()
if err != nil {
log.Error("Cannot start mining without etherbase", "err", err)
return fmt.Errorf("etherbase missing: %v", err)
}
- if XDPoS, ok := s.engine.(*XDPoS.XDPoS); ok {
- wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
+ if XDPoS, ok := e.engine.(*XDPoS.XDPoS); ok {
+ wallet, err := e.accountManager.Find(accounts.Account{Address: eb})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "address", eb, "err", err)
return fmt.Errorf("signer missing: %v", err)
@@ -502,102 +499,104 @@ func (s *Ethereum) StartStaking(local bool) error {
// mechanism introduced to speed sync times. CPU mining on mainnet is ludicrous
// so noone will ever hit this path, whereas marking sync done on CPU mining
// will ensure that private networks work in single miner mode too.
- atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
+ atomic.StoreUint32(&e.protocolManager.acceptTxs, 1)
}
- go s.miner.Start(eb)
+ go e.miner.Start(eb)
return nil
}
-func (s *Ethereum) StopStaking() {
- s.miner.Stop()
+func (e *Ethereum) StopStaking() {
+ e.miner.Stop()
}
-func (s *Ethereum) IsStaking() bool { return s.miner.Mining() }
-func (s *Ethereum) Miner() *miner.Miner { return s.miner }
-func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
-func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
-func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
-func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) Engine() consensus.Engine { return s.engine }
-func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
-func (s *Ethereum) IsListening() bool { return true } // Always listening
-func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
-func (s *Ethereum) NetVersion() uint64 { return s.networkId }
-func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+func (e *Ethereum) IsStaking() bool { return e.miner.Mining() }
+func (e *Ethereum) Miner() *miner.Miner { return e.miner }
+
+func (e *Ethereum) AccountManager() *accounts.Manager { return e.accountManager }
+func (e *Ethereum) BlockChain() *core.BlockChain { return e.blockchain }
+func (e *Ethereum) TxPool() *txpool.TxPool { return e.txPool }
+func (e *Ethereum) EventMux() *event.TypeMux { return e.eventMux }
+func (e *Ethereum) Engine() consensus.Engine { return e.engine }
+func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb }
+func (e *Ethereum) IsListening() bool { return true } // Always listening
+func (e *Ethereum) EthVersion() int { return int(e.protocolManager.SubProtocols[0].Version) }
+func (e *Ethereum) NetVersion() uint64 { return e.networkId }
+func (e *Ethereum) Downloader() *downloader.Downloader { return e.protocolManager.downloader }
// Protocols implements node.Service, returning all the currently configured
// network protocols to start.
-func (s *Ethereum) Protocols() []p2p.Protocol {
- if s.lesServer == nil {
- return s.protocolManager.SubProtocols
+func (e *Ethereum) Protocols() []p2p.Protocol {
+ if e.lesServer == nil {
+ return e.protocolManager.SubProtocols
}
- return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...)
+ return append(e.protocolManager.SubProtocols, e.lesServer.Protocols()...)
}
// Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation.
-func (s *Ethereum) Start(srvr *p2p.Server) error {
+func (e *Ethereum) Start(srvr *p2p.Server) error {
// Start the bloom bits servicing goroutines
- s.startBloomHandlers()
+ e.startBloomHandlers()
// Start the RPC service
- s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
+ e.netRPCService = ethapi.NewPublicNetAPI(srvr, e.NetVersion())
// Figure out a max peers count based on the server limits
maxPeers := srvr.MaxPeers
- if s.config.LightServ > 0 {
- if s.config.LightPeers >= srvr.MaxPeers {
- return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)
+ if e.config.LightServ > 0 {
+ if e.config.LightPeers >= srvr.MaxPeers {
+ return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", e.config.LightPeers, srvr.MaxPeers)
}
- maxPeers -= s.config.LightPeers
+ maxPeers -= e.config.LightPeers
}
// Start the networking layer and the light server if requested
- s.protocolManager.Start(maxPeers)
- if s.lesServer != nil {
- s.lesServer.Start(srvr)
+ e.protocolManager.Start(maxPeers)
+ if e.lesServer != nil {
+ e.lesServer.Start(srvr)
}
return nil
}
-func (s *Ethereum) SaveData() {
- s.blockchain.SaveData()
+
+func (e *Ethereum) SaveData() {
+ e.blockchain.SaveData()
}
// Stop implements node.Service, terminating all internal goroutines used by the
// Ethereum protocol.
-func (s *Ethereum) Stop() error {
- s.bloomIndexer.Close()
- s.blockchain.Stop()
- s.protocolManager.Stop()
- if s.lesServer != nil {
- s.lesServer.Stop()
+func (e *Ethereum) Stop() error {
+ e.bloomIndexer.Close()
+ e.blockchain.Stop()
+ e.protocolManager.Stop()
+ if e.lesServer != nil {
+ e.lesServer.Stop()
}
- s.txPool.Stop()
- s.miner.Stop()
- s.eventMux.Stop()
+ e.txPool.Stop()
+ e.miner.Stop()
+ e.eventMux.Stop()
- s.chainDb.Close()
- close(s.shutdownChan)
+ e.chainDb.Close()
+ close(e.shutdownChan)
return nil
}
-func (s *Ethereum) GetPeer() int {
- return len(s.protocolManager.peers.peers)
+func (e *Ethereum) GetPeer() int {
+ return len(e.protocolManager.peers.peers)
}
-func (s *Ethereum) GetXDCX() *XDCx.XDCX {
- return s.XDCX
+func (e *Ethereum) GetXDCX() *XDCx.XDCX {
+ return e.XDCX
}
-func (s *Ethereum) OrderPool() *core.OrderPool {
- return s.orderPool
+func (e *Ethereum) OrderPool() *txpool.OrderPool {
+ return e.orderPool
}
-func (s *Ethereum) GetXDCXLending() *XDCxlending.Lending {
- return s.Lending
+func (e *Ethereum) GetXDCXLending() *XDCxlending.Lending {
+ return e.Lending
}
// LendingPool geth eth lending pool
-func (s *Ethereum) LendingPool() *core.LendingPool {
- return s.lendingPool
+func (e *Ethereum) LendingPool() *txpool.LendingPool {
+ return e.lendingPool
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 1a500142c8..f5fb6d1c58 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -1586,7 +1586,7 @@ func (d *Downloader) DeliverNodeData(id string, data [][]byte) (err error) {
}
// deliver injects a new batch of data received from a remote node.
-func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter metrics.Meter) (err error) {
+func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, inMeter, dropMeter *metrics.Meter) (err error) {
// Update the delivery metrics for both good and failed deliveries
inMeter.Mark(int64(packet.Items()))
defer func() {
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index bbf5889d0e..3df24e6d37 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -19,6 +19,7 @@ package downloader
import (
"errors"
"fmt"
+ "math"
"math/big"
"sync"
"sync/atomic"
@@ -78,7 +79,7 @@ type downloadTester struct {
// newTester creates a new downloader test mocker.
func newTester() *downloadTester {
testdb := rawdb.NewMemoryDatabase()
- genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
+ genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(math.MaxInt64))
tester := &downloadTester{
genesis: genesis,
@@ -109,7 +110,8 @@ func newTester() *downloadTester {
// reassembly.
func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
// Generate the block chain
- blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) {
+ config := dl.Config()
+ blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) {
block.SetCoinbase(common.Address{seed})
// If a heavy chain is requested, delay blocks to raise difficulty
@@ -118,8 +120,8 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren
}
// If the block number is multiple of 3, send a bonus transaction to the miner
if parent == dl.genesis && i%3 == 0 {
- signer := types.MakeSigner(params.TestChainConfig, block.Number())
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
+ signer := types.MakeSigner(config, block.Number())
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
if err != nil {
panic(err)
}
@@ -464,7 +466,11 @@ func (dl *downloadTester) handleProposedBlock(header *types.Header) error {
}
// Config retrieves the blockchain's chain configuration.
-func (dl *downloadTester) Config() *params.ChainConfig { return params.TestChainConfig }
+func (dl *downloadTester) Config() *params.ChainConfig {
+ config := *params.TestChainConfig
+ config.Eip1559Block = big.NewInt((0))
+ return &config
+}
type downloadTesterPeer struct {
dl *downloadTester
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index 7f55ce7335..b21460c2ef 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -650,7 +650,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int {
// Note, this method expects the queue lock to be already held. The
// reason the lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
-func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int {
+func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter *metrics.Meter) map[string]int {
// Iterate over the expired requests and return each to the queue
expiries := make(map[string]int)
for id, request := range pendPool {
@@ -805,7 +805,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int,
// reason the lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
- pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer metrics.Timer,
+ pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer *metrics.Timer,
results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) {
// Short circuit if the data was never requested
diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go
index 8a491231fb..608546873d 100644
--- a/eth/downloader/statesync.go
+++ b/eth/downloader/statesync.go
@@ -18,7 +18,6 @@ package downloader
import (
"fmt"
- "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
"hash"
"sync"
"time"
@@ -26,10 +25,11 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/ethdb"
+ "github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/trie"
+ "golang.org/x/crypto/sha3"
)
// stateReq represents a batch of state fetch requests groupped together into
@@ -242,7 +242,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
return &stateSync{
d: d,
sched: state.NewStateSync(root, d.stateDB, trie.NewSyncBloom(1, memorydb.New())),
- keccak: sha3.NewKeccak256(),
+ keccak: sha3.NewLegacyKeccak256(),
tasks: make(map[common.Hash]*stateTask),
deliver: make(chan *stateReq),
cancel: make(chan struct{}),
@@ -327,7 +327,7 @@ func (s *stateSync) commit(force bool) error {
b := s.d.stateDB.NewBatch()
s.sched.Commit(b)
if err := b.Write(); err != nil {
- return fmt.Errorf("DB write error: %v", err)
+ return fmt.Errorf("write DB error: %v", err)
}
s.updateStats(s.numUncommitted, 0, 0, time.Since(start))
s.numUncommitted = 0
@@ -419,9 +419,7 @@ func (s *stateSync) process(req *stateReq) error {
default:
return fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err)
}
- if _, ok := req.tasks[hash]; ok {
- delete(req.tasks, hash)
- }
+ delete(req.tasks, hash)
}
// Put unfulfilled tasks back into the retry queue
npeers := s.d.peers.Len()
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index efdd2197d9..642b59c4a0 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -29,6 +29,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
"github.com/XinFinOrg/XDPoSChain/params"
@@ -36,18 +37,22 @@ import (
// FullNodeGPO contains default gasprice oracle settings for full node.
var FullNodeGPO = gasprice.Config{
- Blocks: 20,
- Percentile: 60,
- MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
+ Blocks: 20,
+ Percentile: 60,
+ MaxHeaderHistory: 1024,
+ MaxBlockHistory: 1024,
+ MaxPrice: gasprice.DefaultMaxPrice,
+ IgnorePrice: gasprice.DefaultIgnorePrice,
}
// LightClientGPO contains default gasprice oracle settings for light client.
var LightClientGPO = gasprice.Config{
- Blocks: 2,
- Percentile: 60,
- MaxPrice: gasprice.DefaultMaxPrice,
- IgnorePrice: gasprice.DefaultIgnorePrice,
+ Blocks: 2,
+ Percentile: 60,
+ MaxHeaderHistory: 300,
+ MaxBlockHistory: 5,
+ MaxPrice: gasprice.DefaultMaxPrice,
+ IgnorePrice: gasprice.DefaultIgnorePrice,
}
// Defaults contains default settings for use on the Ethereum main net.
@@ -68,7 +73,7 @@ var Defaults = Config{
FilterLogCacheSize: 32,
GasPrice: big.NewInt(0.25 * params.Shannon),
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: txpool.DefaultConfig,
RPCGasCap: 50000000,
GPO: FullNodeGPO,
RPCTxFeeCap: 1, // 1 ether
@@ -81,8 +86,15 @@ func init() {
home = user.HomeDir
}
}
- if runtime.GOOS == "windows" {
- Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Ethash")
+ if runtime.GOOS == "darwin" {
+ Defaults.Ethash.DatasetDir = filepath.Join(home, "Library", "Ethash")
+ } else if runtime.GOOS == "windows" {
+ localappdata := os.Getenv("LOCALAPPDATA")
+ if localappdata != "" {
+ Defaults.Ethash.DatasetDir = filepath.Join(localappdata, "Ethash")
+ } else {
+ Defaults.Ethash.DatasetDir = filepath.Join(home, "AppData", "Local", "Ethash")
+ }
} else {
Defaults.Ethash.DatasetDir = filepath.Join(home, ".ethash")
}
@@ -125,7 +137,7 @@ type Config struct {
Ethash ethash.Config
// Transaction pool options
- TxPool core.TxPoolConfig
+ TxPool txpool.Config
// Gas Price Oracle options
GPO gasprice.Config
diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go
index 6b27542f19..f11304094e 100644
--- a/eth/ethconfig/gen_config.go
+++ b/eth/ethconfig/gen_config.go
@@ -9,6 +9,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
)
@@ -33,7 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
GasPrice *big.Int
FilterLogCacheSize int
Ethash ethash.Config
- TxPool core.TxPoolConfig
+ TxPool txpool.Config
GPO gasprice.Config
EnablePreimageRecording bool
DocRoot string `toml:"-"`
@@ -87,7 +88,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
GasPrice *big.Int
FilterLogCacheSize *int
Ethash *ethash.Config
- TxPool *core.TxPoolConfig
+ TxPool *txpool.Config
GPO *gasprice.Config
EnablePreimageRecording *bool
DocRoot *string `toml:"-"`
diff --git a/eth/fetcher/fetcher.go b/eth/fetcher/fetcher.go
index 7d1e15fd4d..259781a6bb 100644
--- a/eth/fetcher/fetcher.go
+++ b/eth/fetcher/fetcher.go
@@ -22,9 +22,8 @@ import (
"math/rand"
"time"
- lru "github.com/hashicorp/golang-lru"
-
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/common/prque"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core/types"
@@ -135,7 +134,8 @@ type Fetcher struct {
queue *prque.Prque // Queue containing the import operations (block number sorted)
queues map[string]int // Per peer block counts to prevent memory exhaustion
queued map[common.Hash]*inject // Set of already queued blocks (to dedup imports)
- knowns *lru.ARCCache
+ knowns *lru.Cache[common.Hash, struct{}]
+
// Callbacks
getBlock blockRetrievalFn // Retrieves a block from the local chain
verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
@@ -157,7 +157,6 @@ type Fetcher struct {
// New creates a block fetcher to retrieve blocks based on hash announcements.
func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handleProposedBlock proposeBlockHandlerFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertBlock blockInsertFn, prepareBlock blockPrepareFn, dropPeer peerDropFn) *Fetcher {
- knownBlocks, _ := lru.NewARC(blockLimit)
return &Fetcher{
notify: make(chan *announce),
inject: make(chan *inject),
@@ -174,7 +173,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, handlePropose
queue: prque.New(nil),
queues: make(map[string]int),
queued: make(map[common.Hash]*inject),
- knowns: knownBlocks,
+ knowns: lru.NewCache[common.Hash, struct{}](blockLimit),
getBlock: getBlock,
verifyHeader: verifyHeader,
handleProposedBlock: handleProposedBlock,
@@ -641,7 +640,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) {
}
f.queues[peer] = count
f.queued[hash] = op
- f.knowns.Add(hash, true)
+ f.knowns.Add(hash, struct{}{})
f.queue.Push(op, -int64(block.NumberU64()))
if f.queueChangeHook != nil {
f.queueChangeHook(op.block.Hash(), true)
@@ -679,7 +678,7 @@ func (f *Fetcher) insert(peer string, block *types.Block) {
go f.broadcastBlock(block, true)
}
case consensus.ErrFutureBlock:
- delay := time.Unix(block.Time().Int64(), 0).Sub(time.Now()) // nolint: gosimple
+ delay := time.Until(time.Unix(block.Time().Int64(), 0))
log.Info("Receive future block", "number", block.NumberU64(), "hash", block.Hash().Hex(), "delay", delay)
time.Sleep(delay)
goto again
diff --git a/eth/filters/api_test.go b/eth/filters/api_test.go
index f74c13e643..ca0f0c60eb 100644
--- a/eth/filters/api_test.go
+++ b/eth/filters/api_test.go
@@ -56,7 +56,7 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
// from, to block number
var test1 FilterCriteria
- vector := fmt.Sprintf(`{"fromBlock":"0x%x","toBlock":"0x%x"}`, fromBlock, toBlock)
+ vector := fmt.Sprintf(`{"fromBlock":"%#x","toBlock":"%#x"}`, fromBlock, toBlock)
if err := json.Unmarshal([]byte(vector), &test1); err != nil {
t.Fatal(err)
}
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index 2610376d3b..0d169167db 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -164,7 +164,7 @@ func TestBlockSubscription(t *testing.T) {
db = rawdb.NewMemoryDatabase()
backend, sys = newTestFilterSystem(t, db, Config{})
api = NewFilterAPI(sys, false)
- genesis = new(core.Genesis).MustCommit(db)
+ genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
chainEvents = []core.ChainEvent{}
)
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
new file mode 100644
index 0000000000..c3cffa4902
--- /dev/null
+++ b/eth/gasprice/feehistory.go
@@ -0,0 +1,326 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gasprice
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math"
+ "math/big"
+ "slices"
+ "sync/atomic"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+var (
+ errInvalidPercentile = errors.New("invalid reward percentile")
+ errRequestBeyondHead = errors.New("request beyond head block")
+)
+
+const (
+ // maxBlockFetchers is the max number of goroutines to spin up to pull blocks
+ // for the fee history calculation (mostly relevant for LES).
+ maxBlockFetchers = 4
+)
+
+// blockFees represents a single block for processing
+type blockFees struct {
+ // set by the caller
+ blockNumber uint64
+ header *types.Header
+ block *types.Block // only set if reward percentiles are requested
+ receipts types.Receipts
+ // filled by processBlock
+ results processedFees
+ err error
+}
+
+type cacheKey struct {
+ number uint64
+ percentiles string
+}
+
+// processedFees contains the results of a processed block.
+type processedFees struct {
+ reward []*big.Int
+ baseFee, nextBaseFee *big.Int
+ gasUsedRatio float64
+}
+
+// txGasAndReward is sorted in ascending order based on reward
+type txGasAndReward struct {
+ gasUsed uint64
+ reward *big.Int
+}
+
+// processBlock takes a blockFees structure with the blockNumber, the header and optionally
+// the block field filled in, retrieves the block from the backend if not present yet and
+// fills in the rest of the fields.
+func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
+ chainconfig := oracle.backend.ChainConfig()
+ if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
+ bf.results.baseFee = new(big.Int)
+ }
+ if chainconfig.IsEIP1559(big.NewInt(int64(bf.blockNumber + 1))) {
+ bf.results.nextBaseFee = eip1559.CalcBaseFee(chainconfig, bf.header)
+ } else {
+ bf.results.nextBaseFee = new(big.Int)
+ }
+ bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
+ if len(percentiles) == 0 {
+ // rewards were not requested, return null
+ return
+ }
+ if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
+ log.Error("Block or receipts are missing while reward percentiles are requested")
+ return
+ }
+
+ bf.results.reward = make([]*big.Int, len(percentiles))
+ if len(bf.block.Transactions()) == 0 {
+ // return an all zero row if there are no transactions to gather data from
+ for i := range bf.results.reward {
+ bf.results.reward[i] = new(big.Int)
+ }
+ return
+ }
+
+ sorter := make([]txGasAndReward, len(bf.block.Transactions()))
+ for i, tx := range bf.block.Transactions() {
+ reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
+ sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
+ }
+ slices.SortStableFunc(sorter, func(a, b txGasAndReward) int {
+ return a.reward.Cmp(b.reward)
+ })
+
+ var txIndex int
+ sumGasUsed := sorter[0].gasUsed
+
+ for i, p := range percentiles {
+ thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
+ for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
+ txIndex++
+ sumGasUsed += sorter[txIndex].gasUsed
+ }
+ bf.results.reward[i] = sorter[txIndex].reward
+ }
+}
+
+// resolveBlockRange resolves the specified block range to absolute block numbers while also
+// enforcing backend specific limitations. The pending block and corresponding receipts are
+// also returned if requested and available.
+// Note: an error is only returned if retrieving the head header has failed. If there are no
+// retrievable blocks in the specified range then zero block count is returned with no error.
+func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks uint64) (*types.Block, []*types.Receipt, uint64, uint64, error) {
+ var (
+ headBlock *types.Header
+ pendingBlock *types.Block
+ pendingReceipts types.Receipts
+ err error
+ )
+
+ // Get the chain's current head.
+ if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil {
+ return nil, nil, 0, 0, err
+ }
+ head := rpc.BlockNumber(headBlock.Number.Uint64())
+
+ // Fail if request block is beyond the chain's current head.
+ if head < reqEnd {
+ return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head)
+ }
+
+ // Resolve block tag.
+ if reqEnd < 0 {
+ var (
+ resolved *types.Header
+ err error
+ )
+ switch reqEnd {
+ case rpc.PendingBlockNumber:
+ if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
+ resolved = pendingBlock.Header()
+ } else {
+ // Pending block not supported by backend, process only until latest block.
+ resolved = headBlock
+
+ // Update total blocks to return to account for this.
+ blocks--
+ }
+ case rpc.LatestBlockNumber:
+ // Retrieved above.
+ resolved = headBlock
+ case rpc.CommittedBlockNumber:
+ resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.CommittedBlockNumber)
+ case rpc.EarliestBlockNumber:
+ resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber)
+ }
+ if resolved == nil || err != nil {
+ return nil, nil, 0, 0, err
+ }
+ // Absolute number resolved.
+ reqEnd = rpc.BlockNumber(resolved.Number.Uint64())
+ }
+
+ // If there are no blocks to return, short circuit.
+ if blocks == 0 {
+ return nil, nil, 0, 0, nil
+ }
+ // Ensure not trying to retrieve before genesis.
+ if uint64(reqEnd+1) < blocks {
+ blocks = uint64(reqEnd + 1)
+ }
+ return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil
+}
+
+// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
+// The range can be specified either with absolute block numbers or ending with the latest
+// or pending block. Backends may or may not support gathering data from the pending block
+// or blocks older than a certain age (specified in maxHistory). The first block of the
+// actually processed range is returned to avoid ambiguity when parts of the requested range
+// are not available or when the head has changed during processing this request.
+// Three arrays are returned based on the processed blocks:
+// - reward: the requested percentiles of effective priority fees per gas of transactions in each
+// block, sorted in ascending order and weighted by gas used.
+// - baseFee: base fee per gas in the given block
+// - gasUsedRatio: gasUsed/gasLimit in the given block
+//
+// Note: baseFee includes the next block after the newest of the returned range, because this
+// value can be derived from the newest block.
+func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
+ if blocks < 1 {
+ return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
+ }
+ maxFeeHistory := oracle.maxHeaderHistory
+ if len(rewardPercentiles) != 0 {
+ maxFeeHistory = oracle.maxBlockHistory
+ }
+ if blocks > maxFeeHistory {
+ log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
+ blocks = maxFeeHistory
+ }
+ for i, p := range rewardPercentiles {
+ if p < 0 || p > 100 {
+ return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
+ }
+ if i > 0 && p <= rewardPercentiles[i-1] {
+ return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
+ }
+ }
+ var (
+ pendingBlock *types.Block
+ pendingReceipts []*types.Receipt
+ err error
+ )
+ pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
+ if err != nil || blocks == 0 {
+ return common.Big0, nil, nil, nil, err
+ }
+ oldestBlock := lastBlock + 1 - blocks
+
+ var (
+ next = oldestBlock
+ results = make(chan *blockFees, blocks)
+ )
+ percentileKey := make([]byte, 8*len(rewardPercentiles))
+ for i, p := range rewardPercentiles {
+ binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p))
+ }
+ for i := 0; i < maxBlockFetchers && i < int(blocks); i++ {
+ go func() {
+ for {
+ // Retrieve the next block number to fetch with this goroutine
+ blockNumber := atomic.AddUint64(&next, 1) - 1
+ if blockNumber > lastBlock {
+ return
+ }
+
+ fees := &blockFees{blockNumber: blockNumber}
+ if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
+ fees.block, fees.receipts = pendingBlock, pendingReceipts
+ fees.header = fees.block.Header()
+ oracle.processBlock(fees, rewardPercentiles)
+ results <- fees
+ } else {
+ cacheKey := cacheKey{number: blockNumber, percentiles: string(percentileKey)}
+
+ if p, ok := oracle.historyCache.Get(cacheKey); ok {
+ fees.results = p
+ results <- fees
+ } else {
+ if len(rewardPercentiles) != 0 {
+ fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
+ if fees.block != nil && fees.err == nil {
+ fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
+ fees.header = fees.block.Header()
+ }
+ } else {
+ fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
+ }
+ if fees.header != nil && fees.err == nil {
+ oracle.processBlock(fees, rewardPercentiles)
+ if fees.err == nil {
+ oracle.historyCache.Add(cacheKey, fees.results)
+ }
+ }
+ // send to results even if empty to guarantee that blocks items are sent in total
+ results <- fees
+ }
+ }
+ }
+ }()
+ }
+ var (
+ reward = make([][]*big.Int, blocks)
+ baseFee = make([]*big.Int, blocks+1)
+ gasUsedRatio = make([]float64, blocks)
+ firstMissing = blocks
+ )
+ for ; blocks > 0; blocks-- {
+ fees := <-results
+ if fees.err != nil {
+ return common.Big0, nil, nil, nil, fees.err
+ }
+ i := fees.blockNumber - oldestBlock
+ if fees.results.baseFee != nil {
+ reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio
+ } else {
+ // getting no block and no error means we are requesting into the future (might happen because of a reorg)
+ if i < firstMissing {
+ firstMissing = i
+ }
+ }
+ }
+ if firstMissing == 0 {
+ return common.Big0, nil, nil, nil, nil
+ }
+ if len(rewardPercentiles) != 0 {
+ reward = reward[:firstMissing]
+ } else {
+ reward = nil
+ }
+ baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
+ return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil
+}
diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go
new file mode 100644
index 0000000000..f3344348c2
--- /dev/null
+++ b/eth/gasprice/feehistory_test.go
@@ -0,0 +1,90 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gasprice
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+func TestFeeHistory(t *testing.T) {
+ var cases = []struct {
+ pending bool
+ maxHeader, maxBlock uint64
+ count uint64
+ last rpc.BlockNumber
+ percent []float64
+ expFirst uint64
+ expCount int
+ expErr error
+ }{
+ {false, 1000, 1000, 10, 30, nil, 21, 10, nil},
+ {false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil},
+ {false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
+ {false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil},
+ {false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
+ {false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
+ {true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
+ {false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
+ {false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
+ {false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
+ {false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
+ {false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
+ {true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
+ {true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
+ {false, 1000, 1000, 2, rpc.CommittedBlockNumber, []float64{0, 10}, 32, 1, nil},
+ }
+ for i, c := range cases {
+ config := Config{
+ MaxHeaderHistory: c.maxHeader,
+ MaxBlockHistory: c.maxBlock,
+ }
+ backend := newTestBackend(t, big.NewInt(16), c.pending)
+ oracle := NewOracle(backend, config, nil)
+
+ first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
+
+ expReward := c.expCount
+ if len(c.percent) == 0 {
+ expReward = 0
+ }
+ expBaseFee := c.expCount
+ if expBaseFee != 0 {
+ expBaseFee++
+ }
+
+ if first.Uint64() != c.expFirst {
+ t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first)
+ }
+ if len(reward) != expReward {
+ t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward))
+ }
+ if len(baseFee) != expBaseFee {
+ t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee))
+ }
+ if len(ratio) != c.expCount {
+ t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
+ }
+ if err != c.expErr && !errors.Is(err, c.expErr) {
+ t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err)
+ }
+ }
+}
diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go
index fe3a4b5111..900455fa71 100644
--- a/eth/gasprice/gasprice.go
+++ b/eth/gasprice/gasprice.go
@@ -19,11 +19,14 @@ package gasprice
import (
"context"
"math/big"
- "sort"
+ "slices"
"sync"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
+ "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/event"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rpc"
@@ -31,22 +34,28 @@ import (
const sampleNumber = 3 // Number of transactions sampled in a block
-var DefaultMaxPrice = big.NewInt(500 * params.GWei)
-var DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+var (
+ DefaultMaxPrice = big.NewInt(500 * params.GWei)
+ DefaultIgnorePrice = big.NewInt(2 * params.Wei)
+)
type Config struct {
- Blocks int
- Percentile int
- Default *big.Int `toml:",omitempty"`
- MaxPrice *big.Int `toml:",omitempty"`
- IgnorePrice *big.Int `toml:",omitempty"`
+ Blocks int
+ Percentile int
+ MaxHeaderHistory uint64
+ MaxBlockHistory uint64
+ MaxPrice *big.Int `toml:",omitempty"`
+ IgnorePrice *big.Int `toml:",omitempty"`
}
// OracleBackend includes all necessary background APIs for oracle.
type OracleBackend interface {
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
+ GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
+ PendingBlockAndReceipts() (*types.Block, types.Receipts)
ChainConfig() *params.ChainConfig
+ SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
}
// Oracle recommends gas prices based on the content of recent
@@ -60,13 +69,15 @@ type Oracle struct {
cacheLock sync.RWMutex
fetchLock sync.Mutex
- checkBlocks int
- percentile int
+ checkBlocks, percentile int
+ maxHeaderHistory, maxBlockHistory uint64
+
+ historyCache *lru.Cache[cacheKey, processedFees]
}
// NewOracle returns a new gasprice oracle which can recommend suitable
// gasprice for newly created transaction.
-func NewOracle(backend OracleBackend, params Config) *Oracle {
+func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracle {
blocks := params.Blocks
if blocks < 1 {
blocks = 1
@@ -76,8 +87,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
if percent < 0 {
percent = 0
log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
- }
- if percent > 100 {
+ } else if percent > 100 {
percent = 100
log.Warn("Sanitizing invalid gasprice oracle sample percentile", "provided", params.Percentile, "updated", percent)
}
@@ -93,47 +103,82 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
} else if ignorePrice.Int64() > 0 {
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
}
+ maxHeaderHistory := params.MaxHeaderHistory
+ if maxHeaderHistory < 1 {
+ maxHeaderHistory = 1
+ log.Warn("Sanitizing invalid gasprice oracle max header history", "provided", params.MaxHeaderHistory, "updated", maxHeaderHistory)
+ }
+ maxBlockHistory := params.MaxBlockHistory
+ if maxBlockHistory < 1 {
+ maxBlockHistory = 1
+ log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory)
+ }
+ if startPrice == nil {
+ startPrice = new(big.Int)
+ }
+
+ cache := lru.NewCache[cacheKey, processedFees](2048)
+ headEvent := make(chan core.ChainHeadEvent, 1)
+ backend.SubscribeChainHeadEvent(headEvent)
+ go func() {
+ var lastHead common.Hash
+ for ev := range headEvent {
+ if ev.Block.ParentHash() != lastHead {
+ cache.Purge()
+ }
+ lastHead = ev.Block.Hash()
+ }
+ }()
+
return &Oracle{
- backend: backend,
- lastPrice: params.Default,
- maxPrice: maxPrice,
- ignorePrice: ignorePrice,
- checkBlocks: blocks,
- percentile: percent,
+ backend: backend,
+ lastPrice: startPrice,
+ maxPrice: maxPrice,
+ ignorePrice: ignorePrice,
+ checkBlocks: blocks,
+ percentile: percent,
+ maxHeaderHistory: maxHeaderHistory,
+ maxBlockHistory: maxBlockHistory,
+ historyCache: cache,
}
}
-// SuggestPrice returns the recommended gas price.
-func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
- head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+// SuggestTipCap returns a tip cap so that newly created transaction can have a
+// very high chance to be included in the following blocks.
+//
+// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
+// necessary to add the basefee to the returned number to fall back to the legacy
+// behavior.
+func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
+ head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
headHash := head.Hash()
// If the latest gasprice is still available, return it.
- gpo.cacheLock.RLock()
- lastHead, lastPrice := gpo.lastHead, gpo.lastPrice
- gpo.cacheLock.RUnlock()
+ oracle.cacheLock.RLock()
+ lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
+ oracle.cacheLock.RUnlock()
if headHash == lastHead {
- return lastPrice, nil
+ return new(big.Int).Set(lastPrice), nil
}
- gpo.fetchLock.Lock()
- defer gpo.fetchLock.Unlock()
+ oracle.fetchLock.Lock()
+ defer oracle.fetchLock.Unlock()
// Try checking the cache again, maybe the last fetch fetched what we need
- gpo.cacheLock.RLock()
- lastHead, lastPrice = gpo.lastHead, gpo.lastPrice
- gpo.cacheLock.RUnlock()
+ oracle.cacheLock.RLock()
+ lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
+ oracle.cacheLock.RUnlock()
if headHash == lastHead {
- return lastPrice, nil
+ return new(big.Int).Set(lastPrice), nil
}
var (
sent, exp int
number = head.Number.Uint64()
- result = make(chan getBlockPricesResult, gpo.checkBlocks)
+ result = make(chan results, oracle.checkBlocks)
quit = make(chan struct{})
- txPrices []*big.Int
+ results []*big.Int
)
- for sent < gpo.checkBlocks && number > 0 {
- go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
+ for sent < oracle.checkBlocks && number > 0 {
+ go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
sent++
exp++
number--
@@ -142,99 +187,101 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
res := <-result
if res.err != nil {
close(quit)
- return lastPrice, res.err
+ return new(big.Int).Set(lastPrice), res.err
}
exp--
// Nothing returned. There are two special cases here:
// - The block is empty
// - All the transactions included are sent by the miner itself.
- // In these cases, use the latest calculated price for samping.
- if len(res.prices) == 0 {
- res.prices = []*big.Int{lastPrice}
+ // In these cases, use half of the latest calculated price for samping.
+ if len(res.values) == 0 {
+ res.values = []*big.Int{new(big.Int).Div(lastPrice, common.Big2)}
}
// Besides, in order to collect enough data for sampling, if nothing
// meaningful returned, try to query more blocks. But the maximum
// is 2*checkBlocks.
- if len(res.prices) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 {
- go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
+ if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
+ go oracle.getBlockValues(ctx, number, sampleNumber, oracle.ignorePrice, result, quit)
sent++
exp++
number--
}
- txPrices = append(txPrices, res.prices...)
+ results = append(results, res.values...)
}
price := lastPrice
- if len(txPrices) > 0 {
- sort.Sort(bigIntArray(txPrices))
- price = txPrices[(len(txPrices)-1)*gpo.percentile/100]
+ if len(results) > 0 {
+ slices.SortFunc(results, func(a, b *big.Int) int { return a.Cmp(b) })
+ price = results[(len(results)-1)*oracle.percentile/100]
}
- if price.Cmp(gpo.maxPrice) > 0 {
- price = new(big.Int).Set(gpo.maxPrice)
+ if price.Cmp(oracle.maxPrice) > 0 {
+ price = new(big.Int).Set(oracle.maxPrice)
}
- // Check gas price min.
- minGasPrice := common.GetMinGasPrice(head.Number)
- if price.Cmp(minGasPrice) < 0 {
- price = new(big.Int).Set(minGasPrice)
+ // Check min gas price for non-eip1559 block
+ if head.BaseFee == nil {
+ minGasPrice := common.GetMinGasPrice(head.Number)
+ if price.Cmp(minGasPrice) < 0 {
+ price = new(big.Int).Set(minGasPrice)
+ }
}
- gpo.cacheLock.Lock()
- gpo.lastHead = headHash
- gpo.lastPrice = price
- gpo.cacheLock.Unlock()
- return price, nil
+ oracle.cacheLock.Lock()
+ oracle.lastHead = headHash
+ oracle.lastPrice = price
+ oracle.cacheLock.Unlock()
+
+ return new(big.Int).Set(price), nil
}
-type getBlockPricesResult struct {
- prices []*big.Int
+type results struct {
+ values []*big.Int
err error
}
-type transactionsByGasPrice []*types.Transaction
-
-func (t transactionsByGasPrice) Len() int { return len(t) }
-func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
-func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPriceCmp(t[j]) < 0 }
-
-// getBlockPrices calculates the lowest transaction gas price in a given block
+// getBlockValues calculates the lowest transaction gas price in a given block
// and sends it to the result channel. If the block is empty or all transactions
// are sent by the miner itself(it doesn't make any sense to include this kind of
// transaction prices for sampling), nil gasprice is returned.
-func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan getBlockPricesResult, quit chan struct{}) {
- block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
+func (oracle *Oracle) getBlockValues(ctx context.Context, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
+ block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
if block == nil {
select {
- case result <- getBlockPricesResult{nil, err}:
+ case result <- results{nil, err}:
case <-quit:
}
return
}
- blockTxs := block.Transactions()
- txs := make([]*types.Transaction, len(blockTxs))
- copy(txs, blockTxs)
- sort.Sort(transactionsByGasPrice(txs))
+ signer := types.MakeSigner(oracle.backend.ChainConfig(), block.Number())
+
+ // Sort the transaction by effective tip in ascending sort.
+ txs := block.Transactions()
+ sortedTxs := make([]*types.Transaction, len(txs))
+ copy(sortedTxs, txs)
+ baseFee := block.BaseFee()
+ slices.SortFunc(sortedTxs, func(a, b *types.Transaction) int {
+ // It's okay to discard the error because a tx would never be
+ // accepted into a block with an invalid effective tip.
+ tip1, _ := a.EffectiveGasTip(baseFee)
+ tip2, _ := b.EffectiveGasTip(baseFee)
+ return tip1.Cmp(tip2)
+ })
var prices []*big.Int
- for _, tx := range txs {
- if ignoreUnder != nil && tx.GasPrice().Cmp(ignoreUnder) == -1 {
+ for _, tx := range sortedTxs {
+ tip, _ := tx.EffectiveGasTip(baseFee)
+ if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 {
continue
}
sender, err := types.Sender(signer, tx)
if err == nil && sender != block.Coinbase() {
- prices = append(prices, tx.GasPrice())
+ prices = append(prices, tip)
if len(prices) >= limit {
break
}
}
}
select {
- case result <- getBlockPricesResult{prices, nil}:
+ case result <- results{prices, nil}:
case <-quit:
}
}
-
-type bigIntArray []*big.Int
-
-func (s bigIntArray) Len() int { return len(s) }
-func (s bigIntArray) Less(i, j int) bool { return s[i].Cmp(s[j]) < 0 }
-func (s bigIntArray) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
new file mode 100644
index 0000000000..abaca11901
--- /dev/null
+++ b/eth/gasprice/gasprice_test.go
@@ -0,0 +1,201 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package gasprice
+
+import (
+ "context"
+ "math"
+ "math/big"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
+ "github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/core/vm"
+ "github.com/XinFinOrg/XDPoSChain/crypto"
+ "github.com/XinFinOrg/XDPoSChain/event"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+const testHead = 32
+
+type testBackend struct {
+ chain *core.BlockChain
+ pending bool // pending block available
+}
+
+func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ if number > testHead {
+ return nil, nil
+ }
+ if number == rpc.EarliestBlockNumber {
+ number = 0
+ }
+ if number == rpc.CommittedBlockNumber {
+ return b.chain.CurrentBlock().Header(), nil
+ }
+ if number == rpc.LatestBlockNumber {
+ number = testHead
+ }
+ if number == rpc.PendingBlockNumber {
+ if b.pending {
+ number = testHead + 1
+ } else {
+ return nil, nil
+ }
+ }
+ return b.chain.GetHeaderByNumber(uint64(number)), nil
+}
+
+func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
+ if number > testHead {
+ return nil, nil
+ }
+ if number == rpc.EarliestBlockNumber {
+ number = 0
+ }
+ if number == rpc.CommittedBlockNumber {
+ return b.chain.CurrentBlock(), nil
+ }
+ if number == rpc.LatestBlockNumber {
+ number = testHead
+ }
+ if number == rpc.PendingBlockNumber {
+ if b.pending {
+ number = testHead + 1
+ } else {
+ return nil, nil
+ }
+ }
+ return b.chain.GetBlockByNumber(uint64(number)), nil
+}
+
+func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
+ return b.chain.GetReceiptsByHash(hash), nil
+}
+
+func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ if b.pending {
+ block := b.chain.GetBlockByNumber(testHead + 1)
+ return block, b.chain.GetReceiptsByHash(block.Hash())
+ }
+ return nil, nil
+}
+
+func (b *testBackend) ChainConfig() *params.ChainConfig {
+ return b.chain.Config()
+}
+
+func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
+ return nil
+}
+
+func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBackend {
+ config := *params.TestChainConfig // needs copy because it is modified below
+ config.Eip1559Block = eip1559Block
+
+ var (
+ key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ addr = crypto.PubkeyToAddress(key.PublicKey)
+ gspec = &core.Genesis{
+ Config: &config,
+ Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
+ }
+ signer = types.LatestSigner(gspec.Config)
+ )
+ engine := ethash.NewFaker()
+ db := rawdb.NewMemoryDatabase()
+ genesis, _ := gspec.Commit(db)
+
+ // Generate testing blocks
+ blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) {
+ b.SetCoinbase(common.Address{1})
+
+ var txdata types.TxData
+ if eip1559Block != nil && b.Number().Cmp(eip1559Block) >= 0 {
+ txdata = &types.DynamicFeeTx{
+ ChainID: gspec.Config.ChainId,
+ Nonce: b.TxNonce(addr),
+ To: &common.Address{},
+ Gas: 30000,
+ GasFeeCap: big.NewInt(100 * params.GWei),
+ GasTipCap: big.NewInt(int64(i+1) * params.GWei),
+ Data: []byte{},
+ }
+ } else {
+ txdata = &types.LegacyTx{
+ Nonce: b.TxNonce(addr),
+ To: &common.Address{},
+ Gas: 21000,
+ GasPrice: big.NewInt(int64(i+1) * params.GWei),
+ Value: big.NewInt(100),
+ Data: []byte{},
+ }
+ }
+ b.AddTx(types.MustSignNewTx(key, signer, txdata))
+ })
+ // Construct testing chain
+ diskdb := rawdb.NewMemoryDatabase()
+ gspec.Commit(diskdb)
+ chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{})
+ if err != nil {
+ t.Fatalf("Failed to create local chain, %v", err)
+ }
+ chain.InsertChain(blocks)
+ return &testBackend{chain: chain, pending: pending}
+}
+
+func (b *testBackend) CurrentHeader() *types.Header {
+ return b.chain.CurrentHeader()
+}
+
+func (b *testBackend) GetBlockByNumber(number uint64) *types.Block {
+ return b.chain.GetBlockByNumber(number)
+}
+
+func TestSuggestTipCap(t *testing.T) {
+ config := Config{
+ Blocks: 3,
+ Percentile: 60,
+ }
+ var cases = []struct {
+ fork *big.Int // Eip1559 fork number
+ expect *big.Int // Expected gasprice suggestion
+ }{
+ {nil, big.NewInt(params.GWei * int64(30))},
+ {big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis
+ {big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block
+ {big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block
+ {big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future
+ }
+ for _, c := range cases {
+ backend := newTestBackend(t, c.fork, false)
+ oracle := NewOracle(backend, config, big.NewInt(params.GWei))
+
+ // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
+ got, err := oracle.SuggestTipCap(context.Background())
+ if err != nil {
+ t.Fatalf("Failed to retrieve recommended gas price: %v", err)
+ }
+ if got.Cmp(c.expect) != 0 {
+ t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got)
+ }
+ }
+}
diff --git a/eth/handler.go b/eth/handler.go
index c6c47503e1..7ac5c633f7 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -25,9 +25,8 @@ import (
"sync/atomic"
"time"
- lru "github.com/hashicorp/golang-lru"
-
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
@@ -104,14 +103,14 @@ type ProtocolManager struct {
// wait group is used for graceful shutdowns during downloading
// and processing
wg sync.WaitGroup
- knownTxs *lru.Cache
- knowOrderTxs *lru.Cache
- knowLendingTxs *lru.Cache
+ knownTxs *lru.Cache[common.Hash, struct{}]
+ knowOrderTxs *lru.Cache[common.Hash, struct{}]
+ knowLendingTxs *lru.Cache[common.Hash, struct{}]
// V2 messages
- knownVotes *lru.Cache
- knownSyncInfos *lru.Cache
- knownTimeouts *lru.Cache
+ knownVotes *lru.Cache[common.Hash, struct{}]
+ knownSyncInfos *lru.Cache[common.Hash, struct{}]
+ knownTimeouts *lru.Cache[common.Hash, struct{}]
}
// NewProtocolManagerEx add order pool to protocol
@@ -128,14 +127,6 @@ func NewProtocolManagerEx(config *params.ChainConfig, mode downloader.SyncMode,
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network.
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
- knownTxs, _ := lru.New(maxKnownTxs)
- knowOrderTxs, _ := lru.New(maxKnownOrderTxs)
- knowLendingTxs, _ := lru.New(maxKnownLendingTxs)
-
- knownVotes, _ := lru.New(maxKnownVote)
- knownSyncInfos, _ := lru.New(maxKnownSyncInfo)
- knownTimeouts, _ := lru.New(maxKnownTimeout)
-
// Create the protocol manager with the base fields
manager := &ProtocolManager{
networkId: networkID,
@@ -148,12 +139,12 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
noMorePeers: make(chan struct{}),
txsyncCh: make(chan *txsync),
quitSync: make(chan struct{}),
- knownTxs: knownTxs,
- knowOrderTxs: knowOrderTxs,
- knowLendingTxs: knowLendingTxs,
- knownVotes: knownVotes,
- knownSyncInfos: knownSyncInfos,
- knownTimeouts: knownTimeouts,
+ knownTxs: lru.NewCache[common.Hash, struct{}](maxKnownTxs),
+ knowOrderTxs: lru.NewCache[common.Hash, struct{}](maxKnownOrderTxs),
+ knowLendingTxs: lru.NewCache[common.Hash, struct{}](maxKnownLendingTxs),
+ knownVotes: lru.NewCache[common.Hash, struct{}](maxKnownVote),
+ knownSyncInfos: lru.NewCache[common.Hash, struct{}](maxKnownSyncInfo),
+ knownTimeouts: lru.NewCache[common.Hash, struct{}](maxKnownTimeout),
orderpool: nil,
lendingpool: nil,
orderTxSub: nil,
@@ -266,9 +257,11 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
func (pm *ProtocolManager) addOrderPoolProtocol(orderpool orderPool) {
pm.orderpool = orderpool
}
+
func (pm *ProtocolManager) addLendingPoolProtocol(lendingpool lendingPool) {
pm.lendingpool = lendingpool
}
+
func (pm *ProtocolManager) removePeer(id string) {
// Short circuit if the peer was already removed
peer := pm.peers.Peer(id)
@@ -283,9 +276,7 @@ func (pm *ProtocolManager) removePeer(id string) {
log.Debug("Peer removal failed", "peer", id, "err", err)
}
// Hard disconnect at the networking layer
- if peer != nil {
- peer.Peer.Disconnect(p2p.DiscUselessPeer)
- }
+ peer.Peer.Disconnect(p2p.DiscUselessPeer)
}
func (pm *ProtocolManager) Start(maxPeers int) {
@@ -769,7 +760,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&txs); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
- var unkownTxs []*types.Transaction
for i, tx := range txs {
// Validate and mark the remote transaction
@@ -777,11 +767,10 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "transaction %d is nil", i)
}
p.MarkTransaction(tx.Hash())
- exist, _ := pm.knownTxs.ContainsOrAdd(tx.Hash(), true)
- if !exist {
- unkownTxs = append(unkownTxs, tx)
- } else {
+ if pm.knownTxs.Contains(tx.Hash()) {
log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce(), "to", tx.To())
+ } else {
+ pm.knownTxs.Add(tx.Hash(), struct{}{})
}
}
@@ -797,7 +786,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&txs); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
- var unkownOrderTxs []*types.OrderTransaction
for i, tx := range txs {
// Validate and mark the remote transaction
@@ -805,13 +793,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "transaction %d is nil", i)
}
p.MarkOrderTransaction(tx.Hash())
- exist, _ := pm.knowOrderTxs.ContainsOrAdd(tx.Hash(), true)
- if !exist {
- unkownOrderTxs = append(unkownOrderTxs, tx)
- } else {
+ if pm.knowOrderTxs.Contains(tx.Hash()) {
log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce())
+ } else {
+ pm.knowOrderTxs.Add(tx.Hash(), struct{}{})
}
-
}
if pm.orderpool != nil {
@@ -828,7 +814,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&txs); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
- var unkownLendingTxs []*types.LendingTransaction
for i, tx := range txs {
// Validate and mark the remote transaction
@@ -836,13 +821,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "transaction %d is nil", i)
}
p.MarkLendingTransaction(tx.Hash())
- exist, _ := pm.knowLendingTxs.ContainsOrAdd(tx.Hash(), true)
- if !exist {
- unkownLendingTxs = append(unkownLendingTxs, tx)
- } else {
+ if pm.knowLendingTxs.Contains(tx.Hash()) {
log.Trace("Discard known tx", "hash", tx.Hash(), "nonce", tx.Nonce())
+ } else {
+ pm.knowLendingTxs.Add(tx.Hash(), struct{}{})
}
-
}
if pm.lendingpool != nil {
@@ -859,11 +842,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
p.MarkVote(vote.Hash())
- exist, _ := pm.knownVotes.ContainsOrAdd(vote.Hash(), true)
- if !exist {
- go pm.bft.Vote(p.id, &vote)
- } else {
+ if pm.knownVotes.Contains(vote.Hash()) {
log.Trace("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round)
+ } else {
+ pm.knownVotes.Add(vote.Hash(), struct{}{})
+ go pm.bft.Vote(p.id, &vote)
}
case msg.Code == TimeoutMsg:
@@ -877,12 +860,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
p.MarkTimeout(timeout.Hash())
- exist, _ := pm.knownTimeouts.ContainsOrAdd(timeout.Hash(), true)
-
- if !exist {
- go pm.bft.Timeout(p.id, &timeout)
- } else {
+ if pm.knownTimeouts.Contains(timeout.Hash()) {
log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round)
+ } else {
+ pm.knownTimeouts.Add(timeout.Hash(), struct{}{})
+ go pm.bft.Timeout(p.id, &timeout)
}
case msg.Code == SyncInfoMsg:
@@ -896,11 +878,11 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
p.MarkSyncInfo(syncInfo.Hash())
- exist, _ := pm.knownSyncInfos.ContainsOrAdd(syncInfo.Hash(), true)
- if !exist {
- go pm.bft.SyncInfo(p.id, &syncInfo)
- } else {
+ if pm.knownSyncInfos.Contains(syncInfo.Hash()) {
log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash())
+ } else {
+ pm.knownSyncInfos.Add(syncInfo.Hash(), struct{}{})
+ go pm.bft.SyncInfo(p.id, &syncInfo)
}
default:
@@ -1037,12 +1019,12 @@ func (pm *ProtocolManager) LendingBroadcastTx(hash common.Hash, tx *types.Lendin
}
// minedBroadcastLoop broadcast loop
-func (self *ProtocolManager) minedBroadcastLoop() {
+func (pm *ProtocolManager) minedBroadcastLoop() {
// automatically stops if unsubscribe
- for obj := range self.minedBlockSub.Chan() {
+ for obj := range pm.minedBlockSub.Chan() {
switch ev := obj.Data.(type) {
case core.NewMinedBlockEvent:
- self.BroadcastBlock(ev.Block, true) // First propagate block to peers
+ pm.BroadcastBlock(ev.Block, true) // First propagate block to peers
//self.BroadcastBlock(ev.Block, false) // Only then announce to the rest
}
}
@@ -1062,34 +1044,34 @@ func (pm *ProtocolManager) txBroadcastLoop() {
}
// orderTxBroadcastLoop broadcast order
-func (self *ProtocolManager) orderTxBroadcastLoop() {
- if self.orderTxSub == nil {
+func (pm *ProtocolManager) orderTxBroadcastLoop() {
+ if pm.orderTxSub == nil {
return
}
for {
select {
- case event := <-self.orderTxCh:
- self.OrderBroadcastTx(event.Tx.Hash(), event.Tx)
+ case event := <-pm.orderTxCh:
+ pm.OrderBroadcastTx(event.Tx.Hash(), event.Tx)
// Err() channel will be closed when unsubscribing.
- case <-self.orderTxSub.Err():
+ case <-pm.orderTxSub.Err():
return
}
}
}
// lendingTxBroadcastLoop broadcast order
-func (self *ProtocolManager) lendingTxBroadcastLoop() {
- if self.lendingTxSub == nil {
+func (pm *ProtocolManager) lendingTxBroadcastLoop() {
+ if pm.lendingTxSub == nil {
return
}
for {
select {
- case event := <-self.lendingTxCh:
- self.LendingBroadcastTx(event.Tx.Hash(), event.Tx)
+ case event := <-pm.lendingTxCh:
+ pm.LendingBroadcastTx(event.Tx.Hash(), event.Tx)
// Err() channel will be closed when unsubscribing.
- case <-self.lendingTxSub.Err():
+ case <-pm.lendingTxSub.Err():
return
}
}
@@ -1106,13 +1088,13 @@ type NodeInfo struct {
}
// NodeInfo retrieves some protocol metadata about the running host node.
-func (self *ProtocolManager) NodeInfo() *NodeInfo {
- currentBlock := self.blockchain.CurrentBlock()
+func (pm *ProtocolManager) NodeInfo() *NodeInfo {
+ currentBlock := pm.blockchain.CurrentBlock()
return &NodeInfo{
- Network: self.networkId,
- Difficulty: self.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
- Genesis: self.blockchain.Genesis().Hash(),
- Config: self.blockchain.Config(),
+ Network: pm.networkId,
+ Difficulty: pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
+ Genesis: pm.blockchain.Genesis().Hash(),
+ Config: pm.blockchain.Config(),
Head: currentBlock.Hash(),
}
}
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 4963093c1a..0b793d09bc 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -111,7 +111,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
}
// Pending returns all the transactions known to the pool
-func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
+func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
p.lock.RLock()
defer p.lock.RUnlock()
@@ -123,7 +123,7 @@ func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
for _, batch := range batches {
sort.Sort(types.TxByNonce(batch))
}
- return batches, nil
+ return batches
}
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go
index 65c84d1c30..74f55ed278 100644
--- a/eth/hooks/engine_v1_hooks.go
+++ b/eth/hooks/engine_v1_hooks.go
@@ -30,43 +30,42 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
log.Crit("Can't get state at head of canonical chain", "head number", bc.CurrentHeader().Number.Uint64(), "err", err)
}
prevEpoc := blockNumberEpoc - chain.Config().XDPoS.Epoch
- if prevEpoc >= 0 {
- start := time.Now()
- prevHeader := chain.GetHeaderByNumber(prevEpoc)
- penSigners := adaptor.GetMasternodes(chain, prevHeader)
- if len(penSigners) > 0 {
- // Loop for each block to check missing sign.
- for i := prevEpoc; i < blockNumberEpoc; i++ {
- if i%common.MergeSignRange == 0 || !chainConfig.IsTIP2019(big.NewInt(int64(i))) {
- bheader := chain.GetHeaderByNumber(i)
- bhash := bheader.Hash()
- block := chain.GetBlock(bhash, i)
- if len(penSigners) > 0 {
- signedMasternodes, err := contracts.GetSignersFromContract(canonicalState, block)
- if err != nil {
- return nil, err
- }
- if len(signedMasternodes) > 0 {
- // Check signer signed?
- for _, signed := range signedMasternodes {
- for j, addr := range penSigners {
- if signed == addr {
- // Remove it from dupSigners.
- penSigners = append(penSigners[:j], penSigners[j+1:]...)
- }
+
+ start := time.Now()
+ prevHeader := chain.GetHeaderByNumber(prevEpoc)
+ penSigners := adaptor.GetMasternodes(chain, prevHeader)
+ if len(penSigners) > 0 {
+ // Loop for each block to check missing sign.
+ for i := prevEpoc; i < blockNumberEpoc; i++ {
+ if i%common.MergeSignRange == 0 || !chainConfig.IsTIP2019(big.NewInt(int64(i))) {
+ bheader := chain.GetHeaderByNumber(i)
+ bhash := bheader.Hash()
+ block := chain.GetBlock(bhash, i)
+ if len(penSigners) > 0 {
+ signedMasternodes, err := contracts.GetSignersFromContract(canonicalState, block)
+ if err != nil {
+ return nil, err
+ }
+ if len(signedMasternodes) > 0 {
+ // Check signer signed?
+ for _, signed := range signedMasternodes {
+ for j, addr := range penSigners {
+ if signed == addr {
+ // Remove it from dupSigners.
+ penSigners = append(penSigners[:j], penSigners[j+1:]...)
}
}
}
- } else {
- break
}
+ } else {
+ break
}
}
}
- log.Debug("Time Calculated HookPenalty ", "block", blockNumberEpoc, "time", common.PrettyDuration(time.Since(start)))
- return penSigners, nil
}
- return []common.Address{}, nil
+ log.Debug("Time Calculated HookPenalty ", "block", blockNumberEpoc, "time", common.PrettyDuration(time.Since(start)))
+ return penSigners, nil
+
}
// Hook scans for bad masternodes and decide to penalty them
@@ -77,105 +76,103 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
if header.Number.Uint64() > comebackLength {
combackEpoch = header.Number.Uint64() - comebackLength
}
- if prevEpoc >= 0 {
- start := time.Now()
- listBlockHash := make([]common.Hash, chain.Config().XDPoS.Epoch)
+ start := time.Now()
- // get list block hash & stats total created block
- statMiners := make(map[common.Address]int)
- listBlockHash[0] = header.ParentHash
- parentnumber := header.Number.Uint64() - 1
- parentHash := header.ParentHash
- for i := uint64(1); i < chain.Config().XDPoS.Epoch; i++ {
- parentHeader := chain.GetHeader(parentHash, parentnumber)
- miner, _ := adaptor.RecoverSigner(parentHeader)
- value, exist := statMiners[miner]
- if exist {
- value = value + 1
- } else {
- value = 1
- }
- statMiners[miner] = value
- parentHash = parentHeader.ParentHash
- parentnumber--
- listBlockHash[i] = parentHash
+ listBlockHash := make([]common.Hash, chain.Config().XDPoS.Epoch)
+
+ // get list block hash & stats total created block
+ statMiners := make(map[common.Address]int)
+ listBlockHash[0] = header.ParentHash
+ parentnumber := header.Number.Uint64() - 1
+ parentHash := header.ParentHash
+ for i := uint64(1); i < chain.Config().XDPoS.Epoch; i++ {
+ parentHeader := chain.GetHeader(parentHash, parentnumber)
+ miner, _ := adaptor.RecoverSigner(parentHeader)
+ value, exist := statMiners[miner]
+ if exist {
+ value = value + 1
+ } else {
+ value = 1
}
+ statMiners[miner] = value
+ parentHash = parentHeader.ParentHash
+ parentnumber--
+ listBlockHash[i] = parentHash
+ }
- // add list not miner to penalties
- prevHeader := chain.GetHeaderByNumber(prevEpoc)
- preMasternodes := adaptor.GetMasternodes(chain, prevHeader)
- penalties := []common.Address{}
- for miner, total := range statMiners {
- if total < common.MinimunMinerBlockPerEpoch {
- log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total)
- penalties = append(penalties, miner)
- }
+ // add list not miner to penalties
+ prevHeader := chain.GetHeaderByNumber(prevEpoc)
+ preMasternodes := adaptor.GetMasternodes(chain, prevHeader)
+ penalties := []common.Address{}
+ for miner, total := range statMiners {
+ if total < common.MinimunMinerBlockPerEpoch {
+ log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total)
+ penalties = append(penalties, miner)
}
- for _, addr := range preMasternodes {
- if _, exist := statMiners[addr]; !exist {
- log.Debug("Find a node don't create block", "addr", addr.Hex())
- penalties = append(penalties, addr)
- }
+ }
+ for _, addr := range preMasternodes {
+ if _, exist := statMiners[addr]; !exist {
+ log.Debug("Find a node don't create block", "addr", addr.Hex())
+ penalties = append(penalties, addr)
}
+ }
- // get list check penalties signing block & list master nodes wil comeback
- penComebacks := []common.Address{}
- if combackEpoch > 0 {
- combackHeader := chain.GetHeaderByNumber(combackEpoch)
- penalties := common.ExtractAddressFromBytes(combackHeader.Penalties)
- for _, penaltie := range penalties {
- for _, addr := range candidates {
- if penaltie == addr {
- penComebacks = append(penComebacks, penaltie)
- }
+ // get list check penalties signing block & list master nodes wil comeback
+ penComebacks := []common.Address{}
+ if combackEpoch > 0 {
+ combackHeader := chain.GetHeaderByNumber(combackEpoch)
+ penalties := common.ExtractAddressFromBytes(combackHeader.Penalties)
+ for _, penaltie := range penalties {
+ for _, addr := range candidates {
+ if penaltie == addr {
+ penComebacks = append(penComebacks, penaltie)
}
}
}
+ }
- // Loop for each block to check missing sign. with comeback nodes
- mapBlockHash := map[common.Hash]bool{}
- for i := common.RangeReturnSigner - 1; i >= 0; i-- {
- if len(penComebacks) > 0 {
- blockNumber := header.Number.Uint64() - uint64(i) - 1
- bhash := listBlockHash[i]
- if blockNumber%common.MergeSignRange == 0 {
- mapBlockHash[bhash] = true
- }
- signData, ok := adaptor.GetCachedSigningTxs(bhash)
- if !ok {
- block := chain.GetBlock(bhash, blockNumber)
- txs := block.Transactions()
- signData = adaptor.CacheSigningTxs(bhash, txs)
- }
- txs := signData.([]*types.Transaction)
- // Check signer signed?
- for _, tx := range txs {
- blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
- from := *tx.From()
- if mapBlockHash[blkHash] {
- for j, addr := range penComebacks {
- if from == addr {
- // Remove it from dupSigners.
- penComebacks = append(penComebacks[:j], penComebacks[j+1:]...)
- break
- }
+ // Loop for each block to check missing sign. with comeback nodes
+ mapBlockHash := map[common.Hash]bool{}
+ for i := common.RangeReturnSigner - 1; i >= 0; i-- {
+ if len(penComebacks) > 0 {
+ blockNumber := header.Number.Uint64() - uint64(i) - 1
+ bhash := listBlockHash[i]
+ if blockNumber%common.MergeSignRange == 0 {
+ mapBlockHash[bhash] = true
+ }
+ signingTxs, ok := adaptor.GetCachedSigningTxs(bhash)
+ if !ok {
+ block := chain.GetBlock(bhash, blockNumber)
+ txs := block.Transactions()
+ signingTxs = adaptor.CacheSigningTxs(bhash, txs)
+ }
+ // Check signer signed?
+ for _, tx := range signingTxs {
+ blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
+ from := *tx.From()
+ if mapBlockHash[blkHash] {
+ for j, addr := range penComebacks {
+ if from == addr {
+ // Remove it from dupSigners.
+ penComebacks = append(penComebacks[:j], penComebacks[j+1:]...)
+ break
}
}
}
- } else {
- break
}
+ } else {
+ break
}
-
- log.Debug("Time Calculated HookPenaltyTIPSigning ", "block", header.Number, "hash", header.Hash().Hex(), "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start)))
- penalties = append(penalties, penComebacks...)
- if chain.Config().IsTIPRandomize(header.Number) {
- return penalties, nil
- }
- return penComebacks, nil
}
- return []common.Address{}, nil
+
+ log.Debug("Time Calculated HookPenaltyTIPSigning ", "block", header.Number, "hash", header.Hash().Hex(), "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start)))
+ penalties = append(penalties, penComebacks...)
+ if chain.Config().IsTIPRandomize(header.Number) {
+ return penalties, nil
+ }
+
+ return penComebacks, nil
}
// Hook prepares validators M2 for the current epoch at checkpoint block
@@ -258,13 +255,13 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
}
// Hook calculates reward for masternodes
- adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{}) {
+ adaptor.EngineV1.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, parentState *state.StateDB, header *types.Header) (map[string]interface{}, error) {
number := header.Number.Uint64()
rCheckpoint := chain.Config().XDPoS.RewardCheckpoint
foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr
if foundationWalletAddr == (common.Address{}) {
log.Error("Foundation Wallet Address is empty", "error", foundationWalletAddr)
- return errors.New("Foundation Wallet Address is empty"), nil
+ return nil, errors.New("foundation Wallet Address is empty")
}
rewards := make(map[string]interface{})
if number > 0 && number-rCheckpoint > 0 && foundationWalletAddr != (common.Address{}) {
@@ -290,7 +287,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
voterResults := make(map[common.Address]interface{})
if len(signers) > 0 {
for signer, calcReward := range rewardSigners {
- err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
+ rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
if err != nil {
log.Crit("Fail to calculate reward for holders.", "error", err)
}
@@ -305,7 +302,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
rewards["rewards"] = voterResults
log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start)))
}
- return nil, rewards
+ return rewards, nil
}
}
diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go
index b725811b54..bd5b0ffdd1 100644
--- a/eth/hooks/engine_v2_hooks.go
+++ b/eth/hooks/engine_v2_hooks.go
@@ -115,15 +115,14 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
if blockNumber%common.MergeSignRange == 0 {
mapBlockHash[bhash] = true
}
- signData, ok := adaptor.GetCachedSigningTxs(bhash)
+ signingTxs, ok := adaptor.GetCachedSigningTxs(bhash)
if !ok {
block := chain.GetBlock(bhash, blockNumber)
txs := block.Transactions()
- signData = adaptor.CacheSigningTxs(bhash, txs)
+ signingTxs = adaptor.CacheSigningTxs(bhash, txs)
}
- txs := signData.([]*types.Transaction)
// Check signer signed?
- for _, tx := range txs {
+ for _, tx := range signingTxs {
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
from := *tx.From()
if mapBlockHash[blkHash] {
@@ -195,7 +194,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
voterResults := make(map[common.Address]interface{})
if len(signers) > 0 {
for signer, calcReward := range rewardSigners {
- err, rewards := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
+ rewards, err := contracts.CalculateRewardForHolders(foundationWalletAddr, parentState, signer, calcReward, number)
if err != nil {
log.Error("[HookReward] Fail to calculate reward for holders.", "error", err)
return nil, err
@@ -245,15 +244,14 @@ func GetSigningTxCount(c *XDPoS.XDPoS, chain consensus.ChainReader, header *type
}
}
mapBlkHash[i] = header.Hash()
- signData, ok := c.GetCachedSigningTxs(header.Hash())
+ signingTxs, ok := c.GetCachedSigningTxs(header.Hash())
if !ok {
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
block := chain.GetBlock(header.Hash(), i)
txs := block.Transactions()
- signData = c.CacheSigningTxs(header.Hash(), txs)
+ signingTxs = c.CacheSigningTxs(header.Hash(), txs)
}
- txs := signData.([]*types.Transaction)
- for _, tx := range txs {
+ for _, tx := range signingTxs {
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
from := *tx.From()
data[blkHash] = append(data[blkHash], from)
diff --git a/eth/metrics.go b/eth/metrics.go
index 2f3bd6bfb8..59d9258a06 100644
--- a/eth/metrics.go
+++ b/eth/metrics.go
@@ -66,7 +66,7 @@ type meteredMsgReadWriter struct {
// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the
// metrics system is disabled, this function returns the original object.
func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter {
- if !metrics.Enabled {
+ if !metrics.Enabled() {
return rw
}
return &meteredMsgReadWriter{MsgReadWriter: rw}
diff --git a/eth/protocol.go b/eth/protocol.go
index eb7297a28c..6cba0e1eba 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -108,7 +108,7 @@ type txPool interface {
// Pending should return pending transactions.
// The slice should be modifiable by the caller.
- Pending() (map[common.Address]types.Transactions, error)
+ Pending(enforceTips bool) map[common.Address]types.Transactions
// SubscribeNewTxsEvent should return an event subscription of
// NewTxsEvent and send events to the given channel.
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
index 8c5283cd8b..858e1da5aa 100644
--- a/eth/protocol_test.go
+++ b/eth/protocol_test.go
@@ -32,7 +32,7 @@ import (
)
func init() {
- // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+ // log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
}
var testAccount, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
diff --git a/eth/sync.go b/eth/sync.go
index cbe6d421c8..5a0ea512f5 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -45,7 +45,7 @@ type txsync struct {
// syncTransactions starts sending all currently pending transactions to the given peer.
func (pm *ProtocolManager) syncTransactions(p *peer) {
var txs types.Transactions
- pending, _ := pm.txpool.Pending()
+ pending := pm.txpool.Pending(false)
for _, batch := range pending {
txs = append(txs, batch...)
}
diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go
index 185967bdb6..da9c983243 100644
--- a/eth/tracers/internal/tracers/assets.go
+++ b/eth/tracers/internal/tracers/assets.go
@@ -20,7 +20,6 @@ import (
"crypto/sha256"
"fmt"
"io"
- "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -303,15 +302,15 @@ func unigram_tracerJs() (*asset, error) {
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err)
}
return a.bytes, nil
}
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
// AssetString returns the asset contents as a string (instead of a []byte).
@@ -341,29 +340,29 @@ func MustAssetString(name string) string {
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err)
}
return a.info, nil
}
- return nil, fmt.Errorf("AssetInfo %s not found", name)
+ return nil, fmt.Errorf("not found AssetInfo %s", name)
}
// AssetDigest returns the digest of the file with the given name. It returns an
// error if the asset could not be found or the digest could not be loaded.
func AssetDigest(name string) ([sha256.Size]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
if f, ok := _bindata[canonicalName]; ok {
a, err := f()
if err != nil {
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
+ return [sha256.Size]byte{}, fmt.Errorf("can't read AssetDigest %s by error: %v", name, err)
}
return a.digest, nil
}
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
+ return [sha256.Size]byte{}, fmt.Errorf("not found AssetDigest %s", name)
}
// Digests returns a map of all known files and their checksums.
@@ -422,17 +421,17 @@ const AssetDebug = false
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
pathList := strings.Split(canonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
}
}
if node.Func != nil {
- return nil, fmt.Errorf("Asset %s not found", name)
+ return nil, fmt.Errorf("not found Asset %s", name)
}
rv := make([]string, 0, len(node.Children))
for childName := range node.Children {
@@ -474,7 +473,7 @@ func RestoreAsset(dir, name string) error {
if err != nil {
return err
}
- err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
+ err = os.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
@@ -499,6 +498,6 @@ func RestoreAssets(dir, name string) error {
}
func _filePath(dir, name string) string {
- canonicalName := strings.Replace(name, "\\", "/", -1)
+ canonicalName := strings.ReplaceAll(name, "\\", "/")
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
}
diff --git a/eth/tracers/testing/calltrace_test.go b/eth/tracers/testing/calltrace_test.go
index 9db4702519..35d9407cd9 100644
--- a/eth/tracers/testing/calltrace_test.go
+++ b/eth/tracers/testing/calltrace_test.go
@@ -2,8 +2,8 @@ package testing
import (
"encoding/json"
- "io/ioutil"
"math/big"
+ "os"
"path/filepath"
"reflect"
"strings"
@@ -66,7 +66,7 @@ func TestCallTracer(t *testing.T) {
}
func testCallTracer(tracerName string, dirPath string, t *testing.T) {
- files, err := ioutil.ReadDir(filepath.Join("..", "testdata", dirPath))
+ files, err := os.ReadDir(filepath.Join("..", "testdata", dirPath))
if err != nil {
t.Fatalf("failed to retrieve tracer test suite: %v", err)
}
@@ -83,7 +83,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
tx = new(types.Transaction)
)
// Call tracer test found, read if from disk
- if blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil {
+ if blob, err := os.ReadFile(filepath.Join("..", "testdata", dirPath, file.Name())); err != nil {
t.Fatalf("failed to read testcase: %v", err)
} else if err := json.Unmarshal(blob, test); err != nil {
t.Fatalf("failed to parse testcase: %v", err)
@@ -95,7 +95,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
var (
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
origin, _ = signer.Sender(tx)
- context = vm.Context{
+ txContext = vm.TxContext{
+ Origin: origin,
+ GasPrice: tx.GasPrice(),
+ }
+ context = vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: test.Context.Miner,
@@ -103,8 +107,6 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
- Origin: origin,
- GasPrice: tx.GasPrice(),
}
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
)
@@ -112,13 +114,13 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
- msg, err := tx.AsMessage(signer, nil, nil)
+ evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil, nil, nil)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
+ if _, err, _ = st.TransitionDb(common.Address{}); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
// Retrieve the trace result and compare against the etalon
@@ -169,7 +171,7 @@ func camel(str string) string {
return strings.Join(pieces, "")
}
func BenchmarkTracers(b *testing.B) {
- files, err := ioutil.ReadDir(filepath.Join("..", "testdata", "call_tracer"))
+ files, err := os.ReadDir(filepath.Join("..", "testdata", "call_tracer"))
if err != nil {
b.Fatalf("failed to retrieve tracer test suite: %v", err)
}
@@ -179,7 +181,7 @@ func BenchmarkTracers(b *testing.B) {
}
file := file // capture range variable
b.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(b *testing.B) {
- blob, err := ioutil.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name()))
+ blob, err := os.ReadFile(filepath.Join("..", "testdata", "call_tracer", file.Name()))
if err != nil {
b.Fatalf("failed to read testcase: %v", err)
}
@@ -199,12 +201,16 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.Fatalf("failed to parse testcase input: %v", err)
}
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
- msg, err := tx.AsMessage(signer, nil, nil)
+ msg, err := tx.AsMessage(signer, nil, nil, nil)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
origin, _ := signer.Sender(tx)
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: tx.GasPrice(),
+ }
+ context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: test.Context.Miner,
@@ -212,8 +218,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
- Origin: origin,
- GasPrice: tx.GasPrice(),
}
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
@@ -224,10 +228,10 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+ evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
+ if _, err, _ = st.TransitionDb(common.Address{}); err != nil {
b.Fatalf("failed to execute transaction: %v", err)
}
if _, err = tracer.GetResult(); err != nil {
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index bbec5ad8a9..2fa04cd1ad 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -670,7 +670,7 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
jst.ctx["to"] = to
jst.ctx["input"] = input
jst.ctx["gas"] = gas
- jst.ctx["gasPrice"] = env.Context.GasPrice
+ jst.ctx["gasPrice"] = env.TxContext.GasPrice
jst.ctx["value"] = value
// Initialize the context
@@ -682,9 +682,10 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
// Compute intrinsic gas
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
+ isEIP1559 := env.ChainConfig().IsEIP1559(env.Context.BlockNumber)
// after update core.IntrinsicGas, use isIstanbul in it
// isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
- intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead)
+ intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isEIP1559)
if err != nil {
return
}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index d1b81cfbc6..7721dbed30 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -51,19 +51,20 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) }
type vmContext struct {
- ctx vm.Context // future pr should distinguish blockContext and txContext
+ ctx vm.BlockContext
+ txContext vm.TxContext
}
-func testCtx() *vmContext {
- return &vmContext{ctx: vm.Context{BlockNumber: big.NewInt(1), GasPrice: big.NewInt(100000)}}
-}
-
-func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
- env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer})
+func runTrace(tracer Tracer, blockNumber *big.Int, chaincfg *params.ChainConfig) (json.RawMessage, error) {
var (
- startGas uint64 = 10000
- value = big.NewInt(0)
+ startGas uint64 = 10000
+ value = big.NewInt(0)
+ ctx = vm.BlockContext{BlockNumber: blockNumber}
+ txContext = vm.TxContext{GasPrice: big.NewInt(100000)}
)
+
+ env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Debug: true, Tracer: tracer})
+
contract := vm.NewContract(account{}, account{}, value, startGas)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@@ -83,7 +84,7 @@ func TestTracer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
if err != nil {
return nil, err.Error() // Stringify to allow comparison without nil checks
}
@@ -138,7 +139,7 @@ func TestHalt(t *testing.T) {
time.Sleep(1 * time.Second)
tracer.Stop(timeout)
}()
- if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
+ if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err.Error() != "stahp in server-side tracer function 'step'" {
t.Errorf("Expected timeout error, got %v", err)
}
}
@@ -148,7 +149,7 @@ func TestHaltBetweenSteps(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
}
@@ -165,8 +166,10 @@ func TestHaltBetweenSteps(t *testing.T) {
// TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb
// in 'result'
func TestNoStepExec(t *testing.T) {
- runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) {
- env := vm.NewEVM(vmctx.ctx, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ runEmptyTrace := func(tracer Tracer) (json.RawMessage, error) {
+ ctx := vm.BlockContext{BlockNumber: big.NewInt(1)}
+ txContext := vm.TxContext{GasPrice: big.NewInt(100000)}
+ env := vm.NewEVM(ctx, txContext, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
startGas := uint64(10000)
contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas)
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, big.NewInt(0))
@@ -179,7 +182,7 @@ func TestNoStepExec(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- ret, err := runEmptyTrace(tracer, testCtx())
+ ret, err := runEmptyTrace(tracer)
if err != nil {
t.Fatal(err)
}
@@ -205,12 +208,11 @@ func TestIsPrecompile(t *testing.T) {
chaincfg.ByzantiumBlock = big.NewInt(100)
chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300)
- ctx := vm.Context{BlockNumber: big.NewInt(150), GasPrice: big.NewInt(100000)}
tracer, err := New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
if err != nil {
t.Fatal(err)
}
- res, err := runTrace(tracer, &vmContext{ctx}, chaincfg)
+ res, err := runTrace(tracer, big.NewInt(150), chaincfg)
if err != nil {
t.Error(err)
}
@@ -219,8 +221,7 @@ func TestIsPrecompile(t *testing.T) {
}
tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context))
- ctx = vm.Context{BlockNumber: big.NewInt(250), GasPrice: big.NewInt(100000)}
- res, err = runTrace(tracer, &vmContext{ctx}, chaincfg)
+ res, err = runTrace(tracer, big.NewInt(250), chaincfg)
if err != nil {
t.Error(err)
}
@@ -267,7 +268,7 @@ func TestRegressionPanicSlice(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil {
+ if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
t.Fatal(err)
}
}
@@ -278,7 +279,7 @@ func TestRegressionPanicPeek(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil {
+ if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
t.Fatal(err)
}
}
@@ -289,7 +290,7 @@ func TestRegressionPanicGetUint(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if _, err = runTrace(tracer, testCtx(), params.TestChainConfig); err != nil {
+ if _, err = runTrace(tracer, big.NewInt(1), params.TestChainConfig); err != nil {
t.Fatal(err)
}
}
@@ -300,7 +301,7 @@ func TestTracing(t *testing.T) {
t.Fatal(err)
}
- ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
if err != nil {
t.Fatal(err)
}
@@ -315,7 +316,7 @@ func TestStack(t *testing.T) {
t.Fatal(err)
}
- ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
if err != nil {
t.Fatal(err)
}
@@ -330,7 +331,7 @@ func TestOpcodes(t *testing.T) {
t.Fatal(err)
}
- ret, err := runTrace(tracer, testCtx(), params.TestChainConfig)
+ ret, err := runTrace(tracer, big.NewInt(1), params.TestChainConfig)
if err != nil {
t.Fatal(err)
}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 74c3ff2845..4b3198c95e 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -118,7 +118,11 @@ func TestZeroValueToNotExitCall(t *testing.T) {
t.Fatalf("err %v", err)
}
origin, _ := signer.Sender(tx)
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: big.NewInt(1),
+ }
+ context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: common.Address{},
@@ -126,8 +130,6 @@ func TestZeroValueToNotExitCall(t *testing.T) {
Time: new(big.Int).SetUint64(5),
Difficulty: big.NewInt(0x30000),
GasLimit: uint64(6000000),
- Origin: origin,
- GasPrice: big.NewInt(1),
}
var code = []byte{
byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero
@@ -150,13 +152,13 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
- msg, err := tx.AsMessage(signer, nil, nil)
+ evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil, nil, nil)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
+ if _, err, _ = st.TransitionDb(common.Address{}); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
// Retrieve the trace result and compare against the etalon
@@ -200,16 +202,18 @@ func TestPrestateTracerCreate2(t *testing.T) {
result: 0x60f3f640a8508fC6a86d45DF051962668E1e8AC7
*/
origin, _ := signer.Sender(tx)
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: big.NewInt(1),
+ }
+ context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
- Origin: origin,
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(8000000),
Time: new(big.Int).SetUint64(5),
Difficulty: big.NewInt(0x30000),
GasLimit: uint64(6000000),
- GasPrice: big.NewInt(1),
}
alloc := core.GenesisAlloc{}
@@ -233,14 +237,14 @@ func TestPrestateTracerCreate2(t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
- msg, err := tx.AsMessage(signer, nil, nil)
+ msg, err := tx.AsMessage(signer, nil, nil, nil)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
+ if _, err, _ = st.TransitionDb(common.Address{}); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
// Retrieve the trace result and compare against the etalon
@@ -291,7 +295,11 @@ func BenchmarkTransactionTrace(b *testing.B) {
if err != nil {
b.Fatal(err)
}
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: from,
+ GasPrice: tx.GasPrice(),
+ }
+ context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: common.Address{},
@@ -300,8 +308,6 @@ func BenchmarkTransactionTrace(b *testing.B) {
Difficulty: big.NewInt(0xffffffff),
GasLimit: gas,
// BaseFee: big.NewInt(8),
- Origin: from,
- GasPrice: tx.GasPrice(),
}
alloc := core.GenesisAlloc{}
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
@@ -329,8 +335,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
//EnableMemory: false,
//EnableReturnData: false,
})
- evm := vm.NewEVM(context, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
- msg, err := tx.AsMessage(signer, nil, nil)
+ evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, nil, nil, nil)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
@@ -340,7 +346,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
for i := 0; i < b.N; i++ {
snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
- _, _, _, err, _ = st.TransitionDb(common.Address{})
+ _, err, _ = st.TransitionDb(common.Address{})
if err != nil {
b.Fatal(err)
}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 7e3a439327..936c081534 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -75,7 +75,7 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb
var r []*types.Receipt
err := ec.c.CallContext(ctx, &r, "eth_getBlockReceipts", blockNrOrHash)
if err == nil && r == nil {
- return nil, ethereum.NotFound
+ return nil, ethereum.ErrNotFound
}
return r, err
}
@@ -92,7 +92,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
if err != nil {
return nil, err
} else if len(raw) == 0 {
- return nil, ethereum.NotFound
+ return nil, ethereum.ErrNotFound
}
// Decode header and transactions.
var head *types.Header
@@ -110,10 +110,10 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 {
return nil, errors.New("server returned empty uncle list but block header indicates uncles")
}
- if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 {
+ if head.TxHash == types.EmptyTxsHash && len(body.Transactions) > 0 {
return nil, errors.New("server returned non-empty transaction list but block header indicates no transactions")
}
- if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 {
+ if head.TxHash != types.EmptyTxsHash && len(body.Transactions) == 0 {
return nil, errors.New("server returned empty transaction list but block header indicates transactions")
}
// Load uncles because they are not included in the block response.
@@ -154,7 +154,7 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He
var head *types.Header
err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false)
if err == nil && head == nil {
- err = ethereum.NotFound
+ err = ethereum.ErrNotFound
}
return head, err
}
@@ -165,7 +165,7 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
var head *types.Header
err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false)
if err == nil && head == nil {
- err = ethereum.NotFound
+ err = ethereum.ErrNotFound
}
return head, err
}
@@ -195,7 +195,7 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *
if err != nil {
return nil, false, err
} else if json == nil {
- return nil, false, ethereum.NotFound
+ return nil, false, ethereum.ErrNotFound
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
return nil, false, errors.New("server returned transaction without signature")
}
@@ -241,7 +241,7 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
err := ec.c.CallContext(ctx, &json, "eth_getTransactionByBlockHashAndIndex", blockHash, hexutil.Uint64(index))
if err == nil {
if json == nil {
- return nil, ethereum.NotFound
+ return nil, ethereum.ErrNotFound
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
return nil, errors.New("server returned transaction without signature")
}
@@ -257,7 +257,7 @@ func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*
err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
if err == nil {
if r == nil {
- return nil, ethereum.NotFound
+ return nil, ethereum.ErrNotFound
}
}
return r, err
@@ -268,23 +268,12 @@ func (ec *Client) GetTransactionReceiptResult(ctx context.Context, txHash common
result, err := ec.c.GetResultCallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
if err == nil {
if r == nil {
- return nil, nil, ethereum.NotFound
+ return nil, nil, ethereum.ErrNotFound
}
}
return r, result, err
}
-func toBlockNumArg(number *big.Int) string {
- if number == nil {
- return "latest"
- }
- pending := big.NewInt(-1)
- if number.Cmp(pending) == 0 {
- return "pending"
- }
- return hexutil.EncodeBig(number)
-}
-
type rpcProgress struct {
StartingBlock hexutil.Uint64
CurrentBlock hexutil.Uint64
@@ -436,8 +425,6 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
return uint(num), err
}
-// TODO: SubscribePendingTransactions (needs server side)
-
// Contract Calling
// CallContract executes a message call transaction, which is directly executed in the VM
@@ -476,6 +463,48 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return (*big.Int)(&hex), nil
}
+// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to
+// allow a timely execution of a transaction.
+func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ var hex hexutil.Big
+ if err := ec.c.CallContext(ctx, &hex, "eth_maxPriorityFeePerGas"); err != nil {
+ return nil, err
+ }
+ return (*big.Int)(&hex), nil
+}
+
+type feeHistoryResultMarshaling struct {
+ OldestBlock *hexutil.Big `json:"oldestBlock"`
+ Reward [][]*hexutil.Big `json:"reward,omitempty"`
+ BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
+ GasUsedRatio []float64 `json:"gasUsedRatio"`
+}
+
+// FeeHistory retrieves the fee market history.
+func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
+ var res feeHistoryResultMarshaling
+ if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), toBlockNumArg(lastBlock), rewardPercentiles); err != nil {
+ return nil, err
+ }
+ reward := make([][]*big.Int, len(res.Reward))
+ for i, r := range res.Reward {
+ reward[i] = make([]*big.Int, len(r))
+ for j, r := range r {
+ reward[i][j] = (*big.Int)(r)
+ }
+ }
+ baseFee := make([]*big.Int, len(res.BaseFee))
+ for i, b := range res.BaseFee {
+ baseFee[i] = (*big.Int)(b)
+ }
+ return ðereum.FeeHistory{
+ OldestBlock: (*big.Int)(res.OldestBlock),
+ Reward: reward,
+ BaseFee: baseFee,
+ GasUsedRatio: res.GasUsedRatio,
+ }, nil
+}
+
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
// the current pending state of the backend blockchain. There is no guarantee that this is
// the true gas limit requirement as other transactions may be added or removed by miners,
@@ -501,6 +530,17 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
}
+func toBlockNumArg(number *big.Int) string {
+ if number == nil {
+ return "latest"
+ }
+ pending := big.NewInt(-1)
+ if number.Cmp(pending) == 0 {
+ return "pending"
+ }
+ return hexutil.EncodeBig(number)
+}
+
// SendOrderTransaction injects a signed transaction into the pending pool for execution.
//
// If the transaction was a contract creation use the TransactionReceipt method to get the
@@ -539,5 +579,14 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
+ if msg.GasFeeCap != nil {
+ arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
+ }
+ if msg.GasTipCap != nil {
+ arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
+ }
+ if msg.AccessList != nil {
+ arg["accessList"] = msg.AccessList
+ }
return arg
}
diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go
new file mode 100644
index 0000000000..755705ab7f
--- /dev/null
+++ b/ethclient/gethclient/gethclient.go
@@ -0,0 +1,244 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package gethclient provides an RPC client for geth-specific APIs.
+package gethclient
+
+import (
+ "context"
+ "math/big"
+ "runtime"
+ "runtime/debug"
+
+ ethereum "github.com/XinFinOrg/XDPoSChain"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/p2p"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+// Client is a wrapper around rpc.Client that implements geth-specific functionality.
+//
+// If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead.
+type Client struct {
+ c *rpc.Client
+}
+
+// New creates a client that uses the given RPC client.
+func New(c *rpc.Client) *Client {
+ return &Client{c}
+}
+
+// CreateAccessList tries to create an access list for a specific transaction based on the
+// current pending state of the blockchain.
+func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*types.AccessList, uint64, string, error) {
+ type accessListResult struct {
+ Accesslist *types.AccessList `json:"accessList"`
+ Error string `json:"error,omitempty"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ }
+ var result accessListResult
+ if err := ec.c.CallContext(ctx, &result, "eth_createAccessList", toCallArg(msg)); err != nil {
+ return nil, 0, "", err
+ }
+ return result.Accesslist, uint64(result.GasUsed), result.Error, nil
+}
+
+// AccountResult is the result of a GetProof operation.
+type AccountResult struct {
+ Address common.Address `json:"address"`
+ AccountProof []string `json:"accountProof"`
+ Balance *big.Int `json:"balance"`
+ CodeHash common.Hash `json:"codeHash"`
+ Nonce uint64 `json:"nonce"`
+ StorageHash common.Hash `json:"storageHash"`
+ StorageProof []StorageResult `json:"storageProof"`
+}
+
+// StorageResult provides a proof for a key-value pair.
+type StorageResult struct {
+ Key string `json:"key"`
+ Value *big.Int `json:"value"`
+ Proof []string `json:"proof"`
+}
+
+// GetProof returns the account and storage values of the specified account including the Merkle-proof.
+// The block number can be nil, in which case the value is taken from the latest known block.
+func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []string, blockNumber *big.Int) (*AccountResult, error) {
+
+ type storageResult struct {
+ Key string `json:"key"`
+ Value *hexutil.Big `json:"value"`
+ Proof []string `json:"proof"`
+ }
+
+ type accountResult struct {
+ Address common.Address `json:"address"`
+ AccountProof []string `json:"accountProof"`
+ Balance *hexutil.Big `json:"balance"`
+ CodeHash common.Hash `json:"codeHash"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ StorageHash common.Hash `json:"storageHash"`
+ StorageProof []storageResult `json:"storageProof"`
+ }
+
+ var res accountResult
+ err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber))
+ // Turn hexutils back to normal datatypes
+ storageResults := make([]StorageResult, 0, len(res.StorageProof))
+ for _, st := range res.StorageProof {
+ storageResults = append(storageResults, StorageResult{
+ Key: st.Key,
+ Value: st.Value.ToInt(),
+ Proof: st.Proof,
+ })
+ }
+ result := AccountResult{
+ Address: res.Address,
+ AccountProof: res.AccountProof,
+ Balance: res.Balance.ToInt(),
+ Nonce: uint64(res.Nonce),
+ CodeHash: res.CodeHash,
+ StorageHash: res.StorageHash,
+ }
+ return &result, err
+}
+
+// OverrideAccount specifies the state of an account to be overridden.
+type OverrideAccount struct {
+ Nonce uint64 `json:"nonce"`
+ Code []byte `json:"code"`
+ Balance *big.Int `json:"balance"`
+ State map[common.Hash]common.Hash `json:"state"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+}
+
+// CallContract executes a message call transaction, which is directly executed in the VM
+// of the node, but never mined into the blockchain.
+//
+// blockNumber selects the block height at which the call runs. It can be nil, in which
+// case the code is taken from the latest known block. Note that state from very old
+// blocks might not be available.
+//
+// overrides specifies a map of contract states that should be overwritten before executing
+// the message call.
+// Please use ethclient.CallContract instead if you don't need the override functionality.
+func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount) ([]byte, error) {
+ var hex hexutil.Bytes
+ err := ec.c.CallContext(
+ ctx, &hex, "eth_call", toCallArg(msg),
+ toBlockNumArg(blockNumber), toOverrideMap(overrides),
+ )
+ return hex, err
+}
+
+// GCStats retrieves the current garbage collection stats from a geth node.
+func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
+ var result debug.GCStats
+ err := ec.c.CallContext(ctx, &result, "debug_gcStats")
+ return &result, err
+}
+
+// MemStats retrieves the current memory stats from a geth node.
+func (ec *Client) MemStats(ctx context.Context) (*runtime.MemStats, error) {
+ var result runtime.MemStats
+ err := ec.c.CallContext(ctx, &result, "debug_memStats")
+ return &result, err
+}
+
+// SetHead sets the current head of the local chain by block number.
+// Note, this is a destructive action and may severely damage your chain.
+// Use with extreme caution.
+func (ec *Client) SetHead(ctx context.Context, number *big.Int) error {
+ return ec.c.CallContext(ctx, nil, "debug_setHead", toBlockNumArg(number))
+}
+
+// GetNodeInfo retrieves the node info of a geth node.
+func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
+ var result p2p.NodeInfo
+ err := ec.c.CallContext(ctx, &result, "admin_nodeInfo")
+ return &result, err
+}
+
+// SubscribePendingTransactions subscribes to new pending transactions.
+func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) {
+ return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
+}
+
+func toBlockNumArg(number *big.Int) string {
+ if number == nil {
+ return "latest"
+ }
+ pending := big.NewInt(-1)
+ if number.Cmp(pending) == 0 {
+ return "pending"
+ }
+ return hexutil.EncodeBig(number)
+}
+
+func toCallArg(msg ethereum.CallMsg) interface{} {
+ arg := map[string]interface{}{
+ "from": msg.From,
+ "to": msg.To,
+ }
+ if len(msg.Data) > 0 {
+ arg["data"] = hexutil.Bytes(msg.Data)
+ }
+ if msg.Value != nil {
+ arg["value"] = (*hexutil.Big)(msg.Value)
+ }
+ if msg.Gas != 0 {
+ arg["gas"] = hexutil.Uint64(msg.Gas)
+ }
+ if msg.GasPrice != nil {
+ arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
+ }
+ if msg.GasFeeCap != nil {
+ arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
+ }
+ if msg.GasTipCap != nil {
+ arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
+ }
+ if msg.AccessList != nil {
+ arg["accessList"] = msg.AccessList
+ }
+ return arg
+}
+
+func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
+ if overrides == nil {
+ return nil
+ }
+ type overrideAccount struct {
+ Nonce hexutil.Uint64 `json:"nonce"`
+ Code hexutil.Bytes `json:"code"`
+ Balance *hexutil.Big `json:"balance"`
+ State map[common.Hash]common.Hash `json:"state"`
+ StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
+ }
+ result := make(map[common.Address]overrideAccount)
+ for addr, override := range *overrides {
+ result[addr] = overrideAccount{
+ Nonce: hexutil.Uint64(override.Nonce),
+ Code: override.Code,
+ Balance: (*hexutil.Big)(override.Balance),
+ State: override.State,
+ StateDiff: override.StateDiff,
+ }
+ }
+ return &result
+}
diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go
index 601ffc94a1..cd46039826 100644
--- a/ethdb/leveldb/leveldb.go
+++ b/ethdb/leveldb/leveldb.go
@@ -62,18 +62,18 @@ type Database struct {
fn string // filename for reporting
db *leveldb.DB // LevelDB instance
- compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction
- compReadMeter metrics.Meter // Meter for measuring the data read during compaction
- compWriteMeter metrics.Meter // Meter for measuring the data written during compaction
- writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction
- writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction
- diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database
- diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read
- diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written
- memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction
- level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0
- nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level
- seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt
+ compTimeMeter *metrics.Meter // Meter for measuring the total time spent in database compaction
+ compReadMeter *metrics.Meter // Meter for measuring the data read during compaction
+ compWriteMeter *metrics.Meter // Meter for measuring the data written during compaction
+ writeDelayNMeter *metrics.Meter // Meter for measuring the write delay number due to database compaction
+ writeDelayMeter *metrics.Meter // Meter for measuring the write delay duration due to database compaction
+ diskSizeGauge *metrics.Gauge // Gauge for tracking the size of all the levels in the database
+ diskReadMeter *metrics.Meter // Meter for measuring the effective amount of data read
+ diskWriteMeter *metrics.Meter // Meter for measuring the effective amount of data written
+ memCompGauge *metrics.Gauge // Gauge for tracking the number of memory compaction
+ level0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in level0
+ nonlevel0CompGauge *metrics.Gauge // Gauge for tracking the number of table compaction in non0 level
+ seekCompGauge *metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt
quitLock sync.Mutex // Mutex protecting the quit channel access
quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 57f26188d2..4510478bb0 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -367,7 +367,7 @@ func (s *Service) readLoop(conn *connWrapper) {
// If the network packet is a system ping, respond to it directly
var ping string
if err := json.Unmarshal(blob, &ping); err == nil && strings.HasPrefix(ping, "primus::ping::") {
- if err := conn.WriteJSON(strings.Replace(ping, "ping", "pong", -1)); err != nil {
+ if err := conn.WriteJSON(strings.ReplaceAll(ping, "ping", "pong")); err != nil {
log.Warn("Failed to respond to system ping message", "err", err)
return
}
@@ -816,8 +816,11 @@ func (s *Service) reportStats(conn *connWrapper) error {
sync := s.eth.Downloader().Progress()
syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
- price, _ := s.eth.ApiBackend.SuggestPrice(context.Background())
+ price, _ := s.eth.ApiBackend.SuggestGasTipCap(context.Background())
gasprice = int(price.Uint64())
+ if basefee := s.eth.ApiBackend.CurrentHeader().BaseFee; basefee != nil {
+ gasprice += int(basefee.Uint64())
+ }
} else {
sync := s.les.Downloader().Progress()
syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
diff --git a/event/filter/filter.go b/event/filter/filter.go
index b1fbf30ee4..a6fe46d6a0 100644
--- a/event/filter/filter.go
+++ b/event/filter/filter.go
@@ -45,37 +45,37 @@ func New() *Filters {
}
}
-func (self *Filters) Start() {
- go self.loop()
+func (f *Filters) Start() {
+ go f.loop()
}
-func (self *Filters) Stop() {
- close(self.quit)
+func (f *Filters) Stop() {
+ close(f.quit)
}
-func (self *Filters) Notify(filter Filter, data interface{}) {
- self.ch <- FilterEvent{filter, data}
+func (f *Filters) Notify(filter Filter, data interface{}) {
+ f.ch <- FilterEvent{filter, data}
}
-func (self *Filters) Install(watcher Filter) int {
- self.watchers[self.id] = watcher
- self.id++
+func (f *Filters) Install(watcher Filter) int {
+ f.watchers[f.id] = watcher
+ f.id++
- return self.id - 1
+ return f.id - 1
}
-func (self *Filters) Uninstall(id int) {
- delete(self.watchers, id)
+func (f *Filters) Uninstall(id int) {
+ delete(f.watchers, id)
}
-func (self *Filters) loop() {
+func (f *Filters) loop() {
out:
for {
select {
- case <-self.quit:
+ case <-f.quit:
break out
- case event := <-self.ch:
- for _, watcher := range self.watchers {
+ case event := <-f.ch:
+ for _, watcher := range f.watchers {
if reflect.TypeOf(watcher) == reflect.TypeOf(event.filter) {
if watcher.Compare(event.filter) {
watcher.Trigger(event.data)
@@ -86,10 +86,10 @@ out:
}
}
-func (self *Filters) Match(a, b Filter) bool {
+func (f *Filters) Match(a, b Filter) bool {
return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b)
}
-func (self *Filters) Get(i int) Filter {
- return self.watchers[i]
+func (f *Filters) Get(i int) Filter {
+ return f.watchers[i]
}
diff --git a/event/filter/generic_filter.go b/event/filter/generic_filter.go
index d679b8bfa8..b641f5f070 100644
--- a/event/filter/generic_filter.go
+++ b/event/filter/generic_filter.go
@@ -23,18 +23,18 @@ type Generic struct {
Fn func(data interface{})
}
-// self = registered, f = incoming
-func (self Generic) Compare(f Filter) bool {
+// g = registered, f = incoming
+func (g Generic) Compare(f Filter) bool {
var strMatch, dataMatch = true, true
filter := f.(Generic)
- if (len(self.Str1) > 0 && filter.Str1 != self.Str1) ||
- (len(self.Str2) > 0 && filter.Str2 != self.Str2) ||
- (len(self.Str3) > 0 && filter.Str3 != self.Str3) {
+ if (len(g.Str1) > 0 && filter.Str1 != g.Str1) ||
+ (len(g.Str2) > 0 && filter.Str2 != g.Str2) ||
+ (len(g.Str3) > 0 && filter.Str3 != g.Str3) {
strMatch = false
}
- for k := range self.Data {
+ for k := range g.Data {
if _, ok := filter.Data[k]; !ok {
return false
}
@@ -43,6 +43,6 @@ func (self Generic) Compare(f Filter) bool {
return strMatch && dataMatch
}
-func (self Generic) Trigger(data interface{}) {
- self.Fn(data)
+func (g Generic) Trigger(data interface{}) {
+ g.Fn(data)
}
diff --git a/go.mod b/go.mod
index b7baee7256..23a0995a49 100644
--- a/go.mod
+++ b/go.mod
@@ -1,9 +1,8 @@
module github.com/XinFinOrg/XDPoSChain
-go 1.21
+go 1.22
require (
- bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
github.com/VictoriaMetrics/fastcache v1.12.2
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6
@@ -12,16 +11,12 @@ require (
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.13.0
- github.com/gizak/termui v2.2.0+incompatible
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
- github.com/go-stack/stack v1.8.1
github.com/golang/protobuf v1.5.3
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
github.com/gorilla/websocket v1.4.2
- github.com/hashicorp/golang-lru v0.5.3
github.com/holiman/uint256 v1.2.4
github.com/huin/goupnp v1.3.0
- github.com/influxdata/influxdb v1.7.9
github.com/jackpal/go-nat-pmp v1.0.2
github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/hid v1.0.0
@@ -37,51 +32,65 @@ require (
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570
github.com/stretchr/testify v1.8.4
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
- golang.org/x/crypto v0.15.0
- golang.org/x/net v0.17.0
- golang.org/x/sync v0.4.0
- golang.org/x/sys v0.24.0
- golang.org/x/tools v0.14.0
+ golang.org/x/crypto v0.29.0
+ golang.org/x/sync v0.9.0
+ golang.org/x/sys v0.27.0
+ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
- gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772
- gopkg.in/urfave/cli.v1 v1.20.0
+ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
)
-require github.com/deckarep/golang-set v1.8.0
+require (
+ github.com/btcsuite/btcd/btcec/v2 v2.3.4
+ github.com/consensys/gnark-crypto v0.10.0
+ github.com/crate-crypto/go-kzg-4844 v0.7.0
+ github.com/deckarep/golang-set v1.8.0
+ github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
+ github.com/ethereum/c-kzg-4844 v0.4.0
+ github.com/influxdata/influxdb-client-go/v2 v2.4.0
+ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c
+ github.com/kylelemons/godebug v1.1.0
+ github.com/mattn/go-isatty v0.0.17
+ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
+ github.com/urfave/cli/v2 v2.27.5
+ gopkg.in/natefinch/lumberjack.v2 v2.2.1
+)
require (
github.com/StackExchange/wmi v1.2.1 // indirect
+ github.com/bits-and-blooms/bitset v1.5.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/consensys/bavard v0.1.13 // indirect
+ github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
- github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 // indirect
- github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
- github.com/google/go-cmp v0.6.0 // indirect
- github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/google/uuid v1.3.0 // indirect
+ github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
- github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect
- github.com/maruel/ut v1.0.2 // indirect
- github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
+ github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/naoina/go-stringutil v0.1.0 // indirect
- github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
- github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
+ github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
- github.com/tklauser/go-sysconf v0.3.12 // indirect
- github.com/tklauser/numcpus v0.6.1 // indirect
- golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
- golang.org/x/mod v0.13.0 // indirect
- golang.org/x/term v0.14.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ github.com/supranational/blst v0.3.11 // indirect
+ github.com/tklauser/go-sysconf v0.3.14 // indirect
+ github.com/tklauser/numcpus v0.8.0 // indirect
+ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
+ golang.org/x/mod v0.17.0 // indirect
+ golang.org/x/net v0.25.0 // indirect
+ golang.org/x/term v0.26.0 // indirect
+ golang.org/x/text v0.20.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
+ rsc.io/tmplfunc v0.0.3 // indirect
)
diff --git a/go.sum b/go.sum
index 98d3a63aed..bb2ab2b777 100644
--- a/go.sum
+++ b/go.sum
@@ -1,113 +1,71 @@
-bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw=
-bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
-github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
-github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
-github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
-github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
-github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
-github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
-github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
-github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
-github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
-github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
-github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
-github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
-github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
-github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
-github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
-github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98 h1:7buXGE+m4OPjyo8rUJgA8RmARNMq+m99JJLR+Z+ZWN0=
github.com/aristanetworks/goarista v0.0.0-20231019142648-8c6f0862ab98/go.mod h1:DLTg9Gp4FAXF5EpqYBQnUeBbRsNLY7b2HR94TE5XQtE=
-github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
+github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6 h1:Eey/GGQ/E5Xp1P2Lyx1qj007hLZfbi0+CoVeJruGCtI=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
-github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
+github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.0.1-0.20190104013014-3767db7a7e18/go.mod h1:HD5P3vAIAh+Y2GAxg0PrPN1P8WkepXGpjbUPDHJqqKM=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
-github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
-github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
+github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
+github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA=
+github.com/consensys/gnark-crypto v0.10.0/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU=
+github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
+github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
+github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0=
+github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
-github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
-github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29 h1:Ewd9K+mC725sITA12QQHRqWj78NU4t7EhlFVVgdlzJg=
-github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
-github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
-github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo=
-github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
-github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
-github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
-github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ=
+github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
-github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa h1:XKAhUk/dtp+CV0VO6mhG2V7jA9vbcGcnYF/Ay9NjZrY=
-github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs=
-github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk=
-github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0=
-github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
+github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
-github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
-github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
-github.com/gizak/termui v2.2.0+incompatible h1:qvZU9Xll/Xd/Xr/YO+HfBKXhy8a8/94ao6vV9DSXzUE=
-github.com/gizak/termui v2.2.0+incompatible/go.mod h1:PkJoWUt/zacQKysNfQtcw1RW+eK2SxkieVBtl+4ovLA=
+github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
+github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
-github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -117,56 +75,42 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
-github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
+github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
-github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o=
-github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
-github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
-github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
-github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4=
-github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
-github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k=
+github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
+github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
+github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
+github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/karalabe/hid v1.0.0 h1:+/CIMNXhSU/zIJgnIvBD2nKHxS/bnRHhhs9xBryLpPo=
github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8=
-github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -175,186 +119,159 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e h1:e2z/lz9pvtRrEOgKWaLW2Dw02Nqd3/fqv0qWTQ8ByZE=
-github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e/go.mod h1:nty42YY5QByNC5MM7q/nj938VbgPU7avs45z6NClpxI=
-github.com/maruel/ut v1.0.2 h1:mQTlQk3jubTbdTcza+hwoZQWhzcvE4L6K6RTtAFlA1k=
-github.com/maruel/ut v1.0.2/go.mod h1:RV8PwPD9dd2KFlnlCc/DB2JVvkXmyaalfc5xvmSrRSs=
-github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
+github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
+github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
+github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
-github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
-github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
-github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
+github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
+github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
-github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77 h1:gKl78uP/I7JZ56OFtRf7nc4m1icV38hwV0In5pEGzeA=
-github.com/nsf/termbox-go v0.0.0-20170211012700-3540b76b9c77/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5 h1:K2PKeDFZidfjUWpXk05Gbxhwm8Rnz1l4O+u/bbbcCvc=
github.com/prometheus/prometheus v1.7.2-0.20170814170113-3101606756c5/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
-github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
-github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM=
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA=
+github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
+github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
-github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
-github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
-github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
-github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
-github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
+github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
+github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
+github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
+github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
+github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
-golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
+golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
-golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
-golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
+golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
+golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
@@ -369,23 +286,19 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
-gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
-gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
-gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
-gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -395,3 +308,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
+rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
diff --git a/interfaces.go b/interfaces.go
index dfd96b217a..7c7312679c 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -26,8 +26,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
)
-// NotFound is returned by API methods if the requested item does not exist.
-var NotFound = errors.New("not found")
+// ErrNotFound is returned by API methods if the requested item does not exist.
+var ErrNotFound = errors.New("not found")
// TODO: move subscription to package event
@@ -117,10 +117,13 @@ type CallMsg struct {
To *common.Address // the destination contract (nil for contract creation)
Gas uint64 // if 0, the call executes with near-infinite gas
GasPrice *big.Int // wei <-> gas exchange ratio
+ GasFeeCap *big.Int // EIP-1559 fee cap per gas.
+ GasTipCap *big.Int // EIP-1559 tip per gas.
Value *big.Int // amount of wei sent along with the call
Data []byte // input data, usually an ABI-encoded contract method invocation
BalanceTokenFee *big.Int
- AccessList types.AccessList // EIP-2930 access list.
+
+ AccessList types.AccessList // EIP-2930 access list.
}
// A ContractCaller provides contract calls, essentially transactions that are executed by
@@ -180,6 +183,15 @@ type GasPricer interface {
SuggestGasPrice(ctx context.Context) (*big.Int, error)
}
+// FeeHistory provides recent fee market data that consumers can use to determine
+// a reasonable maxPriorityFeePerGas value.
+type FeeHistory struct {
+ OldestBlock *big.Int // block coresponding to first response value
+ Reward [][]*big.Int // list every txs priority fee per block
+ BaseFee []*big.Int // list of each block's base fee
+ GasUsedRatio []float64 // ratio of gas used out of the total available limit
+}
+
// A PendingStateReader provides access to the pending state, which is the result of all
// known executable transactions which have not yet been included in the blockchain. It is
// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value
diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go
index 40aa87596c..5369678adb 100644
--- a/internal/cmdtest/test_cmd.go
+++ b/internal/cmdtest/test_cmd.go
@@ -26,6 +26,7 @@ import (
"regexp"
"strings"
"sync"
+ "syscall"
"testing"
"text/template"
"time"
@@ -49,6 +50,8 @@ type TestCmd struct {
stdout *bufio.Reader
stdin io.WriteCloser
stderr *testlogger
+ // Err will contain the process exit error or interrupt signal error
+ Err error
}
// Run exec's the current binary using name as argv[0] which will trigger the
@@ -123,12 +126,12 @@ func (tt *TestCmd) matchExactOutput(want []byte) error {
// Find the mismatch position.
for i := 0; i < n; i++ {
if want[i] != buf[i] {
- return fmt.Errorf("Output mismatch at ◊:\n---------------- (stdout text)\n%s%s\n---------------- (expected text)\n%s",
+ return fmt.Errorf("output mismatch at ◊:\n---------------- (stdout text)\n%s%s\n---------------- (expected text)\n%s",
buf[:i], buf[i:n], want)
}
}
if n < len(want) {
- return fmt.Errorf("Not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
+ return fmt.Errorf("not enough output, got until ◊:\n---------------- (stdout text)\n%s\n---------------- (expected text)\n%s◊%s",
buf, want[:n], want[n:])
}
}
@@ -181,11 +184,25 @@ func (tt *TestCmd) ExpectExit() {
}
func (tt *TestCmd) WaitExit() {
- tt.cmd.Wait()
+ tt.Err = tt.cmd.Wait()
}
func (tt *TestCmd) Interrupt() {
- tt.cmd.Process.Signal(os.Interrupt)
+ tt.Err = tt.cmd.Process.Signal(os.Interrupt)
+}
+
+// ExitStatus exposes the process' OS exit code
+// It will only return a valid value after the process has finished.
+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 0
}
// StderrText returns any stderr output written so far.
diff --git a/internal/debug/api.go b/internal/debug/api.go
index 4702f79076..8f1b60ddb3 100644
--- a/internal/debug/api.go
+++ b/internal/debug/api.go
@@ -23,6 +23,7 @@ package debug
import (
"errors"
"io"
+ "log/slog"
"os"
"os/user"
"path/filepath"
@@ -55,19 +56,13 @@ type HandlerT struct {
// Verbosity sets the log verbosity ceiling. The verbosity of individual packages
// and source files can be raised using Vmodule.
func (*HandlerT) Verbosity(level int) {
- Glogger.Verbosity(log.Lvl(level))
+ glogger.Verbosity(slog.Level(level))
}
// Vmodule sets the log verbosity pattern. See package log for details on the
// pattern syntax.
func (*HandlerT) Vmodule(pattern string) error {
- return Glogger.Vmodule(pattern)
-}
-
-// BacktraceAt sets the log backtrace location. See package log for details on
-// the pattern syntax.
-func (*HandlerT) BacktraceAt(location string) error {
- return Glogger.BacktraceAt(location)
+ return glogger.Vmodule(pattern)
}
// MemStats returns detailed runtime memory statistics.
diff --git a/internal/debug/flags.go b/internal/debug/flags.go
index 16a87c9810..cf5b49e475 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -19,86 +19,155 @@ package debug
import (
"fmt"
"io"
+ "log/slog"
"net/http"
_ "net/http/pprof"
"os"
+ "path/filepath"
"runtime"
+ "github.com/XinFinOrg/XDPoSChain/internal/flags"
"github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/log/term"
"github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/metrics/exp"
- colorable "github.com/mattn/go-colorable"
- "gopkg.in/urfave/cli.v1"
+ "github.com/mattn/go-colorable"
+ "github.com/mattn/go-isatty"
+ "github.com/urfave/cli/v2"
+ "gopkg.in/natefinch/lumberjack.v2"
)
var (
- VerbosityFlag = cli.IntFlag{
- Name: "verbosity",
- Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
- Value: 3,
+ verbosityFlag = &cli.IntFlag{
+ Name: "verbosity",
+ Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail",
+ Value: 3,
+ Category: flags.LoggingCategory,
}
- vmoduleFlag = cli.StringFlag{
- Name: "vmodule",
- Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)",
- Value: "",
+ logVmoduleFlag = &cli.StringFlag{
+ Name: "log-vmodule",
+ Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)",
+ Value: "",
+ Category: flags.LoggingCategory,
}
- backtraceAtFlag = cli.StringFlag{
- Name: "backtrace",
- Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")",
- Value: "",
+ vmoduleFlag = &cli.StringFlag{
+ Name: "vmodule",
+ Usage: "Per-module verbosity: comma-separated list of = (e.g. eth/*=5,p2p=4)",
+ Value: "",
+ Category: flags.LoggingCategory,
}
- debugFlag = cli.BoolFlag{
- Name: "debug",
- Usage: "Prepends log messages with call-site location (file and line number)",
+ logjsonFlag = &cli.BoolFlag{
+ Name: "log-json",
+ Usage: "Format logs with JSON",
+ Hidden: true,
+ Category: flags.LoggingCategory,
}
- pprofFlag = cli.BoolFlag{
- Name: "pprof",
- Usage: "Enable the pprof HTTP server",
+ logFormatFlag = &cli.StringFlag{
+ Name: "log-format",
+ Usage: "Log format to use (json|logfmt|terminal)",
+ Category: flags.LoggingCategory,
}
- pprofPortFlag = cli.IntFlag{
- Name: "pprofport",
- Usage: "pprof HTTP server listening port",
- Value: 6060,
+ logFileFlag = &cli.StringFlag{
+ Name: "log-file",
+ Usage: "Write logs to a file",
+ Category: flags.LoggingCategory,
}
- pprofAddrFlag = cli.StringFlag{
- Name: "pprofaddr",
- Usage: "pprof HTTP server listening interface",
- Value: "127.0.0.1",
+ logRotateFlag = &cli.BoolFlag{
+ Name: "log-rotate",
+ Usage: "Enables log file rotation",
+ Category: flags.LoggingCategory,
}
- memprofilerateFlag = cli.IntFlag{
- Name: "memprofilerate",
- Usage: "Turn on memory profiling with the given rate",
- Value: runtime.MemProfileRate,
+ logMaxSizeMBsFlag = &cli.IntFlag{
+ Name: "log-maxsize",
+ Usage: "Maximum size in MBs of a single log file",
+ Value: 100,
+ Category: flags.LoggingCategory,
}
- blockprofilerateFlag = cli.IntFlag{
- Name: "blockprofilerate",
- Usage: "Turn on block profiling with the given rate",
+ logMaxBackupsFlag = &cli.IntFlag{
+ Name: "log-maxbackups",
+ Usage: "Maximum number of log files to retain",
+ Value: 10,
+ Category: flags.LoggingCategory,
}
- cpuprofileFlag = cli.StringFlag{
- Name: "cpuprofile",
- Usage: "Write CPU profile to the given file",
+ logMaxAgeFlag = &cli.IntFlag{
+ Name: "log-maxage",
+ Usage: "Maximum number of days to retain a log file",
+ Value: 30,
+ Category: flags.LoggingCategory,
}
- traceFlag = cli.StringFlag{
- Name: "trace",
- Usage: "Write execution trace to the given file",
+ logCompressFlag = &cli.BoolFlag{
+ Name: "log-compress",
+ Usage: "Compress the log files",
+ Value: false,
+ Category: flags.LoggingCategory,
}
- periodicProfilingFlag = cli.BoolFlag{
- Name: "periodicprofile",
- Usage: "Periodically profile cpu and memory status",
+ pprofFlag = &cli.BoolFlag{
+ Name: "pprof",
+ Usage: "Enable the pprof HTTP server",
+ Category: flags.LoggingCategory,
}
- debugDataDirFlag = cli.StringFlag{
- Name: "debugdatadir",
- Usage: "Debug Data directory for profiling output",
+ pprofPortFlag = &cli.IntFlag{
+ Name: "pprof-port",
+ Aliases: []string{"pprofport"},
+ Usage: "pprof HTTP server listening port",
+ Value: 6060,
+ Category: flags.LoggingCategory,
+ }
+ pprofAddrFlag = &cli.StringFlag{
+ Name: "pprof-addr",
+ Aliases: []string{"pprofaddr"},
+ Usage: "pprof HTTP server listening interface",
+ Value: "127.0.0.1",
+ Category: flags.LoggingCategory,
+ }
+ memprofilerateFlag = &cli.IntFlag{
+ Name: "pprof-memprofilerate",
+ Aliases: []string{"memprofilerate"},
+ Usage: "Turn on memory profiling with the given rate",
+ Value: runtime.MemProfileRate,
+ Category: flags.LoggingCategory,
+ }
+ blockprofilerateFlag = &cli.IntFlag{
+ Name: "pprof-blockprofilerate",
+ Aliases: []string{"blockprofilerate"},
+ Usage: "Turn on block profiling with the given rate",
+ Category: flags.LoggingCategory,
+ }
+ cpuprofileFlag = &cli.StringFlag{
+ Name: "pprof-cpuprofile",
+ Aliases: []string{"cpuprofile"},
+ Usage: "Write CPU profile to the given file",
+ Category: flags.LoggingCategory,
+ }
+ traceFlag = &cli.StringFlag{
+ Name: "trace",
+ Usage: "Write execution trace to the given file",
+ Category: flags.LoggingCategory,
+ }
+ periodicProfilingFlag = &cli.BoolFlag{
+ Name: "periodicprofile",
+ Usage: "Periodically profile cpu and memory status",
+ Category: flags.LoggingCategory,
+ }
+ debugDataDirFlag = &cli.StringFlag{
+ Name: "debugdatadir",
+ Usage: "Debug Data directory for profiling output",
+ Category: flags.LoggingCategory,
}
)
// Flags holds all command-line flags required for debugging.
var Flags = []cli.Flag{
- VerbosityFlag,
- //vmoduleFlag,
- //backtraceAtFlag,
- debugFlag,
+ verbosityFlag,
+ logVmoduleFlag,
+ vmoduleFlag,
+ logjsonFlag,
+ logFormatFlag,
+ logFileFlag,
+ logRotateFlag,
+ logMaxSizeMBsFlag,
+ logMaxBackupsFlag,
+ logMaxAgeFlag,
+ logCompressFlag,
pprofFlag,
pprofAddrFlag,
pprofPortFlag,
@@ -110,66 +179,186 @@ var Flags = []cli.Flag{
debugDataDirFlag,
}
-var Glogger *log.GlogHandler
+var (
+ glogger *log.GlogHandler
+ logOutputFile io.WriteCloser
+ defaultTerminalHandler *log.TerminalHandler
+)
func init() {
- usecolor := term.IsTty(os.Stderr.Fd()) && os.Getenv("TERM") != "dumb"
- output := io.Writer(os.Stderr)
- if usecolor {
- output = colorable.NewColorableStderr()
+ defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false)
+ glogger = log.NewGlogHandler(defaultTerminalHandler)
+ glogger.Verbosity(log.LvlInfo)
+ log.SetDefault(log.NewLogger(glogger))
+}
+
+func ResetLogging() {
+ if defaultTerminalHandler != nil {
+ defaultTerminalHandler.ResetFieldPadding()
}
- Glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor)))
}
// Setup initializes profiling and logging based on the CLI flags.
// It should be called as early as possible in the program.
func Setup(ctx *cli.Context) error {
+ var (
+ handler slog.Handler
+ terminalOutput = io.Writer(os.Stderr)
+ output io.Writer
+ logFmtFlag = ctx.String(logFormatFlag.Name)
+ )
+ var (
+ logFile = ctx.String(logFileFlag.Name)
+ rotation = ctx.Bool(logRotateFlag.Name)
+ )
+ if len(logFile) > 0 {
+ if err := validateLogLocation(filepath.Dir(logFile)); err != nil {
+ return fmt.Errorf("failed to initiatilize file logger: %v", err)
+ }
+ }
+ context := []interface{}{"rotate", rotation}
+ if len(logFmtFlag) > 0 {
+ context = append(context, "format", logFmtFlag)
+ } else {
+ context = append(context, "format", "terminal")
+ }
+ if rotation {
+ // Lumberjack uses -lumberjack.log in is.TempDir() if empty.
+ // so typically /tmp/geth-lumberjack.log on linux
+ if len(logFile) > 0 {
+ context = append(context, "location", logFile)
+ } else {
+ context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
+ }
+ logOutputFile = &lumberjack.Logger{
+ Filename: logFile,
+ MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
+ MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
+ MaxAge: ctx.Int(logMaxAgeFlag.Name),
+ Compress: ctx.Bool(logCompressFlag.Name),
+ }
+ output = io.MultiWriter(terminalOutput, logOutputFile)
+ } else if logFile != "" {
+ var err error
+ if logOutputFile, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err != nil {
+ return err
+ }
+ output = io.MultiWriter(logOutputFile, terminalOutput)
+ context = append(context, "location", logFile)
+ } else {
+ output = terminalOutput
+ }
+
+ switch {
+ case ctx.Bool(logjsonFlag.Name):
+ // Retain backwards compatibility with `--log-json` flag if `--log-format` not set
+ defer log.Warn("The flag '--log-json' is deprecated, please use '--log-format=json' instead")
+ handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
+ case logFmtFlag == "json":
+ handler = log.JSONHandlerWithLevel(output, log.LevelInfo)
+ case logFmtFlag == "logfmt":
+ handler = log.LogfmtHandler(output)
+ case logFmtFlag == "", logFmtFlag == "terminal":
+ useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
+ if useColor {
+ terminalOutput = colorable.NewColorableStderr()
+ if logOutputFile != nil {
+ output = io.MultiWriter(logOutputFile, terminalOutput)
+ } else {
+ output = terminalOutput
+ }
+ }
+ handler = log.NewTerminalHandler(output, useColor)
+ default:
+ // Unknown log format specified
+ return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
+ }
+
+ glogger = log.NewGlogHandler(handler)
+
// logging
- log.PrintOrigins(ctx.GlobalBool(debugFlag.Name))
- Glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
- Glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name))
- Glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name))
- log.Root().SetHandler(Glogger)
+ verbosity := log.FromLegacyLevel(ctx.Int(verbosityFlag.Name))
+ glogger.Verbosity(verbosity)
+ vmodule := ctx.String(logVmoduleFlag.Name)
+ if vmodule == "" {
+ // Retain backwards compatibility with `--vmodule` flag if `--log-vmodule` not set
+ vmodule = ctx.String(vmoduleFlag.Name)
+ if vmodule != "" {
+ defer log.Warn("The flag '--vmodule' is deprecated, please use '--log-vmodule' instead")
+ }
+ }
+ glogger.Vmodule(vmodule)
+
+ log.SetDefault(log.NewLogger(glogger))
// profiling, tracing
- runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name)
- Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name))
- if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" {
+ runtime.MemProfileRate = ctx.Int(memprofilerateFlag.Name)
+ Handler.SetBlockProfileRate(ctx.Int(blockprofilerateFlag.Name))
+ if traceFile := ctx.String(traceFlag.Name); traceFile != "" {
if err := Handler.StartGoTrace(traceFile); err != nil {
return err
}
}
- if cpuFile := ctx.GlobalString(cpuprofileFlag.Name); cpuFile != "" {
+ if cpuFile := ctx.String(cpuprofileFlag.Name); cpuFile != "" {
if err := Handler.StartCPUProfile(cpuFile); err != nil {
return err
}
}
- Handler.filePath = ctx.GlobalString(debugDataDirFlag.Name)
+ Handler.filePath = ctx.String(debugDataDirFlag.Name)
- if periodicProfiling := ctx.GlobalBool(periodicProfilingFlag.Name); periodicProfiling {
+ if periodicProfiling := ctx.Bool(periodicProfilingFlag.Name); periodicProfiling {
Handler.PeriodicComputeProfiling()
}
// pprof server
- if ctx.GlobalBool(pprofFlag.Name) {
- // Hook go-metrics into expvar on any /debug/metrics request, load all vars
- // from the registry into expvar, and execute regular expvar handler.
- exp.Exp(metrics.DefaultRegistry)
-
- address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name))
- go func() {
- log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address))
- if err := http.ListenAndServe(address, nil); err != nil {
- log.Error("Failure in running pprof server", "err", err)
- }
- }()
+ if ctx.Bool(pprofFlag.Name) {
+ address := fmt.Sprintf("%s:%d", ctx.String(pprofAddrFlag.Name), ctx.Int(pprofPortFlag.Name))
+ // This context value ("metrics-addr") represents the utils.MetricsHTTPFlag.Name.
+ // It cannot be imported because it will cause a cyclical dependency.
+ StartPProf(address, !ctx.IsSet("metrics-addr") && !ctx.IsSet("metrics.addr"))
}
+
+ if len(logFile) > 0 || rotation {
+ log.Info("Logging configured", context...)
+ }
+
return nil
}
+func StartPProf(address string, withMetrics bool) {
+ // Hook go-metrics into expvar on any /debug/metrics request, load all vars
+ // from the registry into expvar, and execute regular expvar handler.
+ if withMetrics {
+ exp.Exp(metrics.DefaultRegistry)
+ }
+ log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address))
+ go func() {
+ if err := http.ListenAndServe(address, nil); err != nil {
+ log.Error("Failure in running pprof server", "err", err)
+ }
+ }()
+}
+
// Exit stops all running profiles, flushing their output to the
// respective file.
func Exit() {
Handler.StopCPUProfile()
Handler.StopGoTrace()
+ if logOutputFile != nil {
+ logOutputFile.Close()
+ }
+}
+
+func validateLogLocation(path string) error {
+ if err := os.MkdirAll(path, os.ModePerm); err != nil {
+ return fmt.Errorf("error creating the directory: %w", err)
+ }
+ // Check if the path is writable by trying to create a temporary file
+ tmp := filepath.Join(path, "tmp")
+ if f, err := os.Create(tmp); err != nil {
+ return err
+ } else {
+ f.Close()
+ }
+ return os.Remove(tmp)
}
diff --git a/internal/debug/loudpanic.go b/internal/debug/loudpanic.go
index 572ebcefa1..a7296e7b3f 100644
--- a/internal/debug/loudpanic.go
+++ b/internal/debug/loudpanic.go
@@ -14,8 +14,6 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build go1.6
-
package debug
import "runtime/debug"
diff --git a/internal/debug/trace.go b/internal/debug/trace.go
index 74e3a9b22f..eb5f801c2f 100644
--- a/internal/debug/trace.go
+++ b/internal/debug/trace.go
@@ -14,8 +14,6 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-//+build go1.5
-
package debug
import (
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 96d422ec75..f8687bf626 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -39,6 +39,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
@@ -80,10 +81,60 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI {
return &PublicEthereumAPI{b}
}
-// GasPrice returns a suggestion for a gas price.
+// GasPrice returns a suggestion for a gas price for legacy transactions.
func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
- price, err := s.b.SuggestPrice(ctx)
- return (*hexutil.Big)(price), err
+ tipcap, err := s.b.SuggestGasTipCap(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if head := s.b.CurrentHeader(); head.BaseFee != nil {
+ tipcap.Add(tipcap, head.BaseFee)
+ }
+ return (*hexutil.Big)(tipcap), err
+}
+
+// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic transactions.
+func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
+ tipcap, err := s.b.SuggestGasTipCap(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return (*hexutil.Big)(tipcap), err
+}
+
+type feeHistoryResult struct {
+ OldestBlock *hexutil.Big `json:"oldestBlock"`
+ Reward [][]*hexutil.Big `json:"reward,omitempty"`
+ BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
+ GasUsedRatio []float64 `json:"gasUsedRatio"`
+}
+
+// FeeHistory returns the fee market history.
+func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount hexutil.Uint, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
+ oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles)
+ if err != nil {
+ return nil, err
+ }
+ results := &feeHistoryResult{
+ OldestBlock: (*hexutil.Big)(oldest),
+ GasUsedRatio: gasUsed,
+ }
+ if reward != nil {
+ results.Reward = make([][]*hexutil.Big, len(reward))
+ for i, w := range reward {
+ results.Reward[i] = make([]*hexutil.Big, len(w))
+ for j, v := range w {
+ results.Reward[i][j] = (*hexutil.Big)(v)
+ }
+ }
+ }
+ if baseFee != nil {
+ results.BaseFee = make([]*hexutil.Big, len(baseFee))
+ for i, v := range baseFee {
+ results.BaseFee[i] = (*hexutil.Big)(v)
+ }
+ }
+ return results, nil
}
// ProtocolVersion returns the current Ethereum protocol version this node supports
@@ -132,12 +183,12 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac
"queued": make(map[string]map[string]*RPCTransaction),
}
pending, queue := s.b.TxPoolContent()
-
+ curHeader := s.b.CurrentHeader()
// Flatten the pending transactions
for account, txs := range pending {
dump := make(map[string]*RPCTransaction)
for _, tx := range txs {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx)
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["pending"][account.Hex()] = dump
}
@@ -145,13 +196,36 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac
for account, txs := range queue {
dump := make(map[string]*RPCTransaction)
for _, tx := range txs {
- dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx)
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
}
content["queued"][account.Hex()] = dump
}
return content
}
+// ContentFrom returns the transactions contained within the transaction pool.
+func (s *PublicTxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction {
+ content := make(map[string]map[string]*RPCTransaction, 2)
+ pending, queue := s.b.TxPoolContentFrom(addr)
+ curHeader := s.b.CurrentHeader()
+
+ // Build the pending transactions
+ dump := make(map[string]*RPCTransaction, len(pending))
+ for _, tx := range pending {
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ }
+ content["pending"] = dump
+
+ // Build the queued transactions
+ dump = make(map[string]*RPCTransaction, len(queue))
+ for _, tx := range queue {
+ dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
+ }
+ content["queued"] = dump
+
+ return content
+}
+
// Status returns the number of pending and queued transaction in the pool.
func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint {
pending, queue := s.b.Stats()
@@ -366,7 +440,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *Transacti
return nil, err
}
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Assemble the transaction and sign with the wallet
@@ -410,14 +484,15 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio
if args.Gas == nil {
return nil, errors.New("gas not specified")
}
- if args.GasPrice == nil {
- return nil, errors.New("gasPrice not specified")
+ if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
+ return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
if args.Nonce == nil {
return nil, errors.New("nonce not specified")
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
- if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
+ tx := args.toTransaction()
+ if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
}
signed, err := s.signTransaction(ctx, &args, passwd)
@@ -467,7 +542,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
if err != nil {
return nil, err
}
- signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
+ signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
return signature, nil
}
@@ -1247,23 +1322,20 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
return candidatesWithStakeInfo, nil
}
-func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
+func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if statedb == nil || err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
if header == nil {
- return nil, 0, false, errors.New("nil header in DoCall"), nil
+ return nil, errors.New("nil header in DoCall"), nil
}
if err := overrides.Apply(statedb); err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
- msg := args.ToMessage(b, header.Number, globalGasCap)
- msg.SetBalanceTokenFeeForCall()
-
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc
@@ -1278,24 +1350,32 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
if err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
if block == nil {
- return nil, 0, false, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil
+ return nil, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil
}
author, err := b.GetEngine().Author(block.Header())
if err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
XDCxState, err := b.XDCxService().GetTradingState(block, author)
if err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
- // Get a new instance of the EVM.
- evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vmCfg)
+ // TODO: replace header.BaseFee with blockCtx.BaseFee
+ // reference: https://github.com/ethereum/go-ethereum/pull/29051
+ msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee)
if err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
+ }
+ msg.SetBalanceTokenFeeForCall()
+
+ // Get a new instance of the EVM.
+ evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true})
+ if err != nil {
+ return nil, err, nil
}
// Wait for the context to be done and cancel the evm. Even if the
// EVM has finished, cancelling may be done (repeatedly)
@@ -1307,19 +1387,19 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
// Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
- res, gas, failed, err, vmErr := core.ApplyMessage(evm, msg, gp, owner)
+ result, err, vmErr := core.ApplyMessage(evm, msg, gp, owner)
if err := vmError(); err != nil {
- return nil, 0, false, err, nil
+ return nil, err, nil
}
// If the timer caused an abort, return an appropriate error message
if evm.Cancelled() {
- return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil
+ return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil
}
if err != nil {
- return res, 0, false, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil
+ return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil
}
- return res, gas, failed, err, vmErr
+ return result, err, vmErr
}
func newRevertError(res []byte) *revertError {
@@ -1363,16 +1443,32 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
timeout = 0
}
- result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, vm.Config{}, timeout, s.b.RPCGasCap())
+ result, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap())
if err != nil {
return nil, err
}
// If the result contains a revert reason, try to unpack and return it.
- if failed && len(result) > 0 {
- return nil, newRevertError(result)
+ if result.Failed() && len(result.Return()) > 0 {
+ return nil, newRevertError(result.Return())
}
+ return result.Return(), vmErr
+}
- return (hexutil.Bytes)(result), vmErr
+type estimateGasError struct {
+ error string // Concrete error type if it's failed to estimate gas usage
+ vmerr error // Additional field, it's non-nil if the given transaction is invalid
+ revert string // Additional field, it's non-empty if the transaction is reverted and reason is provided
+}
+
+func (e estimateGasError) Error() string {
+ errMsg := e.error
+ if e.vmerr != nil {
+ errMsg += fmt.Sprintf(" (%v)", e.vmerr)
+ }
+ if e.revert != "" {
+ errMsg += fmt.Sprintf(" (%s)", e.revert)
+ }
+ return errMsg
}
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
@@ -1416,21 +1512,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
- executable := func(gas uint64) (bool, []byte, error, error) {
+ executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)
- res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
+ result, err, _ := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
if err != nil {
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
- return false, nil, nil, nil // Special case, raise gas limit
+ return true, nil, nil // Special case, raise gas limit
}
- return false, nil, err, nil // Bail out
+ return true, nil, err // Bail out
}
- if failed {
- return false, res, nil, vmErr
- }
-
- return true, nil, nil, nil
+ return result.Failed(), result, nil
}
// If the transaction is a plain value transfer, short circuit estimation and
@@ -1439,7 +1531,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// unused access list items). Ever so slightly wasteful, but safer overall.
if args.Data == nil || len(*args.Data) == 0 {
if args.To != nil && state.GetCodeSize(*args.To) == 0 {
- ok, _, err, _ := executable(params.TxGas)
+ ok, _, err := executable(params.TxGas)
if ok && err == nil {
return hexutil.Uint64(params.TxGas), nil
}
@@ -1449,7 +1541,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
mid := (hi + lo) / 2
- ok, _, err, _ := executable(mid)
+ failed, _, err := executable(mid)
// If the error is not nil(consensus error), it means the provided message
// call or transaction will never be accepted no matter how much gas it is
@@ -1458,7 +1550,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
return 0, err
}
- if !ok {
+ if failed {
lo = mid
} else {
hi = mid
@@ -1467,21 +1559,30 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
- ok, res, err, vmErr := executable(hi)
+ failed, result, err := executable(hi)
if err != nil {
return 0, err
}
- if !ok {
- if vmErr != vm.ErrOutOfGas {
- if len(res) > 0 {
- return 0, newRevertError(res)
+ if failed {
+ if result != nil && result.Err != vm.ErrOutOfGas {
+ var revert string
+ if len(result.Revert()) > 0 {
+ ret, err := abi.UnpackRevert(result.Revert())
+ if err != nil {
+ revert = hexutil.Encode(result.Revert())
+ } else {
+ revert = ret
+ }
+ }
+ return 0, estimateGasError{
+ error: "always failing transaction",
+ vmerr: result.Err,
+ revert: revert,
}
- return 0, vmErr
}
-
// Otherwise, the specified gas cap is too low
- return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
+ return 0, estimateGasError{error: fmt.Sprintf("gas required exceeds allowance (%d)", cap)}
}
}
return hexutil.Uint64(hi), nil
@@ -1558,14 +1659,11 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
return formatted
}
-// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
-// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
-// transaction hashes.
-func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) {
- head := b.Header() // copies the header once
- fields := map[string]interface{}{
+// RPCMarshalHeader converts the given header to the RPC output .
+func RPCMarshalHeader(head *types.Header) map[string]interface{} {
+ result := map[string]interface{}{
"number": (*hexutil.Big)(head.Number),
- "hash": b.Hash(),
+ "hash": head.Hash(),
"parentHash": head.ParentHash,
"nonce": head.Nonce,
"mixHash": head.MixDigest,
@@ -1574,9 +1672,8 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
"stateRoot": head.Root,
"miner": head.Coinbase,
"difficulty": (*hexutil.Big)(head.Difficulty),
- "totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
"extraData": hexutil.Bytes(head.Extra),
- "size": hexutil.Uint64(b.Size()),
+ "size": hexutil.Uint64(head.Size()),
"gasLimit": hexutil.Uint64(head.GasLimit),
"gasUsed": hexutil.Uint64(head.GasUsed),
"timestamp": (*hexutil.Big)(head.Time),
@@ -1587,6 +1684,21 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
"penalties": hexutil.Bytes(head.Penalties),
}
+ if head.BaseFee != nil {
+ result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
+ }
+
+ return result
+}
+
+// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
+// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
+// transaction hashes.
+func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) {
+ fields := RPCMarshalHeader(b.Header())
+ fields["size"] = hexutil.Uint64(b.Size())
+ fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(b.Hash()))
+
if inclTx {
formatTx := func(tx *types.Transaction) (interface{}, error) {
return tx.Hash(), nil
@@ -1773,6 +1885,8 @@ type RPCTransaction struct {
From common.Address `json:"from"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
+ GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
+ GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
Hash common.Hash `json:"hash"`
Input hexutil.Bytes `json:"input"`
Nonce hexutil.Uint64 `json:"nonce"`
@@ -1789,7 +1903,7 @@ type RPCTransaction struct {
// newRPCTransaction returns a transaction that will serialize to the RPC
// representation, with the given location metadata set (if available).
-func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
+func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction {
// Determine the signer. For replay-protected transactions, use the most permissive
// signer, because we assume that signers are backwards-compatible with old
// transactions. For non-protected transactions, the homestead signer signer is used
@@ -1800,7 +1914,6 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
} else {
signer = types.HomesteadSigner{}
}
-
from, _ := types.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{
@@ -1822,17 +1935,36 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
result.TransactionIndex = (*hexutil.Uint64)(&index)
}
- if tx.Type() == types.AccessListTxType {
+ switch tx.Type() {
+ case types.AccessListTxType:
al := tx.AccessList()
result.Accesses = &al
result.ChainID = (*hexutil.Big)(tx.ChainId())
+ case types.DynamicFeeTxType:
+ al := tx.AccessList()
+ result.Accesses = &al
+ result.ChainID = (*hexutil.Big)(tx.ChainId())
+ result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
+ result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
+ // if the transaction has been mined, compute the effective gas price
+ if baseFee != nil && blockHash != (common.Hash{}) {
+ // price = min(tip, gasFeeCap - baseFee) + baseFee
+ price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
+ result.GasPrice = (*hexutil.Big)(price)
+ } else {
+ result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
+ }
}
return result
}
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
-func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
- return newRPCTransaction(tx, common.Hash{}, 0, 0)
+func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
+ var baseFee *big.Int
+ if current != nil {
+ baseFee = eip1559.CalcBaseFee(config, current)
+ }
+ return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee)
}
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
@@ -1841,7 +1973,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti
if index >= uint64(len(txs)) {
return nil
}
- return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index)
+ return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee())
}
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
@@ -1917,12 +2049,8 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
}
owner := common.Address{}
- // If the gas amount is not set, extract this as it will depend on access
- // lists and we'll need to reestimate every time
- nogas := args.Gas == nil
-
// Ensure any missing fields are filled, extract the recipient and input data
- if err := args.setDefaults(ctx, b); err != nil {
+ if err := args.setDefaults(ctx, b, true); err != nil {
return nil, 0, nil, err
}
var to common.Address
@@ -1944,38 +2072,36 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
accessList := prevTracer.AccessList()
log.Trace("Creating access list", "input", accessList)
- // If no gas amount was specified, each unique access list needs it's own
- // gas calculation. This is quite expensive, but we need to be accurate
- // and it's convered by the sender only anyway.
- if nogas {
- args.Gas = nil
- if err := args.setDefaults(ctx, b); err != nil {
- return nil, 0, nil, err // shouldn't happen, just in case
- }
- }
// Copy the original db so we don't modify it
statedb := db.Copy()
+ // Set the accesslist to the last al
+ args.AccessList = &accessList
+ msg, err := args.ToMessage(b, block.Number(), b.RPCGasCap(), header.BaseFee)
+ if err != nil {
+ return nil, 0, nil, err
+ }
+
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
var balanceTokenFee *big.Int
if value, ok := feeCapacity[to]; ok {
balanceTokenFee = value
}
- msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), args.data(), accessList, false, balanceTokenFee, header.Number)
+ msg.SetBalanceTokenFee(balanceTokenFee)
// Apply the transaction with the access list tracer
tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles)
- config := vm.Config{Tracer: tracer, Debug: true}
+ config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config)
if err != nil {
return nil, 0, nil, err
}
// TODO: determine the value of owner
- _, UsedGas, _, err, vmErr := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner)
+ res, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner)
if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
}
if tracer.Equal(prevTracer) {
- return accessList, UsedGas, vmErr, nil
+ return accessList, res.UsedGas, res.Err, nil
}
prevTracer = tracer
}
@@ -2077,17 +2203,23 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr
}
// GetTransactionByHash returns the transaction for the given hash
-func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction {
+func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
// Try to return an already finalized transaction
- if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil {
- return newRPCTransaction(tx, blockHash, blockNumber, index)
+ tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
+ if tx != nil {
+ header, err := s.b.HeaderByHash(ctx, blockHash)
+ if err != nil {
+ return nil, err
+ }
+ return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil
}
// No finalized transaction, try to retrieve it from the pool
if tx := s.b.GetPoolTransaction(hash); tx != nil {
- return newRPCPendingTransaction(tx)
+ return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
}
+
// Transaction unknown, return as such
- return nil
+ return nil, nil
}
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
@@ -2108,13 +2240,15 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
if tx == nil {
+ // When the transaction doesn't exist, the RPC method should return JSON null
+ // as per specification.
return nil, nil
}
receipts, err := s.b.GetReceipts(ctx, blockHash)
if err != nil {
return nil, err
}
- if len(receipts) <= int(index) {
+ if uint64(len(receipts)) <= index {
return nil, nil
}
receipt := receipts[index]
@@ -2122,37 +2256,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
// Derive the sender.
bigblock := new(big.Int).SetUint64(blockNumber)
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
- from, _ := types.Sender(signer, tx)
-
- fields := map[string]interface{}{
- "blockHash": blockHash,
- "blockNumber": hexutil.Uint64(blockNumber),
- "transactionHash": hash,
- "transactionIndex": hexutil.Uint64(index),
- "from": from,
- "to": tx.To(),
- "gasUsed": hexutil.Uint64(receipt.GasUsed),
- "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
- "contractAddress": nil,
- "logs": receipt.Logs,
- "logsBloom": receipt.Bloom,
- "type": hexutil.Uint(tx.Type()),
- }
-
- // Assign receipt status or post state.
- if len(receipt.PostState) > 0 {
- fields["root"] = hexutil.Bytes(receipt.PostState)
- } else {
- fields["status"] = hexutil.Uint(receipt.Status)
- }
- if receipt.Logs == nil {
- fields["logs"] = [][]*types.Log{}
- }
- // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
- if receipt.ContractAddress != (common.Address{}) {
- fields["contractAddress"] = receipt.ContractAddress
- }
- return fields, nil
+ return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
}
// marshalReceipt marshals a transaction receipt into a JSON object.
@@ -2172,8 +2276,7 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
"type": hexutil.Uint(tx.Type()),
- // uncomment below line after EIP-1559
- // TODO: "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
+ "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
}
// Assign receipt status or post state.
@@ -2213,7 +2316,7 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
if tx.To() != nil && tx.IsSpecialTransaction() {
- return common.Hash{}, errors.New("Dont allow transaction sent to BlockSigners & RandomizeSMC smart contract via API")
+ return common.Hash{}, errors.New("don't allow transaction sent to BlockSigners & RandomizeSMC smart contract via API")
}
// If the transaction fee cap is already specified, ensure the
@@ -2279,7 +2382,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra
}
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return common.Hash{}, err
}
// Assemble the transaction and sign with the wallet
@@ -2296,11 +2399,12 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra
return SubmitTransaction(ctx, s.b, signed)
}
-// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction,
-// and returns it to the caller for further processing (signing + broadcast)
+// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields)
+// on a given unsigned transaction, and returns it to the caller for further
+// processing (signing + broadcast).
func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Assemble the transaction and obtain rlp
@@ -2530,11 +2634,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBid(ctx context.Context, baseToken
result := PriceVolume{}
block := s.b.CurrentBlock()
if block == nil {
- return result, errors.New("Current block not found")
+ return result, errors.New("current block not found")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return result, errors.New("XDCX service not found")
+ return result, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2546,7 +2650,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBid(ctx context.Context, baseToken
}
result.Price, result.Volume = XDCxState.GetBestBidPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken))
if result.Price.Sign() == 0 {
- return result, errors.New("Bid tree not found")
+ return result, errors.New("not found bid tree")
}
return result, nil
}
@@ -2555,11 +2659,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken
result := PriceVolume{}
block := s.b.CurrentBlock()
if block == nil {
- return result, errors.New("Current block not found")
+ return result, errors.New("not found current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return result, errors.New("XDCX service not found")
+ return result, errors.New("not found XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2571,7 +2675,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken
}
result.Price, result.Volume = XDCxState.GetBestAskPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken))
if result.Price.Sign() == 0 {
- return result, errors.New("Ask tree not found")
+ return result, errors.New("not find ask tree")
}
return result, nil
}
@@ -2579,11 +2683,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestAsk(ctx context.Context, baseToken
func (s *PublicXDCXTransactionPoolAPI) GetBidTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpOrderList, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2603,11 +2707,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBidTree(ctx context.Context, baseToken
func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2619,7 +2723,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken,
}
price := XDCxState.GetLastPrice(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken))
if price == nil || price.Sign() == 0 {
- return common.Big0, errors.New("Order book's price not found")
+ return common.Big0, errors.New("not find order book's price")
}
return price, nil
}
@@ -2627,11 +2731,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetPrice(ctx context.Context, baseToken,
func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2643,7 +2747,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, ba
}
price := XDCxState.GetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken))
if price == nil || price.Sign() == 0 {
- return common.Big0, errors.New("Order book's price not found")
+ return common.Big0, errors.New("not find order book's price")
}
return price, nil
}
@@ -2651,11 +2755,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLastEpochPrice(ctx context.Context, ba
func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context, baseToken, quoteToken common.Address) (*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2667,7 +2771,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context,
}
price, _ := XDCxState.GetMediumPriceAndTotalAmount(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken))
if price == nil || price.Sign() == 0 {
- return common.Big0, errors.New("Order book's price not found")
+ return common.Big0, errors.New("not find order book's price")
}
return price, nil
}
@@ -2675,11 +2779,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetCurrentEpochPrice(ctx context.Context,
func (s *PublicXDCXTransactionPoolAPI) GetAskTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpOrderList, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2699,11 +2803,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetAskTree(ctx context.Context, baseToken
func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseToken, quoteToken common.Address, orderId uint64) (interface{}, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2716,7 +2820,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseTok
orderIdHash := common.BigToHash(new(big.Int).SetUint64(orderId))
orderitem := XDCxState.GetOrder(tradingstate.GetTradingOrderBookHash(baseToken, quoteToken), orderIdHash)
if orderitem.Quantity == nil || orderitem.Quantity.Sign() == 0 {
- return nil, errors.New("Order not found")
+ return nil, errors.New("not found order")
}
return orderitem, nil
}
@@ -2724,11 +2828,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetOrderById(ctx context.Context, baseTok
func (s *PublicXDCXTransactionPoolAPI) GetTradingOrderBookInfo(ctx context.Context, baseToken, quoteToken common.Address) (*tradingstate.DumpOrderBookInfo, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2748,11 +2852,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetTradingOrderBookInfo(ctx context.Conte
func (s *PublicXDCXTransactionPoolAPI) GetLiquidationPriceTree(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]tradingstate.DumpLendingBook, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2772,7 +2876,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLiquidationPriceTree(ctx context.Conte
func (s *PublicXDCXTransactionPoolAPI) GetInvestingTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
@@ -2796,7 +2900,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetInvestingTree(ctx context.Context, len
func (s *PublicXDCXTransactionPoolAPI) GetBorrowingTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
@@ -2820,7 +2924,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetBorrowingTree(ctx context.Context, len
func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderBookInfo(tx context.Context, lendingToken common.Address, term uint64) (*lendingstate.DumpOrderBookInfo, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
@@ -2844,11 +2948,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderBookInfo(tx context.Contex
func (s *PublicXDCXTransactionPoolAPI) getLendingOrderTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.LendingItem, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return nil, errors.New("XDCX Lending service not found")
+ return nil, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2868,11 +2972,11 @@ func (s *PublicXDCXTransactionPoolAPI) getLendingOrderTree(ctx context.Context,
func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.LendingTrade, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return nil, errors.New("XDCX Lending service not found")
+ return nil, errors.New("not find XDCX lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2892,11 +2996,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeTree(ctx context.Context,
func (s *PublicXDCXTransactionPoolAPI) GetLiquidationTimeTree(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]lendingstate.DumpOrderList, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return nil, errors.New("XDCX Lending service not found")
+ return nil, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2916,11 +3020,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLiquidationTimeTree(ctx context.Contex
func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderCount(ctx context.Context, addr common.Address) (*hexutil.Uint64, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return nil, errors.New("XDCX Lending service not found")
+ return nil, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2938,11 +3042,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestInvesting(ctx context.Context, len
result := InterestVolume{}
block := s.b.CurrentBlock()
if block == nil {
- return result, errors.New("Current block not found")
+ return result, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return result, errors.New("XDCX Lending service not found")
+ return result, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2960,11 +3064,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBorrowing(ctx context.Context, len
result := InterestVolume{}
block := s.b.CurrentBlock()
if block == nil {
- return result, errors.New("Current block not found")
+ return result, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return result, errors.New("XDCX Lending service not found")
+ return result, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -2981,11 +3085,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBestBorrowing(ctx context.Context, len
func (s *PublicXDCXTransactionPoolAPI) GetBids(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -3005,11 +3109,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetBids(ctx context.Context, baseToken, q
func (s *PublicXDCXTransactionPoolAPI) GetAsks(ctx context.Context, baseToken, quoteToken common.Address) (map[*big.Int]*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
XDCxService := s.b.XDCxService()
if XDCxService == nil {
- return nil, errors.New("XDCX service not found")
+ return nil, errors.New("not find XDCX service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -3029,7 +3133,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetAsks(ctx context.Context, baseToken, q
func (s *PublicXDCXTransactionPoolAPI) GetInvests(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
@@ -3053,7 +3157,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetInvests(ctx context.Context, lendingTo
func (s *PublicXDCXTransactionPoolAPI) GetBorrows(ctx context.Context, lendingToken common.Address, term uint64) (map[*big.Int]*big.Int, error) {
block := s.b.CurrentBlock()
if block == nil {
- return nil, errors.New("Current block not found")
+ return nil, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
@@ -3111,11 +3215,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderById(ctx context.Context,
lendingItem := lendingstate.LendingItem{}
block := s.b.CurrentBlock()
if block == nil {
- return lendingItem, errors.New("Current block not found")
+ return lendingItem, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return lendingItem, errors.New("XDCX Lending service not found")
+ return lendingItem, errors.New("not find XDCX lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -3129,7 +3233,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingOrderById(ctx context.Context,
orderIdHash := common.BigToHash(new(big.Int).SetUint64(orderId))
lendingItem = lendingState.GetLendingOrder(lendingOrderBook, orderIdHash)
if lendingItem.LendingId != orderId {
- return lendingItem, errors.New("Lending Item not found")
+ return lendingItem, errors.New("not find lending item")
}
return lendingItem, nil
}
@@ -3138,11 +3242,11 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeById(ctx context.Context,
lendingItem := lendingstate.LendingTrade{}
block := s.b.CurrentBlock()
if block == nil {
- return lendingItem, errors.New("Current block not found")
+ return lendingItem, errors.New("not find current block")
}
lendingService := s.b.LendingService()
if lendingService == nil {
- return lendingItem, errors.New("XDCX Lending service not found")
+ return lendingItem, errors.New("not find XDCX Lending service")
}
author, err := s.b.GetEngine().Author(block.Header())
if err != nil {
@@ -3156,7 +3260,7 @@ func (s *PublicXDCXTransactionPoolAPI) GetLendingTradeById(ctx context.Context,
tradeIdHash := common.BigToHash(new(big.Int).SetUint64(tradeId))
lendingItem = lendingState.GetLendingTrade(lendingOrderBook, tradeIdHash)
if lendingItem.TradeId != tradeId {
- return lendingItem, errors.New("Lending Item not found")
+ return lendingItem, errors.New("not find lending item")
}
return lendingItem, nil
}
@@ -3181,7 +3285,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes)
// Sign the requested hash with the wallet
signature, err := wallet.SignHash(account, signHash(data))
if err == nil {
- signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
+ signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
}
return signature, err
}
@@ -3197,26 +3301,27 @@ type SignTransactionResult struct {
// the given from address and it needs to be unlocked.
func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
if args.Gas == nil {
- return nil, errors.New("gas not specified")
+ return nil, errors.New("not specify Gas")
}
- if args.GasPrice == nil {
- return nil, errors.New("gasPrice not specified")
+ if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
+ return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
if args.Nonce == nil {
- return nil, errors.New("nonce not specified")
+ return nil, errors.New("not specify Nonce")
}
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
- if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
+ tx := args.toTransaction()
+ if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
return nil, err
}
- tx, err := s.sign(args.from(), args.toTransaction())
+ signed, err := s.sign(args.from(), tx)
if err != nil {
return nil, err
}
- data, err := tx.MarshalBinary()
+ data, err := signed.MarshalBinary()
if err != nil {
return nil, err
}
@@ -3236,11 +3341,12 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
accounts[account.Address] = struct{}{}
}
}
+ curHeader := s.b.CurrentHeader()
transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending {
from, _ := types.Sender(s.signer, tx)
if _, exists := accounts[from]; exists {
- transactions = append(transactions, newRPCPendingTransaction(tx))
+ transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
}
}
return transactions, nil
@@ -3252,7 +3358,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs Transact
if sendArgs.Nonce == nil {
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
}
- if err := sendArgs.setDefaults(ctx, s.b); err != nil {
+ if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
return common.Hash{}, err
}
matchTx := sendArgs.toTransaction()
@@ -3339,7 +3445,7 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string,
if block == nil {
return "", fmt.Errorf("block #%d not found", number)
}
- return fmt.Sprintf("0x%x", ethash.SeedHash(number)), nil
+ return fmt.Sprintf("%#x", ethash.SeedHash(number)), nil
}
// PrivateDebugAPI is the collection of Ethereum APIs exposed over the private
@@ -3448,14 +3554,14 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash,
limitNumber = currentNumber
}
for i := blockNumber + 1; i <= limitNumber; i++ {
- header, err := b.HeaderByNumber(nil, rpc.BlockNumber(i))
+ header, err := b.HeaderByNumber(context.TODO(), rpc.BlockNumber(i))
if err != nil {
return addrs, err
}
if header == nil {
return addrs, errors.New("nil header in GetSignersFromBlocks")
}
- blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i))
+ blockData, err := b.BlockByNumber(context.TODO(), rpc.BlockNumber(i))
if err != nil {
return addrs, err
}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index a365e52780..b9be939a6a 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -48,7 +48,8 @@ type Backend interface {
// General Ethereum API
Downloader() *downloader.Downloader
ProtocolVersion() int
- SuggestPrice(ctx context.Context) (*big.Int, error)
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+ FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error)
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
@@ -61,6 +62,7 @@ type Backend interface {
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
+ CurrentHeader() *types.Header
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
@@ -82,6 +84,7 @@ type Backend interface {
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int)
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
+ TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions)
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
// Order Pool Transaction
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 48374f8fa9..2202fe580e 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -20,6 +20,7 @@ import (
"bytes"
"context"
"errors"
+ "fmt"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
@@ -33,12 +34,14 @@ import (
// TransactionArgs represents the arguments to construct a new transaction
// or a message call.
type TransactionArgs struct {
- From *common.Address `json:"from"`
- To *common.Address `json:"to"`
- Gas *hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Value *hexutil.Big `json:"value"`
- Nonce *hexutil.Uint64 `json:"nonce"`
+ From *common.Address `json:"from"`
+ To *common.Address `json:"to"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
+ MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
+ Value *hexutil.Big `json:"value"`
+ Nonce *hexutil.Uint64 `json:"nonce"`
// We accept "data" and "input" for backwards-compatibility reasons.
// "input" is the newer name and should be preferred by clients.
@@ -46,38 +49,34 @@ type TransactionArgs struct {
Data *hexutil.Bytes `json:"data"`
Input *hexutil.Bytes `json:"input"`
- // For non-legacy transactions
+ // Introduced by AccessListTxType transaction.
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
}
// from retrieves the transaction sender address.
-func (arg *TransactionArgs) from() common.Address {
- if arg.From == nil {
+func (args *TransactionArgs) from() common.Address {
+ if args.From == nil {
return common.Address{}
}
- return *arg.From
+ return *args.From
}
// data retrieves the transaction calldata. Input field is preferred.
-func (arg *TransactionArgs) data() []byte {
- if arg.Input != nil {
- return *arg.Input
+func (args *TransactionArgs) data() []byte {
+ if args.Input != nil {
+ return *args.Input
}
- if arg.Data != nil {
- return *arg.Data
+ if args.Data != nil {
+ return *args.Data
}
return nil
}
// setDefaults fills in default values for unspecified tx fields.
-func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
- if args.GasPrice == nil {
- price, err := b.SuggestPrice(ctx)
- if err != nil {
- return err
- }
- args.GasPrice = (*hexutil.Big)(price)
+func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error {
+ if err := args.setFeeDefaults(ctx, b); err != nil {
+ return err
}
if args.Value == nil {
args.Value = new(hexutil.Big)
@@ -95,36 +94,141 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
if args.To == nil && len(args.data()) == 0 {
return errors.New(`contract creation without any data provided`)
}
- // Estimate the gas usage if necessary.
+
if args.Gas == nil {
- // These fields are immutable during the estimation, safe to
- // pass the pointer directly.
- data := args.data()
- callArgs := TransactionArgs{
- From: args.From,
- To: args.To,
- GasPrice: args.GasPrice,
- Value: args.Value,
- Data: (*hexutil.Bytes)(&data),
- AccessList: args.AccessList,
+ if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls.
+ gas := hexutil.Uint64(b.RPCGasCap())
+ if gas == 0 {
+ gas = hexutil.Uint64(math.MaxUint64 / 2)
+ }
+ args.Gas = &gas
+ } else { // Estimate the gas usage otherwise.
+ // These fields are immutable during the estimation, safe to
+ // pass the pointer directly.
+ data := args.data()
+ callArgs := TransactionArgs{
+ From: args.From,
+ To: args.To,
+ GasPrice: args.GasPrice,
+ MaxFeePerGas: args.MaxFeePerGas,
+ MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
+ Value: args.Value,
+ Data: (*hexutil.Bytes)(&data),
+ AccessList: args.AccessList,
+ }
+ latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
+ estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap())
+ if err != nil {
+ return err
+ }
+ args.Gas = &estimated
+ log.Trace("Estimate gas usage automatically", "gas", args.Gas)
}
- pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
- estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap())
- if err != nil {
- return err
- }
- args.Gas = &estimated
- log.Trace("Estimate gas usage automatically", "gas", args.Gas)
}
- if args.ChainID == nil {
- id := (*hexutil.Big)(b.ChainConfig().ChainId)
- args.ChainID = id
+
+ // If chain id is provided, ensure it matches the local chain id. Otherwise, set the local
+ // chain id as the default.
+ want := b.ChainConfig().ChainId
+ if args.ChainID != nil {
+ if have := (*big.Int)(args.ChainID); have.Cmp(want) != 0 {
+ return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, want)
+ }
+ } else {
+ args.ChainID = (*hexutil.Big)(want)
}
return nil
}
-// ToMessage converts TransactionArgs to the Message type used by the core evm
-func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64) types.Message {
+// setFeeDefaults fills in default fee values for unspecified tx fields.
+func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b Backend) error {
+ // If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
+ if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
+ return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ }
+ // If the tx has completely specified a fee mechanism, no default is needed.
+ // This allows users who are not yet synced past London to get defaults for
+ // other tx values. See https://github.com/ethereum/go-ethereum/pull/23274
+ // for more information.
+ eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
+
+ // Sanity check the EIP-1559 fee parameters if present.
+ if args.GasPrice == nil && eip1559ParamsSet {
+ if args.MaxFeePerGas.ToInt().Sign() == 0 {
+ return errors.New("maxFeePerGas must be non-zero")
+ }
+ if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
+ return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
+ }
+ return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
+ }
+ // Sanity check the non-EIP-1559 fee parameters.
+ head := b.CurrentHeader()
+ isEIP1559 := b.ChainConfig().IsEIP1559(head.Number)
+ if args.GasPrice != nil && !eip1559ParamsSet {
+ // Zero gas-price is not allowed after London fork
+ if args.GasPrice.ToInt().Sign() == 0 && isEIP1559 {
+ return errors.New("gasPrice must be non-zero after EIP-1559 fork")
+ }
+ return nil // No need to set anything, user already set GasPrice
+ }
+
+ // Now attempt to fill in default value depending on whether London is active or not.
+ if isEIP1559 {
+ // London is active, set maxPriorityFeePerGas and maxFeePerGas.
+ if err := args.setLondonFeeDefaults(ctx, head, b); err != nil {
+ return err
+ }
+ } else {
+ if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil {
+ return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active")
+ }
+ // London not active, set gas price.
+ price, err := b.SuggestGasTipCap(ctx)
+ if err != nil {
+ return err
+ }
+ args.GasPrice = (*hexutil.Big)(price)
+ }
+ return nil
+}
+
+// setLondonFeeDefaults fills in reasonable default fee values for unspecified fields.
+func (args *TransactionArgs) setLondonFeeDefaults(ctx context.Context, head *types.Header, b Backend) error {
+ // Set maxPriorityFeePerGas if it is missing.
+ if args.MaxPriorityFeePerGas == nil {
+ tip, err := b.SuggestGasTipCap(ctx)
+ if err != nil {
+ return err
+ }
+ args.MaxPriorityFeePerGas = (*hexutil.Big)(tip)
+ }
+ // Set maxFeePerGas if it is missing.
+ if args.MaxFeePerGas == nil {
+ // Set the max fee to be 2 times larger than the previous block's base fee.
+ // The additional slack allows the tx to not become invalidated if the base
+ // fee is rising.
+ val := new(big.Int).Add(
+ args.MaxPriorityFeePerGas.ToInt(),
+ new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
+ )
+ args.MaxFeePerGas = (*hexutil.Big)(val)
+ }
+ // Both EIP-1559 fee parameters are now set; sanity check them.
+ if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
+ return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas)
+ }
+ return nil
+}
+
+// ToMessage converts the transaction arguments to the Message type used by the
+// core evm. This method is used in calls and traces that do not require a real
+// live transaction.
+func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (types.Message, error) {
+ // Reject invalid combinations of pre- and post-1559 fee styles
+ if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
+ return types.Message{}, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ }
+
// Set sender address or use zero address if none specified.
addr := args.from()
if addr == (common.Address{}) {
@@ -147,12 +251,44 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap
log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
gas = globalGasCap
}
- gasPrice := new(big.Int)
- if args.GasPrice != nil {
- gasPrice = args.GasPrice.ToInt()
- }
- if gasPrice.Sign() <= 0 {
- gasPrice = new(big.Int).SetUint64(defaultGasPrice)
+
+ var (
+ gasPrice *big.Int
+ gasFeeCap *big.Int
+ gasTipCap *big.Int
+ )
+ if baseFee == nil {
+ // If there's no basefee, then it must be a non-1559 execution
+ gasPrice = new(big.Int)
+ if args.GasPrice != nil {
+ gasPrice = args.GasPrice.ToInt()
+ }
+ if gasPrice.Sign() <= 0 {
+ gasPrice = new(big.Int).SetUint64(defaultGasPrice)
+ }
+ gasFeeCap, gasTipCap = gasPrice, gasPrice
+ } else {
+ // A basefee is provided, necessitating 1559-type execution
+ if args.GasPrice != nil {
+ // User specified the legacy gas field, convert to 1559 gas typing
+ gasPrice = args.GasPrice.ToInt()
+ gasFeeCap, gasTipCap = gasPrice, gasPrice
+ } else {
+ // User specified 1559 gas fields (or none), use those
+ gasFeeCap = new(big.Int)
+ if args.MaxFeePerGas != nil {
+ gasFeeCap = args.MaxFeePerGas.ToInt()
+ }
+ gasTipCap = new(big.Int)
+ if args.MaxPriorityFeePerGas != nil {
+ gasTipCap = args.MaxPriorityFeePerGas.ToInt()
+ }
+ // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
+ gasPrice = new(big.Int)
+ if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
+ gasPrice = math.BigMin(new(big.Int).Add(gasTipCap, baseFee), gasFeeCap)
+ }
+ }
}
value := new(big.Int)
if args.Value != nil {
@@ -164,25 +300,32 @@ func (args *TransactionArgs) ToMessage(b Backend, number *big.Int, globalGasCap
accessList = *args.AccessList
}
- // Create new call message
- msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, accessList, false, nil, number)
- return msg
+ msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, gasFeeCap, gasTipCap, data, accessList, true, nil, number)
+ return msg, nil
}
// toTransaction converts the arguments to a transaction.
// This assumes that setDefaults has been called.
func (args *TransactionArgs) toTransaction() *types.Transaction {
var data types.TxData
- if args.AccessList == nil {
- data = &types.LegacyTx{
- To: args.To,
- Nonce: uint64(*args.Nonce),
- Gas: uint64(*args.Gas),
- GasPrice: (*big.Int)(args.GasPrice),
- Value: (*big.Int)(args.Value),
- Data: args.data(),
+ switch {
+ case args.MaxFeePerGas != nil:
+ al := types.AccessList{}
+ if args.AccessList != nil {
+ al = *args.AccessList
}
- } else {
+ data = &types.DynamicFeeTx{
+ To: args.To,
+ ChainID: (*big.Int)(args.ChainID),
+ Nonce: uint64(*args.Nonce),
+ Gas: uint64(*args.Gas),
+ GasFeeCap: (*big.Int)(args.MaxFeePerGas),
+ GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas),
+ Value: (*big.Int)(args.Value),
+ Data: args.data(),
+ AccessList: al,
+ }
+ case args.AccessList != nil:
data = &types.AccessListTx{
To: args.To,
ChainID: (*big.Int)(args.ChainID),
@@ -193,6 +336,21 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: *args.AccessList,
}
+ default:
+ data = &types.LegacyTx{
+ To: args.To,
+ Nonce: uint64(*args.Nonce),
+ Gas: uint64(*args.Gas),
+ GasPrice: (*big.Int)(args.GasPrice),
+ Value: (*big.Int)(args.Value),
+ Data: args.data(),
+ }
}
return types.NewTx(data)
}
+
+// ToTransaction converts the arguments to a transaction.
+// This assumes that setDefaults has been called.
+func (args *TransactionArgs) ToTransaction() *types.Transaction {
+ return args.toTransaction()
+}
diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go
new file mode 100644
index 0000000000..7e7f249994
--- /dev/null
+++ b/internal/ethapi/transaction_args_test.go
@@ -0,0 +1,472 @@
+// Copyright 2022 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package ethapi
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+ "testing"
+ "time"
+
+ ethereum "github.com/XinFinOrg/XDPoSChain"
+ "github.com/XinFinOrg/XDPoSChain/XDCx"
+ "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
+ "github.com/XinFinOrg/XDPoSChain/XDCxlending"
+ "github.com/XinFinOrg/XDPoSChain/accounts"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
+ "github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/hexutil"
+ "github.com/XinFinOrg/XDPoSChain/consensus"
+ "github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/bloombits"
+ "github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/core/vm"
+ "github.com/XinFinOrg/XDPoSChain/eth/downloader"
+ "github.com/XinFinOrg/XDPoSChain/ethdb"
+ "github.com/XinFinOrg/XDPoSChain/event"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+// TestSetFeeDefaults tests the logic for filling in default fee values works as expected.
+func TestSetFeeDefaults(t *testing.T) {
+ type test struct {
+ name string
+ isLondon bool
+ in *TransactionArgs
+ want *TransactionArgs
+ err error
+ }
+
+ var (
+ b = newBackendMock()
+ zero = (*hexutil.Big)(big.NewInt(0))
+ fortytwo = (*hexutil.Big)(big.NewInt(42))
+ maxFee = (*hexutil.Big)(new(big.Int).Add(new(big.Int).Mul(b.current.BaseFee, big.NewInt(2)), fortytwo.ToInt()))
+ al = &types.AccessList{types.AccessTuple{Address: common.Address{0xaa}, StorageKeys: []common.Hash{{0x01}}}}
+ )
+
+ tests := []test{
+ // Legacy txs
+ {
+ "legacy tx pre-London",
+ false,
+ &TransactionArgs{},
+ &TransactionArgs{GasPrice: fortytwo},
+ nil,
+ },
+ {
+ "legacy tx pre-London with zero price",
+ false,
+ &TransactionArgs{GasPrice: zero},
+ &TransactionArgs{GasPrice: zero},
+ nil,
+ },
+ {
+ "legacy tx post-London, explicit gas price",
+ true,
+ &TransactionArgs{GasPrice: fortytwo},
+ &TransactionArgs{GasPrice: fortytwo},
+ nil,
+ },
+ {
+ "legacy tx post-London with zero price",
+ true,
+ &TransactionArgs{GasPrice: zero},
+ nil,
+ errors.New("gasPrice must be non-zero after EIP-1559 fork"),
+ },
+
+ // Access list txs
+ {
+ "access list tx pre-London",
+ false,
+ &TransactionArgs{AccessList: al},
+ &TransactionArgs{AccessList: al, GasPrice: fortytwo},
+ nil,
+ },
+ {
+ "access list tx post-London, explicit gas price",
+ false,
+ &TransactionArgs{AccessList: al, GasPrice: fortytwo},
+ &TransactionArgs{AccessList: al, GasPrice: fortytwo},
+ nil,
+ },
+ {
+ "access list tx post-London",
+ true,
+ &TransactionArgs{AccessList: al},
+ &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "access list tx post-London, only max fee",
+ true,
+ &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
+ &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "access list tx post-London, only priority fee",
+ true,
+ &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
+ &TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+
+ // Dynamic fee txs
+ {
+ "dynamic tx post-London",
+ true,
+ &TransactionArgs{},
+ &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "dynamic tx post-London, only max fee",
+ true,
+ &TransactionArgs{MaxFeePerGas: maxFee},
+ &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "dynamic tx post-London, only priority fee",
+ true,
+ &TransactionArgs{MaxFeePerGas: maxFee},
+ &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "dynamic fee tx pre-London, maxFee set",
+ false,
+ &TransactionArgs{MaxFeePerGas: maxFee},
+ nil,
+ fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active"),
+ },
+ {
+ "dynamic fee tx pre-London, priorityFee set",
+ false,
+ &TransactionArgs{MaxPriorityFeePerGas: fortytwo},
+ nil,
+ fmt.Errorf("maxFeePerGas and maxPriorityFeePerGas are not valid before EIP-1559 is active"),
+ },
+ {
+ "dynamic fee tx, maxFee < priorityFee",
+ true,
+ &TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))},
+ nil,
+ fmt.Errorf("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"),
+ },
+ {
+ "dynamic fee tx, maxFee < priorityFee while setting default",
+ true,
+ &TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))},
+ nil,
+ fmt.Errorf("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"),
+ },
+ {
+ "dynamic fee tx post-London, explicit gas price",
+ true,
+ &TransactionArgs{MaxFeePerGas: zero, MaxPriorityFeePerGas: zero},
+ nil,
+ errors.New("maxFeePerGas must be non-zero"),
+ },
+
+ // Misc
+ {
+ "set all fee parameters",
+ false,
+ &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
+ },
+ {
+ "set gas price and maxPriorityFee",
+ false,
+ &TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
+ },
+ {
+ "set gas price and maxFee",
+ true,
+ &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee},
+ nil,
+ fmt.Errorf("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
+ },
+ }
+
+ ctx := context.Background()
+ for i, test := range tests {
+ if test.isLondon {
+ b.activateLondon()
+ } else {
+ b.deactivateLondon()
+ }
+ got := test.in
+ err := got.setFeeDefaults(ctx, b)
+ if err != nil && err.Error() == test.err.Error() {
+ // Test threw expected error.
+ continue
+ } else if err != nil {
+ t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err)
+ }
+ if !reflect.DeepEqual(got, test.want) {
+ t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want)
+ }
+ }
+}
+
+type backendMock struct {
+ current *types.Header
+ config *params.ChainConfig
+}
+
+func newBackendMock() *backendMock {
+ config := ¶ms.ChainConfig{
+ ChainId: big.NewInt(42),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: true,
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ BerlinBlock: big.NewInt(0),
+ Eip1559Block: big.NewInt(1000),
+ }
+ return &backendMock{
+ current: &types.Header{
+ Difficulty: big.NewInt(10000000000),
+ Number: big.NewInt(1100),
+ GasLimit: 8_000_000,
+ GasUsed: 8_000_000,
+ Time: big.NewInt(555),
+ Extra: make([]byte, 32),
+ BaseFee: big.NewInt(10),
+ },
+ config: config,
+ }
+}
+
+func (b *backendMock) activateLondon() {
+ b.current.Number = big.NewInt(1100)
+}
+
+func (b *backendMock) deactivateLondon() {
+ b.current.Number = big.NewInt(900)
+}
+
+func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ return big.NewInt(42), nil
+}
+
+func (b *backendMock) CurrentHeader() *types.Header { return b.current }
+
+func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
+
+// Other methods needed to implement Backend interface.
+func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
+func (b *backendMock) FeeHistory(context.Context, uint64, rpc.BlockNumber, []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
+ return nil, nil, nil, nil, nil
+}
+func (b *backendMock) ChainDb() ethdb.Database { return nil }
+func (b *backendMock) AccountManager() *accounts.Manager { return nil }
+func (b *backendMock) ExtRPCEnabled() bool { return false }
+func (b *backendMock) RPCGasCap() uint64 { return 0 }
+func (b *backendMock) RPCEVMTimeout() time.Duration { return time.Second }
+func (b *backendMock) RPCTxFeeCap() float64 { return 0 }
+func (b *backendMock) UnprotectedAllowed() bool { return false }
+func (b *backendMock) SetHead(number uint64) {}
+
+func (b *backendMock) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ return nil, nil
+}
+
+func (b *backendMock) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
+ return nil, nil
+}
+
+func (b *backendMock) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
+ return nil, nil
+}
+
+func (b *backendMock) CurrentBlock() *types.Block { return nil }
+
+func (b *backendMock) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
+ return nil, nil
+}
+
+func (b *backendMock) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
+ return nil, nil
+}
+
+func (b *backendMock) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
+ return nil, nil
+}
+
+func (b *backendMock) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
+ return nil, nil, nil
+}
+
+func (b *backendMock) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
+ return nil, nil, nil
+}
+
+func (b *backendMock) PendingBlockAndReceipts() (*types.Block, types.Receipts) { return nil, nil }
+
+func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
+ return nil, nil
+}
+
+func (b *backendMock) GetTd(common.Hash) *big.Int {
+ return nil
+}
+
+func (b *backendMock) GetEVM(context.Context, core.Message, *state.StateDB, *tradingstate.TradingStateDB, *types.Header, *vm.Config) (*vm.EVM, func() error, error) {
+ return nil, nil, nil
+}
+
+func (b *backendMock) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { return nil }
+func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
+ return nil
+}
+func (b *backendMock) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
+ return nil
+}
+func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
+func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
+ return nil, [32]byte{}, 0, 0, nil
+}
+func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
+func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
+func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
+ return 0, nil
+}
+func (b *backendMock) Stats() (pending int, queued int) { return 0, 0 }
+func (b *backendMock) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
+ return nil, nil
+}
+func (b *backendMock) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ return nil, nil
+}
+func (b *backendMock) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription { return nil }
+func (b *backendMock) BloomStatus() (uint64, uint64) { return 0, 0 }
+func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
+ return nil, nil
+}
+func (b *backendMock) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {}
+func (b *backendMock) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return nil }
+func (b *backendMock) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return nil
+}
+func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
+ return nil
+}
+
+func (b *backendMock) Engine() consensus.Engine { return nil }
+
+func (b *backendMock) AreTwoBlockSamePath(bh1 common.Hash, bh2 common.Hash) bool {
+ return true
+}
+
+func (b *backendMock) Downloader() *downloader.Downloader {
+ return nil
+}
+
+func (b *backendMock) EventMux() *event.TypeMux {
+ return nil
+}
+
+func (b *backendMock) GetBlock(context.Context, common.Hash) (*types.Block, error) {
+ return nil, nil
+}
+
+func (b *backendMock) GetBlocksHashCache(blockNr uint64) []common.Hash {
+ return []common.Hash{}
+}
+
+func (b *backendMock) GetEngine() consensus.Engine {
+ return nil
+}
+
+func (b *backendMock) GetEpochDuration() *big.Int {
+ return nil
+}
+
+func (b *backendMock) GetIPCClient() (bind.ContractBackend, error) {
+ return nil, nil
+}
+
+func (b *backendMock) GetMasternodesCap(uint64) map[common.Address]*big.Int {
+ return nil
+}
+
+func (b *backendMock) GetOrderNonce(common.Hash) (uint64, error) {
+ return 0, nil
+}
+
+func (b *backendMock) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
+ return nil, nil
+}
+
+func (b *backendMock) GetRewardByHash(common.Hash) map[string]map[string]map[string]*big.Int {
+ return nil
+}
+
+func (b *backendMock) GetVotersCap(*big.Int, common.Address, []common.Address) map[common.Address]*big.Int {
+ return nil
+}
+
+func (b *backendMock) GetVotersRewards(common.Address) map[common.Address]*big.Int {
+ return nil
+}
+
+func (b *backendMock) LendingService() *XDCxlending.Lending {
+ return nil
+}
+
+func (b *backendMock) OrderStats() (int, int) {
+ return 0, 0
+}
+
+func (b *backendMock) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) {
+ return nil, nil
+}
+
+func (b *backendMock) ProtocolVersion() int {
+ return 0
+}
+
+func (b *backendMock) SendLendingTx(context.Context, *types.LendingTransaction) error {
+ return nil
+}
+
+func (b *backendMock) SendOrderTx(context.Context, *types.OrderTransaction) error {
+ return nil
+}
+
+func (b *backendMock) XDCxService() *XDCx.XDCX {
+ return nil
+}
diff --git a/internal/flags/categories.go b/internal/flags/categories.go
new file mode 100644
index 0000000000..d58534c0b6
--- /dev/null
+++ b/internal/flags/categories.go
@@ -0,0 +1,45 @@
+// Copyright 2022 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package flags
+
+import "github.com/urfave/cli/v2"
+
+const (
+ EthCategory = "ETHEREUM"
+ LightCategory = "LIGHT CLIENT"
+ DevCategory = "DEVELOPER CHAIN"
+ EthashCategory = "ETHASH"
+ TxPoolCategory = "TRANSACTION POOL"
+ PerfCategory = "PERFORMANCE TUNING"
+ AccountCategory = "ACCOUNT"
+ APICategory = "API AND CONSOLE"
+ NetworkingCategory = "NETWORKING"
+ MinerCategory = "MINER"
+ GasPriceCategory = "GAS PRICE ORACLE"
+ VMCategory = "VIRTUAL MACHINE"
+ LoggingCategory = "LOGGING AND DEBUGGING"
+ MetricsCategory = "METRICS AND STATS"
+ MiscCategory = "MISC"
+ DeprecatedCategory = "ALIASED (deprecated)"
+ XdcCategory = "XDC"
+ XdcxCategory = "XDCx"
+)
+
+func init() {
+ cli.HelpFlag.(*cli.BoolFlag).Category = MiscCategory
+ cli.VersionFlag.(*cli.BoolFlag).Category = MiscCategory
+}
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
new file mode 100644
index 0000000000..427442710a
--- /dev/null
+++ b/internal/flags/flags.go
@@ -0,0 +1,267 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package flags
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "math/big"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strings"
+ "syscall"
+
+ "github.com/XinFinOrg/XDPoSChain/common/math"
+ "github.com/urfave/cli/v2"
+)
+
+// DirectoryString is custom type which is registered in the flags library which cli uses for
+// argument parsing. This allows us to expand Value to an absolute path when
+// the argument is parsed
+type DirectoryString string
+
+func (s *DirectoryString) String() string {
+ return string(*s)
+}
+
+func (s *DirectoryString) Set(value string) error {
+ *s = DirectoryString(expandPath(value))
+ return nil
+}
+
+var (
+ _ cli.Flag = (*DirectoryFlag)(nil)
+ _ cli.RequiredFlag = (*DirectoryFlag)(nil)
+ _ cli.VisibleFlag = (*DirectoryFlag)(nil)
+ _ cli.DocGenerationFlag = (*DirectoryFlag)(nil)
+ _ cli.CategorizableFlag = (*DirectoryFlag)(nil)
+)
+
+// DirectoryFlag is custom cli.Flag type which expand the received string to an absolute path.
+// e.g. ~/.ethereum -> /home/username/.ethereum
+type DirectoryFlag struct {
+ Name string
+
+ Category string
+ DefaultText string
+ Usage string
+
+ Required bool
+ Hidden bool
+ HasBeenSet bool
+
+ Value DirectoryString
+
+ Aliases []string
+ EnvVars []string
+}
+
+// For cli.Flag:
+
+func (f *DirectoryFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
+func (f *DirectoryFlag) IsSet() bool { return f.HasBeenSet }
+func (f *DirectoryFlag) String() string { return cli.FlagStringer(f) }
+
+// Apply called by cli library, grabs variable from environment (if in env)
+// and adds variable to flag set for parsing.
+func (f *DirectoryFlag) Apply(set *flag.FlagSet) error {
+ for _, envVar := range f.EnvVars {
+ envVar = strings.TrimSpace(envVar)
+ if value, found := syscall.Getenv(envVar); found {
+ f.Value.Set(value)
+ f.HasBeenSet = true
+ break
+ }
+ }
+ eachName(f, func(name string) {
+ set.Var(&f.Value, name, f.Usage)
+ })
+ return nil
+}
+
+// For cli.RequiredFlag:
+
+func (f *DirectoryFlag) IsRequired() bool { return f.Required }
+
+// For cli.VisibleFlag:
+
+func (f *DirectoryFlag) IsVisible() bool { return !f.Hidden }
+
+// For cli.CategorizableFlag:
+
+func (f *DirectoryFlag) GetCategory() string { return f.Category }
+
+// For cli.DocGenerationFlag:
+
+func (f *DirectoryFlag) TakesValue() bool { return true }
+func (f *DirectoryFlag) GetUsage() string { return f.Usage }
+func (f *DirectoryFlag) GetValue() string { return f.Value.String() }
+func (f *DirectoryFlag) GetEnvVars() []string { return f.EnvVars }
+
+func (f *DirectoryFlag) GetDefaultText() string {
+ if f.DefaultText != "" {
+ return f.DefaultText
+ }
+ return f.GetValue()
+}
+
+var (
+ _ cli.Flag = (*BigFlag)(nil)
+ _ cli.RequiredFlag = (*BigFlag)(nil)
+ _ cli.VisibleFlag = (*BigFlag)(nil)
+ _ cli.DocGenerationFlag = (*BigFlag)(nil)
+ _ cli.CategorizableFlag = (*BigFlag)(nil)
+)
+
+// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
+// hexadecimal syntax.
+type BigFlag struct {
+ Name string
+
+ Category string
+ DefaultText string
+ Usage string
+
+ Required bool
+ Hidden bool
+ HasBeenSet bool
+
+ Value *big.Int
+ defaultValue *big.Int
+
+ Aliases []string
+ EnvVars []string
+}
+
+// For cli.Flag:
+
+func (f *BigFlag) Names() []string { return append([]string{f.Name}, f.Aliases...) }
+func (f *BigFlag) IsSet() bool { return f.HasBeenSet }
+func (f *BigFlag) String() string { return cli.FlagStringer(f) }
+
+func (f *BigFlag) Apply(set *flag.FlagSet) error {
+ // Set default value so that environment wont be able to overwrite it
+ if f.Value != nil {
+ f.defaultValue = new(big.Int).Set(f.Value)
+ }
+ for _, envVar := range f.EnvVars {
+ envVar = strings.TrimSpace(envVar)
+ if value, found := syscall.Getenv(envVar); found {
+ if _, ok := f.Value.SetString(value, 10); !ok {
+ return fmt.Errorf("could not parse %q from environment variable %q for flag %s", value, envVar, f.Name)
+ }
+ f.HasBeenSet = true
+ break
+ }
+ }
+ eachName(f, func(name string) {
+ f.Value = new(big.Int)
+ set.Var((*bigValue)(f.Value), name, f.Usage)
+ })
+ return nil
+}
+
+// For cli.RequiredFlag:
+
+func (f *BigFlag) IsRequired() bool { return f.Required }
+
+// For cli.VisibleFlag:
+
+func (f *BigFlag) IsVisible() bool { return !f.Hidden }
+
+// For cli.CategorizableFlag:
+
+func (f *BigFlag) GetCategory() string { return f.Category }
+
+// For cli.DocGenerationFlag:
+
+func (f *BigFlag) TakesValue() bool { return true }
+func (f *BigFlag) GetUsage() string { return f.Usage }
+func (f *BigFlag) GetValue() string { return f.Value.String() }
+func (f *BigFlag) GetEnvVars() []string { return f.EnvVars }
+
+func (f *BigFlag) GetDefaultText() string {
+ if f.DefaultText != "" {
+ return f.DefaultText
+ }
+ return f.defaultValue.String()
+}
+
+// bigValue turns *big.Int into a flag.Value
+type bigValue big.Int
+
+func (b *bigValue) String() string {
+ if b == nil {
+ return ""
+ }
+ return (*big.Int)(b).String()
+}
+
+func (b *bigValue) Set(s string) error {
+ intVal, ok := math.ParseBig256(s)
+ if !ok {
+ return errors.New("invalid integer syntax")
+ }
+ *b = (bigValue)(*intVal)
+ return nil
+}
+
+// GlobalBig returns the value of a BigFlag from the global flag set.
+func GlobalBig(ctx *cli.Context, name string) *big.Int {
+ val := ctx.Generic(name)
+ if val == nil {
+ return nil
+ }
+ return (*big.Int)(val.(*bigValue))
+}
+
+// Expands a file path
+// 1. replace tilde with users home dir
+// 2. expands embedded environment variables
+// 3. cleans the path, e.g. /a/b/../c -> /a/c
+// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
+func expandPath(p string) string {
+ // Named pipes are not file paths on windows, ignore
+ if strings.HasPrefix(p, `\\.\pipe`) {
+ return p
+ }
+ if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
+ if home := HomeDir(); home != "" {
+ p = home + p[1:]
+ }
+ }
+ return filepath.Clean(os.ExpandEnv(p))
+}
+
+func HomeDir() string {
+ if home := os.Getenv("HOME"); home != "" {
+ return home
+ }
+ if usr, err := user.Current(); err == nil {
+ return usr.HomeDir
+ }
+ return ""
+}
+
+func eachName(f cli.Flag, fn func(string)) {
+ for _, name := range f.Names() {
+ name = strings.Trim(name, " ")
+ fn(name)
+ }
+}
diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go
new file mode 100644
index 0000000000..681586b46c
--- /dev/null
+++ b/internal/flags/flags_test.go
@@ -0,0 +1,61 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package flags
+
+import (
+ "os"
+ "os/user"
+ "runtime"
+ "testing"
+)
+
+func TestPathExpansion(t *testing.T) {
+ user, _ := user.Current()
+ var tests map[string]string
+
+ if runtime.GOOS == "windows" {
+ tests = map[string]string{
+ `/home/someuser/tmp`: `\home\someuser\tmp`,
+ `~/tmp`: user.HomeDir + `\tmp`,
+ `~thisOtherUser/b/`: `~thisOtherUser\b`,
+ `$DDDXXX/a/b`: `\tmp\a\b`,
+ `/a/b/`: `\a\b`,
+ `C:\Documents\Newsletters\`: `C:\Documents\Newsletters`,
+ `C:\`: `C:\`,
+ `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
+ }
+ } else {
+ tests = map[string]string{
+ `/home/someuser/tmp`: `/home/someuser/tmp`,
+ `~/tmp`: user.HomeDir + `/tmp`,
+ `~thisOtherUser/b/`: `~thisOtherUser/b`,
+ `$DDDXXX/a/b`: `/tmp/a/b`,
+ `/a/b/`: `/a/b`,
+ `C:\Documents\Newsletters\`: `C:\Documents\Newsletters\`,
+ `C:\`: `C:\`,
+ `\\.\pipe\\pipe\geth621383`: `\\.\pipe\\pipe\geth621383`,
+ }
+ }
+
+ os.Setenv(`DDDXXX`, `/tmp`)
+ for test, expected := range tests {
+ got := expandPath(test)
+ if got != expected {
+ t.Errorf(`test %s, got %s, expected %s\n`, test, got, expected)
+ }
+ }
+}
diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go
new file mode 100644
index 0000000000..32935e87f9
--- /dev/null
+++ b/internal/flags/helpers.go
@@ -0,0 +1,296 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package flags
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/params"
+ "github.com/mattn/go-isatty"
+ "github.com/urfave/cli/v2"
+)
+
+// usecolor defines whether the CLI help should use colored output or normal dumb
+// colorless terminal formatting.
+var usecolor = (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) && os.Getenv("TERM") != "dumb"
+
+// NewApp creates an app with sane defaults.
+func NewApp(gitCommit, usage string) *cli.App {
+ app := cli.NewApp()
+ app.EnableBashCompletion = true
+ app.Version = params.VersionWithCommit(gitCommit)
+ app.Usage = usage
+ app.Copyright = "Copyright 2024 XDPoSChain"
+ app.Before = func(ctx *cli.Context) error {
+ MigrateGlobalFlags(ctx)
+ return nil
+ }
+ return app
+}
+
+var migrationApplied = map[*cli.Command]struct{}{}
+
+// MigrateGlobalFlags makes all global flag values available in the
+// context. This should be called as early as possible in app.Before.
+//
+// Example:
+//
+// geth account new --keystore /tmp/mykeystore --lightkdf
+//
+// is equivalent after calling this method with:
+//
+// geth --keystore /tmp/mykeystore --lightkdf account new
+//
+// i.e. in the subcommand Action function of 'account new', ctx.Bool("lightkdf)
+// will return true even if --lightkdf is set as a global option.
+//
+// This function may become unnecessary when https://github.com/urfave/cli/pull/1245 is merged.
+func MigrateGlobalFlags(ctx *cli.Context) {
+ var iterate func(cs []*cli.Command, fn func(*cli.Command))
+ iterate = func(cs []*cli.Command, fn func(*cli.Command)) {
+ for _, cmd := range cs {
+ if _, ok := migrationApplied[cmd]; ok {
+ continue
+ }
+ migrationApplied[cmd] = struct{}{}
+ fn(cmd)
+ iterate(cmd.Subcommands, fn)
+ }
+ }
+
+ // This iterates over all commands and wraps their action function.
+ iterate(ctx.App.Commands, func(cmd *cli.Command) {
+ if cmd.Action == nil {
+ return
+ }
+
+ action := cmd.Action
+ cmd.Action = func(ctx *cli.Context) error {
+ doMigrateFlags(ctx)
+ return action(ctx)
+ }
+ })
+}
+
+func doMigrateFlags(ctx *cli.Context) {
+ // Figure out if there are any aliases of commands. If there are, we want
+ // to ignore them when iterating over the flags.
+ aliases := make(map[string]bool)
+ for _, fl := range ctx.Command.Flags {
+ for _, alias := range fl.Names()[1:] {
+ aliases[alias] = true
+ }
+ }
+ for _, name := range ctx.FlagNames() {
+ for _, parent := range ctx.Lineage()[1:] {
+ if parent.IsSet(name) {
+ // When iterating across the lineage, we will be served both
+ // the 'canon' and alias formats of all commmands. In most cases,
+ // it's fine to set it in the ctx multiple times (one for each
+ // name), however, the Slice-flags are not fine.
+ // The slice-flags accumulate, so if we set it once as
+ // "foo" and once as alias "F", then both will be present in the slice.
+ if _, isAlias := aliases[name]; isAlias {
+ continue
+ }
+ // If it is a string-slice, we need to set it as
+ // "alfa, beta, gamma" instead of "[alfa beta gamma]", in order
+ // for the backing StringSlice to parse it properly.
+ if result := parent.StringSlice(name); len(result) > 0 {
+ ctx.Set(name, strings.Join(result, ","))
+ } else {
+ ctx.Set(name, parent.String(name))
+ }
+ break
+ }
+ }
+ }
+}
+
+func init() {
+ if usecolor {
+ // Annotate all help categories with colors
+ cli.AppHelpTemplate = regexp.MustCompile("[A-Z ]+:").ReplaceAllString(cli.AppHelpTemplate, "\u001B[33m$0\u001B[0m")
+
+ // Annotate flag categories with colors (private template, so need to
+ // copy-paste the entire thing here...)
+ cli.AppHelpTemplate = strings.ReplaceAll(cli.AppHelpTemplate, "{{template \"visibleFlagCategoryTemplate\" .}}", "{{range .VisibleFlagCategories}}\n {{if .Name}}\u001B[33m{{.Name}}\u001B[0m\n\n {{end}}{{$flglen := len .Flags}}{{range $i, $e := .Flags}}{{if eq (subtract $flglen $i) 1}}{{$e}}\n{{else}}{{$e}}\n {{end}}{{end}}{{end}}")
+ }
+ cli.FlagStringer = FlagString
+}
+
+// FlagString prints a single flag in help.
+func FlagString(f cli.Flag) string {
+ df, ok := f.(cli.DocGenerationFlag)
+ if !ok {
+ return ""
+ }
+ needsPlaceholder := df.TakesValue()
+ placeholder := ""
+ if needsPlaceholder {
+ placeholder = "value"
+ }
+
+ namesText := cli.FlagNamePrefixer(df.Names(), placeholder)
+
+ defaultValueString := ""
+ if s := df.GetDefaultText(); s != "" {
+ defaultValueString = " (default: " + s + ")"
+ }
+ envHint := strings.TrimSpace(cli.FlagEnvHinter(df.GetEnvVars(), ""))
+ if envHint != "" {
+ envHint = " (" + envHint[1:len(envHint)-1] + ")"
+ }
+ usage := strings.TrimSpace(df.GetUsage())
+ usage = wordWrap(usage, 80)
+ usage = indent(usage, 10)
+
+ if usecolor {
+ return fmt.Sprintf("\n \u001B[32m%-35s%-35s\u001B[0m%s\n%s", namesText, defaultValueString, envHint, usage)
+ } else {
+ return fmt.Sprintf("\n %-35s%-35s%s\n%s", namesText, defaultValueString, envHint, usage)
+ }
+}
+
+func indent(s string, nspace int) string {
+ ind := strings.Repeat(" ", nspace)
+ return ind + strings.ReplaceAll(s, "\n", "\n"+ind)
+}
+
+func wordWrap(s string, width int) string {
+ var (
+ output strings.Builder
+ lineLength = 0
+ )
+
+ for {
+ sp := strings.IndexByte(s, ' ')
+ var word string
+ if sp == -1 {
+ word = s
+ } else {
+ word = s[:sp]
+ }
+ wlen := len(word)
+ over := lineLength+wlen >= width
+ if over {
+ output.WriteByte('\n')
+ lineLength = 0
+ } else {
+ if lineLength != 0 {
+ output.WriteByte(' ')
+ lineLength++
+ }
+ }
+
+ output.WriteString(word)
+ lineLength += wlen
+
+ if sp == -1 {
+ break
+ }
+ s = s[wlen+1:]
+ }
+
+ return output.String()
+}
+
+// AutoEnvVars extends all the specific CLI flags with automatically generated
+// env vars by capitalizing the flag, replacing . with _ and prefixing it with
+// the specified string.
+//
+// Note, the prefix should *not* contain the separator underscore, that will be
+// added automatically.
+func AutoEnvVars(flags []cli.Flag, prefix string) {
+ for _, flag := range flags {
+ envvar := strings.ToUpper(prefix + "_" + strings.ReplaceAll(strings.ReplaceAll(flag.Names()[0], ".", "_"), "-", "_"))
+
+ switch flag := flag.(type) {
+ case *cli.StringFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.StringSliceFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.BoolFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.IntFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.Int64Flag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.Uint64Flag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.Float64Flag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.DurationFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *cli.PathFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *BigFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+
+ case *DirectoryFlag:
+ flag.EnvVars = append(flag.EnvVars, envvar)
+ }
+ }
+}
+
+// CheckEnvVars iterates over all the environment variables and checks if any of
+// them look like a CLI flag but is not consumed. This can be used to detect old
+// or mistyped names.
+func CheckEnvVars(ctx *cli.Context, flags []cli.Flag, prefix string) {
+ known := make(map[string]string)
+ for _, flag := range flags {
+ docflag, ok := flag.(cli.DocGenerationFlag)
+ if !ok {
+ continue
+ }
+ for _, envvar := range docflag.GetEnvVars() {
+ known[envvar] = flag.Names()[0]
+ }
+ }
+ keyvals := os.Environ()
+ sort.Strings(keyvals)
+
+ for _, keyval := range keyvals {
+ key := strings.Split(keyval, "=")[0]
+ if !strings.HasPrefix(key, prefix) {
+ continue
+ }
+ if flag, ok := known[key]; ok {
+ if ctx.Count(flag) > 0 {
+ log.Info("Config environment variable found", "envvar", key, "shadowedby", "--"+flag)
+ } else {
+ log.Info("Config environment variable found", "envvar", key)
+ }
+ } else {
+ log.Warn("Unknown config environment variable", "envvar", key)
+ }
+ }
+}
diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go
index 5cb51d61f3..89e83b60b5 100644
--- a/internal/jsre/deps/bindata.go
+++ b/internal/jsre/deps/bindata.go
@@ -1,16 +1,15 @@
-// Code generated by go-bindata. DO NOT EDIT.
+// Code generated for package deps by go-bindata DO NOT EDIT. (@generated)
// sources:
-// bignumber.js (17.314kB)
-// web3.js (403.868kB)
-
+// bignumber.js
+// web3.js
package deps
import (
"bytes"
"compress/gzip"
- "crypto/sha256"
"fmt"
"io"
+ "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -20,7 +19,7 @@ import (
func bindataRead(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
- return nil, fmt.Errorf("read %q: %w", name, err)
+ return nil, fmt.Errorf("read %q: %v", name, err)
}
var buf bytes.Buffer
@@ -28,7 +27,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close()
if err != nil {
- return nil, fmt.Errorf("read %q: %w", name, err)
+ return nil, fmt.Errorf("read %q: %v", name, err)
}
if clErr != nil {
return nil, err
@@ -38,9 +37,8 @@ func bindataRead(data []byte, name string) ([]byte, error) {
}
type asset struct {
- bytes []byte
- info os.FileInfo
- digest [sha256.Size]byte
+ bytes []byte
+ info os.FileInfo
}
type bindataFileInfo struct {
@@ -50,21 +48,32 @@ type bindataFileInfo struct {
modTime time.Time
}
+// Name return file name
func (fi bindataFileInfo) Name() string {
return fi.name
}
+
+// Size return file size
func (fi bindataFileInfo) Size() int64 {
return fi.size
}
+
+// Mode return file mode
func (fi bindataFileInfo) Mode() os.FileMode {
return fi.mode
}
+
+// Mode return file modify time
func (fi bindataFileInfo) ModTime() time.Time {
return fi.modTime
}
+
+// IsDir return file whether a directory
func (fi bindataFileInfo) IsDir() bool {
- return false
+ return fi.mode&os.ModeDir != 0
}
+
+// Sys return file is sys mode
func (fi bindataFileInfo) Sys() interface{} {
return nil
}
@@ -85,11 +94,11 @@ func bignumberJs() (*asset, error) {
}
info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
- a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5b, 0x75, 0xfc, 0x15, 0x5e, 0x7d, 0x27, 0x1a, 0x9a, 0xb5, 0xfb, 0x16, 0x90, 0xf4, 0x93, 0xac, 0xcb, 0x6c, 0x9c, 0xcd, 0x68, 0xe6, 0xd0, 0x3a, 0xcf, 0xa3, 0x83, 0x5c, 0x20, 0x34, 0x66, 0x45}}
+ a := &asset{bytes: bytes, info: info}
return a, nil
}
-var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\xcc\x90\xbd\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\xeb\x97\x9f\xde\x1e\x7e\xfc\xf4\xe3\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\x3f\x4a\x04\x31\xc0\x3e\x47\xd7\x01\xcb\x38\x48\x8a\x1d\x04\xcc\xf7\x6d\x1f\xf4\x21\xe4\x88\xe6\x76\xd6\xae\x77\xd6\xd6\x3c\xfd\xe8\x2f\xb2\xb4\xe0\x58\xdb\x25\x09\x3d\x37\xfa\xda\xf9\x72\xdd\xdd\xa9\x2e\xd5\x07\xe9\x25\x5b\x8e\x8b\x94\x35\xee\x81\xad\x6b\xb7\x1f\xe5\x62\xce\x35\x42\x18\x39\x4a\xa4\x08\xbb\x96\xf5\x75\x96\xd8\x87\x79\xeb\x0c\x04\xb6\x3b\xff\x3e\xee\x1c\x0f\x1f\x7d\x77\xf2\xb0\xfb\xef\x93\xee\xb3\x41\x97\x8f\xd3\x3c\x38\x94\x76\xeb\xba\xf7\xa5\x85\x49\xb1\x35\xfa\xae\xd7\xe2\xf4\xd6\x1a\x6d\x3e\xbe\x3e\xe9\x7d\xf3\x3b\x93\xf7\xf3\x34\x8d\x6b\x68\xfb\x94\x81\x94\x10\x36\xcb\x93\xff\x73\x2a\x85\x5f\x8f\xf5\xcf\x13\x94\xbc\x8d\x3f\xea\xc8\x18\x7a\x76\x53\x1a\x66\x85\x57\x21\x62\x0e\x6f\x53\x30\x4b\x5d\x91\x7c\xcd\x22\x15\xb4\xcb\x5b\xac\x2a\x7b\x13\xaa\xfd\x0f\x43\xad\x49\xb3\x0f\xff\xa7\x11\xd1\x8a\xfe\xd4\x53\xec\x93\xdf\x9b\x62\xd9\x1e\xa6\x48\xb6\xf0\xd3\x6c\x31\xa3\x04\x36\x3b\x20\xdc\xbe\x8f\x72\x59\xae\xfa\x21\xe8\x12\x7e\x3e\x46\xbf\x4f\x70\xc6\xb6\xf1\x65\xd2\x2f\x11\x5b\xab\xfa\xf9\xd4\xa8\x47\x14\xf5\x50\x39\x74\xf2\xc6\x64\xce\x4a\xaf\x44\xe7\xbc\x80\x43\xe8\x2c\x79\x55\x4a\x37\xcb\x54\x91\x3a\x6f\xb4\xb2\xf4\xcd\x88\x9d\x55\xc2\x49\xfd\xcb\x66\xef\xba\x7b\x33\xc2\x17\xbd\xab\xa7\xfc\x6f\x9b\x50\xfe\xe0\x21\x74\xf8\xe3\x2c\xca\xc9\x24\x8a\x29\xa3\xd4\x45\x90\x15\x24\x9d\x90\x73\x7a\xba\xdd\xff\x25\xef\xaf\x01\x88\xf8\x62\x00\x93\x8c\x52\x92\xa7\x93\xe2\x3c\xc8\xe8\x88\x5c\xa6\x4b\x32\x0e\x12\x92\xd1\x30\xca\x8b\x2c\x3a\x5d\x16\x94\x44\x05\x09\x92\x70\x90\x66\x64\x9e\x86\xd1\xe4\x12\xea\x88\x0a\xb2\x4c\x42\x9a\x01\xc1\x17\x34\x9b\xe7\xac\x1d\xf6\xf1\xd3\xdb\x23\xf2\x9a\xe6\x39\xcd\xc8\x4f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\xd7\xd1\x98\x26\x39\x25\x41\x4e\x16\x2c\x25\x9f\xd1\x90\x9c\x5e\x0a\x2a\xa2\xe4\x47\xd6\x99\x0f\xa2\x33\xe4\xc7\x74\x99\x84\x01\x1b\x73\x8f\xd0\xa8\x98\xd1\x8c\x9c\xd1\x2c\x67\x33\xb4\x2d\xdb\x12\x35\xf6\x48\x9a\x41\x2d\x9d\xa0\x60\x63\xc8\x48\xba\x60\x05\xbb\x24\x48\x2e\x49\x1c\x14\xba\xac\x8b\x02\x3d\xd2\x90\x44\x09\x54\x3b\x4b\xe5\xca\x8e\x0a\x72\x1e\xc5\x31\x39\xa5\x64\x99\xd3\xc9\x32\xe6\x82\xe3\xe9\xb2\x20\x3f\x1f\x7c\x7c\x75\x78\xf4\x91\xec\xbd\xfd\x17\xf9\x79\xef\xfd\xfb\xbd\xb7\x1f\xff\xb5\x43\xce\xa3\x62\x96\x2e\x0b\xc2\x24\x4a\xa8\x2b\x9a\x2f\xe2\x88\x86\xe4\x3c\xc8\xb2\x20\x29\x2e\x49\x3a\x81\x2a\xde\xbc\x7c\xbf\xff\x6a\xef\xed\xc7\xbd\xe7\x07\xaf\x0f\x3e\xfe\x8b\xa4\x19\xf9\xf1\xe0\xe3\xdb\x97\x1f\x3e\x90\x1f\x0f\xdf\x93\x3d\xf2\x6e\xef\xfd\xc7\x83\xfd\xa3\xd7\x7b\xef\xc9\xbb\xa3\xf7\xef\x0e\x3f\xbc\xec\x13\xf2\x81\xb2\x8e\x51\xa8\xa1\x1e\xd1\x13\x98\xb3\x8c\x92\x90\x16\x41\x14\xcb\xf9\xff\x57\xba\x24\xf9\x2c\x5d\xc6\x21\x99\x05\x67\x94\x64\x74\x4c\xa3\x33\x1a\x92\x80\x8c\xd3\xc5\x65\xe3\x89\x84\xca\x82\x38\x4d\xa6\x30\x6c\x45\x65\x84\x1c\x4c\x48\x92\x16\x3d\x92\x53\x4a\xbe\x9f\x15\xc5\x62\x34\x18\x9c\x9f\x9f\xf7\xa7\xc9\xb2\x9f\x66\xd3\x41\xcc\x2b\xc8\x07\x3f\xf4\xd7\x1e\x0e\x24\xb3\xfd\x1b\x90\xed\x38\x0d\x69\xd6\xff\x05\x58\xe4\xdf\x82\x65\x31\x4b\x33\xf2\x26\xc8\xe8\x67\xf2\xbf\x69\x41\xcf\xa3\xf1\xaf\xe4\xfb\x39\xfb\xfe\x1b\x2d\x66\x21\x3d\xeb\x8f\xd3\xf9\x0f\x00\x1c\x06\x05\x25\x5b\xc3\xcd\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x41\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1c\xc0\x65\x09\xe4\x8b\xcb\x24\x98\x47\x63\xc9\xc6\x51\x89\x90\xe7\x00\x8f\xf2\x95\xfc\x50\x64\x51\x32\x35\xcb\xe4\x90\xe6\x83\x7e\x4f\x03\x6b\x8c\x19\x0d\xbc\x63\x3c\x72\x41\x97\x65\xb0\x9e\x6e\xab\xfe\x02\x70\x94\x8b\x01\x1a\x9c\x39\x47\x55\xf4\x60\x87\x15\x7c\x5a\x5a\x88\xa3\xfc\xbe\xaa\x02\xb6\x11\x0e\x7c\x75\xa5\x4e\x8f\xa4\x04\x7a\x2f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\xec\x33\xfa\x44\x12\x80\x58\x49\x9c\x43\x84\xa4\x48\x09\x4d\x18\x0d\x0f\x42\xca\xfe\x53\xad\x30\x66\x1c\x70\x36\xc9\xb8\x92\x90\x6b\xcd\x8d\x99\xd7\x8d\x47\xcc\xc0\x72\x73\x67\x86\x24\xb2\x0b\x35\xe4\x46\x17\x81\xf7\xcf\x69\x31\x4b\x43\x4f\xb7\xb8\x72\x3d\xcd\xe6\x84\x4b\x2e\xa9\x31\x23\x6b\x84\xaf\x41\x51\xfc\x93\x98\x19\x91\x45\xfe\x06\xbd\x27\x5f\x38\xf1\x5c\x2b\xb1\xfc\x6f\x1c\xf3\x39\xf9\x82\x2b\xbb\x86\x2c\x78\xab\x90\x93\x2f\xf0\xae\xe1\x9a\x88\xcf\x88\xf1\x06\x2e\x11\x31\x32\x84\xbe\xb0\x9d\x88\xb1\x7b\x40\x88\x81\x0c\xb4\x53\xe3\x2e\x39\x38\x92\x28\x62\xd8\xcc\x4d\xf1\x0e\x61\xad\x3f\x89\xe2\x82\x66\x1d\x54\xb6\x8b\x74\x10\x82\x8a\x0a\x21\x14\x48\x22\x00\x9d\x42\xf7\x78\x78\xb2\xc3\xf9\x67\x34\x21\x9d\x75\xdc\x08\xae\x83\x3f\xd0\xe0\x4f\x39\xda\x51\x72\x16\xc4\x51\xa8\x69\x80\xd5\xb8\x3e\x22\x6d\xb2\x41\x70\xe5\x6b\x58\xd6\xc0\x35\x9b\x14\x58\x42\x69\x64\x11\x07\x51\xc2\xe9\xcb\x9a\x46\x0e\xf0\x4e\xe4\x94\xcf\xa2\x48\x3f\x3c\xfd\x85\x8e\x8b\x6b\xab\x42\x39\xc9\xba\x1c\xaf\x36\xb4\xe0\xca\xa7\x0e\x75\xc3\x99\xb9\x1e\x2f\x6f\x09\x5c\x30\x69\xa8\x58\xde\x39\x66\xc0\x27\x3d\x72\x0c\xe0\x27\xdd\x66\xa8\x89\xa3\x1c\x24\x20\xbe\xf8\xca\xb1\x93\x63\x34\x00\x0b\xe0\xd8\xf1\xa5\x2f\x74\x81\x32\xc4\x38\xcd\x36\xc2\x4d\xee\x2e\x7d\x81\x9d\xbc\x8c\xbe\x73\x49\xe0\x53\x5a\xe0\x15\x98\x0b\xce\x21\x48\x96\x15\x13\x7d\x63\x25\x8c\x1a\xfa\xf3\x60\xd1\x29\xe3\xb1\xa0\x95\xf3\xac\x11\x83\x77\xf2\x9a\x3b\xbc\xa7\xc7\x50\xe4\x84\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x0e\x27\x93\x9c\x16\x4e\xa7\x32\x1a\x2e\xc7\x14\xf5\x2b\x18\x8f\x7b\xa4\xa6\x73\x80\x9d\x22\x28\xa2\xf1\xbb\x20\x2b\x5e\xc3\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfa\xf4\xfc\xe0\xa7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfa\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x5f\xd3\x49\xd1\xe1\x5f\x45\xfa\xf1\x3c\xcd\xf7\x15\x16\x45\x9b\xfd\x22\x15\xe2\xd2\xe6\x93\x6e\x8f\x3c\x79\x6c\xde\xf0\xe0\xdd\x12\x86\xd3\xe1\x8d\x98\x06\x18\xe6\xc4\xcb\xc3\x6f\x09\xce\x9f\xab\xb3\xb1\x79\x68\x5e\x15\x87\xae\xd4\x61\x60\xd1\x83\x90\x22\x7d\x45\x2f\xe4\xb8\xf3\xe5\x69\x5e\x64\x9d\x2d\x84\xbf\xd8\xba\xda\xe7\xc5\xa5\x96\x7b\x83\x3c\xd9\xee\x92\x01\x46\x91\x8d\xee\xf7\xd1\x74\x56\x88\x62\x3d\x12\x93\x87\x5f\x19\x9f\x62\x07\xbe\x53\xb4\x96\xca\x74\xb7\xc6\xae\x3c\x9e\x99\x68\x55\xda\xb9\xdf\x6d\x06\x2c\xb5\x29\x6f\xac\xdb\xe7\x6b\x7e\x83\xd4\x4f\x50\x1d\xa7\xe3\x92\x7c\xf9\x8a\xf8\x20\xf3\x6f\x3b\x77\xca\xb8\xb3\xf9\xac\x4d\xb2\x74\x7e\x54\x4c\x9e\xde\x4f\x9c\x67\xe2\xc4\x3b\xa3\x32\x46\x26\x5e\x21\xc9\x49\x63\xdf\x34\x48\x56\x67\x64\xf6\x93\xa3\xf2\x39\x6b\x0f\x6f\xf7\xd7\x26\x1b\xa2\x7a\xf2\x8c\x90\xf6\x66\x9b\x8c\x48\x7b\xd8\xbe\x3d\x8f\xaa\xc3\x24\x3b\xb1\xb2\x52\xff\x60\x70\x39\x61\x82\xf1\x7c\x19\x17\x11\x17\x2a\x4f\x2f\xc9\xd6\x7f\xe6\x4c\x3c\x57\x36\x74\x01\xab\xb9\xa0\x53\x9a\x55\x6c\x25\xef\x45\xad\x75\xfb\xf7\xaa\x33\x22\x6c\x99\x4b\x66\x44\xa0\xc9\xa2\x3e\x86\x35\xd5\xa2\xda\x5c\xa3\x39\xcd\xad\xac\xad\x6e\x7f\x91\x9e\x77\x36\xb7\x9e\x76\xbb\x26\x4a\xf7\x67\x74\xfc\x99\x44\x13\x03\xa7\x48\x2c\xb2\x10\x91\x47\xd3\x84\x86\x07\xf9\x5b\x9d\xed\x28\xa2\x55\x1d\x33\x7a\x21\x7a\x6c\x22\x43\x12\x2d\x1c\xfa\xa0\xed\xc2\x94\xc4\x52\x76\x64\x39\x8f\x98\x18\x1e\xc4\xb9\xb6\x5a\xb6\x5b\xaf\xc5\x97\x0f\x43\x92\xdd\x0c\x7b\x64\xb3\xdb\x23\x9b\x4f\x90\x3c\xb2\xd5\x35\x72\xbb\x64\x77\x77\x97\x91\xac\x97\x0a\x33\xc6\x3e\x1e\x05\x31\x74\x8a\x70\xd5\x81\xbe\xf0\xe0\xa2\xa6\x4b\x44\x5c\x91\x60\x0b\x81\x06\x79\x38\x76\xb0\x0c\x67\x5a\x30\xac\x68\x57\x09\x87\xb0\x2c\xa2\x29\xe1\x72\xba\x45\x6f\xaa\x0b\x06\xfe\x0c\xa3\x58\x06\xcc\xe7\x71\x97\xf7\x06\xe9\x32\x3b\x5d\x72\x75\x45\x5a\xc3\x96\xd0\x11\x0f\x06\x64\xac\xa8\x88\x09\xcf\x72\x22\x55\xeb\x1c\x28\x2a\xf8\x44\x2b\x49\xdb\x15\xb2\xe5\xfd\xad\x35\xcf\x62\x6e\x3d\x2a\x48\xcf\xfc\xf2\x29\x9d\x47\xc9\xd2\x5e\x05\xed\xc9\x2d\xff\xda\x50\xb7\xac\x7c\x53\x5d\x8f\x35\xe8\xd0\x0d\x28\x68\x59\x4d\x42\x47\x95\x34\xe4\xa3\x1e\xba\x12\xf9\x88\xe6\x5d\xc2\x39\xba\x0b\xca\xf9\x3a\x28\x13\x2c\xbf\x0c\x65\x0e\xef\xae\x45\x19\x60\x0c\x89\xc4\x26\x8a\x44\x73\x2e\x8a\x1c\x66\xee\xb3\x38\xb7\x16\xa3\x80\xe9\x87\xd1\x59\x14\xd2\xf0\xf9\x65\x05\x0f\xbf\x09\x35\xd5\xe0\xe6\xe8\xae\x91\xb3\x2c\xc5\xce\xd1\xca\xe8\x39\xba\x0d\x7e\xdc\x5b\x58\x5e\xb5\x42\x51\x99\xc4\xa5\x1f\x4c\x37\xc6\x8b\xdc\xd9\xcc\xb9\x28\xc5\x91\x68\xda\x45\x91\x23\x9f\xf9\x30\xe4\x59\x5e\xb0\x5f\xdd\x52\x60\xdb\x6c\x93\x67\x7c\x6b\x16\x9e\x31\x56\xc3\x66\xe9\xc9\x11\xbd\xcb\xad\xd8\xfb\x62\x3a\xd1\x88\x63\x12\x44\xc5\xd9\xc6\x11\x3d\x92\x60\x4e\xf9\x03\x1f\xf6\xcb\x12\xc1\x04\x0c\xab\x53\xd5\xe0\xc1\xbc\x73\x08\x85\x36\x7a\x04\x2b\xcb\x59\x21\xf1\xc4\x9a\xec\x92\xb2\x97\xba\x0f\xbb\x03\x74\xa4\xc9\xa3\x5f\x05\x4f\xcc\xe1\x96\x4a\x94\x3f\xde\x3c\x31\x45\xe1\xf6\xf0\x82\x89\xcc\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x99\x6c\x91\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x14\x7e\x67\x13\xb4\x2a\xfa\x4b\x55\x01\xce\x26\xa3\x8e\x88\x16\x1f\xe0\x88\x13\x97\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\xa7\x4a\xdf\x0c\x58\x15\x71\xe9\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x95\xf3\xe1\x7b\x8e\xe5\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\xc7\xf9\xdf\x10\xf3\x52\xab\x03\xda\x81\x66\xf8\x5f\x6d\x02\xb4\x7b\x9a\xb2\x19\xd8\xd3\x0e\x6c\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xc0\x66\x05\x69\xca\xc0\x5d\x6b\x78\xd1\x22\x1b\x44\x9c\x71\x00\xed\xfc\xb7\x32\x2b\x78\x3c\xec\x11\x9c\x54\xe6\x33\xe0\x8b\x34\xfd\x40\x67\xcd\x91\xf5\xdd\xb3\x61\x60\xc5\x8e\x9c\x14\x07\x0e\x2f\xf0\x51\x59\x86\x53\x8a\x23\x73\xe4\x26\xb9\xfd\x48\xd3\x78\x64\x27\x38\x50\x4c\x02\x19\xd9\x09\x18\x4a\x89\x65\x23\x3b\xc1\x85\x3a\x72\xc0\x8e\xbc\x70\xb8\x51\x9d\xe2\xa9\xcf\x05\x3c\xf2\x43\xe2\xc1\xea\x14\x0f\x1c\xc6\x36\x4a\x72\x21\x7d\xd3\xe3\xe6\xb8\xe5\xcc\x09\xc2\x69\x2e\xac\xa0\xfa\x91\x77\xdd\x5d\xcb\x6b\x5d\xf3\x72\xa8\x35\xda\x7c\xda\x6b\x99\x97\x4a\xad\xd1\x16\x58\x30\xc0\xc2\x68\x8d\x36\x37\x7b\x2d\x7c\x35\xd5\x1a\x99\x9f\xd7\x27\xbd\xcd\xe1\xef\xec\xd2\xe5\x80\xdb\xc6\x57\xf8\x20\x8a\x92\xa2\xcc\x05\x91\xb8\xbd\x8a\x92\x82\x7b\x67\x61\x3f\x1e\xab\x5f\x27\x3a\x71\x1b\xfd\xb6\x9c\xb7\x44\x49\xc1\x5d\xb7\x44\x49\xf1\xe4\xb1\x02\x7b\xaa\x2b\xda\xfa\xe6\x49\x49\x5d\x0c\xbe\xc6\x95\x91\x7d\x34\xfc\x8a\xde\xb8\x00\xdc\x36\x43\x38\x48\x8a\x15\x2d\x2f\x8c\x12\x15\x06\x17\xd0\x5c\x45\xc9\x1b\x99\x57\x44\x49\x21\x45\xc5\x67\x37\x72\xe9\xc2\x7b\x55\x6f\x06\xb1\xd9\x28\x8a\xdd\xbd\x1d\xc4\xbd\x1d\xc4\x9f\xd7\x0e\x82\x68\x43\x08\x2e\x2a\xdd\x91\x0d\x44\x03\xd3\x06\x9b\xd5\x73\xd3\x85\x14\x0c\xd2\xb5\xe7\x8e\xbe\x47\x42\x3d\x9f\xd1\x44\xbd\x57\xec\x71\xdb\x6f\x26\x80\x2b\x07\x0e\x52\xb2\x1c\x78\x6d\x23\x2c\xf5\xb7\xfd\x3c\x11\x38\xa9\x94\x1f\xf9\xff\x57\x57\xa4\xdd\x46\x7c\x36\x95\x2f\x17\xf8\x8f\x1d\xf4\xd4\x30\x4a\x44\xeb\x8d\x3d\x7e\x4c\x69\x81\x4d\x7e\xc1\x80\xbc\x9d\xcb\x87\xa0\xc0\x4b\x58\x25\x86\xb5\xbb\x96\xef\xb9\xb1\xab\x29\x45\x4b\x35\x93\xae\x15\x57\x46\x3a\xb2\x8f\x5d\xc3\xa0\x1d\xd0\x83\x0d\xda\xed\x46\x2a\x4d\xd1\xc0\xca\xdf\x38\x76\xe0\xeb\xc7\xc6\xc8\x18\x67\x94\x11\x93\x5c\x0f\xa6\x5b\x16\x4e\xee\x61\x34\x99\x50\x30\x48\xe6\x28\xb7\xce\x25\xe7\xea\x5d\x08\x3e\x8e\x48\x94\x88\x59\x92\xb6\xcb\x89\xf7\x10\x62\x1e\x5d\xd8\x76\xe8\xeb\x47\xb0\xe0\x1c\x46\xf5\xa2\x1c\x95\xe7\xfe\x37\xb3\x26\xdd\x95\xde\xea\x69\x82\x54\xa4\xba\x0a\x46\xd3\xf9\x69\x94\xb8\x1e\x6e\x8a\x74\x4a\x19\x77\x67\x35\xd0\x69\x9f\x2f\xaa\x60\xb1\xa0\x09\xac\xa5\x20\xe1\x6f\x20\x2c\xec\x8a\xda\xea\xee\x61\x04\x63\x9a\x45\x63\xc6\x9e\x64\xaf\xea\x0b\x8b\x0b\xd4\x74\x22\x60\x61\x1f\xaa\x44\xad\x1c\x5e\x9d\xde\xaf\x0a\xad\x4a\x6f\xc1\xaf\x4c\x76\x48\x3d\x76\xc7\x41\x1c\x0b\xfc\xca\x6b\x1c\x3e\xa2\x59\xa0\x97\x6e\x1e\xfd\x2a\x9c\x0b\xc2\x75\xdd\x2c\xc8\x7b\xec\x7f\x49\x68\xe0\xfe\xd7\x73\x6f\x87\xf1\xad\x6c\x41\xfd\x3a\xd3\x4a\xd4\xf8\xbd\x33\xf9\x16\xae\x58\x15\xeb\xbb\xbb\x20\x5d\x4c\xa2\xc4\x7a\xab\x54\x87\x04\xed\xb5\x48\x54\x25\x6e\x98\x6d\xa5\x01\xcf\xdd\xcb\x9f\x97\x1f\xfd\xb9\xc6\xd7\xd5\xd0\x34\x58\x66\x46\xed\x55\x83\x5e\x87\x51\x6b\x17\x00\x5d\xf2\x8c\xb4\xdb\x64\xd4\xcc\x20\x0b\xa1\xcc\x6b\x96\xb5\x02\xde\x18\xef\xe7\xca\x09\x25\x33\xfa\x9e\x7b\x69\xfd\x85\x1f\x67\x72\xef\x91\xb7\xc2\x01\x66\xf8\xc1\x1c\x13\x19\x90\x78\x25\x16\x75\x63\x5e\x14\x82\x5f\x25\x1b\x7f\x3e\xff\x4c\x6a\x79\xed\x10\x7e\xe5\x47\x4a\xe8\x4e\x4c\x58\x67\x75\xd4\x19\xdb\x5a\x09\xee\xd0\xa6\xe4\x47\x9e\x4c\x08\xe4\x25\x7c\x03\x2c\xd2\xf9\xa2\xb8\xc4\x2a\xc1\x06\x9b\x68\xed\x2a\x34\xe9\x11\xb1\xa7\x11\x48\x1f\x2b\xe0\x46\x7a\x9c\x2a\xf5\x35\xe5\xc5\x44\xe5\x40\x44\x95\x75\x63\x30\x2e\x56\x36\x3c\x62\xc1\x4d\xc6\xa1\x1f\xe3\x95\xfb\x87\x7a\x1d\xe5\x85\xf3\xf2\xef\xd8\x18\xcd\x89\xc7\x29\x54\xe5\xe8\x75\xcd\xee\xf6\xa2\xde\x05\xc9\x9b\xfa\xe5\x22\xe4\x96\xad\xe2\x1d\x9c\x52\x45\x16\x69\x81\xde\xba\xf2\xc2\x52\x38\xe2\x7e\x87\x88\xf1\xb6\x4f\x3d\x21\x14\xa0\xe6\xb3\x22\x63\x6f\x53\xeb\x91\x6f\x5f\x25\x0b\xd2\xbe\xfd\xb2\x9d\x85\x98\xcd\x93\x5d\xdc\x63\x0d\x8b\x87\xb1\xb1\xeb\x2a\xfa\xc5\x6b\x2d\xf7\x85\x16\x87\xd4\x22\x50\x27\xc5\xaf\x6e\xd5\xab\xb9\xc1\x40\x4e\x37\x3d\xa3\xd9\x65\x31\x03\x5f\x24\xa8\x1e\x8c\x1d\xd7\xf1\x94\xb4\x48\x73\xf0\x63\xbc\xd4\xf5\xdf\x50\x28\xdf\x4b\x77\xda\x84\xab\x74\xbe\xee\x91\x76\x5b\x2a\xdf\x2b\x94\x14\xef\xf8\x2c\x59\x3a\x3d\xa5\xbe\xbb\x3e\xe9\x6d\x36\x8a\xb5\xf7\x15\x75\x72\x70\x1b\x5d\xad\x94\xcb\x18\x48\x89\x56\x4e\x9a\x99\xb1\xff\xb9\xaa\x0c\x7e\x3d\xd6\x3f\x4f\x50\xf2\x36\xfe\xb0\x74\x73\x2c\x8d\x2b\xe7\xd8\x2f\xa9\x9d\x63\xbf\x9f\xa2\xea\x90\x7e\xce\xa9\xb1\x81\x86\xce\xb9\x7b\x5f\x45\x45\xc7\x0a\xaf\xa2\xa3\xe3\xf0\xb6\x92\x8e\xa5\xae\xa8\xa5\x33\x8b\x54\xa8\xe9\x78\x8b\x55\x65\x6f\xa2\xa8\x63\xb8\x2d\x51\xd4\x35\x73\x94\x2f\xba\xd5\x40\x51\xd7\x28\x9a\xd7\xd7\x7a\x5c\xe7\xb9\xfd\x5b\x85\x3c\x78\xf1\x55\x08\x44\x96\xb0\x49\x84\xa7\xaf\x48\x24\x76\xa1\x0a\x32\x91\xed\x56\x97\xbf\x91\x4e\x97\x4b\x52\x4d\xde\xcc\x79\xda\xbb\xdb\xd7\x72\x6a\x94\x0d\xe8\xee\xee\xa3\x8f\x54\xbe\xdf\xf1\xf0\x61\xe4\xe2\x36\xca\x9b\xfb\xb6\x1d\xd3\xac\x08\xa2\xc4\xef\xdf\xd6\x41\x24\xbf\x4d\xaa\x21\x6a\x0e\xd4\x37\xd3\xab\xc9\x5a\x14\xb1\x32\x6a\xbd\x41\x14\x34\x9b\xb3\x23\x7f\x34\x81\x9a\xcd\x7e\x87\xc2\x6b\x2d\x99\x46\x67\x34\x91\x26\x2d\xe6\x91\xba\xcc\x5d\xae\x65\xff\xc2\x8f\xd9\xda\xe2\x16\xb0\xcc\x2b\x77\xda\xf5\xdb\xdf\x62\x88\xe6\x4b\x84\x3b\xa7\x6d\x15\x5e\xe1\x38\x3d\xa3\x59\x76\x9e\x45\x45\x41\xc1\xdc\x8b\xf7\xaa\x45\x36\xa0\xf7\x8d\x71\x77\x0e\x5a\xf6\x1c\x3f\xe4\x07\x2b\x08\x7d\x14\x8d\x12\x81\xc2\xc2\xf5\x3b\x6c\xbf\xb5\x6f\x84\x4c\x57\x2b\x69\x35\xa7\xb5\xb6\x25\x78\xf3\xb8\x10\xf0\x63\x70\x30\x00\x55\x78\x30\x67\xab\x02\xbc\x1e\x0a\x6d\x16\x1b\x2f\xe3\x04\x94\xdf\x31\xc4\xd1\x67\x4a\x02\x92\x47\xc9\x34\xa6\xca\x0f\x17\x40\xf6\x0d\x93\x68\xa0\x60\xee\x66\x86\xbb\xe5\xe0\xad\x5d\x5d\x91\xe3\xf6\xf1\xe6\x49\xfb\xa4\xab\x84\xc1\x1a\x37\x00\xa2\x7b\x26\xde\xd9\x17\x76\x6d\x58\x22\xba\x73\x1b\x28\x8e\x0a\xb0\x55\xd8\xec\x91\x47\x60\x8f\x3d\x84\xbe\x6c\x62\x47\x34\xba\x43\x8e\x20\x2b\x1d\x35\xf4\xa4\x6b\x87\xb2\xd3\x82\x74\xe8\xf0\x50\x02\xea\x06\x06\x03\x12\xc4\x31\x39\x0d\xf2\x68\xcc\xfd\x1f\xc0\x63\x81\xed\x2d\xa1\xc0\x89\x53\x76\x32\x96\xbd\xe9\x91\xed\xad\x3a\xa3\x13\x73\x61\x0b\x8e\x26\x4f\xe0\x52\x17\x49\xe8\x14\x04\x48\x08\x0a\x75\x7c\xd2\x22\xbb\x3f\xc0\xfa\xd4\x69\x8f\x79\x62\xa5\x32\x6d\x4f\xd6\xb6\x2a\x07\x98\xd1\xd2\x9e\x55\xac\x76\xdc\x6a\x29\xcd\x6a\xb7\x5f\x86\x43\x18\x87\xe8\x76\xac\x6d\x14\x15\x79\xf0\x80\xe0\xef\x63\xf4\x1b\xb9\x80\x3b\x91\xbb\xae\x8a\x8c\x31\x98\xde\x68\x6e\xc4\xf2\xad\x9a\x1a\x39\x0b\xe6\xdc\x88\x09\x33\xa7\x06\x79\x5c\xbb\xe5\xcc\x58\xfd\xaa\x98\x18\xd4\xe6\xd7\x9e\x97\xbb\x9c\x18\xd3\xf5\x89\x66\xa4\x68\x26\xe0\x6c\xd4\x02\x5b\x84\x2d\x8e\x74\x7e\x48\x6a\x09\x63\x85\x4d\x31\x15\x9b\x8f\x15\xe0\xd6\xc9\xf1\xb6\x00\x95\x69\x1c\x44\x41\x6c\x9e\x58\x09\xfa\xdb\xdd\x1d\x00\xab\x37\xd8\x1e\xf0\x58\xc4\x10\xeb\xf7\x04\xd4\xd8\x1d\x4d\x64\x34\x21\x1d\x94\x85\x38\xa4\xcd\x8f\x6f\x38\xb1\xc0\xb0\x7d\xaf\x21\x36\x2b\xa6\x5c\x6c\x12\xf2\x54\xed\x9b\x67\x98\x37\xdf\x54\xb7\x54\xfc\x3d\x67\xc2\xc5\x67\xcb\x98\x77\xa3\xa2\x63\xb3\x72\x3c\xdd\xda\xfb\x5a\xa3\x79\x56\x19\x7c\x28\x22\xbf\x74\x7e\x0d\x2f\x8a\xa5\xbb\xbd\xf0\x56\x14\x07\x79\x41\x8e\x4f\x98\x30\xc1\xeb\xbd\xd1\xb4\xaf\xfb\xe7\x5d\xcd\x01\xc8\x59\xc4\xf1\xb1\x04\x07\x1a\xfd\x12\x0a\x3e\x15\x0d\x34\x21\x92\x0a\xe3\x58\x74\x84\x51\x1c\xd8\xbe\x69\x22\xa7\x97\x24\xa4\x93\x60\x19\x83\x22\x34\x5f\x32\x39\x55\x6d\xcc\x2d\xe1\xa6\xa6\x27\xc2\x3c\xda\xb3\x68\x1c\xa3\x6e\xc0\x80\xf5\x8e\xb8\xa2\x28\xdc\xf0\xf4\x56\x6a\x54\x2f\x7d\xb5\x4b\x1d\x31\x5a\x22\xb9\xbd\x46\x80\xe2\x05\x29\x1f\xb7\x18\xc5\xf7\x48\x8b\x2d\x02\xf6\xdf\x49\xeb\x44\x53\xbb\x80\x40\x69\x50\x28\x59\xc6\xf6\xb3\x07\x34\x9b\x8d\xd0\x66\x3b\x98\xb3\xfa\x5b\xb3\x10\x5c\x27\x55\xce\x4a\xe0\x7b\x83\x70\x96\xc7\x67\x3d\x87\x1b\x5e\x36\x1c\x63\xbc\xec\x5f\x58\xf5\x16\x11\x0b\x6e\xd5\xf9\xf7\x31\x3f\x8d\xff\xfb\xa4\x5b\x2f\x22\x08\xe5\xad\xf2\xf6\x50\x7e\xef\x60\x85\xb1\x90\xd0\xcd\x59\x87\x7c\x7b\xea\xde\x65\x59\x38\xf3\x5c\x5a\x88\x7b\x74\x7b\x63\xf0\xfa\xa3\x36\x6f\x65\x84\x2b\x54\xe9\x04\xd5\x66\x0b\x35\xde\x60\x95\xfd\x37\x36\x26\xde\x21\xa5\x7f\x7e\xc7\xa8\xae\x5f\x59\x1a\x4f\xb0\x3f\x59\xc1\xca\x9c\x42\xea\x65\xf2\xf1\x89\xcf\x89\x78\x7f\xb1\xcc\x67\x1d\xc7\x33\xa9\x7c\xa9\x2d\xdd\x8c\xba\x35\xb3\xb1\xb8\x3e\xd7\xcf\x7c\x0e\x40\x71\x4b\xc8\x8f\x67\xe7\xac\x47\xb0\x7f\x59\xcb\x3d\xe9\xad\x9c\xfa\x8a\x09\xc4\xce\x7c\x6f\x3d\x7f\xd0\x75\x47\xea\x10\x88\xff\xed\xe7\xcf\xe7\x91\xb5\xc6\x13\x6b\xe9\x44\xb0\xd9\x04\x57\xa9\x15\xf3\xb1\xf2\x6c\xac\x39\x77\x84\x96\xee\xc8\x58\x92\xc8\xa3\x6d\x13\x9f\xa0\xfc\x7e\x74\x92\xa5\x73\xaf\xb9\x01\x87\xf2\xf1\x96\x53\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4f\xb3\x86\x55\x7d\x15\x5e\x05\x7b\x13\xbe\xb1\xe4\x82\xae\x78\xe2\x03\xdd\x93\x82\x8e\x40\xd7\x43\xb2\x05\xc6\x0f\x5d\xe9\xd1\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe6\x4e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x7c\x77\x6b\xce\x51\xe2\xb3\x9b\x54\x62\x4f\x84\x8e\x79\x5b\x56\x3f\xf6\xeb\x51\xea\x46\xda\x8e\x79\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xb7\x1a\xa6\x11\xa3\x32\x1d\xce\xa2\x69\xdf\x20\xd1\xdd\x72\xad\x3f\xc4\x1e\x82\xff\x1a\x52\xbf\x34\x48\x6d\xf8\xf7\x87\x22\xfe\x7b\xda\x47\x7f\xbf\x0b\xed\x13\x2f\xe9\xe3\x00\x8d\x37\x25\x7d\x3b\x8c\xd8\x8a\x9b\x8a\x43\xac\x76\xfd\xcd\x76\x16\xb3\x17\xab\xd4\x2f\xe6\xcf\x4b\x6f\xb1\x43\x5f\xfe\xf5\x57\xbe\x84\x17\xe2\xd6\xcf\x35\x52\xad\xeb\x7e\x87\x6c\x92\x0d\xb3\x77\x5d\xee\x93\x89\x47\x12\xf3\x4c\x3d\xf7\x40\x6c\x5d\xba\x19\x0f\xb6\x2b\xfc\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xd5\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x49\x6f\xf3\xf7\x0e\xbd\x7f\x54\xff\xec\x6d\x59\xf1\xee\x4d\x78\x22\x81\xff\xb9\xad\xcb\x52\x3f\x7d\x5b\xa2\xb7\x6f\x4b\xfc\x60\x6d\xe9\x79\xfd\xb6\x54\xcf\xdf\x96\xe8\xfd\xdb\x12\x3d\x80\x5b\x9a\x2f\xe0\x9c\x1a\x1b\x58\xd8\x38\xfe\x51\xbe\xe2\x23\xb8\x23\xef\x2b\xb8\xa3\xd5\x9f\xc1\x1d\x35\x7d\x07\x77\xe4\x3e\x84\x3b\xba\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6a\xfc\x16\xee\xf7\x8e\xeb\x7f\xd4\xc0\xe2\x6c\x59\x65\x72\x26\x5d\xab\xf0\x1f\x82\x38\x91\xd5\xd9\x12\x9b\x9d\x2d\x0d\x2b\xb1\xa5\xcf\xf0\x6c\xa9\x2d\xcf\x96\xd8\xf4\x6c\x89\x6d\xcf\x96\x96\xf1\x99\xa7\xde\x26\x8b\xe3\x37\xb5\x3f\x3b\xf2\x1b\xa0\x1d\xdd\xc0\x02\xed\xa8\xb1\x09\xda\x91\xc7\x06\xcd\x2e\x7d\xb3\x35\x52\x61\x86\xd6\x74\x91\x34\x37\x44\xfb\xb6\xc9\x2a\x69\x2f\x73\x0a\x8a\xd9\x71\xd1\xe6\x01\xf9\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\xf0\x61\x4b\xfe\xf9\xe6\xf5\xab\xa2\x58\xbc\xa7\xff\x6f\x49\xf3\x62\x0d\x04\xb3\xcb\x05\x4d\x27\x56\x0e\xf7\x63\xa3\xde\x6f\xb4\x25\x5e\x44\xc3\x7d\x1b\x9a\x7c\xb9\xde\x59\x33\x82\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xa3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xed\xfe\xfe\xe5\xec\xfd\xcb\xd9\x3f\xf1\xcb\x59\xfe\x6a\x56\xd8\xb0\x19\xcf\x66\xf9\x86\x43\x6e\xf6\x7a\x56\xec\x7d\x47\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x8e\xa1\xc8\x89\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\x70\x66\xdd\xdc\xcf\x39\x40\xb2\x9a\x8e\xde\x1e\x7c\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\xe0\x34\x95\x1f\x93\x28\x49\xe8\xa5\xfa\xa2\xf3\x22\x85\xa7\x8c\x32\x65\x11\x8d\xcd\x84\x24\x48\xcc\x84\x79\x34\xce\xec\x94\x38\x8e\x9c\x42\x06\xbc\x01\x2a\x3f\x8c\x22\xd3\x2c\x48\x42\x35\x14\x23\xeb\x27\xe3\xeb\xa3\xf1\xf5\xce\xf8\x7a\x69\x7c\xfd\x9f\xf1\xf5\x2f\xe3\xeb\xad\xf1\xf5\xc2\xf8\xfa\x87\xf1\x75\xc4\xbf\xd6\x4e\xca\x5d\xd7\xb0\x39\x7a\xb7\xf7\x82\x4d\xf1\x88\x6c\x6f\xf5\x54\xe2\x87\x83\x9f\xde\xee\x7d\x3c\x7a\xff\xf2\xd3\xeb\x97\x6f\x7f\xfa\xf8\x6a\x44\x1e\xeb\x4c\x98\xd5\x91\xfe\xa9\x73\x4a\x28\x67\x44\xbe\x10\x2b\x41\xfb\x51\x87\x8c\x4f\x2f\x0e\x7f\x7e\x4b\xae\x75\x4d\xef\x0e\x5f\xbf\x66\xd0\x1f\x0f\xde\xbc\x3c\x3c\xfa\x38\x22\x9b\xc3\xe1\x70\x20\x7a\x28\x6e\xbc\x9f\xc7\xe9\xf8\xf3\x88\xb4\x19\xeb\xcc\x8b\xb6\x91\xb7\x37\x86\x50\xc6\x23\xfd\xb6\x91\x3f\xc0\x60\xfb\x79\x9d\xef\x93\xfb\x50\x18\xf7\x1b\xd9\x5f\x7d\x23\x5b\x53\x2e\x20\xf2\x59\xb0\x7d\x57\x1e\x20\xf6\xb3\xcb\x45\x91\xfe\xfd\x03\xde\x1c\xc6\x90\xf6\x48\x47\xc0\x60\x0d\x7a\x01\x06\x2c\xa7\xed\x8d\xee\xe4\xba\x6f\x00\x8a\xcb\xf1\x03\x55\x91\x44\x1e\x3c\x90\xb9\x7d\xe9\x2f\x82\x8b\xc9\x33\x7a\xd1\xb6\x5f\xd1\x19\x9e\xbf\x7e\x20\x5b\xac\xb4\xed\xfd\x78\x4b\xba\x8b\x34\x8b\x13\x79\x19\xae\x2e\xf8\x2d\xff\xec\xc4\x7a\x6d\xc7\x41\x25\x8e\x58\xe7\xfa\xaf\xe8\x45\x1f\xb4\x97\xc2\x73\xaf\xcf\xc6\x88\x61\x45\x0e\x5b\xb7\xce\x4f\x74\x5c\xfd\x36\x22\x5b\xdf\x3c\xe1\x25\xd1\xe3\x64\xf9\xe6\x8c\xb1\x3c\x85\xe3\xd6\xe8\x9b\xef\x7a\x2d\x13\xe5\xad\xd1\xd3\xe1\xf5\x49\x6f\xab\x91\xcf\xa7\x7b\xbe\x77\xcf\xf7\xfe\xbc\x7c\x4f\xb3\x3d\xfe\xce\xff\x0e\xf8\x9e\x25\xbb\xaf\x2e\xba\x7b\x24\x77\x59\xd0\x27\xb8\xaf\x14\x6d\xc8\xe6\xb5\xfd\x81\x60\xf7\x3a\x1c\xd1\xe4\x29\x06\x60\xdf\x4a\x84\x5f\x26\x51\xf1\x26\x58\x28\x71\xb1\x2d\x25\xea\x11\xe7\x41\xed\xa1\x94\x35\x99\xd4\x3e\xd2\x6c\xb1\xbd\x69\xc8\xf9\x23\x94\x31\x1c\xaa\x42\xff\x5b\x91\x77\x1a\x9c\x9e\x06\x53\xaa\x5a\xc2\x79\x48\xf8\x1f\xd9\x79\x73\x4f\x9d\x28\xfb\x4d\x75\x76\x9c\x9e\xd1\x38\x18\xcb\x66\xed\x6c\x7d\xc6\x18\xf9\xb2\xa7\xfe\xca\x11\xc4\x4f\xb5\x10\xf9\x2c\x48\x92\x34\x31\xc6\x6d\x42\xe8\x73\xcd\xa8\x02\xa2\xa6\x15\x38\x59\x8d\x3c\x10\x18\x95\xfa\xbc\x34\xaa\x06\xaa\xab\x49\x9c\xdd\x46\x5e\x20\xa3\x32\x75\x1e\xb3\xc7\xe6\x01\xf4\x0f\xd1\x04\x34\xc8\xd5\x03\x87\x40\x3f\x9b\xb0\x3e\x50\x3c\xd7\x70\xea\xab\xac\x18\xf7\xb7\x51\xdd\xb8\xfa\xa6\x05\x50\x99\x62\x85\x32\xac\x98\xdf\xd8\x4a\x3b\x62\x58\x04\xa1\x30\x25\x05\x53\xcf\x8b\x05\x1d\xb3\xcd\x4b\x99\xe7\x63\xa3\x2b\xe1\x3d\xc5\x67\x39\xa5\xab\x38\xa5\x0c\x2e\x14\x11\xb9\x2c\x1b\xac\xf1\x2c\xc8\x82\x71\x41\xb3\x5c\xaa\xf8\xe1\x5e\x5e\x94\x46\xfb\x88\xb7\x8d\x68\x9a\xf4\x90\x2d\x34\x19\xae\xf9\xdd\x7e\x44\xd3\x59\x41\xa4\x47\x5a\xcb\xbb\xaf\x18\x83\x21\x6d\x72\x90\x1e\xf4\x2e\xef\x41\x3b\x1e\x1f\x43\xdc\x42\x04\x60\x20\x28\x2d\xbc\x56\x55\x37\xc4\x9b\xdd\xfe\x2f\x69\x94\x40\xb0\x06\xf2\x0c\xea\x20\x23\xd2\x1a\xb6\xba\x64\x43\x00\x97\x18\xbe\xdd\x78\x2e\x20\x60\xcf\x9f\x7d\x32\x60\x10\x2b\xce\x86\xe8\xe1\x06\xf7\xb8\x7c\xd3\x79\x29\x33\x44\x34\x1d\xd1\xc0\xd6\x09\x66\x88\x10\xcc\xc3\xf5\x31\x6d\xcd\x0b\xf7\xd6\x5c\x31\x2b\x51\xc2\x2a\xf1\x23\x0b\xfb\xa3\xf6\x38\x4a\x62\x8d\x6b\xb3\x43\xee\x81\xe4\x88\x6f\xed\x4a\xa4\x9f\xf1\x78\xcf\x83\x01\xf9\x31\x4a\x42\xc2\x1f\x77\x89\x8e\xaa\x78\xcd\x4c\xa2\x68\xb5\xf4\x4d\x3e\xd8\xbe\xf4\x20\x84\xd4\x8c\x5e\x48\x13\x66\x75\xe6\x62\x69\xfc\xd4\xc3\x4e\x1c\xe5\x67\x25\x56\xcd\x16\x7e\xf7\x02\xc6\x35\xc2\xa6\x66\x87\x44\x1b\xbb\x5b\x18\x5c\xc6\x42\xc6\xb6\x1d\xba\xa9\x4e\xc4\xda\x11\xa1\x2f\x54\x0b\x13\xd2\xe1\x45\x76\x77\xc9\xb0\x6b\x9c\xd2\x4e\x33\x1a\x7c\xd6\xa0\x6c\x94\x1b\xbb\x44\xbc\x2a\x67\x33\xb8\x3f\x0b\xb2\xfd\x34\xa4\x50\x83\xf7\x10\xc6\x26\x5b\x9a\xe3\xe4\x45\xd6\x8c\x42\xf8\xa4\xad\x44\x22\x7b\xac\xc8\x6f\x47\x23\xd0\xdc\x7f\x0f\x91\xdc\x64\xe6\xf3\xa2\xec\x75\xba\x39\xd9\x1e\x1f\xf3\x9d\x45\x46\x27\xd1\x05\x0f\xa2\x35\xbc\xe8\xb2\x59\x00\xae\xe1\x77\x6f\x2f\xa2\xbd\x95\xcf\xbe\xd7\x76\x19\x8e\xa0\x41\x0c\xdc\xbc\x32\x98\x80\x2f\xca\xa7\xe1\x6b\x5f\xb8\x5d\x17\xdd\xc0\x54\xc1\x28\x5e\x60\x9e\xcf\x3e\x2c\x07\x61\xb6\xcd\x97\x83\x9c\x11\xd6\x92\xa6\x8e\x49\x9a\xd9\x26\x74\x79\x91\x95\x45\xc4\x47\x33\xca\xa0\xc6\x62\x6e\xf6\x8a\x4e\x74\xb3\x95\x0e\xd6\x89\x22\x38\xb8\xe1\xb5\x4d\x83\xb0\xfe\x6e\xec\x92\x44\xee\x0b\xdf\x93\x2d\xf2\x8c\x9d\x6c\xc8\x06\x61\xfb\x41\xe2\xa3\x09\xe1\x42\x7e\x46\x2f\xee\x92\x34\xac\x98\x03\x36\x6d\xd4\xb0\x86\xdf\x8c\x38\x1c\x9e\x81\xa8\xe3\xb7\xa1\x80\xdf\x6d\x5a\x2d\x8f\xa5\x93\x65\x1c\x2b\x34\x0c\xe8\x19\x4d\x0a\xfe\x50\x00\x58\xfe\x2f\x79\x9a\x90\xe0\x34\xb2\x79\xbc\x74\x9b\xf8\x31\xfd\x71\x19\xc7\xf6\x1b\x4a\xf9\x98\x80\x95\x7e\xc4\x4b\xbb\x8f\xa1\x78\xc3\x4e\xbb\x9a\xb1\xbb\x6d\x18\x82\x14\xab\x1c\xab\x4e\xd9\x77\x1f\x4c\x28\xa2\x24\xa4\x17\x87\x93\x4e\xbb\xd3\xee\x82\x6f\xc8\x47\x9b\x9e\xe7\x90\x0a\xde\xb1\x13\x2c\x2e\x17\x54\x34\x07\x40\x40\x45\xa6\x3f\xb3\x4e\xd4\xfd\x22\x43\x08\xf7\x19\xfc\x0e\xb9\x16\xa2\x98\x69\xf9\xa7\x5a\x21\x1b\xa4\xdd\x61\x33\xa7\x6a\xdf\x20\xed\x6e\xbb\xd1\xda\x0b\xa3\x7c\x11\x07\x97\x7c\x5e\xc0\xc7\x68\x52\x30\xd9\x56\x61\xc3\x7e\xb3\x76\x01\xd9\x2f\x78\xb1\xaa\x17\xae\xac\x36\x73\xf2\xfd\xcb\xcb\xe8\x01\xdb\xd2\x2c\x8a\xa1\xd3\xbe\x8c\xb7\x78\xd9\x11\x66\x75\x5d\xf2\xe8\x07\x95\xa8\xa6\xd5\xed\x5b\xe5\xc3\x67\x65\xb3\xe9\xcc\xac\x81\x66\x01\xc6\x27\x9b\x3c\xb3\xdf\xb4\x8a\xf7\x60\x6c\xcd\x68\x67\x23\x83\x81\x1e\x68\x7a\x46\xb3\x38\x0d\x42\x1a\x2a\x45\xb0\x67\x4d\xe0\x01\x7c\xd4\x44\x52\xf6\xa6\x71\x40\x3e\x1e\xbe\x38\x1c\x91\x79\xf0\x19\x54\xc3\x51\x72\xb6\x8c\x13\x9a\x05\xa7\x31\xbd\xcb\x01\xea\xd3\x80\xfd\x7a\x77\x93\x3c\x22\x28\xbb\xdb\xed\x67\x74\x11\x07\x63\xda\x69\x93\x36\x38\x75\x63\xa7\x85\x96\x19\x24\x32\x4d\xce\x68\x56\xe4\x3a\xe4\x26\xc8\x7d\x21\x1d\x47\xf3\x20\xb6\x99\x6c\x94\xf8\x99\x7d\x91\xbe\xe0\x05\x5c\xca\xab\x0c\x9f\x69\xba\x35\xe4\x02\x9e\xa8\xa9\x36\x00\x64\x91\xba\xf1\x31\x55\xf8\x99\x26\x63\xac\x95\x6d\x19\x4f\xbc\xab\x71\xa1\xba\xaa\x83\xb3\x26\x52\x4b\xea\x8e\xcf\x13\x9a\x5b\xa8\x4f\xcd\x1d\xc5\x38\xec\x73\x80\x98\xe6\xf9\xc7\x59\x90\x74\x86\xe0\x44\xf6\x11\xb7\x3a\x17\xd6\xfb\x82\xb0\x36\xbb\x10\xbe\x15\xe5\x18\x58\xdc\x5b\x82\x9b\x66\x81\xca\x20\xb9\x14\x8e\x77\x84\x3b\xd2\xa4\x1c\xad\x7d\x81\xd7\xbd\x24\xe4\xea\x7f\x4e\x43\xd1\xe4\x32\x17\x8e\xd4\x73\x72\x4a\x27\x69\x46\xfb\x0e\x5d\xbd\x12\x47\x87\x6a\xdc\x5f\x89\x3d\xa8\x86\xb4\x5e\xc1\x3e\x6f\x20\x5f\xad\xdf\x87\xc2\x54\x6c\x1e\x5c\xf0\xb0\x95\x17\x51\x71\x39\x22\x4f\x41\x85\x2d\x77\x9d\x28\x17\x2e\x8d\xa1\x68\xd7\xde\x64\xd0\x24\x77\x36\x18\xc4\x8e\x51\x14\x4f\x67\x75\x61\xab\xac\x30\xa4\x3b\x63\xb4\xc3\x4e\x21\x1c\x69\x6d\x6f\x15\x10\x5f\xe9\xef\x1f\x0e\xdf\xf6\x15\x96\x79\x7b\xda\x81\x25\xb8\x8e\xcd\x49\x60\x47\xf3\xec\x91\x45\x90\xe7\x8c\x77\x15\xb3\x2c\x5d\x4e\x67\xe6\x0a\x50\x03\x11\xb4\x06\xb5\xba\x97\x93\x9a\xab\x3d\x82\xd3\x92\x47\xe6\x2d\x1d\xb1\x04\x10\x6f\x3b\xcc\xea\x6a\x6a\x3b\x93\xf6\xa3\xa8\x02\xd2\x59\x8f\xf2\x1f\xa3\x24\x2a\xa8\x85\x74\xab\x1b\x20\x21\xa2\x4e\x98\x52\x96\xdb\x51\xb4\x2e\xde\x8b\x4d\x85\xaf\x03\x76\x5e\x4a\x80\xfb\x93\x9f\xa9\x2d\x48\x4d\x69\x01\x11\x8b\x0f\x27\x47\x49\xe4\xd5\x76\x41\xd9\x62\x46\xc5\x0f\xb5\xe0\x48\x91\xf6\x94\x76\x4a\x39\x44\xf7\x46\x6d\x54\xfd\x50\xd5\x74\x78\x67\xba\x50\x04\xdc\x76\xe5\x84\x66\x59\x9a\x49\x97\x34\xbc\xc7\x39\x49\xd2\x82\x8c\xd3\x2c\xa3\xe3\x62\x74\xae\xd6\x8d\xd9\x6b\x63\x01\xb1\x82\x92\x04\x96\x3c\x13\xfe\x7b\x06\xff\xf5\x8b\xf4\x75\x7a\x4e\xb3\xfd\x20\xa7\x1d\x60\x2e\x5c\xdf\xab\xf9\x18\x83\xfa\x87\xb8\x65\x16\x57\x37\xc7\xec\xff\x13\x7d\x14\x47\x20\xd8\xef\x37\x26\x3c\xee\x89\x2c\xa1\xe7\xe4\x25\x1b\x55\xa7\x0d\x57\xbd\xd0\x11\xb0\x55\xfd\x77\xbb\x20\xf4\x22\xca\x8b\xbc\x47\x16\x31\x0d\x72\x10\x8b\x61\xe4\x69\xa2\x50\x35\x49\xe3\x38\x3d\x8f\x92\x29\x94\xcc\x19\x17\xb4\x96\x91\xe8\x61\x0f\xfc\x2b\xf4\xf4\xb3\x8f\x8a\x28\xb1\xaa\xf7\xe0\xfd\xca\xf4\x2a\x1c\x7c\xa6\xb0\x08\x39\xc3\x87\xcb\xe8\x08\xec\x69\x15\x93\xe5\x24\xc0\x58\x2d\xf8\xaa\xe0\x13\xcf\x51\x2b\x28\xeb\x5d\x9a\xe7\xd1\x69\xcc\xa7\x10\x5c\x68\x08\xa3\xbe\x0f\x07\x4c\xbe\xcc\x0a\xfe\x93\x89\xd4\x12\x5b\x2f\x27\x93\x68\x7a\x29\x3e\x0e\x25\x29\x3d\x22\x9f\x59\xf3\xfc\x4f\x5f\x57\xc1\xa7\xb8\xd9\xe2\x60\x73\x0d\xa6\x2e\x97\xf8\xa7\xbc\x8a\xe2\x70\x53\x0d\xa7\xee\x7f\xf8\xa7\xb8\x30\xd2\x79\xbc\xc0\xa3\x47\x6a\x61\xea\x7b\x1c\x5e\xe0\xd7\xe0\x34\x35\xf2\x3c\x25\xe4\x3d\x0c\x1f\x00\x5c\xdf\xe0\x3c\x5e\x02\xf5\x02\x15\xe6\x9f\x02\x0b\x08\x84\x58\x10\xe8\x03\x2e\x53\x04\x42\xa8\xc6\xe1\x14\xfd\x2e\xe4\x6f\x5b\xa4\xe0\x7c\xc1\x3a\xf9\x5e\x29\x39\x9d\x93\xc3\x38\x48\xd8\xc9\x20\x50\xac\x59\xa4\x0b\x5d\x59\x9a\x91\x80\xbc\x7a\xf9\x4f\x38\x84\x4b\x69\xed\xce\x18\x8a\xda\x67\xe5\xd1\xee\xe7\x19\x95\x7e\xf6\x02\x74\x95\x2b\xa2\xa0\xa0\x60\x01\x6c\x3d\x05\x39\x39\xa7\x6c\x81\x68\x07\x2b\x72\x18\x6b\x48\x1a\xfa\x99\x1a\x47\x72\x39\x4e\xcc\x52\xb8\xa8\xc3\x6a\x96\x4c\x02\x0b\x45\xbc\x04\x8e\x1a\x6b\x72\x2a\xce\x9d\x2c\x79\x08\x6f\xc3\xa2\x02\xf2\xcc\x68\x64\x84\xbf\x90\x64\x55\xbb\x7c\x03\x8e\x63\xcf\x0a\x3e\xa7\xd1\xfd\x82\xfd\x6f\x59\xe2\x45\x5a\xb5\xc0\xd1\x79\xe1\x37\x5b\xea\x6c\xb5\xfd\x8e\x8b\x1d\x10\x72\x37\x4b\xbd\x88\xe6\x34\xff\x3d\x96\x79\x22\x94\x8b\x6c\x71\x2b\x55\x55\xce\x8f\xf9\xb0\x45\x13\x65\xcb\xe2\x90\x83\xea\x49\x23\xa2\xd0\x64\x20\xef\x0e\xd9\xdc\x6b\x5a\x30\x6b\x53\x5e\xae\x74\x05\x1a\x40\xe1\x1f\x1b\xdf\x58\xb3\x50\x73\xfe\xf9\x86\x09\x81\xb0\xec\x65\x79\xf1\xe3\xea\x8a\x0c\x77\xbc\x87\x1b\x51\xaf\x73\x38\xe1\xe9\xc6\x89\x48\xe0\x5c\xf6\xe4\xc1\x03\x22\x7e\xfb\x84\x7e\xd6\xa4\x9d\x8b\x4f\x18\x3e\x1f\x68\x86\x2c\x26\x0a\x2b\x9d\xc8\xf0\xa2\xdd\x6b\xb7\xf1\x85\x8b\xe5\x29\xcd\x57\x1a\x13\x4a\xa9\x4c\x97\xc8\xd8\xb1\x1e\x52\x51\x74\xc2\xc1\x64\x14\x0f\x75\x14\x13\x66\x93\x00\x5b\x9c\xa7\xed\x9c\x8c\x55\x4c\x17\x87\xb4\xcc\x90\x2f\x4d\xe8\xab\x84\x6a\xd0\x21\xd9\xac\xd3\x54\x78\x19\x24\xc3\xc0\x4f\x11\x65\xf9\x16\x2c\x3c\xf9\xee\x20\xaf\x75\xaa\x00\x56\x49\xd4\x4e\x5d\x6b\x72\xcb\xbf\x16\xcc\x72\x7f\x11\x2f\x73\xdd\x05\xf1\xed\x75\x6f\xa8\x80\x4c\x4d\xd2\x8c\x8e\x3f\xe7\xf2\xd8\xc4\x79\xa4\xbc\xe6\xcc\xc5\x63\xb9\xf8\x12\xfc\xf8\x7a\xa3\x11\x73\x92\x1f\x7b\x23\x11\x9b\x31\x85\x51\x03\x6c\xfd\x07\x1a\x1e\x3b\xb6\x83\xe0\x4a\x62\xe6\xac\xba\x8d\x89\x13\x95\x5a\x1a\xb4\xc1\x7f\x86\x17\xc7\xc3\x47\xdf\x05\x8f\x26\x27\x5f\x1e\x0f\xaf\xff\x67\x10\xf5\x0b\x9a\x17\x0a\x7c\x85\xb1\x57\x0c\xf9\xeb\x0c\xb6\xc1\x30\xe1\xfc\x3f\xf8\x4f\x67\x78\xd1\x7d\x56\x39\x4e\x4c\x7f\x83\x81\x8e\x95\xc5\xa3\x61\x41\xef\xb8\x07\x61\x61\x74\x38\x87\x77\xbc\x6c\x3f\x46\xa3\x36\xe9\x57\x38\x02\x24\xa6\xab\x0a\x6f\x67\xcc\xbe\x30\x36\x87\xc0\xf6\x1e\xfd\xe8\x05\xb3\xba\x0c\xa1\xbb\xda\x39\x38\x3b\xce\xe7\xec\xdf\x71\xb0\xc8\x41\x76\x88\x63\x22\xbf\x7b\xd8\x43\xa3\xdd\x63\xee\x78\x1e\x75\xd8\x68\xe0\x50\x6d\xef\x1c\x3b\x34\x18\xcf\xc8\x38\xc8\x9d\x6a\xa2\x9c\x13\xca\x72\x2e\x66\x08\x51\x13\x5f\x65\xcd\x69\x8a\xb7\x95\x2f\xe7\x73\x1a\x96\x92\x97\xd5\xdc\x1d\x93\x99\x55\x7b\x15\xb9\x0d\x06\x7c\x3c\x16\x6e\x02\x55\x52\xfc\x72\x76\x20\xad\x0f\x11\x10\xaf\x82\x1c\x9c\xd1\xcc\x82\x6d\xd9\x88\xa9\x4b\x91\xd2\x8e\xcf\xe1\xcb\xe3\x21\xdc\x51\x12\x8b\x42\xc0\x79\x77\x31\x23\x31\x85\xe7\xd4\x28\x02\xdf\x62\x41\x33\xd6\x5b\x39\x0d\x09\x44\x2f\x9c\x46\x3c\xc0\x5d\x90\xd3\x79\xb0\x60\xd3\xb1\x69\x68\xfa\x3a\xca\x82\x01\x75\x1a\xdc\xb2\x6d\x3e\xe9\x92\x1f\xc8\xb7\x6c\x3b\x17\x59\xc7\xd1\x49\xbf\x48\x8f\x58\x43\x42\x17\xb4\xbe\xbb\x8b\x32\x81\xe8\xab\x2b\xfc\x7e\xd7\x53\x23\xd6\x2e\x59\x35\x96\xf8\x0a\x47\xcb\x52\xb3\x7c\x83\xf1\xeb\xf8\x0b\x8a\x4a\xdf\x88\xa3\x9e\xa4\xc6\x12\x52\x2c\xd2\xbb\x24\x45\xa9\xbd\x56\xfb\xf2\x0a\x94\x88\x74\xc6\x8a\xfa\xec\x57\xd7\xa2\x9d\x76\x5b\x90\x92\x4b\xa6\x06\x7e\x6f\x44\xb4\x08\x68\xec\xf4\x9e\x55\x54\x41\xc6\xb2\x17\xe8\xda\xdd\x26\x69\x60\x7a\x33\x6d\xfa\xc7\x88\xf4\x3b\x76\xf0\x99\x70\x07\xfa\xf2\x26\x4e\x51\xb8\x41\xc0\x75\xf4\x6b\x52\x90\xdd\xff\x8d\xdd\x52\xe2\x46\xe4\x65\x33\xd2\xda\x9a\x2a\x49\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\xd8\x68\x91\x32\x89\x32\x0a\xc9\xd6\x90\xfb\x0c\x7a\x24\x2e\x08\x79\x9b\xfc\x7d\xc2\xf0\x82\x70\xe3\x0e\xd7\xb8\xab\x96\x92\xfd\xb7\xfd\xc2\xfb\x00\xe6\xda\xca\x80\xab\x19\xfd\x5a\xe2\x8c\x77\xe3\x93\x4e\x75\x25\x3e\x90\x0c\xcf\x77\xdb\xaa\x8d\xd6\x53\x91\xb8\xfc\xf2\xd5\x67\x42\xc8\xd0\x8b\x70\xa5\xa4\x6a\xd4\xaf\xa9\x7a\xe4\xf1\xd0\x7f\x4b\x20\x1d\x11\xcb\xd3\x74\xae\xa5\xdc\xfa\x20\x9b\xde\x93\xa4\xef\xea\xcb\x08\xbc\xc9\x37\x32\xdf\x19\x90\x74\x78\x37\x2c\xb9\x50\xf6\x2d\xc9\x8b\x20\x19\x33\x2e\xa2\x0b\x5f\x5d\x29\xa4\x89\xc2\xf0\x7a\x0d\x7e\x19\x8e\x33\xbc\xa9\xdc\x36\x02\x78\x91\xaa\xb2\xdd\x14\x51\xf2\x3c\x5c\x87\xa5\x0f\x8e\x71\x51\x43\x14\x79\x42\x24\x79\xf1\x23\x58\xab\xe8\x19\x8c\x86\xf7\xad\x7d\x77\xe8\xe1\x7d\x69\x8c\x1b\xd9\xe3\x7a\xec\xfc\xa8\x8d\x48\x56\xc5\x8f\x2c\x7a\x23\x0c\xc9\x12\xed\x86\x23\x62\x7d\x2a\xea\x87\xc3\xbb\x7e\x83\xc1\x1c\x8a\xbe\x35\x5c\x0c\x4c\xbc\x48\x96\x71\x0c\x51\x12\x3a\xee\x0a\x01\xc3\x6d\x50\x61\x78\xc6\x2e\xee\x6b\x1b\x8e\xfc\x94\x77\xb6\x01\x3b\xe0\x80\x37\x61\x06\x3c\xe9\x46\x13\x29\xba\xd7\x74\x34\xe0\x02\xb0\x7e\x2c\x4e\x44\x8d\x86\x23\x71\xa3\x62\x34\x64\x69\x50\xb0\x72\x0c\xf6\x71\x84\xef\xa3\x60\x23\x97\x4a\xaa\x33\x07\xf1\xf7\xdc\x5c\x57\xda\x02\xa1\x72\x0c\xac\x98\xfd\x6a\x40\xb9\x4e\xca\x2e\xdd\x7d\x6a\x7d\x1d\x6e\x26\xf9\x33\x5c\x6d\xcc\x7a\x4d\xc6\x10\xf6\xa9\x43\x3d\x7b\x1b\x3e\x90\xae\x32\xea\x40\x8c\xfb\x25\x9b\x40\xba\x9c\x93\xd3\x38\x1d\x7f\x26\x33\x1a\x84\x34\x63\x1f\xe9\xdc\xb6\xda\x88\xf2\xe7\x2c\xd9\x27\x34\xcc\xe8\x85\xf2\x8b\x0e\x65\xc9\x24\x8a\x0b\x5b\x99\xe9\x21\x58\x80\x35\xdc\x0f\xb3\x94\xca\x93\xfe\x37\x9b\x5b\xfa\xa8\xcf\xc1\x6b\xf0\x52\x7e\x50\xe7\x75\xe1\xaa\x7c\xe7\x74\x17\xca\x17\x71\x58\x9f\xb3\xd7\xdc\x7e\xdc\x60\x66\xe2\x94\x89\x79\x8b\x68\xec\xce\xc3\x47\x96\x5c\x37\x0f\x85\x02\xaa\x98\x00\xa8\xc9\x98\x00\x28\x56\x39\x01\x4f\x1e\x6b\xfc\x73\xe8\x1b\xe3\x1f\xaa\xc2\x35\xf9\xd0\xef\x00\xdd\x08\xfb\x25\x8e\x47\x84\xc8\x37\x92\x3f\x7a\x32\x15\x1e\xfd\x8c\xd4\x2f\x9e\x0e\x82\xe1\x88\xff\x27\x53\x84\x05\xc9\x48\xff\xe4\x39\xc8\xba\x64\x84\x3f\x64\xb9\xa3\x62\xf2\x74\x24\xfe\x97\x69\x60\xaf\x32\x92\x3f\x74\x3d\x1c\x56\xfe\xd2\xe9\x02\x5e\xfd\x14\xf5\xb8\x46\xb7\x23\x5f\x22\x87\x76\x6d\x39\x47\x9e\x34\x03\x56\x9a\x4d\x8e\xec\x04\x39\x8e\x9f\x29\x8c\xe2\x67\x8a\xc6\x00\x69\xe2\x87\x84\x53\xd2\xe2\x08\x7f\xc8\x5c\x53\x65\x3d\x72\x52\x14\xd6\xb8\xa0\x3e\xd2\x3f\x79\x0e\x92\x8e\x47\xf8\x43\xe6\x1a\x27\x91\x91\x9d\x20\xa1\x50\xbe\x95\x63\x1d\xdd\x47\x6e\x92\xec\xa1\x03\xe9\x24\xc9\x3a\xa5\x30\x36\x42\xbf\x71\x7f\x93\xe9\x48\xfd\x92\xe9\x7c\x4f\x1d\xa9\x5f\x6a\xf4\x7c\xbd\x8f\xf4\x4f\x35\x26\xb6\x4b\x8e\xe4\x0f\x99\xca\x36\xac\x91\xf8\x5f\xd5\xc1\xf8\xdd\x48\xfe\x90\xa9\xc0\x36\x46\xf2\x47\x0f\x16\x18\x77\x50\x27\x5e\x75\xb7\x46\x9b\xdf\xf5\x2a\xfd\xdb\xf4\x5a\xcb\x62\xf2\xb4\x35\x7a\xfa\xcd\xf5\x49\x6f\x6b\xb3\x89\xc7\x07\x73\x09\xef\xf2\x05\xdc\x12\x8e\x0e\x5a\x23\xd2\x1a\xf6\xb7\x86\xfd\xcd\xd6\xda\xb5\x74\x05\xb7\xd5\x28\x52\xf1\xbd\x27\x89\x7b\x4f\x12\x7f\x05\x4f\x12\xa2\x96\x35\xd7\x17\xdc\xdf\xe9\x64\x92\xd1\x4b\xf2\x73\x14\x8f\x3f\x53\xf2\xfd\x2f\x74\x32\xb1\xdd\x49\x34\xf4\x18\x07\x60\x51\x90\x90\x43\x26\x71\x07\x00\x15\x05\x89\x0b\xf6\x63\x70\xca\xc0\xfe\x91\x4e\x69\x9c\x17\x34\x8e\x69\x46\xbe\x9f\x40\xa2\x0b\xfc\x53\x70\x46\x7e\x4e\xd3\x90\x7c\x3f\x2d\x75\x73\xf1\x58\xbb\xf7\x11\xbe\x20\xdf\x04\x49\x30\x35\x7d\x4f\xf4\x07\x0c\x0b\x83\x8c\x03\xcc\x39\x80\xf4\x31\x71\x70\x0a\x87\x23\x1b\x38\x3a\x0d\x12\x09\xf2\x12\xcc\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\xa9\xf2\x37\x3b\xab\xaa\x2f\x9f\xa9\xfa\xde\x82\x67\xf2\x32\xc0\x84\x16\x12\xf0\x1d\xcd\x72\x78\x4a\x55\x0e\xbd\x10\x20\xaa\x13\xe7\x41\x36\xaf\xea\x06\xcb\x57\xc0\xb4\x28\x20\x6a\x93\x0b\x9f\x8b\x2c\x09\x2a\xb9\x8a\x01\x29\xd9\x05\x3b\x51\x69\xe7\x1e\x51\x6c\x55\x88\xc2\xca\x97\xfb\x08\xe1\x40\xd2\x1b\x93\x78\xb8\x41\x93\xd0\xd3\x37\x9e\x21\xc1\x9e\xc3\x89\xc9\x85\x3a\x65\xe9\x0a\x93\x59\xba\xa0\x59\x71\xe9\x81\x5b\x88\x2c\x09\xfa\xaa\x28\x16\xef\xb2\xf4\x2c\x0a\xbd\xe4\xc6\x16\xea\x42\x64\x2b\x62\x5b\x8c\x2b\x4a\x44\x8b\xb1\x5d\xa0\x99\x47\xc3\xb5\x35\x25\xab\xff\x4c\x4f\xb7\x49\x47\x56\x63\x7a\xe5\xcd\xec\x15\x92\xd0\x73\x6b\xd9\xe8\x92\xc8\x41\xaf\x08\xb5\x8a\x7a\x2e\xa1\x10\x10\xe5\x6f\x5d\xe8\x39\x5b\x2e\xe0\xa8\x1f\x57\x11\x9e\x8a\xcc\x17\xcf\x9d\xbc\x7c\x26\x4b\x7e\x98\xb9\x25\x13\x58\x03\x2c\xf7\x2d\x2d\x9c\xdc\x85\x26\x7c\x06\x22\xd7\x81\x03\x77\xfa\xeb\xaf\xb2\x0d\x46\xd7\x6e\x1f\x34\x81\x03\x90\xf8\xec\x60\x18\x4d\xd9\xfa\xa8\x11\x2c\xa2\x91\xda\x0c\xc5\xff\xfc\xc8\x81\x3b\x29\xb0\x95\x1b\x45\x31\xf9\x8c\x8c\xaf\x9e\x82\x41\xf4\x32\xc2\x1f\x4e\x13\x9f\xd4\x1a\xe0\x3f\x9c\x01\x0a\x80\x8e\x6e\x5f\x90\x73\x44\xf3\x11\xfa\xdd\xe1\xc6\x3c\xd7\xdd\x1d\x26\x31\x0d\x06\xe0\x82\x37\xa7\x44\x8f\x21\xe5\x3b\x31\xf8\x04\x5a\x63\xe4\xe6\x19\x5f\xdd\xd8\x4a\xc7\xc5\x84\x46\x59\xa7\x8c\xa7\x49\x31\xe5\xe1\x98\xc1\xf5\x34\x8e\x0b\xaf\x4c\xda\x9e\xbe\x64\x94\x07\x8b\xd0\xbd\xf8\x4c\xe9\xe2\x20\xff\x70\x99\x8c\xa3\x64\x5a\xd9\x15\x28\x6b\xc1\x37\xa3\x40\x4f\x47\x30\x5f\x78\xae\xed\x57\x2c\x28\xf9\x0c\x86\xbb\x93\x82\x2f\x0f\x8c\x7c\x32\x2b\xa1\xe0\xdb\x03\x27\xde\x5d\x4b\x30\xf6\xe9\x40\xe1\x27\xb8\x1c\x50\xa5\x78\x61\x8d\x3a\x65\x82\xa7\x6d\xfd\x9e\x4a\x36\x2f\x52\xbc\xb5\xda\xd0\x28\xcd\x53\x37\xc6\xa5\xac\xbd\x0a\xa7\xdc\xc4\x51\x42\xfe\x4c\xfd\x23\xc3\x50\xe2\xdb\x81\xc3\xa6\x2d\x1c\x52\xa5\x78\x60\xdd\x5b\x61\x59\x66\xdf\xbe\x2d\x74\xfa\x5c\x56\xd6\xc9\xf1\xb4\x7b\xf0\x7c\xef\x2d\x6a\x8c\x7d\x3a\x50\xda\x3d\x0d\x07\x13\xdf\x3e\x38\xe9\x39\x45\x01\x42\x02\xdb\xc5\xec\x85\xcf\xb7\x7e\xfc\x92\x9b\x5f\x0a\x99\xde\x15\xcd\xeb\x3a\xb8\x93\xb6\x21\xcb\xae\x4f\xc3\x28\x03\x55\xf1\x38\x58\xc0\xeb\x0b\x74\x81\xe9\x99\xd1\x83\xfd\xbd\x77\xc6\xda\x67\xe5\xb0\x85\x5c\xc4\x45\x49\xb6\x7c\x99\x54\xc9\xf3\x8d\xc7\x9e\x0c\xa2\x2f\x9a\x91\x2b\x1b\x1c\xca\x28\xfe\x5b\x15\x71\xf4\x58\xf1\x6e\xd8\xeb\x84\x38\xd2\x31\xef\x9c\x13\xd0\xc1\xb4\xe5\x9e\x94\xa4\x21\x6d\xf7\x0c\x88\x29\x98\x85\x8c\x48\x9b\x09\x1d\x9f\xc6\x71\x44\x93\xe2\x1f\x1c\xbc\xad\xef\xa4\xbb\xbd\x9b\xb4\x46\x8b\xf3\x34\xfb\x5c\xd6\x60\x42\x8b\x4f\x02\xd4\x02\x31\x03\x06\x8c\xec\x55\x7e\xcb\x6e\x51\xa1\xd0\x2e\xeb\x17\x2d\x66\x9f\x60\xae\xc7\x69\xfc\x8f\xdf\xa1\x7f\xe7\xb3\x28\x5f\x28\xdf\xc8\x4e\xf7\xf2\xd9\xec\xd6\x68\x83\x9f\x27\xde\xbd\x24\xca\xf7\xd3\x24\xe1\x3e\x9b\xd0\x72\xeb\x1a\xb4\xd7\xf1\x6e\x97\x0f\x1e\x78\xb7\x51\x5c\x65\xa7\xeb\xdf\xc1\xb8\x97\x02\x29\x93\x97\xd2\x3c\x18\x87\x42\xe4\x04\x21\xd1\x78\xf5\xb6\xac\x6e\xe9\x4d\x14\x9f\x10\xb8\xca\xc9\x38\x58\xb4\x46\x5b\x43\x96\x84\x8f\x24\xad\xd1\xd6\x26\x4b\xd3\xc7\x81\xd6\x68\xeb\xb1\x4a\xe1\xa2\x53\x6b\xb4\xf5\x54\x25\x61\xe1\xbe\x35\xda\xde\x52\x19\x6c\x85\xb7\x46\xdb\xdb\x3a\x41\x0b\xf5\xad\xd1\xb6\xae\x54\x1f\x0b\x5b\xa3\xed\x6f\x9d\x64\x5a\xcc\x5a\xa3\xed\xa7\x4e\x7a\x42\x8b\xd6\x68\xfb\x3b\x27\x5d\x0a\xc2\xad\xd1\xe3\xa1\x93\x99\xcf\x66\xad\xd1\xe3\x4d\x37\x9d\xc9\xc2\xad\xd1\x63\xdd\x7d\x79\xc6\x69\x8d\x1e\x7f\xa3\x12\xcd\x83\x73\x6b\xf4\xf8\x89\xca\x92\x52\x4b\x6b\xf4\xf8\xdb\x6a\xdd\xde\xf5\x49\x6f\x6b\xfb\x5e\xf3\x76\xaf\x79\xfb\x6f\xd1\xbc\x05\x71\x0c\x0e\x26\x6e\xe7\xc7\x15\x29\xb8\x1c\x55\x88\x4f\x17\x22\xc3\xc4\xbc\x3c\xe3\x16\xfd\x48\xc7\x00\xbd\x91\x70\x3a\x68\x4c\x5d\x74\x24\x57\x4f\xe3\x55\xd4\xfc\x08\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x7e\x4c\x33\xd9\x23\x8f\xef\x01\x79\x60\xf5\x91\xa4\x5c\x97\xd7\x4a\xb5\xb8\x66\xb8\xc8\x3d\x92\x97\x1b\xfa\x59\x5b\x49\x8b\xd8\xe0\x41\xb6\x84\x14\x96\x9a\x7c\x21\x60\x33\x44\x92\x71\xd4\xbc\x3f\x42\x94\xf2\x5d\xf9\xb0\x0c\xf2\x07\x03\x72\x1e\x44\x5c\x5d\x0e\x22\xd7\xa2\xd0\x2a\x58\x79\x53\x66\xce\xbb\x58\x0a\x2a\x60\xb4\xee\x18\xed\x9a\x9e\x97\xd7\x29\x3c\x4d\x36\xda\xb7\x77\x25\xe8\xef\xc6\xc6\x8e\x79\x6c\x1a\x0c\x48\x5e\xa4\x0b\xae\xab\x8d\x92\x29\x09\x26\xac\x2b\xdf\x0c\xf9\x5c\xe5\xa4\x53\x44\x73\x9a\x2e\x8b\xae\x73\x74\xe4\x08\xf8\x81\x7c\x33\xf4\x1e\x16\x79\xef\xfb\xac\xf6\x9f\x45\xe5\x3a\xa6\x42\x97\x7c\xb9\xf6\x9c\xe9\x6c\x04\xf2\x07\x7b\xde\x73\xa8\x9a\x11\xef\x69\x53\x9f\xfc\xb4\x63\x60\xc5\x98\xe0\xbe\x24\xe0\x2b\x63\xcc\x08\x1b\x9c\x04\x9f\x32\x89\x79\x99\x84\x36\x06\xda\xbe\xc3\x27\x8d\x91\x43\x11\xfc\xe7\xb8\x23\xbe\x71\xab\x6c\xf9\xe1\x9a\x95\x3f\x11\x17\x6b\x06\xd5\x4c\x69\xf1\x51\x37\xf5\x9e\x93\x9a\xe6\x28\xa8\x1b\xaf\x82\x7c\x86\x89\xaa\x27\x09\xb3\xeb\x3f\xc2\x47\x93\x8e\x00\xf0\x53\x9b\xb7\x90\xb7\x83\x10\xc2\x48\xd4\xd5\x1f\x9b\x0b\xd0\xec\x11\xc4\x39\xf2\x77\x47\xfe\x95\x79\x6f\x7f\xa2\xbc\xb7\x97\xfd\x45\x93\x8e\x49\x71\x57\x57\x64\x1d\x5a\xac\x2c\x46\x14\xeb\xf6\xd0\x26\xfe\xbb\xc9\x12\xc0\x7f\x0d\x97\x83\x3d\xa4\x34\x44\x21\xa2\xb7\x2b\x67\x46\xfe\x0d\x06\xea\x9e\x2f\x4e\xa7\x88\x6a\xe1\x58\x21\xd9\xf8\x7a\xbb\x5b\xd3\x3c\x31\x44\x35\xc5\x51\x4b\xa6\xba\x41\x65\x83\x01\xe1\x9b\x95\x14\x17\x82\x24\x24\xe2\x66\x84\x04\xd3\x20\x4a\xc4\xca\x39\xa7\x22\xc2\x5f\xcd\x9f\x5f\xf6\xb4\x37\xc0\x9a\x1a\x6c\x59\xc7\xd9\xfe\x6b\x86\x34\xe6\x4e\xd9\xc4\xa5\x20\xdb\x12\xd8\xee\x98\xd3\x71\x9a\x84\x84\x31\xdc\xda\x4a\x10\xe9\xd6\x13\x2b\x31\x38\x22\xe8\xc2\x9a\x76\xd8\xeb\xc5\xe8\x8e\x3b\x84\x7d\xb7\x23\x51\x42\x9c\x68\x11\xa7\xcc\x8b\x34\xa3\xa1\xf2\xe3\xce\x25\x10\xd0\xf8\x4c\x83\x9c\x04\x73\xb6\x21\xf5\xbd\xfc\xda\xfe\x2b\xe5\xdf\xf6\x9f\xc7\xbd\xfc\x5d\x74\xb1\xba\x87\xd7\xa5\xb9\x65\x1c\xc3\x2d\x61\x43\x22\xed\x64\xd3\x03\x05\xba\x62\x90\x84\xfe\x63\xc0\x8e\xd9\x97\xca\x97\x86\x25\xc5\x59\x60\x35\x87\x06\xbb\x52\x7c\x60\x80\x53\x55\x70\x1a\x19\x97\x0b\xfc\x45\x11\x95\xc7\x77\x48\x0b\x4e\x23\xb2\xcb\x20\xa5\x9c\xf5\x90\x6b\x42\xeb\xc7\xa4\x4f\x48\x09\x09\x90\x68\x2a\x8a\xcb\x5a\xe4\xd8\x12\x7a\xae\x92\xe4\x98\x92\xcb\x6b\x4c\x0c\x96\x6e\x64\x53\xda\x14\x04\x71\x77\xc5\xa2\x5b\x15\x45\x6d\x39\xd8\x90\x2c\x84\xaf\x13\xa9\x28\x0e\x9d\xd2\x3e\x49\x59\x40\x28\x69\x59\x1f\xff\x64\x92\x6a\x4b\x4f\x3c\x14\x1a\xe8\x89\x60\x28\xf5\x5d\xbf\x90\x8a\x2d\xfa\x5b\x59\x03\xfb\x53\x3f\xb8\x74\xad\x4e\x91\x98\xfe\x3a\x92\x0e\x7a\x6a\xf6\x31\x07\x1b\x0c\x78\x6c\x45\x6d\x65\x61\x54\xaa\x6d\x25\xbe\x5c\xef\x30\x60\x89\xa5\x75\xb3\x6d\x81\x18\x54\x31\x9c\x71\x33\x78\x8b\x03\x84\x8c\x1f\x25\xc4\xd1\x98\xc2\x55\x83\xb6\xd7\xb0\xc2\xff\xf9\x6c\x47\xc0\xfe\xa3\xdc\x62\x84\x38\x56\x23\x79\x7f\x91\x2e\x0c\x07\x73\x66\xf7\xe2\x20\x2f\x04\xa4\x53\xb5\xbf\x3b\x9c\x90\x3a\xac\x20\x38\x2f\x5a\x57\x2f\x4e\x20\x10\x2d\xa4\xdb\x7d\xd2\x28\xac\xe9\x12\x6b\x48\x00\xf7\x79\x54\x92\x1f\xc8\xd0\xae\x4d\xcc\xb4\xa4\xfd\x3d\xb9\x96\xeb\xb5\x00\xf2\xef\x56\x2a\x41\x84\x26\x8b\x59\x4a\x75\x9a\x32\xb5\xc3\xc3\x5a\x37\xbb\xdc\x5f\x04\x97\xc1\x69\x4c\x7d\xdd\x73\x8f\x03\xdc\x7e\x2a\xa7\x49\xa8\x23\x52\x25\x69\xf2\x48\x54\x82\xd1\x61\x6f\x13\xd7\x65\x53\x0f\xbe\xfd\x18\x67\xf4\xab\x60\x3b\x72\xa9\xf4\x60\xc4\xa8\x56\x39\x41\x60\xfb\xb6\xb1\xcb\x2b\xda\x31\x27\xb1\xf4\x46\x10\x9f\x68\x0d\x1d\x80\x94\xfb\x82\x30\xb1\xb5\x04\x21\x25\xe7\x41\xae\x04\xca\x35\x13\x57\x7c\x69\xc3\xd5\x2b\x3a\xc2\x68\xc3\x2c\xeb\xfe\x75\x16\xe4\x33\x1f\xd2\x59\xaf\x69\x96\x95\xdd\x44\xe2\x2b\x47\xdf\xbd\x62\x95\xc4\xc3\xc4\xd1\x30\xe4\xd7\x5e\x88\xeb\xb2\x9e\xf8\xdb\x2a\x39\x76\x91\x5d\x28\x53\x22\x7c\x95\x4a\x88\x93\x28\xcb\x8b\x72\x01\x71\x45\x19\xaf\x44\x03\xe2\x53\x7b\xf8\xae\x5f\x8d\xaf\x3a\xc7\x97\x10\x69\x93\x0f\xbc\x6e\x9e\xad\xc6\x9a\xa2\xbc\x16\xd5\xab\x0c\xdd\xcf\xd3\x94\x4e\x9e\x03\x09\x5d\x99\xc0\xae\xdc\x04\xd9\xf9\xf6\x05\xb7\x2b\x85\x24\xf1\x69\x18\xa0\xdd\x58\xf0\xb2\xb5\x66\x75\xda\x59\xcf\xa6\x2e\x6a\xba\x36\x65\xa0\x89\xaa\x7f\xb0\x36\x18\x58\x3b\xb0\x71\x81\xa3\x3d\x1e\x23\xf5\xa5\x55\x79\x87\xef\xcb\x83\x81\xe1\x4a\xb7\x34\xee\xf4\x78\x0c\x5e\x71\x53\x1e\xa8\x29\x4a\xa6\x15\xb2\x99\xa9\xc6\x36\x47\xce\x27\xf1\xda\xe5\x44\x58\x1c\xaa\x12\x85\xc8\x17\x24\x75\x35\x95\x88\x26\x24\x49\x75\x0d\x8c\xbd\x2d\x82\x3c\xa7\x61\x8f\x55\xa1\x5d\xdf\x31\x88\x1c\x2d\x69\x93\x97\x29\xc2\x83\x19\xb0\xd0\x69\x98\x43\xfa\x7c\xa7\x9a\x36\xab\x64\x65\x19\x4a\x5b\xca\x6b\x6d\x65\x31\x43\xae\x25\x21\x58\x0d\x84\x08\x93\x46\x05\xaa\x4b\x3d\x59\xe0\x94\x8e\x83\x65\x4e\xd9\x49\x3c\x4c\x93\x82\x9c\x07\x09\xd8\x24\xe5\x8b\x34\x8a\xf9\x6d\x78\x52\xd0\x6c\x12\x8c\x95\x6f\xec\x06\x27\xf1\x26\xa7\x6d\x7b\x9b\xaa\xe7\x87\xc4\x71\xaf\xab\xd6\x34\x5a\x9b\x3f\xd1\x82\x3b\x6b\x66\xfb\x63\x8f\x9c\xcf\xa2\xf1\x0c\x8c\x06\xd8\xf2\x2e\x52\xb1\x8d\x91\x45\xbc\xcc\xeb\xaf\x5e\x05\x1f\xa8\x99\x5f\xcd\x3c\xfc\x86\x4c\x35\x22\xec\xea\x72\xaa\x2a\x56\x2f\x3f\xde\x46\x76\x2c\x97\x1b\x91\xb1\xf2\x8d\xe4\x98\x2a\x19\xc6\x7c\xe9\xd0\xe7\x06\xe9\xcd\x99\xaf\xe7\xd4\xe3\x3d\xee\x36\xb8\x3e\x2f\x63\x4d\xce\x61\xd8\x7b\x0a\x2e\x79\xc9\xe2\x3b\x0f\xbb\xbb\x9f\xb6\x0b\xe7\xf8\x73\x1f\xaf\x10\xcf\x61\xda\x6b\xb6\x64\xd1\xed\x8e\x32\x7f\x36\x6d\x25\x5a\xa3\x6f\xcb\x2c\xa0\x95\x45\x43\x6b\xb4\xb5\xed\x9a\x44\x8b\x91\xb7\x46\xdb\x9b\xd7\x27\xbd\xad\x27\xf7\xa6\x4f\xf7\xa6\x4f\x7f\x6d\xd3\x27\x64\xeb\x2c\x4c\x20\xef\xc0\xd8\xb9\xc4\x8d\xa5\x30\xae\xe4\xef\xb2\x0e\x27\xf2\xce\x79\x2f\x9b\xe6\xa3\x12\xcd\x0d\x92\xf1\xc4\x09\x56\x54\x82\x63\xdf\xc9\xed\x84\xb1\x4f\x59\x29\xc1\x26\x4e\xc0\xe7\x7b\xbe\x3e\xbc\x7f\xb7\xcf\x99\xfb\x6d\x3a\xc0\xe3\x2d\x01\xab\xa5\xf0\x80\xb1\x48\xc9\xfb\x77\xfb\xe2\x9e\xc0\xdf\x01\xf1\x1c\x1d\x9c\x28\xea\x96\x67\x69\x8e\x6f\xbf\xdc\xc6\xf7\x0f\xdf\xbe\x7d\xb9\xff\xf1\xe0\xf0\x2d\x79\xf9\xfe\xfd\xe1\xfb\x11\xd9\x57\xea\xdf\x31\xaf\x92\x9f\xe8\x43\x4a\xda\x1b\x84\xd5\x47\x36\xda\x7d\x7f\x1f\xb4\xc7\x9b\xa6\x63\x57\xef\xec\xb9\x12\xa1\x60\xab\x27\xe2\x95\xf9\x9b\x90\x86\xb4\x23\x62\x1b\x05\xa3\x61\xc2\xb3\x34\x9a\xe7\xc1\x94\x92\x5d\xb2\xbe\x2e\x5e\x1a\xb2\x6d\x5d\xfc\xee\xf3\x90\xb1\x4e\x4a\x5f\x16\x7b\x46\xbc\xc9\x23\xa2\xa6\xeb\xef\x1f\x0e\xdf\xc2\xac\x64\xaa\x4b\x9e\x30\xab\xa2\x6f\xce\x5b\x32\x8d\x03\x51\xb5\x39\x5a\x3d\x9b\x1f\xf9\x75\x35\x1e\xef\x3c\x6f\x3a\xa5\x1f\x0f\xde\xbc\x3c\x3c\xfa\x38\x22\xe2\xd2\x9b\x11\x17\xeb\xe4\x3c\x27\x1b\xa4\xcd\xfe\x0b\xc6\x33\xc6\x31\xda\x46\x40\x1b\xe1\x46\xf2\xdb\xfb\xdd\xea\x7e\xb7\xfa\x6b\xef\x56\x68\xb3\x82\x57\x97\x7f\x54\x2b\xdd\xe6\x8f\xd9\x1b\xbd\xa1\xbf\xc3\xa7\xec\xd2\xe7\x10\x5b\xff\xea\x70\x86\x23\x32\xe5\xc6\x31\x44\xbc\xb1\x85\xb6\xf4\x61\xc1\x36\x42\xfe\xda\xef\xe0\x17\xd2\x94\x17\x29\xd2\x71\x3e\x0f\x5d\x41\x2a\x9e\x23\xe7\x69\xd2\xad\x79\x42\x8f\x32\x93\x34\xb9\x9c\xa7\x4b\xd5\xa2\x4a\x28\x39\xbd\x49\xa4\x4d\xa9\xc4\x15\x0d\xb9\x3c\x00\x41\x0c\x9c\x60\x4d\x22\x4d\x1d\xcf\x9e\xa7\x69\x7c\x0d\xe1\x55\x43\x70\x41\xce\x37\x09\xca\x21\x43\x34\x3b\xf0\x3e\x84\x86\x86\xc3\x74\x79\xe2\x83\x60\x04\x6c\x51\x8a\xda\x07\x6b\xc6\x34\x61\xef\x5b\x0c\xc2\x74\x1c\xc5\xeb\xb5\x03\x30\x20\xe4\xbb\x57\x22\x91\x47\x54\x88\xfa\xa2\x26\xb8\xdf\x10\xbf\x4b\xcc\x5d\xfd\xe5\xb5\xbd\x72\xe9\x0d\x31\xc6\x36\xa7\xcf\x90\xbb\x00\x07\x2f\x46\x16\xae\x43\xed\x1d\xdc\x1b\x2d\xc8\x5b\x41\x39\xea\x50\x75\x55\x4e\x82\x38\x25\xba\x0e\xca\x3b\x9a\x5e\x9b\x8f\x0e\x56\xa8\x67\x68\x85\xf0\x67\x5e\x31\x2e\x5c\xb4\x9a\x1e\x56\x1a\x91\xf4\xa4\x7e\xa3\xe1\xe4\xd1\x34\x09\x8a\x65\x66\x0f\x07\xa7\x97\x8d\x07\xc3\x94\x8f\x47\x41\x55\x0d\x08\x1c\x18\x34\xef\xbf\x78\xe1\x20\xc9\x5b\x70\xa4\x20\x09\x95\x6a\xa9\x48\x21\x28\xf1\x24\x4a\x82\xd8\x6f\xf5\xcc\xeb\xf0\xd9\x94\xe2\x75\x6d\x65\x89\xea\x0d\xa4\xc8\x3c\x7a\x46\xb3\xcb\x62\xc6\x35\xd6\xf3\xd3\x08\x58\x46\xca\xa3\x44\x43\xdf\x44\x98\x85\x4a\x6c\x79\x5c\x83\x88\xee\x38\x9e\xed\xd4\xe2\x56\xbf\xd0\x23\xc0\x7b\x07\x22\xda\x5d\x87\xf2\xcf\x51\xe7\x59\x44\xea\x35\xd7\xad\x9d\xc7\xed\xa7\xa8\x9c\x3f\x6c\x15\xbe\x05\xb9\x9f\x4e\x49\xed\x9d\xae\xab\xd2\x14\xf3\xf4\x51\x76\xec\xb6\x2c\x1d\x85\xb0\xa8\xe4\xe7\xe0\x78\x59\x04\xd3\x16\xe5\x8f\x23\x08\x31\x65\x19\x03\x08\x20\x3c\x7f\x8c\x6e\x74\x72\xb2\x8c\xe3\x92\x17\x2e\x5a\xb3\x48\xdc\xcb\x7f\x53\x21\x0c\xf5\x95\x05\x66\x84\x4c\x6b\x34\x67\x15\xb7\xfd\x02\xfb\xce\xdb\x98\x0e\xdf\xbe\x7a\xe4\xcc\xbe\x39\xef\xda\xb1\xf5\x56\xaa\x0d\xfa\x5e\x43\x71\x26\x91\x8c\xd3\x64\x1c\x14\x1d\x63\xf6\xbb\xe5\x7e\x6c\x4a\xb9\x9e\x70\x62\x53\xce\xf5\xec\xdd\x96\x96\x71\xb8\x90\xdf\x3d\xb8\x3c\x4c\x70\x05\x61\x38\x04\x27\x04\x5e\x4b\xa8\x9a\x7d\xf0\x00\xf4\x0d\x66\x2f\xaa\xb7\xe9\x72\xe7\x3b\x80\x83\x3b\xf4\xbe\x13\x64\x53\x6b\x75\x69\xf1\xf1\x99\x51\x72\x84\xbf\x84\x67\x9e\x4d\xe4\x09\x45\x8c\x4f\xdc\xbf\xa8\x7a\xed\x97\x5a\x7c\x32\xc9\x17\x25\xa5\xe1\xfa\xb6\xba\x3b\x6c\x65\xfe\x92\x46\x49\xa7\xd5\x72\x2b\x57\x8f\xe2\x38\xb9\x71\x3c\xe1\xeb\x0d\x90\x0d\x3b\x6c\x99\x77\x7b\xb8\x47\xf8\xaa\x26\x49\x8b\x03\xa3\xaf\x0a\x85\x1e\x7f\x43\x1a\xb8\x61\xdb\xf0\x6a\xa1\xdb\xb3\x5a\xc1\xed\xab\x8d\x04\x71\xed\x74\x59\x2c\x96\xc5\xeb\x74\xaa\xd9\xb5\xf0\xc5\x83\x56\x8b\x74\xfe\xc3\xfd\xcc\x20\xb1\xcc\x04\xd3\xdc\x1a\xc6\x64\xbb\x81\xe2\x30\xfc\x96\xcb\xe0\xa7\x19\x0d\x97\x63\x8a\xe6\x2a\x18\x8f\x7b\x44\xb8\xa2\xc4\xfc\x24\x18\x8f\x8f\x45\x32\xe7\x89\x0c\x29\xe2\x5b\x52\xf9\x33\x73\xca\xfa\xf9\x2c\x9a\x14\x9d\x2e\x19\x39\x18\x95\x59\x8e\xd2\x2a\x18\x8f\xa5\x96\x8a\x1b\x7b\x73\xd2\xa6\x31\x2d\xa8\x1c\x87\x76\x92\x64\xa6\x73\xaa\xba\x01\xcb\x40\xf7\x57\xe2\x5d\x89\x58\xda\x6c\xab\xe7\x62\x5c\xa9\x63\x85\xbb\x92\x8b\x8c\x86\xab\x85\x1f\x8f\xe3\x06\x5b\xfa\xf9\xa3\x7b\x64\xda\xaa\xf7\xc8\x54\x55\x7c\xb3\xdc\xc6\xce\xac\x80\x18\x12\xa0\xe1\xfb\xc1\x16\x3b\x6c\xb7\x4f\x8e\x40\xf9\x87\xf2\xff\x54\x4a\xcb\xd8\xf4\xbf\xc1\xa3\x46\xeb\x55\x9b\xf7\x45\x63\x25\x35\x7e\x2d\x67\x53\x0c\xd4\x3c\xb9\x96\x71\x40\x69\x5f\x08\x2d\x1d\x23\x80\x13\x83\x7a\x7d\x00\xd8\x7f\x95\x26\x0a\x2f\xe8\xb1\x62\xf7\xbc\xed\x93\xd2\x01\x18\x56\x13\xde\x3b\x61\x03\x97\xc8\x23\x56\xd5\x95\x70\x9d\x9f\xac\x1b\xba\xc6\x7a\xda\x44\x01\x7f\x5b\x5f\x97\x03\xbf\x6e\xf2\x0d\xa7\x41\x8f\xfe\xaf\x3a\x90\x08\x8e\x21\xb2\x36\x18\x90\x8f\x87\x2f\x0e\x47\x24\xa3\xdc\x20\xab\x47\xf2\x54\x98\xce\xa8\x2b\x2e\x6d\x8b\x13\x70\x4d\x57\x9f\x95\x8b\x8a\x76\x4e\x12\x3a\xa6\x79\x1e\x64\x97\x6c\xb1\x40\x04\xec\x9c\x91\x5b\x1b\xfc\x15\x83\xb7\x68\x72\x9e\x66\x9f\xb9\x94\x37\x5f\xc6\x45\xb4\x88\x51\x24\x07\x33\x76\x8a\xdf\xbd\xd1\xe0\x21\xf1\xda\x72\x7f\x23\x4d\xb9\x79\x1d\xa6\x19\x83\x6c\xde\xb0\x21\xd5\x8d\xd1\x90\x6f\x1c\xe6\xc9\x44\x95\xea\x4b\x1c\xf9\x1c\xd8\xac\xb3\xce\x1d\xbb\xb0\x27\xbe\xf3\x43\x19\xac\xc5\x4e\x89\x63\xdf\x68\xf6\x53\xf8\x73\xf2\xd5\x54\x63\x06\xe9\xad\xa7\xf4\x08\xa5\xeb\x17\x04\x6f\x8f\xc9\x01\xf0\x1c\xb9\x79\x8e\x0f\x1b\x3c\x47\x31\x3d\x61\xd2\x63\x76\xd1\x63\xf9\x29\x8a\xe5\xb4\xb0\x22\xc5\xf8\x7c\x5c\x55\x1e\xc4\xaa\xa7\x3b\xa2\x15\xe3\xd5\x30\x9e\x21\x97\xd1\x0b\xd1\x41\x4e\x2e\x57\x1e\xb6\x2a\x78\x07\x03\x27\xc8\x6e\x94\x5e\xf4\x0d\x76\xa4\x3f\x76\x88\x04\x90\x5c\x08\xfe\xdf\x91\xa9\x8a\xe5\xf0\x1f\x2a\x1d\x31\x1a\xf9\xd3\x94\x23\xe9\x85\x78\xde\xed\x72\x73\x8e\x06\xed\x99\xa8\x84\x3f\x97\x70\xe4\xd6\x68\x1b\x3c\x18\x61\xa7\xe1\x8c\x31\x7f\x77\x7f\x33\x7a\x7f\x33\xfa\xd7\xbe\x19\x15\xd7\xa2\xe2\xc9\xef\x7f\x45\x7c\xbd\x3b\xf5\x18\x0e\x87\x80\x87\x64\x3f\x4d\xce\x28\x63\x45\x81\x08\x79\x0c\xe7\x60\x38\x0b\x40\xdc\x62\x19\xc8\x85\x11\x70\x10\xe7\x29\x09\xe2\x38\x3d\xcf\x79\x78\x76\x50\xd4\xe5\xfd\x35\x56\x91\x14\xfc\xdf\x44\x17\x34\xbc\xe6\x59\x6b\xee\xbd\xc6\x9a\xb8\x51\x2d\x52\x3b\xc8\xb1\x50\x59\xaa\x03\x67\xc7\x54\x89\x92\xab\x2b\x19\x20\x5d\x67\xb4\x95\x0e\xb5\xdd\xb5\x95\x01\xfc\x2c\x27\x44\x24\xae\x98\xe5\x7d\xe8\x48\xfd\xa2\xd1\x10\xd7\x43\x1c\x4e\x40\xd5\xdc\x85\xda\x87\x4e\x9d\x00\x29\xf8\x3e\x7e\xd1\x6a\xdc\x19\xc9\x20\x4a\xaa\x1d\x38\x72\x31\x51\x93\x71\x5a\x79\xf9\x63\x5b\xc2\xa6\x4a\xbf\x2f\x0e\x5b\x3d\x36\x09\x67\x34\x8b\x26\xe0\xd7\x23\xa3\xe3\x80\x71\x1c\x14\xa8\xe6\xc1\x03\x12\x07\xbf\x5e\x92\x38\x0d\x42\x12\x5e\x26\xc1\x3c\x1a\x93\x34\xa1\x39\xb4\x26\x26\x44\x37\x24\x82\x59\xa7\x4a\x4f\x00\x50\xd2\xbe\x5e\x36\xee\x40\xb1\xd9\x9a\xd2\xe2\x50\x1d\x92\x3d\x1e\x9c\xd9\xc4\x68\x81\xb5\xce\x3d\x00\x56\x26\x88\x29\x91\xc7\xe4\xf2\x5b\x0f\x43\xd3\x5f\x7a\xf5\xc2\xb3\xf3\xf3\x08\xe2\x95\xa0\x5e\x11\xd0\x41\xe4\x94\x9f\xa0\x47\xce\xcb\x2a\x2e\xbc\x2f\x33\x2a\xd4\x8b\x3d\xb8\xc0\x1b\xf3\xd5\xc1\x0f\xc7\x33\x7a\xe1\x53\x1b\x68\xad\xa9\x95\x60\x79\xa2\x6c\x50\xc4\xd0\x7c\x8a\xb0\xda\xa5\x4a\x79\x4b\xe1\x2f\x83\x70\x3f\x13\xe1\xc9\x59\x55\x62\x91\x75\xc9\x48\xae\x37\x01\xe6\xca\x4a\xbe\x6b\x02\xcf\xf3\x3a\xe8\xe6\xc8\xea\x76\xcf\x81\x63\x4b\x40\x43\xb1\x2f\x17\xa6\x48\x71\x3d\x6e\x7e\x20\xa3\x32\x4b\xa0\x00\xc7\x64\xb6\x5b\x83\xfb\xab\xd1\x4a\xd7\x5a\x7d\x55\xae\xeb\xeb\xdd\x4d\x6a\x14\xa5\x4c\xfd\x14\x3a\xe8\x70\x0a\xcc\x67\x8c\x02\x3d\x08\xb7\x48\x5d\xaa\x6a\xf6\xc2\x90\x3f\x8b\x50\x4a\xb4\x20\x09\x49\x4e\x8b\x9c\x2c\x17\x90\x21\x4e\x23\xc0\x32\xa2\x82\x66\x6c\xef\x48\xcf\x84\xb0\x25\xdc\x98\xf6\xd7\xd6\xd0\xd3\x88\xd7\xe9\x34\xdf\x2b\x3e\x14\x41\x56\xac\xd9\x9a\xc6\x9c\xc6\x13\x95\x38\x71\xdf\x2f\x0b\x16\x6e\xd6\x62\xc4\x09\xa3\xf1\xc4\xf1\xe1\x23\x1f\xd9\x4d\x69\xc1\xf5\x59\xac\xb0\xf5\xd2\x0e\xf4\x0b\x7a\x98\x39\x74\x8f\xc8\x93\xa7\xc5\x33\x58\x2b\x7d\x1f\xe3\x80\x8c\x29\x2d\x3a\xd6\x9b\x1f\x61\xc9\xe8\x9c\x72\x06\x03\x12\xa6\x49\x5b\xbc\x12\x65\x7d\x14\x68\x03\xb3\x49\xb8\xe8\x96\x89\xd2\xec\x08\x3c\x61\xf4\xfb\x7d\xf2\xcb\x92\x3b\x02\x66\x6d\x32\xde\xeb\x9c\x97\x4b\x1e\x46\x56\x3c\x8a\xbc\xb6\x5f\xc0\x5a\x2b\x5d\x0d\xc3\x7f\xc6\xe4\x99\xde\x83\x29\x37\xe4\xac\x7b\xa6\xc9\x1f\xef\x98\x66\x9f\x46\xff\xea\xfd\xb0\x7e\x3d\xd2\x5d\xa4\x71\xcc\xc9\xc7\x4f\xb6\x82\x36\x35\x98\x4d\x97\x4a\x25\x02\x6a\xdb\xe4\x8d\x32\xc3\x35\x88\x25\x2d\x21\x17\x31\xa3\xa9\x33\xa7\xd2\xc8\x82\x91\x9e\x1c\xab\x6f\x12\x7c\xcf\xa6\x7c\x34\x91\x36\x3e\xc9\x37\xa5\x8e\x9b\x51\x86\x36\x53\x86\xa1\x69\xe5\xf5\x33\x2b\x41\x57\x32\x92\x85\x5c\xd2\xb9\x15\x7a\x6e\x47\xa4\xa5\xfa\x00\xe8\x93\xed\x8c\x9a\x31\x9e\x77\x69\x1c\x33\x3e\xa3\x7b\xc2\x69\x70\xc4\x8b\xb0\x73\x1a\x9d\xd3\xa4\x80\x23\x67\x9f\x51\x1c\x0c\x4d\xef\x25\x0b\x61\x68\x7f\xcc\x31\x05\xe4\x78\x10\x9e\xf4\xe4\x15\x95\x91\xdc\xd3\xc4\x28\x72\xb0\x1b\x23\xae\x20\x06\xfa\x65\x9b\xb5\x8c\x5a\xe8\x90\xb8\x25\x93\xf5\x88\x13\xde\x43\x2e\x37\xcf\xed\x40\x4f\x9c\xa6\xf6\x33\x0a\x63\x02\x7b\xed\x7d\xcf\x43\x47\x60\x76\x5c\x83\x8d\x2e\x5c\x0d\x7c\x20\x0d\xdf\x2a\xaa\xb2\x52\x5d\x57\xa9\xb2\xc7\xaf\x54\x33\x3b\x83\x6c\x09\x48\xa9\xc7\xf8\x52\x6b\x4c\x2d\x6c\x6a\x31\xd8\x12\x7d\x11\xb4\x83\x06\x33\x01\x41\xca\x99\x77\x9f\x8c\xa9\x15\x22\x2c\x6b\x54\x86\xd8\x72\xf7\xcb\xf2\x35\xdb\x73\xb2\xf0\xb5\x93\xfa\x5d\xda\xef\x7e\x42\xcf\xc5\xad\x13\xc6\x01\xf6\x15\xc6\x99\x64\x14\x1a\xae\xf1\xfc\xcc\xb1\x66\xd9\x77\xc6\xa7\x1e\x31\x77\x7c\x5a\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x43\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x07\x10\xbb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0b\x46\x57\x94\x4e\x00\x98\x15\x62\x52\x4f\x97\x2b\x57\x54\xeb\xcb\x24\xc8\xf3\x68\x9a\xd0\xb0\xef\xf6\xd1\xa6\x28\x1f\x4f\xf6\xcd\x8e\x92\xb1\xc6\xa7\x35\x13\xe4\x6d\x06\x9b\x8c\xa1\x91\x68\x7b\x62\x12\x63\xe9\x30\x88\x33\x1a\x84\x97\xfa\xbd\xba\x16\x14\xf3\xdb\x53\x9a\x29\xc8\x4a\xe9\xb5\x6e\x5c\xd1\xa4\x63\xb5\xa6\x7c\xc0\x0d\x5d\x8f\x5c\x7a\x65\x72\x2e\xee\x73\x0b\xc9\xa4\xe8\x22\x15\x63\x8b\xe6\x73\x1a\x46\x41\x41\xe3\x4b\xbb\x59\x41\xee\xe3\xa6\xb4\x6d\x4a\x27\x50\x7d\xa7\xc4\xd3\x84\xcf\x6b\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x44\xe9\xb0\x37\x73\x93\xb7\x71\xc3\x3e\xd4\x0f\xa9\x8e\x31\x98\x23\x1e\x8d\x35\x4f\xe2\xba\xd4\x1d\x08\xc2\x35\xba\x13\xbe\x6e\x3a\x10\xbc\xef\xd6\x8f\xc7\x91\x1c\xd2\x85\x14\x1c\xcc\x81\xd4\xf0\x77\x78\x5a\x3e\x4f\xcf\xa4\x4a\x93\x04\xf9\x65\x32\x56\x87\x1f\x9f\x60\xe4\xe3\xdb\xcb\x04\xde\x4e\x1b\x08\x40\x32\x86\x85\x2d\x87\x77\x61\x43\xf8\x55\x6a\x36\x04\x7f\x07\xa3\x53\x2b\x64\xbb\xcf\x79\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4c\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xf7\x2b\x24\x2a\x88\xf6\xd2\xa5\xcb\x9d\xcf\x28\x18\x63\x88\xd1\x77\x09\x67\xaa\x39\x89\x0a\x93\xad\x7a\x37\x6a\x87\xd8\xd5\x32\xf3\xed\x1e\x3e\xf4\x8b\x1a\xed\x09\xc5\xfb\x31\x44\x48\xf1\xf0\xb7\xaf\xe8\x9f\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x62\x7b\x42\x89\x3b\x3e\x7f\xc0\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9b\x29\x21\x88\x1f\xbc\xe0\x09\x23\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x3a\x2c\x04\xb7\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\xf2\x5e\x28\x0c\x03\xd8\x6e\x6f\x7b\x22\xe2\xd5\x08\x61\xb7\xed\x35\xaf\x05\xa7\x12\xa9\xb6\xd2\x6b\x42\x40\xae\x0d\x1d\xab\xa1\x7e\x0e\x6f\x31\x6f\xd7\x35\xfc\xd8\xb9\x22\xe5\xb8\x75\x62\xbf\x55\x5c\x90\x5e\x9f\xf4\xb6\x87\x4d\xae\x40\xdb\xcb\x9c\xeb\xc5\xc7\x45\x7b\xed\xfe\x42\xf4\xfe\x42\xf4\x4f\x7c\x21\xaa\x9f\x8a\x22\x95\xf5\x4d\xde\x8b\x0a\xe0\x15\x6e\x32\x7d\xb1\xdf\x1a\x3f\x31\x4d\x26\xd1\xd4\x0b\xc7\xb3\x24\xe0\xc1\x69\x60\xc5\x74\x89\x4e\x83\xc4\x13\xa7\x05\xb4\xc9\x3c\xd0\x14\xb7\x91\xe6\x97\x99\xa7\xd1\x54\x78\x30\xb0\xac\x18\x39\xd0\xf3\x68\x6a\x29\xf5\xb1\x35\x23\xd7\x38\x5f\x71\x88\x2b\x05\x7b\x6d\x3a\xad\xd2\xe9\xd8\x12\x17\xf4\x8c\x25\x6d\x18\x52\x11\xef\x9d\xf7\x19\x5a\x91\xaa\xb2\x12\x6c\x47\x29\x81\xa2\xfc\x5d\x46\xc5\x35\x28\xba\x9d\x30\xea\x3e\xd5\xe9\x56\x03\xc7\xca\xdd\x7d\x5b\x9c\x3c\xdb\xbd\x36\x0d\xb2\x38\xe2\x89\xe3\x74\x3e\x8f\x8a\x82\x86\xed\x13\x75\x45\x6a\xd4\xf6\xc3\x2e\x19\xa2\xce\x24\x8b\x65\xf1\x82\x4e\x82\x65\xec\xbd\x2a\xa9\xeb\x15\xdb\x83\x4f\xf1\x20\xf0\x43\x19\x6f\xbc\x16\x46\x24\xfd\x10\xb5\xe8\xf1\x36\x55\x7e\x73\x83\xbb\x60\x8d\xe2\xb7\xe8\xbe\xfd\x86\x8b\x8b\x24\xac\x96\x92\x59\x35\x1a\xf5\x54\x88\xb2\x3d\x78\x90\xd4\xf4\x8a\x5e\x54\x8d\xfc\xe5\x22\x1d\xcf\xaa\x46\x4e\x35\x00\xef\x03\x88\x98\x3a\xd1\x7d\xdf\x64\x67\xb6\x38\xd5\xb5\x2c\x6a\x94\xc9\xac\xef\xac\xe7\x9e\x7e\xe3\xb6\x0d\x73\x66\xde\xd5\x1c\x99\x6f\xa6\x13\x12\x18\x4e\x0c\x83\x24\x94\x77\xba\x39\xdc\xe9\x70\x0b\x06\xc6\x21\x5e\xbd\xfc\xa7\xc5\x18\xa0\x0e\x26\xc1\x7b\x59\x82\xbc\x75\x30\x9c\x01\x3b\x26\xfa\xf2\x32\x5f\xde\x4b\xb8\x75\x7a\x43\x94\x7f\x31\xae\xb9\xe1\xa2\x12\x5d\x16\xc3\xe7\xd5\x95\x45\xfb\x7b\x63\x08\x10\x81\x5c\xb4\x61\x78\x8f\x6f\x30\x59\x2d\xf4\x49\x38\xcc\xf2\x5f\x92\x9a\x12\x1b\xae\xba\x48\x45\x64\xeb\xa8\x20\xf3\x68\x3a\xe3\x22\xae\x72\xb3\x2c\xd4\x69\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x1e\xb7\xa7\x41\xfe\x2e\x8b\xc6\xb4\xdd\x23\xec\x37\xfb\x0f\xa6\x8f\xfd\x48\xd2\x64\x4c\x7d\xef\x28\x3f\xd3\xcb\x8a\x97\x94\x9f\xe9\x65\xd3\xb7\x94\x50\x93\x83\x43\x5e\xc3\x2e\xb2\xfc\x78\x41\xc7\xd1\x3c\x88\x3b\x18\xc0\x7d\xcb\x66\x5e\xf7\x7e\x6d\x22\x46\x4e\x3f\xef\x9a\x96\x7d\x55\xdf\x3d\x49\xdf\x94\x6a\xef\xe9\xf5\xb7\xa4\x57\x21\x6e\x39\x04\x0b\x37\xbb\x32\x4a\x91\xa0\x56\xaf\x10\xd6\x98\x4e\x2f\x4c\xc1\x4b\xa4\xaf\x19\xd2\x56\x2d\x65\x16\x17\xdd\x2f\x4a\xe7\x78\xd1\xc7\xdb\xf6\xba\x3c\xf7\x6b\x5d\x9b\x09\xa0\x7c\x6f\xa4\x12\x7f\x26\x80\x7a\x5d\xc2\xd2\x11\x2e\xe0\x1d\x9b\xbf\x7a\x07\xca\xdb\x86\x0d\x25\xd5\x8a\x17\x7d\x20\x29\x7f\x21\xc8\xd2\x90\xd3\x20\xf7\xc3\x4d\x83\xdc\x80\x02\xf2\x45\xa0\x5a\x08\x45\xf9\xba\x84\x34\xb3\xf3\x82\xe3\x47\xbd\xf2\xcc\x7f\xb1\x32\x29\xc9\x78\x38\x37\x21\x29\x11\x9a\xa7\x92\xb2\x54\xa4\xa8\x55\xc8\xcb\xae\xd8\x72\x10\x83\x43\xfc\xe8\x90\x3e\x35\xf4\xe6\x83\x72\xe7\xcc\x03\xa5\x29\x4f\x66\x36\x20\xbf\x52\xd0\xf2\x26\x4b\x08\x51\x45\x9e\x59\xce\x97\x71\x50\x44\x67\xf4\xa7\x20\x3f\xca\xe1\x05\x5d\x59\x55\x0e\xac\x55\xd7\xb4\xb6\x86\xa9\x2a\x27\x07\x6f\x1a\x45\x48\xb8\x38\x9d\xda\x36\x86\x3a\x03\xc5\xcd\x71\x94\x60\xa0\xc9\xf2\xaa\xc0\x3c\xef\x70\x19\x6c\x9d\xbe\x4b\xb4\xd4\x68\x01\xc0\xec\x36\x27\x79\x38\x2d\x54\x52\x39\x54\xd8\x80\xc6\xcd\x9a\xb0\x25\x0d\xd4\xa0\x4c\x69\x06\x03\xa2\x9c\x10\x81\x37\x3e\x71\xce\x26\x84\x37\xc5\xe6\xe7\x75\x34\x8f\x0a\xcf\x14\x9a\x00\x02\x57\x2a\xb1\x64\xde\x8d\x7c\xa3\x4c\x1e\xfd\xea\x63\x82\x3a\xd3\x80\x2e\xa2\x39\xcd\x8b\x60\xbe\x28\x2d\xa2\x20\xf4\xba\xe2\x19\x49\xd9\xca\x35\xb2\xcb\xaa\x55\x87\x78\xd4\x99\x30\x9a\x4c\xa2\xf1\x32\x86\x87\x24\x2e\x0f\xb5\x81\xcc\x81\xa4\x45\x10\xbf\x68\x52\x81\x05\x89\x85\x24\x73\xcd\x08\x70\xbd\xcc\xcd\x95\xe3\x66\xbb\x22\x48\x54\xd0\x79\xd7\x7e\x42\xe6\xd8\xf1\x01\x94\x7b\x63\x6a\xac\x2f\xdf\x66\xce\x0b\xd6\x2d\xb4\x53\xae\x12\xb8\xde\x69\xb4\xca\x3e\x44\xd3\x84\x66\x24\x8e\x9c\x68\xf8\x2b\xad\x2d\x5e\x4d\xee\x5f\x62\xc4\x5d\x63\x02\xbe\x7c\xa9\x09\x00\x7d\xd8\xf6\xcc\x95\x84\x91\xb3\x84\x13\x6b\xe6\xa6\x7e\x56\x04\x36\xb9\x62\xed\x10\x7a\x2e\x7d\xf8\xa3\x69\xe0\x53\x80\x0e\xee\xb8\x0f\x8d\x78\x5d\x9c\x4e\xbd\x88\xc7\x0c\xd6\x87\xf6\x38\x9d\x6a\xa5\x9b\x8b\x74\xa8\xd7\xc0\x3b\xae\x10\xa3\x1b\x5d\x76\x44\x13\xf6\x65\x6c\xae\x0a\x1f\x56\x86\x67\xa1\xdb\x45\x77\x70\x9d\xce\xee\x69\x54\xdc\x60\x1b\xf6\x56\x62\x34\x11\xa7\x53\x4f\xd5\x32\xb5\xa4\x4a\x55\xc8\x94\xfc\xe1\x02\xa7\xfe\x94\x7a\x3e\x8b\x72\xb6\x39\x2d\xd2\xbc\xb8\xc1\x31\xf5\x5d\x9a\x57\x4b\x67\x6e\xe0\xa5\xca\x4d\xcc\xad\x14\x4f\x34\xeb\x24\xde\xc1\xd8\x77\x7f\x11\x5c\xc2\x6b\x8a\x5d\x43\xe5\x84\xb3\x04\x92\x21\xa9\x28\x62\xef\x59\x4a\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x59\x94\x66\x11\x62\xeb\x4e\x41\x09\x81\xe2\x09\x4c\x70\xb8\x29\xc3\x7e\x9a\xf3\x0c\xde\x49\xee\x5c\x05\x33\x76\x94\x4e\x76\x8d\xaf\x67\xe4\x18\x7d\x9e\x90\x91\x32\x5e\xb8\xd6\xad\x72\xcd\x3b\x57\xc2\xc7\x71\x7a\x0e\x8f\x49\xa4\x2e\xa1\xaa\xfa\xea\xc7\x0f\x3c\x60\x22\x23\x26\x92\x26\xf1\x25\x8f\x02\x51\x18\x6f\x32\xe4\xbb\x08\xfe\xfe\xc1\xf7\x9c\x47\x3e\x8e\x20\x23\xfb\xa9\x0e\x7e\x16\x61\x1f\x7b\x59\x1f\x1b\xf1\x2e\x75\x0b\x04\xf4\x2f\x6c\x53\xbd\xdc\xac\x8e\xd2\x9b\x6c\x1c\xd5\x84\x2d\xe8\x1a\xf0\x4b\x2f\x16\x51\x76\xe9\x59\xf1\x28\x17\x93\x5b\xce\x9d\xc6\x78\xa1\x59\x5e\xd9\x12\xb0\x40\x3d\x0b\x00\x28\xdb\x27\xd0\x59\x10\xdd\x1d\xdf\xaa\x7c\x1f\x9c\x4b\x92\x11\x29\x5e\x30\x54\xfd\x5e\x3e\x8e\x22\x7b\xf9\xca\x32\x78\x1b\xfd\x7b\x2e\x10\xa7\xe0\x74\xd8\x15\xbd\x2a\x74\x03\xe0\x45\x19\x42\x9f\xf9\x98\xc3\x60\xb0\xca\x8a\x80\xb5\x89\x57\x63\xe9\x62\xd4\xcb\xed\x16\x2b\xc9\x52\xa9\x73\x14\x35\xa3\x7f\xc5\x54\x6d\x2d\x98\x2f\x46\x0a\xbe\x12\x11\x49\xfd\x7c\x79\xca\xdf\x97\x75\x86\xbd\x6d\xbe\x2a\x5b\x17\xe1\xb8\x65\xf8\x0a\x52\xce\x88\x5a\xc3\x8b\x16\xd9\x20\x6e\xe1\x6d\x23\xa2\x08\xf4\x8a\xdf\x0e\x26\xf4\x1c\x2e\x0a\x3b\x66\x88\x6e\xb8\x4f\x39\x0d\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\x21\x34\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\x4c\x11\x9e\xca\x00\x50\xd4\xd8\xe9\x3a\x37\x35\x58\x70\xd2\x91\x8d\x64\x0f\x3c\xd5\x1a\xce\xf0\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xeb\x61\x6c\xd3\x14\xe1\xae\x5c\xc2\xb6\x65\xa8\x71\xcd\x49\x3e\x5c\x26\xe3\x28\xf1\x8b\x43\xc2\x3f\x38\x9a\xcb\x75\x33\x89\xb8\xee\x92\x0c\x21\x1c\x9c\x2b\x81\x6d\x63\x94\x4c\x41\xd8\xf5\x9e\xe3\x5d\x30\xd3\x45\x95\xf0\x16\x55\x53\x01\x86\x32\xcb\xcf\xa2\xe9\x8c\xe6\x75\xe5\x31\x14\xa2\x1d\x91\xfb\x39\x49\xcf\x93\x0f\x45\x50\x50\x9f\xbb\x42\x94\x5b\xde\x00\xae\x62\xc7\xae\x61\xb1\x8c\x63\x1a\xd6\x55\x81\xa1\x4a\x54\x0b\xda\x6b\x55\x49\x60\x82\xba\x5b\xda\x51\x2d\x44\x4f\xd7\x53\x51\x41\x4d\x49\xdf\x3d\xe3\xa8\x3c\x0b\x95\x34\xae\xd0\x46\x9e\x34\x04\xeb\x3b\x3b\x8e\xca\xb3\x50\x49\x9b\xcd\x8d\xfc\xc9\xa8\x84\xb1\x29\x8f\x3c\x69\x1c\xb6\xcc\x1e\x60\x54\x9a\x83\xcb\xf9\x07\x54\x9e\x57\x52\xd6\x56\x5b\x7a\xaa\xb0\x41\x8c\xde\x1b\x47\xe1\x91\x37\xd5\x81\xb7\x0f\xba\xa3\xaa\x4c\x5c\x1a\x1f\xd7\x46\x9e\x34\x0c\x6b\x4d\x82\x27\x11\x43\xdb\xdc\x6f\x54\x92\xce\xb9\xa6\x61\x82\xc6\x6f\xab\x5a\xa3\xcd\xa7\x65\x8e\x95\xd8\xd6\xd1\x1a\x6d\x6f\x5f\x9f\xf4\xb6\x37\xef\x9d\x72\xdc\xdb\xa0\xfd\xd7\xd8\xa0\x09\x4a\xbf\x8b\xe8\x3a\xab\x85\x22\x68\x68\x78\xc6\x83\xff\x98\x16\x65\x3c\xed\x2b\xc4\x34\x68\x1e\x85\x20\x88\xe3\x81\x15\xa7\x13\x9e\x17\xdb\x51\x7e\xdc\xd8\x04\xd2\x46\xde\x0d\x68\x56\x11\x93\xc0\x17\xd1\xec\x13\xdf\x1a\x85\xcf\x7c\x1c\xca\x77\x75\x7f\xf6\xba\x52\xb1\xb7\xe0\x5a\x79\xd2\xed\xaa\x85\x30\x80\x01\x9c\x57\xa1\x4e\xf9\x8d\x61\x64\xa8\x5f\x01\x22\x3e\x31\xc4\x9d\xc4\x53\x60\xfb\x83\x3d\x19\x86\xe7\x4d\x30\x32\xd0\x2f\xd2\xf0\x91\x29\x9b\x1a\xe7\xa5\x1b\x04\xb0\x96\x67\x0b\x1d\xed\x0f\xdc\x5a\x00\xaf\xe7\x6f\xa8\xb2\x69\xce\x83\x26\xac\x0b\xa1\xb1\x59\x87\xb1\x10\x58\xd9\x69\xdc\xbd\x1f\x1c\x52\x92\x39\x38\x76\xa1\x78\xac\xe9\x0e\xce\x3f\x36\xdb\x15\x43\x85\x78\xda\xd1\x78\x68\x88\x88\xaa\x08\x85\x38\xa6\xb2\x2f\x0c\x57\x94\x93\x71\x9a\x65\xae\x87\x4c\x38\x79\x05\x05\xdd\xcb\xa6\xb9\x2f\x68\xa1\x8e\x9a\xfe\x90\xfc\x0d\x4e\x6e\x39\xf9\x02\xe7\xb6\x6b\xd6\x5e\x54\x88\x27\x2a\x86\x13\x4d\xcf\x54\xe1\x76\x4a\xe7\x48\x9f\xde\x39\x14\xa0\xc8\x31\x40\x09\x34\xe2\x07\x03\xf9\x16\x09\x34\x5d\x86\x77\x1a\xd8\x3c\xc1\x27\xa2\x0e\x46\xc6\xb6\xda\x00\x5e\x32\x66\xc1\xa5\x7c\x97\x27\xe6\x6e\xbd\xe3\xc4\xb2\x0c\xba\xca\xc3\x3a\x3b\x8f\x3b\x17\x40\xd6\x1d\x87\x00\xe7\xde\x92\x2b\xe1\xf5\x95\x97\x51\xc6\x2a\x60\x3d\x0a\x06\x1d\x81\xc4\x8e\x24\xc4\xf5\xdd\xdd\x32\x42\x36\x1f\x62\xb1\x33\xb7\x08\x27\x57\x11\xf4\xad\xe3\xf8\x47\xa8\xf2\x28\x2c\xb5\x4d\x60\xf0\x84\x49\xc5\x88\x89\x91\xbe\xe3\x60\x1e\xf2\x72\x36\x0d\xed\x49\xbc\xc4\xbb\x70\x10\xab\x56\xb5\xfd\x57\x25\xe5\xa9\xf6\x2b\xc9\xce\x88\xa2\xba\x3a\xc3\x58\x95\x5f\x98\xc1\x4f\x4b\xa2\xab\x5e\x6b\x6e\x8e\x97\x4f\xc7\x13\xea\xb4\x48\xfd\x81\x0c\x8c\x50\xa8\xbb\xa4\x24\x48\x81\xcf\xd7\xbd\x78\x86\x83\x86\x6b\xc4\x58\xad\x30\xb8\x2a\x09\xc2\x23\x51\x7f\xb3\x60\x23\xde\xe2\x95\xf3\x7e\xa3\x90\x23\xc2\x5b\xfa\xb0\x47\x9e\x4a\x2d\x54\x45\x13\xcb\x64\x11\x8c\x3f\xf3\xbb\x46\xd3\xa4\x10\x92\x0c\x9d\x94\x99\xa4\xbb\x60\x7a\x8d\x92\x55\xf1\x1f\x8a\xf4\x76\xc9\x16\x79\x26\x13\xa5\x43\x77\x22\xcf\x81\xda\xc3\x81\x72\xc3\x5e\xe6\xcf\x1d\x0b\x39\x3d\x51\xdc\x9c\x51\xa1\xc3\xc1\xde\xa8\x55\x28\xbf\xe3\xe1\x09\x19\xf9\x7c\x8e\xef\x43\x24\xeb\x00\x05\x0f\x97\xc8\xb2\xc3\x93\x07\x71\x8c\x17\x77\xbf\xdf\x97\xeb\x7b\xdf\x2e\x6b\x6d\x3e\x8e\xb7\x9f\x03\xbe\xdd\x41\x94\x62\x09\xca\x76\xa3\x40\xd5\xd0\xe3\x8e\x5d\xec\x8a\xb9\x6b\x3b\x78\x4a\x29\x0f\x5d\x81\xf1\xd8\x2d\x48\x42\xd3\x25\x8c\x04\xe3\x61\xbc\xf9\xc9\x88\xd5\xc1\x63\x20\x32\x70\x81\x36\x2f\xed\x8a\x59\x85\x30\xc4\x75\x54\x0b\xbd\x2a\x0b\xf5\xbc\x4a\x1c\x67\xff\xbe\x29\x65\x30\xcb\x44\x53\xed\x31\x70\x90\xd1\xf2\x9f\xf0\x00\x6d\x88\x85\x98\xfd\x80\x17\x6b\x53\xfa\xc2\x45\xb0\xf8\x63\x17\xd3\x37\x15\xdc\xf3\xb4\xe4\xd2\x12\x4e\x1b\x7d\xac\xfb\x5e\x16\x6b\xdd\xb0\x62\x7c\xb4\x98\x71\x24\x88\xaa\x7b\x46\xd7\xdc\x77\x84\x50\x0a\x2f\xe1\x8e\xb1\x1e\x90\xef\x74\xe7\xe5\x6f\x93\x06\x7b\xae\xbf\x1e\x97\x07\x20\x6f\x3d\xf2\xb9\x88\xe1\xd3\xa1\xc7\x4d\x77\x76\x4c\x1f\xc9\xbc\xd3\x34\x74\xfc\xc1\x17\xd9\xa5\xf5\x0c\x12\x81\xc2\xcb\xc7\xf2\xf1\x12\xe3\xa9\xe6\x18\xde\xca\x77\x1c\x8f\x3b\x9c\xe2\x77\x09\xf5\xfa\xfa\xb1\x3b\x2f\x5b\x47\x92\x4c\xe5\x46\xd1\xe4\x5c\x69\x6f\x1b\x66\x91\xda\x5d\xc1\x6a\xe1\x4f\xb5\xd4\x6a\xd7\x8c\x24\x29\x01\x28\xec\x5d\x7f\x20\x43\x38\xd4\x18\x67\x4d\x57\x3a\xc4\x01\x50\x83\x84\x3f\x3b\x4f\x42\xe1\x9a\x12\x22\xd8\x26\x8f\xe4\x41\xd5\x09\xe5\x5b\xb3\x5c\x8d\xf0\x75\x6c\xdd\x58\xf3\xd0\x31\xaf\x27\x45\x75\xb5\xe0\xcd\xe3\x06\xd0\xbc\x88\xe6\x41\x41\x7f\x0a\x40\x81\x58\x47\x55\x08\xbc\x8e\xa2\x70\xcd\x77\x41\x4d\x5f\x9f\x3a\x9a\xcd\x10\x1a\x57\xdd\xec\x78\x40\xcb\x66\xe6\xbd\x6c\x86\xca\x48\x68\x10\xd1\x45\xea\x02\x85\x7c\x80\xa7\x62\x4a\x8b\x17\x76\xa4\x22\xb9\xb3\xda\xd5\xd4\xcd\x95\xa8\xeb\x8e\xe7\xa9\x11\xe2\xe5\x55\xb5\x58\x99\x3c\xc8\x4b\x73\xa9\xf9\x16\xf1\x14\x71\x51\x89\x67\x44\xf6\x95\x08\xfb\x6d\x83\x2b\xaa\xfa\x6f\x14\x5f\x51\x15\x5a\x75\x90\x5f\x33\xd8\xa2\xd6\xd1\xb0\x01\x66\x8b\xb1\xf4\xe2\x95\xf3\x53\x73\x1d\x23\x12\xd0\xe5\xe6\x36\x15\xe3\x12\x65\xff\xd8\x5c\x89\x18\x41\x67\x24\x18\x16\x53\x8c\xd8\x29\x78\x4e\x5c\xb7\x77\x96\xc6\xf5\x19\xf8\xd2\xfd\xc4\x7a\xdc\x26\x23\xfe\x61\xed\x24\xed\x9e\x23\xbc\x8c\xb4\xbb\x39\x95\xa7\x1c\xe5\x89\xe1\x9c\xe8\x2c\xde\x71\xe9\x86\x95\x33\xc8\x5a\x62\x90\x51\x62\xca\xb6\x1f\x15\x7f\xa9\x7a\xeb\xf1\x84\x4a\xc2\x13\x5c\x18\x82\xce\xba\x89\x1d\x6d\x66\x04\xdb\x7c\x81\x65\x28\xe9\x6a\x44\xa7\x95\x6d\x15\x16\x3a\xfb\xc1\x62\x11\x5f\x0a\xc7\x47\x8d\x08\xab\x6b\xdb\xe7\xf1\x2d\xc0\x6a\x86\x25\xde\xa8\xee\x9a\x79\x10\xe1\x84\x34\xe3\xd1\x11\x85\x6e\x1d\x4a\xc8\x33\x61\x5f\x2b\x9a\x90\x4c\xd7\x2b\x1e\x7b\x7e\x2a\x05\x17\x87\x4d\x8d\xe1\x32\x40\x57\x6a\xf6\x4e\x7e\x59\x71\x53\x44\xe2\x23\xd1\x49\xa5\xc5\xf4\x6e\x2d\x3d\x16\xb1\xcf\x3f\x65\x28\x25\x59\x16\x08\x3c\xca\xc6\xcb\x38\xc8\xd6\xd7\xd7\xd7\xab\x03\x28\x49\x0a\xda\xb9\x93\x10\x4a\x5c\xfb\xdb\x1a\x6d\x3d\xf1\xfb\xa3\xd9\xba\xbf\xfd\xbf\xbf\xfd\xff\x6b\xdf\xfe\x8b\xab\x7f\x06\x2b\x43\x5c\xf9\x03\x73\xfc\x6e\x21\x37\x7c\x96\x05\xd5\x86\x00\x6b\x83\x01\x84\xf0\x0a\x32\x46\xca\x6c\x07\x5b\xe6\xe6\x10\x19\xc1\x85\xd1\x64\x42\x33\x9a\x14\x84\x26\x67\x39\x14\x3a\xcd\xd2\xf3\x9c\x66\x6b\xc8\x3f\xe9\x79\x94\x84\xe9\x39\x68\x2c\x50\xe0\x0a\xf2\xe0\x81\xc8\xe9\xff\xf3\xcd\xeb\x57\x45\xb1\x10\xae\x6f\x39\xd7\x34\xd3\xc8\xae\x1f\x16\x58\x9f\x88\xbb\x10\x4d\x93\x94\x31\x82\x38\x4a\x28\xeb\x49\x92\x86\x74\x0d\x39\x3b\x73\x6a\x54\x03\xbf\x98\xc7\x6c\x64\x62\x63\x6b\x77\x9b\x36\x72\xcd\x31\xf9\xcf\x57\xef\xb7\x8c\xea\x66\xd9\x56\xbb\x5b\x5a\x4a\x4a\x0e\xac\x85\x77\x12\x99\xae\x49\x04\xc8\x4f\x4c\xb4\x07\x6f\x9f\xdc\x37\x38\xeb\xa5\x32\x80\x30\xca\xe3\x2d\x7f\x96\xe6\x45\x8f\x14\xd1\x9c\xa6\xcb\xa2\xc7\x2a\xcc\x7a\xa0\x64\x3e\x4f\x33\xe1\x8c\x05\x36\x13\x06\x47\x76\x09\xfc\x77\x75\x45\xda\x82\xd8\xe3\x74\x1c\xc4\x2c\x71\xf4\xf4\x9b\xc7\xdf\x40\x9c\x5c\xbe\xf7\xf0\x0a\xd9\x4e\x28\x7e\x5d\x5d\x91\xa1\xca\x66\xcd\x90\x5d\x68\x4d\xa5\xc9\x46\xc9\xae\x6a\xbf\x56\x78\x5a\x64\x74\x01\x81\xe7\xe8\xb9\x35\x65\x96\xec\x24\x00\xdf\xa3\xb3\x8c\x90\x9c\x9e\xa7\x69\x4c\x83\xe4\x1a\xee\x58\xd9\xfe\x2c\x25\x18\x8d\x65\xe1\x65\x12\x1d\xf8\xcc\xb6\x0c\x57\x46\x18\xd3\x48\xee\x32\x3b\x60\x5e\x04\xb2\xea\x39\xaa\xf9\x0d\x0a\x27\xa4\x35\xf1\x8a\x0d\x65\x13\xa2\xc5\x2b\x18\xf2\xab\xf7\x5b\x3a\x4c\x2d\x97\xb4\x10\xe6\xd1\x44\xc0\x93\x33\xec\xcb\xcf\xaa\xc8\x18\x4f\x47\xbc\x50\x5b\xd3\xb5\xa6\x0b\x9a\x74\xda\xef\x0e\x3f\x7c\x94\x91\x35\x39\xe1\xf0\xce\xed\xac\x21\xc7\x80\x30\xb7\x0f\x1e\x98\x93\x6a\x1c\xfa\x96\x60\x50\xd3\x7e\x1e\xe4\xd1\x98\xb4\xc9\x06\x74\xe1\xf9\x92\xb1\x07\x54\xc5\x06\x69\x8f\xd4\x55\xa1\xaa\xa7\x5f\xa4\xe2\xf1\x5d\xfb\x34\xc8\xe9\x93\xc7\x6d\x6b\xfc\xda\x2d\xf6\x2b\x1a\x84\x34\xeb\xb4\xf7\x80\xaf\x46\xbf\x06\xfc\xb4\x05\xed\xf3\x11\x56\x14\x62\xf2\x31\x4d\x8a\x47\xec\xa0\xdd\xee\x91\x36\x93\xfc\xa3\x31\x54\x31\xf8\x25\x97\x6a\x47\x75\x63\x25\xa6\xac\x86\x5c\x79\x00\x95\xcb\x64\x8c\x0e\xd5\xb6\x26\xd9\x77\xf1\xbc\x40\xd7\xd7\xfe\x50\xd9\x55\xa4\x97\xdb\xa1\x13\xa5\x2e\xcd\x26\x39\x49\x33\x26\xad\x8a\xd8\xcb\x40\x8f\x5a\xbb\xaf\x31\x97\x84\x1d\x78\xe9\xc1\xdf\x1d\x44\x93\x4b\x55\xbf\x40\xb2\x54\xe4\x63\xaf\xd7\x3e\x6b\x80\xfd\x34\x49\xa8\x78\x8f\x21\x29\x4c\x53\xa2\x71\xb9\x28\x5b\x97\xf1\x27\x3e\xd2\x8b\xc2\xe9\xa0\x80\x45\xcf\x50\x84\x55\xbe\xd9\xad\xaa\x2e\xbd\x17\xf5\x77\x7c\x0d\xe2\x55\xd2\x3c\x14\x32\xd0\x40\x50\x43\x04\x7b\x8a\xe3\x54\x50\x82\xc8\xfa\xd1\x09\x3e\x42\x8a\x2c\x9a\x4e\x69\xc6\x23\x26\xb1\xd9\x07\xb1\x45\xb9\x3f\x65\x38\xa8\x23\x18\xe8\x81\x8f\x6a\xcc\xc0\xc7\x4d\xe8\x07\x8c\x57\x76\x0c\x6e\x92\x80\xab\xea\xbc\x08\x0a\x3a\x9e\x05\xc9\xd4\xaf\x40\xe0\xcf\x0a\x24\xe2\x83\xf0\x12\x0c\xfa\xe1\x46\xf8\x31\xe3\x30\x36\xcb\x5b\x37\x03\x17\x37\xa0\x18\x0d\x28\x6f\x95\x50\x44\x2c\xfb\x32\xab\x86\xa2\xe0\x4c\xe6\xbd\xb5\x52\x37\x56\x2b\xd2\x16\xc1\x57\x5b\xf6\xc5\x96\xd1\x32\x3b\x0b\x5e\x5b\x28\xd6\x1b\x81\x8b\x59\xb3\xb2\xbc\xaf\x97\xde\x47\x5e\xaa\x83\x37\x0f\xb1\x90\xef\x96\x03\xd8\x5d\xa8\x62\x02\x62\xa5\xe1\x75\xa5\x2f\xcb\xe3\x4b\x46\xef\xfc\xd1\x2c\x2c\x2e\x46\xd5\x25\x6b\x2b\xca\x45\xfd\xd4\x64\xa6\x4a\x08\x90\x0a\x4e\x5b\x18\x60\xe7\x87\xa4\x5d\x90\x49\x10\xc5\x34\xec\x93\x43\x76\x4e\x3b\x8f\xd8\xd9\x23\x80\x20\x67\xe5\xab\x09\xb5\xe9\x99\x0b\x8d\x4f\xa5\xcf\x50\xc1\x34\xa2\x70\x44\xbe\x53\x7f\x52\xdf\xc7\x76\x9f\x6c\x31\x1e\x91\xf6\x56\x7f\xa8\x94\x87\x52\xff\xd8\x4e\x68\xf1\x29\x8e\xf2\x82\x26\xe0\xa6\x70\xcd\xd2\x1e\x9e\x18\x06\x5d\x52\xc1\x95\xf1\x88\x6d\x2e\xf9\x4a\xab\x42\x36\x48\x3d\x09\x8e\xba\x00\x0f\x5d\xaa\x0a\x8c\xd3\x3e\x13\x73\x5b\xa3\xa7\xec\x97\x21\x3f\xb7\x46\x9b\xdf\xb2\x93\xff\xf6\xfd\xc9\xff\xfe\xe4\xff\x17\x3f\xf9\x6b\xc3\x7f\x78\x2c\x79\x47\x46\xff\xca\x90\x13\x9f\x2a\x4f\xa3\x29\xb7\xc1\xed\xff\xc2\x4f\xe8\xfc\x1e\x24\x7c\x4d\x27\xe6\x86\xa0\x42\x57\x5e\xa2\x07\x7b\xc6\xc6\xc9\x21\x38\xbb\x38\x9f\xb1\xde\x77\x4c\x03\xad\xef\x79\x61\xf2\x90\x6c\xb9\x2f\xfe\xc0\xe2\x8f\x49\xf1\xe6\xbb\x47\xe2\x7f\x89\x27\x98\xfb\x3b\x71\xaa\x0b\x12\x72\xf0\x7c\xef\xad\x98\xe4\x90\x7c\xf7\x2d\x19\xa7\xf3\xc5\x52\x84\x8d\x39\xbd\x24\xf3\xf4\x2c\x4a\xa6\x28\x38\xda\x63\x32\x9e\x05\x19\xec\x05\xfc\x66\x36\xe4\xa6\x54\xd2\x5c\x5d\x42\xc7\x94\x3f\x5a\x28\x52\xd6\x20\xc7\x55\x4e\x3a\x7b\x64\x97\x6c\x0e\x7b\xe4\x39\xfb\x7f\xb3\x47\xfa\xfd\x7e\x8f\xfc\x1f\xd9\x25\xdb\xdf\x74\xd9\x61\x87\xe4\x0b\x3a\x8e\x26\x11\x5f\x48\x07\x1f\x0e\x37\xb7\x9f\x6c\x3e\xb1\x4d\xcc\xa2\x3c\x85\x74\x31\x0e\xd7\x49\xee\x35\x7f\x8b\xcb\x3a\xc2\x06\x68\x5e\xad\xe1\x9b\x65\x21\x49\x85\x12\x4c\xf8\x6c\x30\xeb\x37\x26\x94\x55\x8c\xe7\x91\x8d\xa8\xbd\xd7\xee\x33\xb4\xec\xa7\x21\xdd\x2b\x3a\x43\xa4\xb5\x66\x63\x6b\xff\x9f\x93\xcd\x19\x20\x7f\x2f\x0c\xc4\x5a\xa4\x47\x8b\x05\xcd\xf6\x83\x5c\xab\xb2\x51\x36\x7f\x76\xdc\x79\xdc\x95\x2f\x81\x45\xc2\xb0\xf7\xd8\xba\x31\xe3\xb9\x8b\x38\x2a\x3a\xed\x76\xd7\x7c\x85\x9d\x74\x4d\xeb\xaa\x71\x1a\xb2\xc1\x25\xbe\xce\x4b\xf9\x10\x60\x7e\xd8\x25\x7b\x4c\x20\x84\x8f\xef\x77\xc9\xff\x75\x9d\x90\x06\x9e\x99\x15\x13\x6b\x40\x2a\x8f\xb9\x21\x25\x8f\xc8\x1e\xd9\x20\x9b\x43\x64\x67\xe4\x73\xf3\x2f\x43\xa9\xda\x36\x4c\xd7\xdd\xfe\x2f\x69\x94\xb0\x61\xda\x96\x8a\xe3\x25\xb8\x70\x85\x29\x7e\x73\xf8\x82\x11\xf6\xe6\x50\x32\x25\x61\xe1\x07\x94\xef\xa1\xb8\x6f\x87\x4f\x1e\xdb\x04\x37\x4f\xc3\xef\xbe\xdd\x1c\x96\x11\x9a\x49\x5f\xda\x2d\x33\xa7\x26\x51\xb8\x92\x8a\x32\x3a\x0f\xa2\x84\xeb\x8e\x58\x9e\xbe\x7b\x14\xae\x83\x4c\xf6\x20\x80\xb5\xdd\xf2\x56\xd7\x72\x8a\x04\xcc\x4a\x82\x29\x8b\xd7\xef\x0c\x13\x39\xdd\x24\xc8\xda\x07\x49\xc1\x7d\xf8\xf4\xc8\xe6\xb0\x4b\xfe\xff\x0c\x6b\x1b\x4e\x2d\xdc\xe5\x92\x30\x3f\xf7\xbd\xfc\x55\x75\xa9\x92\xba\x3e\x63\x9e\xea\xdf\x21\x71\x13\x74\x58\x07\xc2\xe0\x1f\x2e\xd4\x21\x41\xbc\x75\x10\xec\x53\xce\x97\x7f\x72\x06\xd8\x79\xb7\x7f\x12\x84\x25\xb4\x5e\x72\x6e\x57\x9d\xa0\xb9\x75\xfd\xa4\x10\x53\x69\x39\x97\xaf\x73\x2c\xa2\x62\x30\x7b\x2a\xc7\xe9\x7b\x80\xb2\xa4\x18\xcd\x86\x70\xad\xd8\x1a\xd6\x8a\xb1\x9c\x3e\xaa\xb1\xca\x19\x02\xe8\x88\xf2\xe7\xd2\x57\x01\x7a\xa9\x20\x82\x9b\x92\xcd\x27\x88\x85\x9d\x06\x39\xdd\x7e\x42\x76\xa1\x8c\x56\x0f\x6d\x3f\x31\x4c\x00\xc2\x90\x72\xcd\x22\xec\x81\x1d\x5e\xa8\x47\x36\xbf\x31\x25\x61\xd5\xcf\xe7\xa7\x41\xd2\xe1\xc5\x4c\xe6\x67\x2d\x66\xe1\x6f\x05\x2d\xdc\xe7\x6c\xe8\x45\x6a\xec\x5e\x6c\xfa\x08\xf8\x69\xcd\x2e\xe5\x8a\xe6\xca\x24\xb0\xd7\x7d\xc7\x43\x5b\x24\x69\x21\x84\xb2\xef\xa3\x1f\x5a\x53\x90\x48\xb8\x1f\x9f\x89\x46\x6a\x3e\x0b\xb8\xb4\x06\xfb\xdb\xc5\x38\x5e\xe6\xd1\x99\x8a\xc4\x19\x9d\x46\x71\x54\x28\x01\xe7\x34\x48\x3e\x0f\x4e\xb3\x20\x19\xcf\x48\x4e\xb3\xb3\x68\x2c\x37\xc0\x80\xbb\x8d\x6d\x7d\x3f\x88\x7e\xe8\xdb\x34\xa4\xa2\x62\xe4\x72\x17\x9a\xd0\x8c\x6d\x43\x41\x3c\x4d\xb3\xa8\x98\xcd\x49\x48\xf3\x71\x16\x9d\x72\xb6\x24\xe4\x1f\x9a\xf4\xcf\xa3\xcf\xd1\x82\x86\x51\x00\x42\x10\xfb\x1a\x1c\x24\x05\xcd\x92\x80\x3f\x9d\xf8\xf4\x3c\x48\x3e\x7f\x12\x3e\x6b\x3f\xf1\x79\xfd\xff\xfd\x24\x46\x9a\x4c\x3f\xb1\x21\x7e\x82\xb7\x44\x9f\xc2\x68\x1a\x39\x4f\x39\xe4\xd4\xf8\x28\xf2\x54\xee\xa9\x72\x06\xa4\x33\x9c\x22\xf5\x6c\xb3\x0d\x68\xf5\xb9\xbd\x22\x4f\x2d\xb6\x28\x66\x74\x9f\xef\x53\xed\x7f\xbe\x6c\xef\xac\x79\x79\xa6\xe0\xb1\x1d\x6b\xe7\xee\xe0\x0a\x36\x48\x7b\x08\xa2\x12\xb4\x82\xcd\x5d\x18\x3a\x5e\x30\x6c\x90\x5d\xd2\xe1\xe2\x54\xe7\xbb\xa7\xe4\x91\x6e\xa2\x2b\x9f\x0d\x3c\xda\xb2\xf6\x5b\xe5\xed\xc3\x6c\x0a\xd5\x29\x1a\xac\x51\x5b\x09\x26\x82\x70\x05\x84\xcd\xe3\xa1\x47\x49\x5e\x44\xc5\xb2\x90\x9e\x97\xa3\x90\x26\x05\xdb\xb4\xec\x38\x02\xbc\x96\x83\x24\x8c\x32\x6a\x1a\x30\x98\x6f\x6c\xf2\x9e\x94\x65\xd5\x23\x1b\x78\x35\xd5\x42\x2d\xb5\xa0\xa9\x96\x6e\xab\xb5\x0a\x2f\x32\x7b\xe2\xf5\xc6\x6c\x1e\x81\x4d\xce\xd0\x7e\xf9\xf1\x15\x9b\x07\xf9\xba\x05\x63\x00\xa5\xaa\xbe\x75\x2d\x7e\x9d\x56\xf1\x6b\xf9\x94\x8e\x23\x57\x04\x1b\x8f\x72\xfe\x52\x0e\xf3\x71\x47\xee\x04\xcf\x2d\xa5\xf2\xa6\xda\x8b\x3c\x8a\x0f\xa9\xf0\xe0\xcf\xe9\x78\x4b\x4a\xe8\x3c\x40\x7e\x61\x2a\xe5\x84\x08\xfb\x97\x89\x38\x59\x61\xe1\x4f\x3b\x97\xa9\xd5\x95\x2b\x2c\x40\xd7\x4b\x5f\x0f\xe2\x31\xeb\x20\x13\xde\x51\xf5\x48\xea\xd1\xda\xc0\xd8\xb0\xb6\xc6\x1d\xa5\x45\x09\x83\xff\xfc\xf3\xe5\xf1\xf0\xd1\x77\x27\x5f\xb6\xae\x3b\x2f\x3f\xbe\x62\xbf\xf7\x1e\xfd\xdf\xc9\x97\xcd\xed\xeb\x2b\xf5\xb1\x3d\xec\x6d\x6f\x5e\x77\xff\x67\xd0\x2f\x40\x09\xaa\x36\x70\xe3\x5d\x5e\x19\x63\x40\xe0\xfc\x79\xde\xe6\x8a\x08\x13\x4f\x30\xe1\xf4\xef\x45\xdb\x0b\xbd\x04\xef\x06\x6f\x2f\xdc\x95\x64\x21\x4e\x0f\x0a\x3f\xee\xd9\x7e\x4c\xae\xae\xca\xf2\xbe\xb9\xe1\xb0\x27\x24\x4a\x4a\x06\x6e\x70\x9f\xbb\x19\xba\x97\x8d\x34\x1a\xfc\xd6\xb0\x91\xd5\x26\x17\x29\xd9\x48\xf3\xe5\x9c\x01\x1e\xe5\xe2\xf8\x30\x4f\xc3\x47\xdf\x7d\xfb\x68\x73\xa8\xb2\xe1\x8c\x0b\xbd\x1b\xa7\x31\xe9\x1c\x7c\x38\x1c\x1c\xbc\xdc\x27\xec\xdc\x30\xda\x1a\x0e\xb7\xbb\x36\x4f\x46\xd5\xba\xa7\x50\x94\xeb\x0c\x5c\xe6\x35\x1c\xb6\x38\x13\x6e\xf5\xc8\x56\x33\x5b\x55\xcc\x54\x8d\x2d\x85\xd0\x69\x9f\xfc\xf3\xfd\xcb\x9f\x1c\x0f\x89\xaa\x80\x7f\x34\xa5\x35\xba\x93\x8a\x20\xeb\x86\xa7\x09\xa0\x03\xee\xf3\x9c\x21\x7f\xdb\x23\x8f\xbb\x64\x44\xda\xed\x46\xe3\x1e\xc7\x11\x3c\x24\x53\x1d\x04\xe5\x53\x94\xd8\xe3\x63\x58\xf8\x69\xef\x1f\x87\x3f\xfe\xeb\xf0\xfd\xff\xda\xb3\x0a\x75\x94\xcc\xa9\x5d\xbf\x77\x72\x39\xd0\xad\xc7\xbe\xb9\xb9\xfa\xc8\xc5\x6a\xf2\x9f\x4b\xdc\x83\x87\x3b\x34\xa7\x02\x67\x78\x81\xe7\x1c\x82\xef\x9d\xc4\xe0\x7c\x8e\xcf\x8c\x43\x87\x3b\xe0\xc7\xe8\x10\x5b\x7a\x94\x91\xe7\x0f\x75\x4a\x31\x4e\xa8\xfc\x8c\x62\x9e\x67\x36\x9f\x74\x7b\x64\x6b\xa8\x5c\xab\x19\x52\x9e\x44\xaf\x35\x48\x59\xb8\xd9\x02\x2d\xf1\x86\x75\x00\x59\x5c\xa9\x8f\xf5\x8a\xad\x91\xf9\x79\x7d\xd2\xdb\x7e\x7c\xaf\xc6\xbf\x57\xe3\xff\xc5\xd5\xf8\x42\x85\xbf\x18\x57\xdb\xef\xdd\xc2\xe2\xae\xa5\x23\x2e\xb6\x76\x56\x8a\x14\x57\x63\xa7\xc7\xf5\x4c\x8b\xb1\xd7\x12\x6c\x11\x14\xb3\x1e\x49\xa8\x61\xfd\xfd\x09\x34\x17\xce\xc3\x53\x79\x55\x8d\x63\x55\x4b\xaf\x05\xc2\x5e\x07\x6c\x7c\xd8\x7f\x3c\x55\x67\x8d\xd5\x0d\x2f\x70\xc5\x42\x26\x74\xbe\x30\xe8\x91\x2e\xaf\x7c\x6c\x5a\xc5\xfa\x69\xd2\x69\xc3\xa8\xda\x38\xb6\x6b\xd7\xb0\x9f\xce\x53\xc6\xc4\xf8\x5b\xc2\x83\x77\xfb\x44\xdf\x2b\xf3\x17\x86\xed\x1e\xa1\x88\xf5\x7e\xe2\x6c\x50\x5c\x78\x77\x6c\x2f\x9f\xde\x1e\x24\x21\x6e\x1f\x35\x5f\x5a\x19\x59\x53\x6f\x0c\x5e\x1f\x7c\xf8\xf8\xf2\x2d\xac\xa0\xfd\xc3\xb7\x6f\x5f\xee\x7f\x3c\x38\x7c\x4b\xde\xbf\xfc\xf0\xee\xf0\xed\x87\x97\x1f\x4a\x5b\x0d\x83\x22\xc0\xcd\xb2\x6f\xbc\x39\x0d\x1e\x0a\x33\xc2\x79\x70\x31\x4e\xe7\x8b\x98\x5e\x44\xc5\xe5\x88\x3c\x01\xca\xb2\x7a\x08\xba\x50\x65\x87\xc0\xaa\xd2\xfb\x4d\xd7\x13\x2e\x47\xd8\x1c\x7c\x31\xe3\x72\xc3\xc1\x2f\xb4\x6d\x27\x44\x77\x78\xbc\x72\xe0\x2f\x21\x39\x9f\x45\xe3\x19\x99\x07\xc5\x78\x26\xc4\x57\xbe\x09\x31\x86\x16\x1a\xe5\x3c\xd1\x29\xa0\x69\x7f\xe0\x6e\xb8\x8e\x72\x7a\x0b\x16\x08\xfe\xb0\xba\xd1\xa4\xf3\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\x10\xe8\xa5\xa1\x81\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\xe3\x14\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xea\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x90\x3b\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x88\x25\x25\x2c\x2b\x9d\x0e\x73\x16\xc1\xe3\xdb\x9b\x76\x7b\x96\xb6\x1e\x83\x0b\xe7\x3f\x8c\x47\xfe\x24\xb6\xd0\x20\x0c\x73\x1c\xf8\x5c\x58\x39\xb8\xd2\x18\x57\x0f\xf7\xd6\xf8\x86\x2b\x0f\x06\xe2\xac\x1d\x71\x3f\xfc\x82\xeb\xc1\x6e\x2c\x6f\x85\x54\xea\x41\xc8\x4b\x05\x59\x16\x9d\x51\xcc\x70\x83\x50\xcd\x9e\x6c\xaf\x82\xc3\x7a\xa0\x0d\x37\xfc\x7e\x9b\x52\x24\x53\xc8\xd7\xea\x11\x44\x48\x15\x5f\xc7\xc3\x13\xb5\x65\xc2\x15\x36\xef\x9b\x86\x16\x09\x66\x09\x9e\x88\x25\x3a\xef\xe6\x45\x76\x55\x6f\xaa\x24\x5e\x06\xda\x57\x0d\xcb\xba\xe5\xae\x26\xd7\x11\x5e\xa9\xe4\x7c\x46\xa5\xdf\x81\x90\x8b\xe5\x70\xfa\x02\x8d\x3b\xdb\xdf\x43\x84\x66\x41\xc4\x15\xa8\x75\xed\x3b\xd5\xd1\x7e\x92\x66\x1d\x86\x97\xcf\xf4\x92\x9f\x14\x7d\x03\x30\x9d\xc0\x74\xfc\x40\xfd\x59\x90\x1f\x9e\x27\xef\x20\xa0\x56\x71\x09\x01\x13\x2d\x2e\x50\x82\x9e\xcf\xf4\xf2\xa4\xdc\xb6\xb3\x9d\x26\xe4\xe0\xdd\x7e\xbb\x6b\x2d\x7e\x21\x5b\x54\xd4\xe9\x98\x59\xe8\x65\xb2\x8f\x7d\x10\x0a\x37\xe7\x04\x1d\x37\xa2\x9c\xe4\x45\xc4\xa3\xac\x44\x21\x22\x6a\x6c\x16\x5a\x8a\x70\xbf\x1d\x67\xa7\xfc\xb4\x24\xe5\x00\xb6\x7b\x64\x54\xf4\xa3\xc7\xa9\xc0\xec\xd5\x34\x4d\xa8\xd0\x3c\x75\xd6\x3f\xd9\x62\xff\x79\x16\x15\xe0\x2f\xc5\xe2\x46\x08\xc4\x3a\x42\x7d\x72\xcf\x50\xd2\xc5\xe0\x7a\x59\xed\x42\x81\xe4\x1d\x7a\xd5\x0b\x82\x35\x4c\x3f\x56\xbd\xf4\x03\x7a\xba\x42\x8c\x4d\x76\xc7\xe0\xdc\x2b\xa0\x48\xa2\xa9\x1e\x4b\xc4\x73\x84\xaa\x3d\x6b\xca\x5e\x86\xe8\xd9\xaf\x6f\x54\x15\x16\xcf\x37\x13\x1b\x14\x55\x63\xa9\xc1\x1c\x4a\xed\x3e\x4a\xac\x3f\xdf\x3e\x69\x99\xdd\x09\x6d\xa2\x75\x46\x71\xdc\xf1\xfc\x2b\x5d\x82\x95\xb5\x7e\x6d\xd6\x6a\x6f\xd8\xec\x76\xa3\xdd\x22\x39\x36\xcc\xee\x63\x3b\x6d\xcd\x07\xe1\xc5\x56\x5a\x90\x7c\xb9\x58\xa4\x59\x01\xba\x35\x7e\x53\xfb\x6e\x9f\x28\xad\x4a\xdb\x70\x04\x59\x4e\x98\x8d\x5f\x2a\xdc\x64\x31\xd6\x53\xd9\x4a\x14\xe6\x3d\xd6\x03\x4d\x55\x5a\xd0\x23\x87\xba\xf6\x6e\x5a\xea\xed\xc6\xd5\xe3\x6a\x0c\x3a\x4e\xda\x4b\x5e\x69\x5f\x9f\xf4\xb6\xbf\xb9\x57\xe9\xde\xab\x74\xff\x2b\x54\xba\xe2\x61\xc5\xad\x9e\x63\xef\x05\x59\x9a\x90\xff\x5d\xce\x83\xb3\x28\x27\xdf\x07\xec\xf3\x6f\x9f\xf9\x67\x7f\x4e\xbd\xea\xde\xc1\x80\x1c\x24\x51\x11\x05\x71\xf4\x2b\x25\x7f\xe7\xbd\x60\x84\x1a\x90\x1c\x2c\xb1\xa4\xc1\x0d\x0c\x94\x2d\x55\xc3\xc9\x79\x1f\xb4\xba\xb2\x98\x8c\x5e\x22\x22\x6b\x1d\x84\x23\x32\xac\xbb\x79\xe3\xd6\x1e\x6c\xf8\xb6\x5b\x5d\xaf\x99\x89\xd7\x9d\xae\x7e\x85\x26\x83\x78\x4d\x24\x42\xa1\x25\x6d\xd0\xe3\x71\xc2\xcb\x5f\xa7\xf4\x90\xaa\x67\x22\xab\x91\x59\xd2\xf7\xae\xd7\x0d\x11\x1a\x01\x6b\xcf\xe9\xfd\x60\x4d\xa0\xa7\xc4\x15\x2f\x6f\xab\x27\x1a\x33\x9c\xa6\xf2\xac\x6e\x99\x6a\x59\x36\xe9\x18\xf3\x28\xb3\xdd\xf5\x36\x0a\xa7\x15\x84\x67\xec\x8c\x2a\x67\x87\x1c\xbc\x80\x1c\xd9\x3b\x35\x69\x1b\x1b\x65\x7e\x86\xfc\xaf\x7f\xf8\x5b\x21\xa7\x1a\x9d\x2d\x9f\x07\x89\x91\xaa\x74\xf9\x2e\x88\xff\xcf\x0e\x4c\xf2\x85\x50\x73\xc3\x0b\x89\x03\x75\x78\x94\x06\x44\x7e\x53\x1d\xa5\xac\xab\x0b\xe9\xe6\x79\x99\x6d\x35\xe0\x37\xcf\x90\x68\xb0\xda\xb3\x22\x4e\xf3\x44\xeb\x32\x94\xfb\xf4\x41\x3a\x67\x01\xf4\x4c\xb5\xdd\xa7\x67\x34\xbb\xec\x48\x6f\xc8\x1f\xa2\x64\x1a\xd3\x37\x1c\xe1\x5d\x32\x22\xde\x0c\x5d\x93\x98\x56\xd5\x11\x3f\xb8\x98\x40\x75\xd0\x52\xc2\xbb\xa4\x1b\x64\x41\x24\xd3\x38\x45\x1a\xb6\x45\x22\x43\xce\xcf\xee\xee\x2e\xa7\x1a\x0c\x24\xdc\x2e\x48\x58\x76\xe6\x66\x60\xfc\x5a\xb7\xed\xab\x4e\xc8\xb0\x96\x4f\xc9\xc1\x80\xc7\x1c\x54\x49\xc2\x2b\x3b\x66\x2e\x72\x3d\x36\xf2\x27\xcf\x19\xd1\x29\xbc\x47\xab\x61\x47\xcf\x19\x50\xb9\x8b\x6f\xd1\x71\x8b\xbf\xf0\xba\x72\xce\x54\x45\x55\x52\xc0\x09\xbb\xa0\x3c\x12\x8b\xa2\x23\x79\x4f\x97\x4c\x22\x1a\x87\x96\xe9\x81\x68\xc5\xe8\xa9\xc5\x73\x70\x07\x2d\xc6\xc3\xbb\x66\x91\xa1\x4c\xb6\xa2\x3e\x48\xb2\x70\x1d\x61\x39\xec\x4d\xc2\xf6\x25\x6b\x93\xdf\x82\xc5\x99\x7a\x78\x47\x56\x14\xf5\x09\x39\x91\x89\x81\x4f\xee\xc5\xc0\x7b\x31\xf0\xaf\x2d\x06\xea\xf7\x79\x7c\xd1\xdc\xd5\x0b\xbd\xbb\xb9\xbb\x67\x20\x6f\xa4\xba\xb1\xd4\x58\x19\xce\x89\x22\x52\x8b\xb4\x42\x66\x9f\xe8\x14\x29\x5c\xae\xc9\x5c\xf6\x69\x5c\xdc\x03\xcf\xd3\xf9\x5a\x32\x18\x22\x30\xf0\xc9\x8f\x83\x21\x6a\x43\x68\x9c\x81\x4a\x70\x4f\xcf\xbe\x22\x56\x8e\xa1\x74\x05\x8d\xc1\x9b\x20\x09\xa6\x54\xbf\xce\x67\x2c\x8b\xa3\xc2\x50\x05\x48\x17\x1e\x1a\x1c\xed\xf7\x73\x03\x43\x4e\xc5\xd9\xbc\xc6\xfe\x3d\xa4\x8c\xc3\x44\x89\xe9\xdf\xd3\x12\xff\x4e\x83\x9c\xfb\x5c\x28\x8b\x44\x31\xa5\xe0\xa5\xd2\xb3\x49\x99\x9e\xe6\x6d\xc7\xa2\xb2\x4d\xb3\x3d\x20\x31\x07\x11\xa2\x8d\xd2\x58\x13\x86\x3b\x51\x14\x3e\x47\x11\x87\xb2\xe3\x93\xbe\x0c\x73\x26\xd8\xa8\x94\x3a\x37\xc7\xdc\x19\xa7\xbe\xa4\x10\xa1\x39\xc4\xb6\xab\xc6\xd9\x27\x6f\x18\x2b\x8f\x68\x2e\xa2\x63\x03\x3e\x1c\x2f\x94\x86\x67\xcf\xc6\x78\x93\x83\xba\x7a\xbb\x8c\x63\xed\x18\xa3\xc7\xa4\x48\x7a\x11\xc1\xb5\x99\x0f\x77\x7f\xcc\xf8\x43\x77\x16\x76\x87\xac\x7d\xad\xb8\x3b\x0e\x26\x1b\x45\xdb\xb1\x03\x9c\xa8\x50\x32\xe6\x41\x8c\xd4\x84\x8f\x79\xff\x6e\x5f\x44\x98\xa8\x8e\x1d\xa3\xd1\x26\x5c\xbd\x72\xc2\x03\xa4\xab\x13\xa7\x8d\x26\x0e\x7a\xc0\x20\x5d\x2c\x19\x44\xa7\x92\x3c\xe8\x40\xb5\x54\x62\x63\xdd\xc3\x5d\x4b\x28\xc8\xf7\xb8\xd1\x53\xda\x92\x21\x95\xd3\xc5\x1e\x81\xe8\xdf\x55\x21\xa4\xc8\x33\xfd\x9b\x53\x37\x14\x39\x61\xec\x00\x7d\xd6\x78\xd6\x77\xb0\xce\xf9\xbd\x8a\x9a\x8b\x31\xef\x22\x9e\x3b\xe0\xad\x3e\x2b\x9a\xee\x88\x4b\x70\xef\x89\x91\x62\x06\xe9\xc5\x28\xb4\x37\x2b\x70\x36\x03\xc7\x9e\x67\x5e\x00\x55\x95\x37\x36\x89\xc0\x85\x2f\x64\x91\x7c\x3f\x25\xe9\x70\x85\xc8\x45\x81\x5c\xb7\x8d\x90\xd0\x2c\x06\x11\x76\xc7\x2a\xf6\x11\xdb\x4b\xf2\xca\xce\x97\x85\x3c\x01\xc0\x68\x19\x60\x40\xc8\x33\x02\x0c\xa9\x63\x8a\x5f\x0b\x22\xd5\x19\xa0\x59\x2a\x51\x66\x54\xb9\x55\xc6\x2a\x0e\x07\x55\xd2\x45\x2e\xc7\xa7\x29\x6d\x9d\xfe\x82\xd1\xc5\x32\xe4\xd0\x4e\x97\x51\x1c\x02\xc2\xc4\xa0\x58\xa6\xe3\xdf\x16\x18\xfe\xc7\xc3\x17\x87\xeb\xeb\xeb\x20\xde\xb7\x73\xb2\x9c\xc6\x97\x7d\x11\x45\x8c\x1d\x08\x96\x39\xdb\x13\x0b\xd5\x4a\x82\x5c\xca\xb2\xdf\xd2\xae\x46\xdd\x90\x30\xc6\x01\x19\xea\xbd\xf5\xa6\x11\xe9\xe9\xf4\x97\x63\x96\x7d\x3c\x3c\x39\x61\x62\x17\xfe\xbc\xba\x52\x76\x9b\x36\x28\xff\xb1\x09\x65\xd8\x58\x76\xfc\x57\x45\x56\xed\x00\x49\x10\x17\x76\xd0\xab\x10\x55\x76\x8b\xaa\x2e\xd5\xb5\xd1\x29\x0f\x81\x92\xf8\x9f\x65\x11\xc7\xcf\xb7\x90\xdf\xf5\x69\x78\x15\x3f\xd0\xc4\x8a\x60\xe1\x0b\x55\x60\x9c\xd5\xa1\x2d\x53\xa2\xd4\x17\x53\xfa\x7e\xc6\x88\xc5\xa2\xcc\xeb\x3c\xa6\x79\x76\xc3\x1c\x5e\xb4\x83\x99\x99\x32\x8a\xb4\x0c\x68\xbc\xe1\x54\xcc\xee\x1a\xd5\x94\x0f\xc1\xbe\x86\x12\xa4\xc2\xb2\x9a\x7a\x7a\x96\x61\xae\x68\x52\xef\xce\x51\x72\xc8\x65\x46\xe1\x86\xf4\xfd\xbb\x7d\xe5\x81\x89\x9b\xb2\x8c\x83\x44\x09\x9b\x51\x22\x94\x2e\x7e\x5f\x4f\x99\xeb\xeb\xb1\xdf\xef\x5f\xe3\xf8\x6e\xb6\x2f\x3d\xad\xc9\x94\x45\x3d\x9c\xb4\xce\xa7\x7d\xa9\xbb\xf9\x55\x88\x50\xd2\x80\xe9\x93\x1e\xcf\x5a\x19\xa2\x45\xc9\x12\xc5\xce\x1b\x69\x03\xd3\xf4\xfa\xef\xdb\x7b\xbd\xcf\xbd\xde\xe7\xaf\xad\xf7\x11\x4a\x9f\xf0\xf4\x16\x37\x7f\x3e\xbd\x8f\xd2\xd6\x60\xc5\x0f\x67\x4e\x4a\xa3\xf3\xe2\xb9\xc1\x47\xd8\x30\x4c\x97\x1f\x8e\xa6\x02\x46\x6a\x25\xef\x54\x04\x0a\x5b\xd3\xf2\x52\xde\xf1\xd8\xf4\x8b\x0b\x2e\xf2\x85\x58\xd2\x95\x25\x07\x75\x58\xcd\x68\x67\x11\x40\x8e\xda\xa5\xe3\xeb\xa0\xa5\x6f\xd6\xbb\x7c\x79\xc0\xa2\xc5\xb2\x50\x8f\xd7\x12\x7a\x2e\xb0\xd9\xd1\xdb\x25\x13\x3a\x46\xa4\xad\xe0\xac\x38\x1a\x23\xd2\x0e\x4f\x3f\xf9\x72\xa5\x98\xb8\xad\xfa\xa4\x1a\x9d\xd2\x66\x8d\x2a\x38\x6f\xa3\xbe\x5c\xd9\xe8\x96\xdb\xe8\x62\x59\xbc\xa2\x17\xf5\xc3\x7c\x45\x2f\xca\xc6\x68\x66\x55\x0f\xb0\xbe\x2d\x0e\x54\x36\x34\x7f\x5b\xd6\xb8\xc4\x66\x74\xac\xe1\xe4\x44\xf4\x34\x92\x7b\x62\xe8\x3d\xd1\x2d\x00\x3e\x29\xd9\xb9\x5e\x3c\xd7\xbb\x16\xa7\x9d\xd6\x68\x1b\xb6\xa8\xa7\xf7\x5b\xd4\xfd\x16\xf5\xd7\xde\xa2\xf4\xd5\x04\x2d\x66\x37\xba\x97\x10\xc0\x77\xfb\x2a\xb1\x24\xfa\xbf\x2f\xfc\xbf\xef\x12\xc4\x7f\x0f\x52\xb3\x6d\x32\x10\x69\x8e\x6c\x01\x2d\x44\xb2\x04\x1b\x97\xb5\x37\x4e\x93\x49\x34\x95\x60\x28\x14\x0e\x86\x96\x91\x55\x24\xd8\xb9\x78\xb6\x66\x5c\xd0\x88\x44\x09\xf3\x23\x0f\x05\x6e\x21\x03\x12\x25\xc8\x41\xfe\xe1\x32\x19\xf3\x2d\x06\x43\xe5\x3c\x55\x82\x31\x56\x9c\x51\x1b\x48\xa4\xaa\xba\xb8\x83\x22\x0c\x11\x9d\x06\x89\xcc\xe6\x5e\x0f\x9d\xfe\xc8\x64\x25\x84\x80\xcf\xb4\x26\x77\x06\x4a\xe7\x2d\xde\x08\x82\x12\x70\x78\xd2\x25\x0f\x1e\x10\xf1\xbb\x0f\x3a\xc1\xc3\x49\xa7\x3d\xbc\x68\x73\xd7\x25\xc3\x2e\x79\x46\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbf\x7c\x15\xe4\xb3\x16\x19\xd9\xc9\x5c\xa3\xdb\xd2\x52\x02\x74\xed\x43\x34\x4d\x68\x96\x57\xf4\xf0\x8e\xfb\x27\x1a\x2c\xe9\xa6\xca\x75\x7a\x9b\x17\xc1\x67\x9a\xbd\x3f\x3c\xa8\xef\xea\xcd\x7a\x8a\x3a\xfa\x41\xb6\xf5\x26\xc8\x0b\x9a\x25\x69\x48\x71\x4f\x55\xb6\x8d\xcc\x1f\xa3\x24\x88\xa3\xe2\xf2\xb7\xc3\xa6\x6c\xb1\x04\x9d\x3a\xdb\xc1\x27\x8a\xfe\xf5\x63\x96\xce\x9f\xff\x06\x74\xda\x16\x5d\x43\x41\xa5\x9e\x5f\x42\xc3\xac\xf3\x7b\x49\x78\xc0\xca\xa9\x58\x6e\x5e\x48\x3e\x0e\x05\xab\xc7\xb3\x4c\xc6\x31\xfd\x8d\x06\x70\xc4\xda\xaa\xe9\x3a\x86\x29\xed\xb4\x9c\x27\x34\xce\xfd\x74\x99\x34\xba\x64\xbc\x83\x71\x78\xdb\xe6\xa4\x84\x87\x52\x02\xc6\x47\xe5\x4c\xc1\x6f\xd8\xff\x23\xd5\x20\x9a\x0c\x67\x12\x30\x80\xd1\x67\xd5\xbd\x97\xc5\xec\xae\x8f\x87\x8d\x8f\x86\x77\x74\x32\x84\xf0\xcf\xe5\x27\x43\xae\xf8\xe2\x7b\x78\x44\xbd\x3d\x5a\xe0\xce\x2c\x6a\xfa\xb1\xb8\x41\x17\x90\x85\x03\xdf\x5b\xb9\xf7\x13\x82\xfd\xb3\x1f\x3c\xdf\x7b\x6b\x85\xa2\x13\x3b\x2a\xd7\xc9\xf1\xe7\xd3\x42\x33\x77\xbd\xb6\xc6\x7b\xd7\xe7\x76\x71\xea\x25\xd5\xcb\x62\xa6\x75\x81\x3d\xd2\xc6\x81\xbb\xdb\x3d\x31\xcc\x29\x2d\x46\x25\x1a\x6f\xe9\xa9\xb6\x8f\x0b\x8a\x91\xf4\x84\x96\xd6\x28\x7c\x16\xc4\x46\x8c\xb9\xbe\x15\x36\xfd\x2c\x88\x1d\x57\x34\x2a\xed\x7a\x0d\xd0\xb3\xd2\x50\x84\x97\xc7\x9b\x0c\x46\x14\xbd\xc9\x70\x44\xd1\x86\x03\x6a\xa2\x89\x60\xdc\x25\x88\xc1\x6e\xb7\xf6\xdc\x2c\x00\xdd\xb3\xb3\x64\x53\x4e\xbe\x3a\x40\x23\x5b\x5e\xe3\x02\x77\x44\x8e\xb5\x38\xcd\x2f\x77\x85\x13\xd5\x1f\xf5\x5d\xae\x0d\x81\xe3\xde\x73\x7e\xa2\x80\x51\xe0\x50\xeb\x16\x73\x84\xab\xe1\x79\xca\x63\x91\x02\x2a\x51\x9a\xa4\x59\x30\xa5\x7b\x45\x13\xbd\x89\x00\x2d\xc5\x91\x0f\x42\xa9\x34\x2a\xb0\xc4\xd7\x1d\xe7\xd8\x45\x0a\x7a\x85\x55\xd0\xe2\x1d\x98\x70\xed\x59\x33\x26\x06\x55\x3a\x1c\x2b\xf3\xb7\x9f\x6f\xef\xc0\xe4\xaa\xaf\xa3\x67\xce\x8e\xac\xa1\xa9\x03\xc3\xed\x86\xe5\xeb\x6d\xcf\x59\xe2\xda\xfa\x99\x2d\x5e\x72\xbd\x1a\xfd\x82\x84\x59\x49\xbb\x58\xa6\xf7\x23\xc4\x42\x87\x80\x55\x58\x41\x38\x41\x27\x15\x3b\xf2\xc6\xa6\x4c\xb8\x11\x5a\xd4\xa0\x1b\x0e\x59\x74\xa4\x6e\xd5\x8a\x33\x42\x93\x55\x2b\x40\x1d\x5a\x30\xce\x3c\x2e\x3d\x6c\x56\xd1\x83\xdc\xd6\x2d\x5e\x4e\x0c\x7e\x8d\xad\x50\x4a\xd6\x03\xaf\x60\x64\x3e\xf0\x6f\x4a\x28\xf8\x52\xed\x3d\x0d\xe2\x72\x22\x91\x27\x95\x46\x54\x22\x81\x7d\x64\x82\xcf\x60\xbf\x03\x9d\xe0\x11\x1f\x24\x85\x77\xc0\x20\x95\xd6\xd3\x05\x80\x39\x34\xa1\xce\x39\x5f\x83\x3f\x20\x06\x7f\x0b\x56\xd0\x5b\x2b\xe1\xf7\xf3\x45\x14\x97\x72\x02\x93\xe9\x0b\xd0\x0a\xce\xef\x42\x48\x3c\x0c\xcb\xc9\xcc\x3e\xc5\x34\xe4\xd2\x76\x31\xa7\x5b\x55\x07\xb9\x15\x17\xee\x2a\x94\xe8\x99\x1b\x39\x85\x2f\xe8\x38\x9a\x57\xad\x38\x7d\x36\x6a\x88\x04\x5d\xa0\x84\x28\xff\xb8\x03\x36\x8f\x14\x35\x83\x2d\x8f\x1f\x5f\xa2\x98\x80\x53\x67\xe5\xa0\xeb\x57\x10\xaa\xb0\x7a\x63\xf9\xe8\xd1\xdb\xac\x34\x26\x55\xca\x19\x5c\x99\x4a\xe8\x8f\xc4\x69\x6e\x82\xa7\xf7\x74\x4c\xa3\x45\x03\x32\x77\xcb\x34\x21\x00\x17\xf4\xb6\x14\x20\x6a\x6c\x3c\xc0\x86\xab\xb8\x96\x8b\x79\x06\x67\x03\x36\xa1\x00\x7e\x58\xb8\xa3\x63\x53\xd9\xf2\x26\xf2\xfe\x5d\xda\xaf\xbd\x0f\xce\x9b\x2f\x73\xb7\x80\x1f\x19\x95\x70\x4d\xb8\x1b\xc3\x85\xe7\x94\xe0\x86\xdc\xaf\xeb\x6d\xa3\xae\xde\xbc\x9f\xf6\x6c\xf9\xd6\x99\x6f\x1c\xd1\x34\x59\x61\x1c\x26\x74\xc9\x38\x4a\x81\xbe\xf2\x38\x1a\x74\xbe\xbc\xc7\x77\x7e\x0a\x2d\x21\x1c\x61\xf4\x5a\xd5\x51\x06\xe2\xef\xa8\x95\x73\x93\x8e\xb2\xfd\xe0\xce\xce\xca\x34\x2f\xa2\x79\x50\xd0\x9f\x82\x3a\x99\x10\x41\xfa\x87\xe6\x07\xb8\x09\xc5\x18\x23\xbc\x95\xe0\x31\xe6\x32\xea\x87\x34\x8e\xc2\xd2\xa3\x8d\x9e\x36\x0e\xdd\xcf\x05\x78\xc9\x14\x9a\x75\xfa\xc6\x5a\xda\x91\xd7\xaf\x5f\x37\xec\x43\x5c\x4a\x41\xaa\xa6\x95\x5a\xfe\x40\xb3\x05\xad\xdd\xa2\x14\x06\x38\x74\x35\x02\x1c\x98\x8a\x5e\xe4\xcb\xd3\x79\x54\xfc\x9c\x66\x75\x92\x92\x06\x2c\x59\xe9\xbe\xfc\x6a\x93\xa0\x06\xad\x0a\xa8\xd2\xed\xb8\xa4\x3d\xff\x31\x67\x3f\x48\x42\xfe\xec\xbd\x08\x8a\x65\xad\xd6\xc5\x02\xb7\x4e\xd4\xea\xb4\x55\x02\xe5\x70\x90\x3b\x50\xb7\xbd\x5c\xa4\xe3\x59\x29\xef\xf0\x8d\xb4\xc9\x81\x52\xc1\x96\x9f\x28\x7d\x20\x37\x61\x20\xe5\x03\x70\x82\xc3\x6a\x1b\x2e\xad\xbf\xee\xe1\x34\xa5\xaf\x35\x52\xf7\xd3\xd0\x04\x03\x7e\xeb\xa6\x08\x4d\x95\x55\xa3\xd0\x29\xb9\xe0\x52\x75\x62\xb6\xa5\xd0\xe2\x4f\xe6\x14\x61\xe4\xc1\x41\xd1\xea\xb0\x38\xad\xbb\x8d\xda\xb2\xa6\x0b\xa1\xcf\x9d\x46\x1e\x2a\x58\x96\xae\x4e\x31\x65\x00\x42\xd0\x2e\xcb\xb6\x1a\x35\x5f\xc4\xa0\x1d\x47\x27\xba\xf2\x22\xca\x33\x05\x1b\xb3\x50\x69\x09\xd4\xbc\xc9\xfa\x9d\x8c\xd7\xaf\x5f\xbb\xc0\xff\x1f\x7b\xef\xbe\xd6\x46\xae\x2c\x8e\xfe\x9d\x3c\x85\x26\xbf\xb3\x06\x3b\x34\xc6\x77\x88\x13\x66\x6f\x62\x20\xb0\x12\x02\x3f\x20\x33\xb3\x36\x1f\xc3\xd7\xb6\x65\xdc\x89\xdd\xed\xd5\xdd\xe6\x32\x13\xf6\xfb\x9c\xe7\x38\x2f\x76\x3e\x95\x2e\xad\x6b\xbb\xcd\x25\x93\x99\x05\x6b\xef\x89\xbb\x5b\x2a\x95\xa4\x52\xa9\x54\xaa\x0b\x65\x93\x12\x48\xc1\xc1\x94\x4e\x93\x17\xf0\xcc\x4c\x04\x69\x52\x53\x71\x5f\x98\x97\xe4\x20\xa3\x61\xb2\x06\xc5\xc5\x95\xaa\x72\x14\x1c\x3e\x08\x7b\x7e\x22\x6b\xb5\x18\x02\xb0\x94\x18\x83\x67\x65\x44\x91\xdb\xb2\xb7\x40\x1b\x93\x20\x54\xcd\x43\x8d\x16\x58\x89\x3b\xc2\x1f\xf9\xc9\x28\xf6\xd3\xdc\x3e\x38\xca\x14\x12\x21\x16\xc7\x88\x9b\x37\xe5\x20\x64\x2f\x32\xff\x50\xca\xec\xa9\xd4\x93\xe8\xe2\x18\x5e\xf8\xc9\x61\x1c\xf4\x73\xc7\xcc\x51\xe6\xce\xf7\x68\x8b\x63\xc9\xf2\xf6\x25\x79\x58\x8a\x32\x77\x6c\xa3\x27\x59\x21\xe4\x34\xe3\x2e\xf6\x48\x34\xc4\x93\x1a\xfd\x4c\x8d\x55\xf3\x70\xd3\x8b\x4a\x2d\xca\x2c\x44\xb9\xb9\xae\xf4\x33\x43\x40\xc9\x2a\xa4\x17\x28\x2e\x7f\x7e\x3f\x8d\x62\x2e\x27\x73\xd3\x41\xf0\xc3\xf1\x10\x29\xab\xec\x9d\xac\xb4\xad\xb1\x21\x37\x15\x34\x62\x39\x78\x92\xbf\x3a\x2d\xd5\x8d\x31\x98\xfa\x82\xf7\x75\x57\xf3\x47\x93\x12\xfd\x51\xf3\xc3\x0c\x0e\x19\x8a\x25\xcf\x6a\x2a\xe2\x71\x7b\xc6\x0a\x4e\x47\xa5\xb2\x67\x92\xec\x87\xe8\x42\x92\x80\x8a\xa1\x64\xeb\x68\x66\xc9\x98\x9f\x83\x86\x8f\x7e\x89\x55\xa8\x5c\x8c\xa3\x9e\x3f\xae\x90\x41\xad\xf8\xe6\x6b\x96\x34\xd4\xd6\x64\xd0\xf7\xa7\x1f\xef\xda\x2c\xa9\x6c\x34\x4a\x5f\xe6\x35\x29\x99\x75\x66\x0d\xea\xbe\x83\x72\x52\x46\x5e\xa1\x64\x9f\x9e\x79\xe1\x1c\xb7\xd3\x51\x66\x10\xaf\x59\xb6\xbe\xe8\xd4\xd6\xbd\x17\x86\x85\x2d\xf3\xf0\xca\x4c\x5b\x5f\x74\xea\x2d\x78\x41\xe7\xf4\x45\xa7\xfe\x8a\x3e\x0a\x5a\x78\xd1\x69\xd0\x2a\x41\xcf\x0f\x5f\x74\x1a\x0d\x4f\xb5\xbf\x87\x47\x36\x48\x2f\x3a\xcd\x26\x3c\x73\x3b\xdc\x17\x9d\x26\x05\xcf\x38\xfb\x8b\x4e\x93\xa2\xc5\xed\x65\x5e\x74\x9a\xa4\x41\x6e\x45\xfb\xa2\xd3\x6c\xdc\x9e\x79\x8d\x57\x4f\x06\xfd\x4f\x06\xfd\x7f\x6f\x83\x7e\x97\x35\xff\xbd\x9d\xce\x8a\xdb\xd9\x17\x30\xa2\x87\x72\x1f\x71\xfa\x98\x3e\x6a\xf0\x76\xbe\xd5\x5f\xe6\x9d\x76\x17\xb3\xbf\x02\x3e\x69\xab\xab\xab\x59\x50\x37\x5b\xa0\x38\x96\xf1\x98\xb0\x78\x00\x87\xd3\x11\xf2\xa7\x81\x84\xfb\x23\x1d\x48\xc6\x41\x92\xe2\xbc\xf3\x42\x88\xd3\xf3\xac\xd0\x5d\x85\x2b\x8c\x63\xfd\x22\xc5\x68\xc5\x55\x68\x01\x81\x4f\x16\xbf\x8c\x4d\xed\x23\x4e\x2d\x9b\x9a\xba\x79\xc9\xbb\xcb\xed\x99\xd7\xac\x3e\xed\x16\x4f\xbb\xc5\xdf\x7b\xb7\xf8\x4e\xdd\xbf\x1e\xce\x53\xab\xa0\x23\x59\x66\x0d\x7f\x88\xe3\x24\x0a\xfd\xf1\x93\x49\xfc\x63\x9b\xc4\xdf\x16\x33\x92\x0e\xf1\x55\x66\x79\x9d\xa7\xe8\xce\x0a\x9a\x5a\xee\x29\x9b\xd5\x73\x6b\xa1\x7b\x5c\x65\x07\x13\xb2\x11\x1c\xf9\x57\xef\xf1\xbc\x2b\x2e\xb9\xe8\x92\xf7\xfc\xd9\x33\x1d\x37\xa3\x40\x8e\x6b\x77\xf1\x2b\x5b\xb3\x1d\xf1\x41\xb2\x7d\x7e\xf6\xac\xa0\x21\x43\xe1\xbb\x5a\xdc\x3f\xc2\xfd\xe8\x92\x46\x57\xcc\xbb\xdc\xe4\xe5\xac\xb8\xaa\x5f\x73\x06\x64\x16\x8e\xa3\xfe\x97\x62\x94\xa2\x94\xcd\x21\x16\x57\xb9\x22\x36\xe3\xc5\xc6\xcd\x39\x7a\x0f\x6c\x22\x91\xcd\xfd\x5c\x3b\x89\x45\xee\xc3\x6d\xf6\x05\xce\x2e\x15\x9f\x9f\x62\xb3\x93\x3f\x37\x8b\xdc\x59\xe9\x73\xa3\x21\x6f\x93\xac\x59\xc3\x52\x23\xd2\xe2\xcd\xde\x2a\x14\x24\xdd\x9e\x70\xaa\x76\xdd\x76\x38\x2f\x45\x24\x70\xb2\xbc\xfb\x78\xe7\x83\xcd\x39\x6a\xe1\x6c\x3a\xe4\xc2\x0e\xb1\xdc\x94\xcb\xf9\x76\x9b\x09\xe7\x16\x15\x91\xa6\x15\xd2\xe5\xf4\xda\x93\x9c\xfe\x24\xa7\xff\xbd\xe5\x74\x26\xa4\x27\x23\x87\x56\x67\x8e\xf8\x8d\x63\x3c\x9b\x10\xd0\x3f\xcd\x51\x02\xf5\xa3\x18\x57\x82\x48\x95\xd3\xd7\x0a\x47\x1e\x2a\x18\xa9\x60\x5e\xc0\x03\x28\x74\x3c\x1a\x3d\xba\x76\xe8\xfb\x91\xc7\x09\x77\x3c\x1e\x29\xb7\x1b\xf8\x8a\x65\x6d\xd8\xf9\x16\x17\x3a\xc9\x68\xfe\x85\x4e\x32\x82\x0b\x1d\x2a\xb8\x2c\x72\x6f\x93\x27\xe7\xbb\x37\x27\x43\x3c\x90\xb6\xa6\x4b\xeb\x4d\x1d\x13\x11\x92\xd1\xe8\xdc\x5e\x40\xb5\x1e\x42\x16\x5d\x56\x5e\xa3\x41\x38\x8c\xdc\x2d\x5a\xbe\xde\xaf\xb9\x04\xa7\xfb\xfe\x35\x23\x82\xe3\xe0\x77\xfd\x72\x58\x6a\x7b\x5e\x51\xd5\x3c\xec\x2e\x88\x04\xe1\x61\xf4\x4b\x3e\x02\xb6\x22\xf7\x6b\x78\xe2\xc7\x5f\x4e\xe2\x59\x92\xe2\xc1\x21\x36\x2e\x83\xa5\xe6\xf3\x0b\xde\x0f\x89\x10\x13\x99\xee\xd0\x0f\x72\xda\x77\x96\xb9\x1f\x05\xf8\x83\xc1\x61\x1c\x5c\xfa\x29\xa6\x47\x42\x47\xeb\x79\xc5\xee\xd7\x77\x9a\x35\x73\x6e\xf7\xf3\x8a\xdd\x0f\x81\x91\x9f\xcc\x6d\xdd\x59\xe6\x7e\x4d\x5f\xe0\x94\x6e\xe8\xb9\x63\x9f\x53\xea\xfe\xcd\x17\x98\xfb\xbc\x62\xf7\xa6\xfb\xe3\x9b\x49\x6e\xe3\xae\x22\xf7\xa6\xfa\x79\x0d\xbb\x8a\xdc\x77\xc8\x89\x1c\x97\x62\x0a\x7a\x27\x8e\x26\x87\x7e\x92\x5c\x45\xf1\x20\x6f\xfc\x0b\xd6\xb9\xf7\x3a\x98\x37\x26\xae\x22\xf7\x26\xc3\x79\x0d\xbb\x8a\x3c\x04\xeb\x99\xd7\x76\x4e\x29\x7b\xf3\xe2\x61\x75\x15\x25\xb3\x1e\xdc\xbc\x61\x48\xca\x34\x0b\xb3\xe7\x49\x90\x24\x41\x78\xf1\xbc\x30\xb6\xd3\x28\xd1\xaf\xae\x24\x2c\x2d\x5f\x2d\x7a\x0a\x94\xaf\x77\x44\xf3\x6f\xb9\x8e\x47\x23\x29\x03\xa7\x66\x7b\xa1\x9c\xa2\x35\xcb\x88\x66\xfd\xe9\x0c\xfd\x74\x86\xfe\x7b\x9f\xa1\xb3\xbb\xae\xde\xef\xbf\x6b\x77\x5d\x9b\x63\x7c\x8d\xde\xe2\x18\x5f\x24\xbf\xfb\xc9\xef\x01\x7a\xe3\x8f\xf1\xf5\x7f\xc7\xe9\x30\xa9\x8c\x66\xea\x71\xb8\xcd\xc2\x81\x1f\xe1\x21\x8e\x71\xd8\xc7\x1d\x44\xda\x4f\x3a\xab\xab\x17\x41\x3a\x9a\xf5\x2a\xfd\x68\xb2\xfa\x6b\x10\xee\x04\xe1\x41\x7c\xb1\xfa\xeb\xd6\x61\x74\xdc\x1d\xf9\x41\xb8\xda\x1b\x47\xbd\xd5\xe4\xca\x8f\x27\xab\x41\x98\xe2\x38\xf4\xc7\xab\xa4\x4b\xf8\x3a\xe5\xff\x56\x2e\xa2\xff\xf3\xa1\xd1\x78\xe4\xab\xb1\xec\xbe\xeb\x98\x60\xf3\x37\x3f\x5c\xc3\x8f\xbf\xc4\x65\x17\xb5\x7c\xc5\xe9\x55\x14\x7f\x39\xc2\x10\xeb\x3d\x4f\x51\xae\x17\x37\xb5\xe5\xbd\xdf\x7f\x3f\xcf\x29\x75\x1f\x27\xce\x9b\xb0\xbf\x1d\xfa\xbd\x31\x9e\x87\xa5\x54\xd2\x8e\xa0\xbd\xc0\x7d\x70\xbb\xf2\xa7\x05\x71\xcb\x4a\x3a\x70\xb3\x16\xb8\x07\x6e\x83\xe8\x2a\x64\x61\xfc\xf3\x10\xe3\xc5\xec\x58\x59\xbe\x16\xf7\x4d\x76\x20\x36\x9b\x16\x40\x8b\x16\xb2\x23\x65\x7c\xbb\x37\x4a\x31\x4e\xe3\x00\x5f\xce\x0b\x17\xc2\x8b\xd9\xd1\xb2\x7c\xbd\x0f\x69\xa5\x64\xb7\x9b\x43\x54\xa4\x8c\x83\x9c\xb4\x4f\xf7\x1e\xa2\x0b\x5c\xc0\xf7\xdd\x8e\x8b\xfa\xe1\x1e\x63\x42\xd3\x1f\xcd\x09\x32\x6e\xc7\x41\xfd\x70\xef\xd1\x60\x19\xcf\xf2\x91\xa1\x85\xec\xf8\x18\xdf\x38\x4a\xcd\x42\x28\xe5\xdc\xea\x1a\x2a\x4e\x9d\x2d\x4b\xb7\x7f\x19\x3f\x94\x5e\x66\x8c\x28\x7b\xc9\xf9\x80\x74\xe3\x38\x55\x9f\x39\xf5\x4b\x80\x08\x09\x66\x8f\x17\x58\xba\x98\x9c\xce\xa4\x07\x49\x16\x7f\xd4\x6b\xc6\x51\x70\xe9\xf4\x8d\x21\x73\x02\xdf\x9d\x67\xc8\x7c\xd8\x16\xa5\xac\x02\x1b\xbe\x3b\x8e\x57\x96\xf3\x15\x11\x96\x6c\xd1\xe2\xad\xf7\x92\x8d\xa7\x33\xd5\xd3\x99\xea\xef\x7d\xa6\x62\x07\x2a\x7e\x41\xf4\x6d\xd3\x9c\xdc\xc5\xb0\x9a\x7b\x47\xf9\xd3\x80\x0b\xe3\x34\x47\x6e\x3a\xca\xb3\x40\xa3\xd7\x65\xb9\x81\x7d\x79\xe9\xf4\x66\x4a\xe4\x03\x16\xc4\xf7\xf5\x73\x89\x81\x07\x69\x7f\x54\x22\xdf\xf5\x98\x74\x7d\x3f\xc1\x68\x89\x50\x7c\x92\x2e\x75\x94\x4f\x30\x59\xf1\x45\x52\x49\x46\xc1\x30\x2d\x69\x19\xb9\x90\x91\x5d\xb7\x6a\x16\x60\x2c\x19\xdc\xd7\x42\x7c\xc5\xbc\x9d\xe1\x42\xf6\xb5\x05\x8d\x29\x0e\x07\x41\x78\xf1\xe8\x78\x1c\xd2\x76\x64\x1b\x22\x1b\x52\x2c\xfa\xaa\x89\x8d\x06\xce\xa8\x4c\x33\x94\xdd\x4a\xd2\x81\x28\x35\xdf\x92\x90\x41\xd3\x65\x04\x85\x14\x2c\xb2\x93\x45\xaa\x0e\x83\x30\x49\xfd\xf1\xb8\x50\xcb\x5a\x69\xbb\xbb\xbe\xbb\x50\x0e\x1e\x17\x38\xfd\x10\x5d\x14\x08\x16\x40\x4a\x39\xc3\x04\xd0\x16\xb5\x22\x39\xad\x4e\xa3\xb9\x01\x5b\x48\x91\x39\xed\x75\x47\x7e\x78\x61\x8f\x4c\x30\x47\xc6\x12\xf3\x25\x9b\x64\x29\xa3\xa7\x08\x42\xa4\x63\x52\x23\x11\x0b\xfa\x78\x76\x47\x47\x8e\x64\x34\xaa\x00\x6b\x34\xd8\x4d\x32\x32\xd9\x8d\x5b\x7c\x9a\x73\x4b\x63\x90\x01\x32\x6e\x69\x14\x4b\x82\x07\x55\xd3\xbb\x89\x11\xd9\x34\xf5\x8f\x87\x88\x49\xba\xc8\xb8\xa6\xa0\xcd\x32\x1c\xf4\xa2\xf7\x6b\x5e\x23\xe3\x07\x68\x5b\x26\x3d\x43\x12\xa5\x38\xe0\x74\xd4\x21\xff\xa1\xc0\x92\xd1\xa8\x43\xfe\xe3\x51\xe9\xd5\x96\xd2\xa8\xd9\x7c\x92\x49\x9f\x64\xd2\xbf\xb9\x4c\x9a\x29\xfa\xb9\x93\xf5\x5d\x1c\x5b\x2c\x02\x29\x75\x10\x3f\xc2\x17\x64\x9e\xfd\x78\xb3\x17\x38\x32\xfb\x24\xab\xef\xd4\xa2\x95\xcf\x49\x24\xb2\xe7\x04\x7d\x7f\x2a\x03\x71\xc1\xd8\xeb\x6e\x1e\x9a\x10\x24\x4c\x98\x27\x3a\x33\x5f\x46\x1b\x68\xa9\x7a\xdd\x6f\x0f\x5e\x0d\xea\xfd\x41\xb3\xf9\xca\x5f\x6b\x35\xfb\xcd\x57\xcd\x7a\xbb\x89\x6b\xeb\xd5\x57\xfd\x56\x15\x37\x9a\x83\x76\xb3\xd5\xae\xf7\x96\x32\x5c\x6c\x60\xfc\x9a\x5f\xab\xd5\x7a\xfd\xea\x5a\xb3\xff\xaa\x3f\xf4\xd7\xd6\x6b\xc3\x6a\xbf\xb1\x8e\xdb\x8d\xde\xa0\x55\xeb\xbf\xaa\xf5\xd6\xfd\x61\xb5\xba\xe4\x66\x4e\x14\xc7\x8e\x24\xea\xfa\xbd\xa0\x63\x19\xc4\x8c\x15\x32\x3f\xf8\x8e\xb5\x7f\x74\xab\xa7\x85\x09\xda\x06\x64\x7d\x5c\x2d\x70\xcd\xee\x52\xa8\x0a\xc7\xcc\x9f\xc5\x17\x9d\x9a\xf7\x62\xce\x3c\xbd\xe8\xd4\x09\xb3\x6d\x3d\x31\xdb\x27\x66\xfb\xf7\x66\xb6\x19\xaf\xe5\xda\x2f\x8d\xd9\xe6\x59\x26\x0f\xe3\xe8\x77\x3c\xf1\xc3\xca\x00\xff\xf4\x20\x0c\xda\xe6\xa3\xae\x39\xa8\xeb\x37\xa4\x86\x4d\xad\x72\x0d\xca\x32\xa4\xb3\x4f\xf0\x28\xe5\x6c\xa1\x9a\x44\xe9\x3b\x7d\xa1\x64\x75\xd1\x4a\x24\x7a\x09\xcd\xc1\x59\x2a\xaa\x7d\x91\xea\xa8\x0a\x68\xa9\x8a\xfa\x41\xaa\x61\xdc\xe6\x86\xb3\xf1\x98\x8a\x96\x7c\x2c\xe4\xfc\xd1\xfa\xed\xa6\x32\x4e\xf1\x44\x19\x22\x03\x74\x3c\x99\x9b\x8e\x9b\x25\x9f\x06\x74\x41\xab\x40\x68\x97\x0a\xaa\x5a\xae\x6d\x29\xaf\xbf\x9c\x6f\x1b\x32\x5e\xdf\x2a\xa9\xb6\xc5\xab\x55\x5b\x97\x24\x38\xba\x06\xc7\x16\xbb\x45\x1b\xe1\xff\xb2\xbd\xa5\x75\x3b\x04\xff\xa2\x1d\x8e\x94\xec\xea\x73\x3a\x4d\xc3\xe8\xcb\xbd\x66\xd9\xc4\x6d\x19\xc6\xf3\xfb\x4d\x41\xa9\xb3\xa8\x64\x89\x97\xfb\xae\x53\xe4\x8f\x3f\xb2\x94\xf2\xe8\x87\x0d\x4a\x38\xda\x2b\xb2\xa1\x0c\x83\x10\x0f\xf8\x38\x69\x10\x44\x5b\x1d\x56\xcb\x31\x5c\x90\x7b\x3d\x8d\x10\xbe\xa6\xc1\x92\xb8\x85\x39\x1a\xc6\xd1\x24\x3b\x6d\x8b\x94\xe6\x15\xb4\x4f\x36\xb6\x00\x27\x8c\x92\x60\x98\xb4\xb1\x64\xc0\xb8\x41\xba\x49\x44\x19\x3c\x65\x5c\x77\xd8\x48\x7d\xfd\x38\x1b\x8f\x6f\x25\x6b\xf7\x60\x88\xf0\x75\x90\x40\x71\xeb\x90\x6b\x2d\x3a\x15\x86\xc1\x30\xcb\x02\xc6\x5b\xa3\x79\xc0\x40\xcf\x36\xc6\xe1\x45\x3a\x42\x2b\xa8\x76\x56\xb6\x64\x34\x82\x32\xd3\x68\x5a\x2a\xbf\x46\xab\xab\xfc\xe2\x8b\xf0\x7f\x58\x4f\x30\x5a\x3f\xc8\xc2\x8d\x3a\xdc\xd4\xc0\x21\xc3\x2c\x8d\xec\xa4\xa8\x1a\x42\xb8\x88\x91\xbd\xe2\xbd\x70\x52\xa3\x0a\x4d\xe5\xbe\xbd\xcf\x4a\x92\x66\x52\x47\x88\x92\x88\x27\x79\x02\xf2\xea\xcd\x82\xf1\xe0\x1d\x4e\x4b\xd2\xf1\x1c\x87\xb3\x09\x8e\xfd\xde\x18\x77\x50\x1a\xcf\xb0\xa9\xfb\xf3\x27\x70\x63\x25\xd8\x7a\x25\x99\x8e\x83\xb4\xb4\x54\x59\x92\x02\x6b\x32\x7e\x0f\x85\x41\x7b\xcb\x27\x0a\xde\xf0\x39\xf9\x09\xd5\xe4\x19\x89\x7a\x9f\x4f\x79\x8d\x33\xc2\x8d\x95\xe7\xaf\x5f\xd1\x1f\xb7\xaf\xe5\xc2\x7a\x91\xd7\x8a\x4a\x4c\x34\x5f\x3b\xe3\x09\xa5\xe0\x1f\x7b\x86\xac\xa8\xf7\xd9\x83\xf2\x1e\x1d\x32\xd6\x17\x02\xdf\x4f\x6e\xc2\xfe\x3b\xd8\x6f\x88\xc8\x0b\x5d\x28\x9f\xf1\x21\x80\x41\xdc\x64\x45\x4a\x92\x9f\x86\x56\x4d\x99\x24\x00\xa1\xb2\x0c\xb8\x5e\x46\xcb\x80\x43\xa5\x3f\xf2\xe3\xcd\xb4\x54\x2d\x57\xd2\xe8\xd3\x74\x8a\xe3\xae\x9f\xe0\x52\x99\x7f\x4e\x88\xfc\x50\xaa\x95\x9d\x1b\x0f\x9f\x59\x77\xee\xee\x6c\xe3\xce\x12\x91\xf3\x90\x68\xbc\xc6\x05\xe9\x90\xb9\x62\x84\x80\x22\xf3\xc4\x92\x78\xab\xee\x63\x90\x8f\x4d\xd3\xf4\xd0\x25\xd1\xc9\x00\xd1\xed\x5e\x52\xd9\x70\x83\x9f\xfc\x0e\xf2\x51\x5f\xac\x97\xd9\x65\xbf\x3b\x0a\x18\xca\xec\x9c\xac\x1d\x82\x96\x17\xed\x95\x9c\x38\x09\xc7\xb1\x87\xd4\xad\x83\xff\x71\x5c\x68\x19\xfb\x60\xb3\x9a\xd2\xe5\xc1\x6d\x36\x64\x6c\x91\x73\xbc\x39\xa1\xb2\x47\x9a\x01\x8f\xe5\xbe\x93\x66\xf5\x02\xbb\xb6\x93\x6c\xf7\xed\xc7\x98\xc8\x8a\xd3\x59\x8c\xd1\x3f\x8f\x0f\x3e\x1e\x1d\x76\x11\x6f\xe5\x6a\x14\xf4\x47\x70\x78\xe2\x3b\x50\x10\xa2\x1e\x28\x6d\x59\x11\x8d\x23\x66\x6f\x05\xdf\xab\x54\x2a\xb7\x4c\x85\x67\xdb\x9b\x11\x39\x12\xc6\xd3\xbe\x54\xd5\xca\x1d\xb3\x8e\x3b\xc8\xc2\xbf\x61\x16\x3a\xba\xdd\x5c\x47\x96\x47\x4d\x2d\xf9\xe9\x99\xaa\x5f\x27\xd3\xc4\xaa\x68\x9b\x55\x09\xf6\x44\x59\x14\x24\x4b\xb6\x42\x2a\x95\xc4\x3e\x59\x2e\xcb\x53\xc6\xb0\x62\x13\xcd\x67\x4d\x9e\x76\xd7\xd4\xb1\x9a\x0e\x0d\x27\x1f\x20\xe9\x60\xae\x05\xed\x21\x47\xec\xf6\xd3\x11\xfb\xe9\x88\xfd\xf7\x3e\x62\x4b\xfa\x4c\xc6\x21\x26\x8c\xa5\xab\x27\xed\x7f\xe2\xe1\x30\xc6\x37\xe8\x97\x60\xdc\xff\x82\xd1\x9b\xcf\x78\x38\x74\x85\xeb\x59\x28\xb6\xcf\xbe\x1f\x93\x23\xfc\x81\x1f\xf6\xb1\x0f\x65\x6d\x51\x7d\xee\x10\x08\x88\x55\x79\xe7\x5f\xa2\x5f\xa2\x68\x80\xde\x5c\x38\x0f\xf9\xcd\xec\x90\xff\x4f\xc6\x4d\x15\xef\x61\xc6\x62\xf3\x92\xc2\x5b\x22\xd5\xe9\x79\xdc\x6d\x49\xdc\x71\x1c\x47\x5a\xf4\xa0\x55\xfa\x8e\x1a\x21\xd0\x6d\x67\x2f\x5d\x4a\xc8\xc6\x38\x8d\xc2\x24\xe8\x8d\x29\x81\x4d\x7d\xf0\x22\x41\x13\x76\xe9\x43\xf6\xa2\x69\x1c\x5d\x06\x03\x1c\x27\xa2\x96\x3f\x4e\x22\xb3\x6a\x34\x1e\x93\xaa\x84\xda\xb8\x03\x37\x0a\xa3\x01\xfd\x1a\x84\xfd\x68\x22\x43\x26\xc0\x58\xf6\x09\x7a\xe7\x9a\x06\x13\x4c\x16\x5b\x90\xa0\x1a\x4a\x70\x3f\x0a\x07\xb0\x3b\x06\xe1\xc5\x18\xa7\x51\x08\xc3\x49\xba\x97\x73\xd0\xe7\xa8\x2a\xc7\x7d\xfe\x12\x6d\x88\xae\x48\x7a\x06\xd2\x36\x68\x80\x6f\xa5\x97\x1c\x17\x59\xeb\xe0\x3c\xfc\x11\x09\x65\x14\x47\x61\x34\x4b\xc6\x37\x10\x07\xc3\xb1\x0f\x93\x4f\x96\xf3\x08\x1a\xf8\xa9\xef\x3c\x21\xab\xbd\x55\x54\x1e\xe1\x40\xe9\x3c\x01\x23\x9f\xd4\x7e\x50\x7a\xaf\x24\x88\x8d\xc2\x24\x22\x5b\x17\x21\x8a\x12\x25\x8d\xca\x5e\x78\xe9\x8f\x83\xc1\x21\x2b\x5f\x92\x65\x1e\xee\x86\x0d\x83\x21\x49\xf8\xea\x1e\xcf\xc8\xbc\x92\x46\x87\xf4\x1d\xa0\x54\xa1\xbd\xf7\xa0\x9b\xcc\xda\x42\x3a\xbf\xb0\x53\xf9\x86\x3a\x57\x54\x98\x65\xa0\xf9\x5d\x39\x74\x8a\x37\x12\x24\x3f\x13\x74\x8f\x28\x15\x62\x21\xa8\x49\xdd\x4c\x47\x71\x74\x85\xd4\xee\xe9\xe5\x95\xee\xb0\x6e\xd2\x4f\x95\x42\x27\x7f\x7f\xa1\xd9\x07\x69\x36\x97\x04\xf4\x73\xa9\x90\x7e\xe6\x13\x03\x00\x37\x28\x42\x8a\x9e\x5b\x88\x36\x78\xfa\x61\x49\x36\xce\xa3\x8e\x87\x21\x04\x73\xee\xa9\xdc\xcf\x40\x16\x90\xe7\x49\xa7\x70\x1c\x3b\x52\x67\xca\xbd\x29\xeb\xf6\x36\x88\x67\xa6\xba\x0b\x8d\xcd\x1f\x32\xa3\xb6\xdc\xbe\x21\xe4\xb2\x8c\xd9\x0a\x09\xea\xd1\x39\xdd\xc7\x06\x1b\x35\xe6\x9d\x0c\x48\x81\xb7\xe4\xbb\x45\xc9\x44\xeb\x3d\x04\x61\x42\x0b\xdf\x19\x61\x02\x4e\x32\x75\x72\x26\x73\x37\x52\x4c\x1e\x80\x16\x55\x1a\xe4\x7a\x36\x98\x8d\x12\x6f\xe5\x5e\xa4\x97\xcc\xa3\x3d\xa5\x43\x82\xe8\xd0\x9c\xed\x0f\xa7\x62\x5f\x25\xd2\x26\x3f\x13\x32\x91\xcf\xa0\xb8\x94\x4f\x95\x5d\x35\x97\x4b\x4b\xa2\xae\xba\xeb\x3b\xb7\xfb\x79\x3b\x77\x4a\x8e\x54\x4c\x70\xd1\x11\x25\xdf\x0e\xc5\xa7\xb9\x1c\x9b\x06\xff\xbf\x05\x68\x7b\x83\xb9\x4b\xc6\xf2\x55\xd8\x25\x71\x4c\xd2\x68\x10\xa1\xfe\x18\xfb\xe1\x6c\x8a\x42\x80\x4f\x06\x58\x1c\xdb\xf3\x86\x4a\xc2\xde\xb2\xf2\x28\x92\x72\x44\x14\xd1\xb8\x3a\x96\x44\x38\x3a\xa5\xa5\xcf\x88\x90\x44\xaa\x77\x10\x05\x12\x0c\x3a\x06\xa0\x8e\x0d\x64\x27\xfb\x09\x7a\x5d\xc4\x7c\x99\xd5\xd1\x57\x18\x00\x13\xc0\xd4\xdd\x9c\x21\x54\x12\x2b\x7c\xce\xe4\x46\x53\x21\x94\x12\x11\x94\xd9\xd1\xc2\xe9\xe6\x22\x20\x47\xba\x40\xd7\x1d\x93\x3a\x96\x39\x37\xe6\x36\x77\xe4\x05\x08\x95\x48\xa1\x2e\xef\x10\x35\x2d\xb3\x0c\xf2\x6b\x69\x78\x32\xfc\xd9\xe8\x94\x98\x46\xf5\x0b\xbe\x49\x4a\x59\xdd\x32\xd7\xf2\x6e\x6c\x6c\xa0\x2a\xfa\xf1\x47\xe4\x1a\x43\x42\x4c\xf1\x09\x7d\x5f\x52\x0a\xbd\x56\xc7\x59\x17\x80\x73\xc6\x3b\xdb\x7d\x62\x4c\x78\x01\x91\xff\xf9\xb0\x4f\x70\x7f\xe4\x87\x41\x32\xe1\xc7\xd0\x7c\xe6\x00\x00\xf2\x87\x97\xb6\x21\x0f\xec\x17\x8c\xa7\x22\x81\x00\xef\xec\xea\xcb\xcf\xc9\x28\x08\x49\x43\xd7\xfd\x68\x32\x1d\xe3\xeb\x20\xbd\xe9\xb4\xe0\x48\x46\x0a\x10\x82\x28\x91\xcd\xe1\x0b\xbe\xa1\x9a\x02\x31\x9a\xd2\x78\xad\xae\xa2\x18\x4f\xa2\x4b\x8c\xfc\xf1\x18\x7a\x95\x78\x08\x5f\xf7\xf1\x34\x05\xb1\x9f\xbd\x92\xcb\xa7\x23\x7c\x83\x42\x4c\x47\xa4\x87\x59\xfd\x01\xe9\xf1\xcc\x1f\x8f\x6f\x50\xef\x06\x86\x8c\x0c\x0f\xcb\x05\x00\x34\xf3\x0b\xd9\x90\x82\xf0\xa2\x54\x96\xf6\x81\xd2\x0f\x4a\xef\xd0\xd7\xaf\x04\xdf\x4a\x10\x0e\xf0\xf5\xc1\xb0\x04\x7e\x8a\x84\xd8\xce\x97\xca\x30\xf9\x2b\x35\x7d\x83\x90\x28\xec\x0b\xbe\x39\xab\x88\x95\xa8\xdb\x43\x9b\x14\x49\xca\x1b\xb6\xc9\x7f\x61\xf2\x84\x53\x26\x99\xf7\x3e\x35\xce\x45\x51\x58\x84\x27\x50\x9b\xda\x3c\x9a\x64\x26\xc3\xa6\x0a\xd4\x41\x85\xa8\x4d\xc0\x59\x3a\x93\xe0\x54\xe9\x3d\x01\x2c\xa9\x22\x3d\xd4\xaf\x6c\x9f\xec\x9e\x1f\x1e\x7c\xf8\xb0\xf7\xf1\xdd\xf9\xc9\xde\xfe\xf6\xc1\xa7\x13\xf9\x78\x54\x64\x06\x4c\xa1\x4a\x91\x98\x1e\xe5\xe8\x68\xca\x64\x04\xaf\x2d\x3f\xf5\xd1\x06\x3a\x3d\x7b\xad\xbe\xdf\x03\x7f\x63\xfe\xba\xd8\x52\x15\x00\x2b\xd3\x59\x32\x2a\xe9\x74\xcf\x44\x3c\xa5\xf4\xde\x20\xa1\x85\xbf\xe0\x9b\xb2\x31\x06\x19\xc0\x05\x06\xaf\x90\xb8\x29\x20\xcb\x69\x71\x57\x57\xd1\xc4\x9f\x2a\x4c\x32\x00\xb2\x05\x86\x02\x24\x46\x48\x53\x1d\xa6\x7d\x7f\x2a\xa9\x2e\x24\xbd\xb6\xea\x2a\x4e\x05\x57\xe0\x1a\xe5\x3f\xf4\x31\xd8\xf7\xa7\xa7\x50\x2d\x80\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x5c\xd2\x45\xe3\x8a\xe3\x3c\x5a\x58\x66\x8e\x54\xa9\x59\x89\x6f\x72\x72\xb0\x75\xd0\xe1\x44\x86\xc6\xd1\xc5\x7f\xe9\x52\x75\xe4\x90\xab\xef\x2b\x49\x17\x50\x16\x24\xd6\xa3\x23\xfb\x56\x99\xf8\xd3\x92\xcb\x58\x81\xff\x81\xfd\xe2\x20\x1b\x65\x32\xf6\xec\xa8\x17\x0c\x64\xcf\x1b\x41\x11\x5f\x30\x4a\x66\x31\xe8\x89\x39\xb3\x0a\x12\x94\xa4\x01\xa1\x07\xca\xc9\xf1\x00\xf9\x43\xf0\x10\x8a\xe3\xe0\xd2\x1f\x6b\x7b\xad\x02\x93\x0c\x08\xf8\xfd\xd3\xa5\x11\x0c\xce\x74\x14\xb3\x2e\x55\xfa\x99\x3d\x80\x5a\x47\x7c\x71\x7a\xcc\x70\xdd\x89\xfc\xe9\x16\xe1\x31\xd3\x33\x5b\x6a\x0c\xfd\x71\x82\xe5\x5b\x36\xe6\xf7\x34\x77\x4c\x59\xfd\x1f\x7e\x60\x6d\xa2\x3b\xc0\x20\xf3\x02\x33\x2e\x2d\x5a\xc7\xe1\xff\xb5\x31\x9e\x3f\x40\xcd\x02\xe3\x58\x5c\x31\x80\x14\x0a\x93\x7a\x09\x15\xd5\x51\xd2\x16\xbb\x7b\x98\x54\x5c\xdc\x7a\x06\x24\x5f\x72\xba\x52\x2e\x1d\xe9\x51\x35\xd4\x1b\x2f\x2d\xf7\x92\x99\xbb\x82\x29\xa4\x5f\x74\xea\x10\xdb\x87\x29\xc3\x5f\x74\x1a\xe0\x87\xba\x56\xe4\x8e\x8c\x05\xdd\xc4\x69\x1a\x84\x17\x76\xd7\x5e\x60\x4c\x03\x29\xc7\x31\xda\x10\x4e\x6b\xaf\x8d\x12\x59\xa8\x67\x61\x1f\xe4\x8a\x5a\xc4\x1a\x65\xfd\x26\x28\xaf\x3f\x5d\xeb\x3d\x5d\xeb\xfd\xcd\xaf\xf5\x58\x48\x5f\x76\x6a\xb9\x4b\x58\xdf\x79\xe6\xb0\x8e\xe4\x17\x5a\xee\x8b\x45\x0c\x67\xf9\x92\xae\xb1\xc3\xc1\xe6\x60\x90\xc0\xd0\x89\xdd\xcd\x0f\x41\x2d\x95\xa0\x19\x15\xbf\x98\xd7\x9b\x47\x84\xaf\x20\x85\x50\x79\x08\xb2\x02\xd0\x4d\x95\xee\xf6\xcf\x9f\xcb\xe7\x03\x76\x3e\x7b\xae\x2b\x89\xc8\xb6\xf9\x9c\x5d\x5b\x49\xe5\x24\x5e\x45\x03\xf5\x70\x5f\x3a\x52\x2e\x0a\x99\xc7\x95\xc2\xd1\x98\xdc\x44\xc6\xde\xa2\x6a\x74\x09\x45\x74\xdf\xe6\x3d\x4d\x2c\x9b\x85\xcd\x1e\x87\xff\xa9\xfb\x96\xbe\x3d\xb9\x74\x97\xc2\x42\x90\x47\x22\x02\x94\x7f\xfc\x11\x70\xa7\x8a\xa9\x20\xbc\x00\x6e\x5c\x56\x20\xf2\xeb\x8b\x79\x39\x4d\x29\x44\xd9\x4d\xf9\xae\x9d\x14\xd2\xd0\xd8\x4f\xa0\x99\xe3\x94\x4c\xf6\x0f\x1b\x1b\xc6\x40\xf3\x3f\xe3\xc5\xea\x2a\xcd\xf1\xaf\x90\x14\x2c\xb5\x34\x9e\x11\x99\x2d\x4e\x52\x94\x44\xd4\xce\x71\x3a\x05\xd6\x0d\x67\x67\x3f\xbc\x49\xc9\x81\xdf\x43\x3d\x3c\x24\x0c\x80\x2e\x71\x7e\x85\x0a\xa3\x41\x95\x8c\xda\x5f\x30\x2c\xfd\x60\xc1\xfa\xc7\x1f\x91\x6d\xe4\xcb\x46\x7d\x64\x5e\x37\x10\x54\x2d\xfe\xd1\xce\xce\x46\x94\x6f\x86\xf8\x3a\x45\xdd\xc3\x4f\xa8\x7f\xd3\x1f\x63\x4f\x74\x13\x86\x5d\x6c\x36\xd0\x13\xe8\x32\xb3\x59\x9a\xc6\x51\x9f\xf0\xac\x84\x8e\x8e\xd1\x8a\x74\x0c\x16\xcb\xc4\x36\x17\x96\x8e\x30\xd2\xd0\x4b\xdd\x7a\xa8\x5a\xa4\x7f\x96\x61\xa5\xa4\xe0\x12\xcd\x24\x63\xb0\xe7\x02\x80\x6e\xc6\x26\xe9\x62\x4b\xa6\x1d\x94\x23\xdf\xaf\x6e\x09\x75\xeb\x65\x42\xf8\xde\xc0\xcb\xd8\x04\x7b\x2f\xeb\x90\xa8\xce\x00\x38\x0b\x59\x27\xdc\x4e\x72\xcf\x9a\x97\xd3\x99\x6c\x33\xdf\x64\x5e\x93\xff\x90\xac\x6b\xda\x23\x72\xb4\xa4\x9c\x5a\xa6\x5c\x78\x79\x59\x2a\x27\xd6\xab\x74\xd2\x87\x0f\xfe\x60\x20\x6c\xbb\xa4\xc4\x9f\xe2\xbb\x3e\x3d\xd2\xc1\x41\x62\xb1\xdc\x78\x0b\xde\x4b\xb6\xe2\x54\xa0\x13\x23\x21\x5b\xfa\x66\xed\xe6\x5a\x2c\x06\xc3\xec\x95\xaa\x95\xca\x58\x10\x68\x15\x34\xe4\x0b\x21\x21\xcf\xa2\x5b\xa2\x35\x08\x4c\xa8\x9c\x4b\xd2\x1c\x94\x73\x46\xdb\x2a\xd5\x0a\x84\xdc\x06\x6c\x44\x56\x57\xd3\x5d\x10\xd9\xf7\x29\x49\xe9\x93\xec\xfb\x77\x97\x7d\x33\x93\x36\x9e\xb1\xf7\xa1\x7c\x74\xf7\x7a\x7e\xa8\x4a\xbb\x41\xcf\x17\xae\xb7\xf8\x9a\xaa\xab\xf3\x5c\x77\x8f\x27\x7e\x9c\x6e\xb3\x82\x99\xdb\xad\xf3\x6a\x0c\xd4\x4a\xd0\x2c\xef\x8b\xa6\xf3\x96\x5e\x8b\x4b\xb0\xe3\x34\x0e\xc2\x8b\x5b\x70\x6d\xb1\xbd\x27\xd2\x72\xcf\x0f\xe5\x4f\x3f\xfb\xe3\x19\xbe\x45\x97\xe4\x1f\x76\x1d\x42\x20\x0f\x71\x8c\xe7\xdc\x90\x7a\xaa\x79\x01\x44\xa9\x61\x38\xa9\x62\x71\x3a\xf2\x00\x23\x22\xad\x7b\xb4\x25\x73\x0b\x03\xb5\x1b\x1d\x65\x48\x37\xdd\xf3\xc3\x52\x1a\x95\x99\xaa\x08\x74\x38\xe4\x33\x57\xf9\x94\x2c\x56\x44\xa4\x1e\xe4\x89\x28\x2d\x05\x54\x7d\x43\x21\x32\x3f\xdd\x25\x53\x7f\xcc\x20\x6e\x05\x31\x91\xc5\x6c\x0e\x31\xbc\x47\x27\x11\xf3\xec\x95\xbb\x03\xd5\x19\xf4\x52\xd9\xec\x1a\x6f\x4f\xc8\x31\xd0\x0d\x9b\xa4\x0b\x2e\x12\xc2\x53\x1a\xa7\x23\x39\x27\x78\xa9\x0c\x8d\x30\x6c\xc3\x24\x0d\xd2\x19\x15\xb8\x4c\xf3\xaf\x01\x9e\x46\x49\x90\xca\x58\x32\xb8\x02\x3d\x00\xd3\x1f\x07\x38\x4c\x75\x4b\x8c\xc2\x0d\x1b\x26\x16\x3c\xd7\xb8\x39\x82\x8b\x62\x64\x8e\x1f\x57\xc1\xe7\x5e\x25\x0b\xd2\x1b\xce\xc2\x01\xd8\x44\xf6\x71\x9c\xfa\x81\x98\x7e\xc7\xf2\x11\x13\xbb\xd8\x3a\x7a\xf4\x25\x24\xf0\xba\xc3\x5a\x62\x23\x4f\x66\x53\x4b\xf9\x25\xc9\xb6\xc2\x7b\x3d\x8d\x32\x89\x96\x80\xee\xd0\x06\x24\xda\x1c\xcf\x70\x87\xfe\xc3\xc5\x5c\x2d\xdb\xbb\x73\x56\xd8\xe4\x67\x93\x02\x81\xed\x83\x3e\xe2\x9c\x10\x71\x0e\x89\x4a\x93\x59\x92\xc2\x56\x87\x27\x38\x4c\x05\xdd\xf4\x6e\x52\x9c\x34\xea\x65\x26\x8c\xff\x50\xd6\x26\x92\x95\x7b\xf0\xe9\x4b\x8c\xf9\xe3\xd5\x29\xa5\xa2\x59\x18\xfc\x7b\x86\x51\x30\xc0\x61\x1a\x0c\x03\x95\x13\x17\x9a\x6b\x3e\x3a\x05\x66\x18\x9a\xb4\x73\x4d\x1f\x76\x1d\x69\x0f\x7a\xad\x13\x01\x1f\xe3\x92\xdf\x0b\xca\x15\x3f\x25\x8c\xb5\xc2\xc7\x97\x83\xfe\xe3\xbe\x44\x60\xc8\xaa\x7c\x14\xad\x41\x10\xcc\xfd\xf0\x45\xa7\x41\x44\x57\x9e\xb9\xff\xf6\xcc\x6b\x15\xca\x95\xcc\xb4\xbb\xad\x42\x09\xdb\x5e\xcb\x4a\xf8\x88\xc8\x17\x43\xbf\x9f\x46\xf1\x8d\x47\x15\xca\x64\x60\x9f\x11\x36\x4d\x44\xfd\x68\x88\x44\x6f\x36\x36\xd0\x0b\x1a\x91\xe9\x05\x94\x79\xb6\xba\x8a\xba\xd1\x64\x12\x85\xff\x3c\x7e\xfe\xec\x99\xd1\xf9\xec\x17\x6b\x80\xe3\x54\x7a\x41\x86\x21\xc6\x2f\xca\x1e\x92\x5e\xe1\xb0\xbf\xd2\xf3\x13\xdc\x6e\x6a\x1f\x26\x83\x96\x5e\xf4\x72\xfa\x65\x30\xd4\x5e\xf6\x83\xe9\x08\xc7\x2b\x14\x72\xf9\xf5\xf3\x67\xb7\xcf\x9f\xe1\x71\x82\x91\xd4\x19\xaa\x30\xa7\x7d\xe1\xc3\xf0\x02\xfd\xf8\x23\xfb\x50\xf1\x27\x03\xd1\xb7\xcd\xfd\xad\xe7\xcf\x9e\xd1\x0f\xa5\x53\x8e\xb3\x87\x54\x54\xe1\x99\x60\x48\x3f\x50\xc4\xe0\xb7\x8c\xcf\x99\x18\x65\x19\x31\xd6\x10\x8d\x86\x81\x4a\xbd\x38\xba\x4a\x70\x5c\x7e\xfe\xec\x99\x18\xb1\x28\x4a\x2b\xdd\xf8\x66\x9a\x46\xff\x3c\xa6\x55\x6f\xe1\xf4\x24\x6f\x3f\xe2\x3b\xfa\xe3\xf9\xf3\x67\x25\xf5\x38\xf6\x0c\x51\x8d\xc8\xf1\x28\x8a\xd3\xfe\x2c\x4d\xe8\x1b\xb2\x6c\xba\x68\x03\xf1\xba\xaf\xa5\xd7\xe7\xe3\xa0\x47\x3e\x55\xc6\x41\x4f\x7a\x0f\xca\xb0\x2e\x74\x8a\x7c\x25\xa5\x2a\xd2\x3b\x05\x82\x3f\xbe\x88\x00\x04\xf9\xf1\xfa\xb9\xc0\xe2\x43\x14\x7d\x99\x4d\x51\xea\xf7\xc6\x58\xc2\xe4\xf8\xed\xc1\xaf\xec\xcc\x27\xde\xed\x7d\xfc\xf9\xdc\xf6\xfe\xf8\xd3\xdb\xf3\xfd\xbd\x5f\xcf\xab\xae\x0f\x35\xd7\x87\xba\xeb\x43\xc3\xda\xb6\xab\x1d\xf9\xa3\xd1\x96\xfc\xd1\x68\x4f\xfe\xc8\xdb\x14\x43\xd3\x8d\x26\x53\x72\x50\x1c\x9b\x43\x64\x9b\x52\xad\xd6\x20\x9a\xf5\x88\xd4\x4f\x6a\x65\x05\x80\xc5\xca\x58\x20\xd9\x52\x21\x80\x70\x82\x28\x40\x6f\x50\xbd\xd5\x7e\x8d\x82\xe5\x65\x05\xbc\x90\x11\xd1\x1b\x54\xab\xaf\x1b\xdf\xc8\xdf\xe0\x34\x38\x43\x1b\x04\xc6\x1b\x54\x7b\xad\x7e\xa7\x57\xa9\x39\xb5\x4a\xb4\x5a\x19\xfd\x86\xaa\xd7\xb5\x5a\x4f\xaf\x9f\x3d\xde\x3e\x57\x7a\xfd\x8b\x3f\xfe\x82\xde\xed\x94\xea\xbf\xad\x97\xd5\xde\x5e\xd3\x10\x89\xea\xbb\x40\x7b\xb9\xd0\x08\x48\x83\x9c\xf4\xa2\x6b\xf5\x23\x18\x1a\x90\x36\xaf\x03\xf4\x1b\x2a\x5d\x67\x1d\x62\xbf\xeb\xd2\xef\x86\xf4\xbb\x59\xd6\x3a\x0b\x50\x4a\xc9\x35\xfa\xe9\xa7\x9f\xd0\x3a\x94\x4c\xae\xd1\x8f\xa8\x7a\x3d\x1c\xd2\x01\x6a\x37\xb4\x2a\x64\x75\x9c\x5e\x93\x81\x4c\xae\xb5\x4f\x7c\xf1\x9c\x26\xf0\xfd\xfa\xf5\x73\x67\xa7\x26\xb3\x71\x1a\x4c\xc7\x41\x1f\xb4\x04\x66\xf7\xae\x09\x19\x0f\x4e\xaf\xcf\x5e\x5b\xbe\x35\xe9\xb7\xba\xf5\xe3\x3a\xfd\xd8\x3c\xcb\x69\x3d\x99\xf5\x10\xc8\x37\x1e\x9a\x04\xd7\xa8\x1f\x8d\x67\x93\x30\x51\xa8\x5f\x86\x49\x24\x85\xd2\x00\x7a\xf5\x92\xd0\x4c\xb5\xc6\x47\x8a\x3d\x56\x6b\xd5\xaa\x3e\xb4\x62\x25\xd3\xc1\x2a\xa5\x30\x31\xcd\x32\xfa\x4a\x7e\xd3\xf1\x76\x54\xa9\xc9\x55\x6a\x6d\xa9\x4a\xad\xed\xaa\x53\x97\xeb\xac\x97\x51\x56\xa7\x6e\xcc\xba\xe0\x06\xb4\x4e\x9a\x33\x52\x41\x78\x29\x8f\x16\x79\x2c\x3c\x62\xd7\xeb\xd2\xf8\x30\xf2\x6c\xb2\x57\x55\xfe\xa2\xae\x0c\x69\xee\x88\x2a\xfc\x91\xd1\x58\x91\x61\x55\x58\xa7\x52\x6f\xce\xd8\x2a\x6c\x55\xa9\x38\x67\x80\x15\x96\xcb\x2a\xe6\x8d\x32\x5c\x16\x80\x1e\x18\xc7\x26\x27\xfc\xe1\xda\xca\x04\x19\x03\xd8\x58\x80\x03\x42\x95\x3a\xfa\x0d\x0d\x4e\xc9\xff\xae\xd7\xd1\x6f\xe8\xba\x7e\x76\xa6\x2f\x24\x28\x1b\xa0\xdf\x36\xa0\xe0\x75\x60\x14\x50\x98\x24\xfc\xbc\x85\x33\xad\xd8\x57\x0e\x63\xdc\xa7\x9d\x1b\xa0\xa3\x7e\x14\xb2\x0d\x26\xdb\x95\x8e\xba\x07\x1f\xc9\x1e\x51\xbd\xae\x56\x3d\x54\xbd\xae\xd6\xe0\xbf\x75\xf8\x6f\x13\xfe\xbb\xee\x01\x2d\x90\xff\xd6\xe1\xbf\x4d\xf8\xef\x3a\xfc\xb7\xd6\x23\xff\x6d\xb4\xb3\xcd\xec\xe5\x4b\x86\xd4\x4b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\xc4\x41\x3a\x9a\x54\x78\x99\xd5\x0c\x15\x52\x7a\x83\x89\x0f\x15\xfa\x20\x49\x18\x15\x7c\x9d\xd2\xe8\x01\xa2\xcb\xe7\x83\xe8\x08\x27\x38\xed\x20\xc7\x16\xc9\x06\xe1\xf8\x4b\x30\x65\x96\xbf\xd1\x10\x85\x47\x11\x9c\xc6\x46\x7e\x82\x7a\x18\x87\xe0\x1d\xc0\xee\xb7\xfc\x70\x00\x26\x7c\x83\x60\x80\xc2\x28\x65\x66\x98\x26\x29\xd0\x6c\x2e\x1c\x12\x37\x17\x3d\xff\x82\x6f\x0e\xe3\x20\x8a\x8f\xa8\x05\xf0\xc6\x46\xf6\xde\x4a\x3a\xdc\x2c\x4c\x9b\x53\xb3\x03\xaa\xf8\xc6\xff\xb8\xc1\xe1\x86\xbd\xf9\xec\xad\x85\x3f\x7f\xc1\x37\xbf\x44\x31\x18\x31\x7e\xc1\x37\x95\x2b\xf2\xdb\x5e\xec\x38\xf8\x1d\xb3\x52\x49\x70\xf1\x96\x30\x20\xb4\x8a\x9a\x79\xcb\x48\xf8\x01\xc4\x30\x40\x26\x58\x3e\x72\x1c\xc7\xec\x99\x37\xb8\x8c\xda\x85\x5a\x20\xfd\x4f\xfa\x23\x4c\x8e\x1f\x88\x88\xd0\x96\x3e\x24\x47\xd1\x15\x81\x5d\xe2\xcd\x2c\x93\x5d\xfa\x65\x6e\x1f\x64\xb8\xf6\x61\xe1\x8d\x4a\xe3\x2c\xbd\x3b\xd5\x97\x6a\x66\x22\x4a\xd0\xa1\xa2\x07\xfd\xf9\x86\x61\xc8\x9e\x2d\x52\x08\x62\x64\x27\xca\xd3\x41\xb2\x96\x23\x7f\x12\x2a\xa7\x50\xe7\x8c\x8e\x2c\xcc\x38\x7b\x63\x61\x35\x6e\x86\x85\xa4\xfd\xc4\x00\x0e\xd1\x74\xf4\xa1\x94\xd1\xfe\x81\x21\xfe\x0f\x81\xb8\x13\x73\x36\x0b\x47\x51\x8a\x08\x49\xba\x0b\xa5\xf2\x1e\xa0\x6e\x01\xb9\x90\x8f\x67\xbd\x22\x90\x41\x7c\xe2\x30\xcf\xa4\xbd\x0d\x3e\x64\x3b\x15\x93\xd1\xce\xa4\x5d\x4c\x2e\xb1\xae\x14\x00\x4c\x19\x64\xf6\x7a\x0e\xb6\xfb\xc1\x35\xb0\xed\x3c\x6c\x7f\xdb\x00\x26\x7e\xca\x06\x79\x35\xa3\x8e\xaf\xa8\xca\x50\xb7\x4c\x36\xca\x26\x1c\x48\x8b\xad\xbb\x9f\x50\x9b\xf0\x33\x6d\xc2\xd0\xc6\x06\x6a\xce\x9b\xb4\xef\x6e\x68\xed\x7d\x76\x8c\xb8\x6b\xcd\x18\xb4\xce\x86\xe4\x0c\xfd\x46\x64\x09\x73\x11\xcd\xe5\xe6\xb2\x4c\x97\xcf\x66\x82\xf0\xf2\xbd\x85\xd3\x18\xaf\xdd\xcc\x86\x14\xcd\xf8\x8d\x78\xca\x58\x0e\x7f\xe5\xe0\x3a\x32\xc3\x62\x7c\x74\x45\xd4\xb1\x11\x2f\x1c\x19\x79\x33\xff\xc8\x21\x1a\x27\x3b\x79\x58\xce\xd4\xb4\x82\x9b\x87\xf8\x1b\xd4\x04\x47\x16\xfa\x90\x47\xfb\xea\x5c\x9c\x72\x08\x4c\xd2\x5c\xb0\x23\x39\xc0\x54\xa1\x5b\x5d\x43\x84\x14\x55\xe1\xda\xb1\x94\xce\xd0\x6f\xee\xc5\xe9\xf8\x53\x85\x6f\xfb\x0a\xd4\x11\x68\x9c\xaa\x4b\xd1\x3e\x07\x4e\x49\xd6\x93\xa6\x07\x87\xfd\xf8\x66\x4a\x2d\x63\x65\x39\x6f\xdf\x43\xd1\x70\x98\xe0\xd4\x98\x19\xba\x46\x06\x51\x57\xd4\xcb\x0a\x7b\xe6\x5e\xed\x65\x27\xc4\xec\x67\x2d\xfb\x59\xcf\x7e\x36\x3c\x60\x31\xf2\x29\x43\xc1\x75\x80\x17\xc5\x95\x70\xcd\x2b\x7f\x8a\xea\xe1\x00\x64\xcf\x66\x3a\x72\x08\x31\x84\xbe\xf7\x4f\x29\x18\x22\xbf\xe8\x43\xaa\x7c\x53\xcb\x36\x72\xca\x36\xac\x47\xa2\x22\x43\xa8\xd2\xaa\xa7\x12\xa8\xfa\x58\x53\x1f\xeb\xea\x63\xc3\x13\x0a\x0b\x63\xf3\x5e\x5d\x45\x7b\xe4\xe4\xfb\x5d\x8c\x91\x7d\xd2\x95\x61\xb2\xce\xba\x87\xee\x47\x6e\x36\xa2\x61\x07\x82\xc2\x92\xb5\x65\x60\xdf\x61\x16\x2b\x14\x2e\x24\xa9\xa8\x4e\x30\xb5\xe8\xb8\xaa\xd2\x60\x9d\xc1\xeb\xdf\x14\x66\x5b\xb5\x69\x80\x92\x9a\x3e\x1d\x5a\x2d\x63\x7e\xa0\x56\x5d\xad\x55\xd7\x6b\x59\xb5\x4d\x49\x43\x9f\x4e\xad\x56\xc3\xa6\x86\x7a\xaf\x9d\x1d\xec\x47\x7f\x79\x0b\xb4\x9d\x18\x8e\x2c\x67\x1c\xb1\xff\xd2\x51\xdd\x40\xb5\xd7\xec\xe7\x1b\x3e\x43\xec\x85\x63\xdf\x85\x39\x0e\x86\x29\x50\xba\xe7\x50\x94\xe5\x4e\x1c\x47\x3d\x25\x93\x27\xa9\x6b\xaa\x42\xf2\xfa\x4d\x52\x74\x95\x92\x9a\x21\x77\xfd\x26\x29\xb5\x4a\x49\x5d\x97\xba\x7e\x93\xf4\x57\x49\x43\x7a\x6d\x6c\xc3\xcb\xcb\xb6\x0d\x00\x90\xab\xa9\xc8\xd5\x1c\xc8\xd5\xe7\x20\xd7\xc8\x45\xae\x7a\x47\xe4\xea\x2a\x72\x75\x07\x72\x8d\x39\xc8\x55\x73\x91\xab\xdd\x11\xb9\x86\x8a\x5c\xc3\x81\x5c\x75\x0e\x72\xb5\x5c\xe4\xea\x73\x91\xb3\x92\xee\xa7\x29\xd8\x10\x25\xa9\x9f\x62\xb3\x00\xb0\x93\xb4\x6a\xe9\x18\xb0\x8c\x54\xd7\xa3\xc1\x17\x32\x17\x69\xdd\xf6\x85\x0c\x44\xaa\x6b\xc7\xad\x4a\x14\xeb\x7a\x9a\xc3\xfb\x60\xf9\x94\xe8\xc9\x43\x5a\x3b\xfa\xa9\xc5\xb2\x7c\xf4\x63\x8b\xb9\x82\x94\x73\x4b\xb6\x84\xca\xc5\x28\x41\xac\x1f\x8e\x5d\xcd\x8d\x9d\xb9\x7e\x0c\xec\x8c\x25\xa4\x62\x57\xbd\x0b\x76\x75\x09\xbb\xba\x1b\x3b\x73\x01\x19\xd8\x19\x6b\x48\xc5\xae\x76\x17\xec\x1a\x12\x76\x0d\x37\x76\xe6\x0a\x32\xb0\x33\x16\x91\x8a\x5d\x7d\x3e\x76\x26\xb5\x62\x1e\xd8\xda\x2e\x97\xd0\x6d\xd8\xb2\x8e\x74\x21\xc7\x58\x4e\xea\xe6\x6a\x59\x55\x86\xe8\xd3\x70\xc9\x3e\xec\x28\xdc\x41\xf5\x56\x7b\xb5\x51\x67\x1a\xe8\xb2\x4d\x15\xcc\x25\x16\x21\x20\x25\xcc\x71\x98\xa9\x86\x97\x12\x96\xf0\x09\x41\x0e\xef\xa1\xdf\xc7\x42\x47\x2c\x80\xfc\x37\xbe\xf6\x27\x53\x71\x52\xce\x3e\xf0\x39\xa5\xb0\x52\x7c\x9d\x4a\xb7\xdb\x95\xcd\xed\xe3\x0a\x3b\x47\x94\x26\xdc\x22\xfd\x0b\xbe\xf1\x50\x7f\x78\x21\xa4\xf9\x0c\xca\x74\xec\x13\x24\xae\x53\xa4\x43\x61\x12\x7e\x29\x6b\xc7\x06\x88\xe9\xb4\xbb\x16\x25\xf6\x39\x8d\x9a\xba\x8b\xc7\x53\x1c\x97\x36\xb7\xe9\xb5\x3e\xd5\xd9\x3f\x7f\xc6\x6c\x56\xe4\x26\x5f\x3f\x7f\x0e\x11\x70\xc1\x80\x44\xb1\x2a\xe8\xb4\xea\x1e\xb7\x4b\xe8\xb4\xc0\x76\x44\xb2\x4c\xe8\xb4\x9a\x5e\x66\x92\xd0\x69\x81\x0b\xe3\x64\xd0\x7a\xd1\x69\xd7\x6e\xcf\xbc\x56\xfd\x5e\xd6\x22\xdf\xd2\x4c\xe4\xd1\x8c\x39\xbe\xa1\x59\x06\x5d\x09\x2f\x11\x33\xa0\x20\xcd\xa3\x7e\x34\x99\x46\x21\x84\x5c\x27\xdf\x56\x9f\x3f\x13\xf3\x3e\x0e\x7a\x15\x56\xf4\xeb\x57\xd9\x00\x40\x38\x7d\x3e\xb0\x71\x87\x9f\xe0\xcc\xaa\xc3\x4f\xb0\xf4\xed\x97\x28\x1e\x80\x5b\xba\x28\x20\xde\xc8\x10\x66\x43\xb0\x17\x03\x5a\xdf\xe4\xb7\x3c\x19\x4c\xeb\x67\x05\x33\x0c\x9e\x55\x5d\xb2\x50\xa5\xf7\x9f\xd2\xe1\x3a\x40\xc1\x61\xbf\x42\x1e\x34\xac\xdb\x4d\xf1\x95\x3e\xe6\x19\xa2\x88\x2f\xdb\x97\xd3\xf7\x5b\x3b\xd9\x65\x13\x7d\xb6\xde\x60\xf5\x12\x6a\x9e\x47\x96\x15\xbf\xc5\x4a\xf1\x64\x3a\xf6\x53\x1b\x83\x12\x41\xa6\xff\x08\x59\x40\x1e\xae\x41\x05\xa7\x02\xc1\xeb\x40\xef\x17\xfc\x8e\x2b\x3c\xc0\x64\x07\x35\x51\xa9\x56\x5f\x47\xbd\x20\x4d\xca\x79\x00\x83\x4b\x0b\xbc\xbd\x9f\xef\x0a\xee\x7c\xfb\x63\xf7\xfc\xd7\x9d\x83\xa3\xfd\xf3\xfd\x83\xad\x6d\xb4\x09\xa1\x0d\x52\x3f\x4c\x51\x8c\xa7\x31\x4e\x70\x98\x06\xe1\x05\x57\xc4\x10\x32\x9c\x44\x83\xac\xef\x56\x98\x5b\xdb\x85\x60\x32\x76\x6a\xc0\x94\x2e\x05\x35\x93\x23\xf1\x68\xa7\x28\xcb\x25\x61\x36\x9b\x14\xdd\x2e\xb8\x7d\xcf\x62\x30\x78\x10\x39\x3e\xe4\x22\x4a\x71\xa9\x77\x82\xee\xc9\x1c\xa0\x93\x11\x26\xa3\x9e\x46\x68\xc6\xdc\x04\x08\x0b\x40\xa4\x30\x80\x56\x40\xae\x66\x0f\xfd\xe1\x45\x07\x48\x97\xe3\x5a\x96\x77\x54\x03\x5b\xd8\x2e\x12\x0a\x9b\x91\x5f\x10\xba\x26\xc3\x86\x3e\xb5\xc7\x94\x70\x27\xa4\x47\x90\xff\x82\x6f\x2a\xd6\xb2\xdc\x33\xb4\x3f\xbc\x40\xa5\x03\x68\xc5\x1f\x97\xa1\x4e\xdf\x36\x78\x05\xc7\x40\x6d\x8b\xc7\x11\xa5\x13\x7a\x4b\x48\x84\xf7\x8e\x10\x4a\x3f\xaf\x4f\xe4\x5c\x11\xf4\xdd\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\x73\x85\x6e\xd3\xdb\x74\x98\xa3\xb8\xc4\x2e\xcf\x60\x08\x3d\xf4\x07\x0a\x2e\x3b\x28\xb8\xcc\x78\xe3\xad\x62\x7a\xa0\xcc\xb7\x0a\xa9\xa3\x84\x85\x62\x92\x83\xae\x01\x90\x13\x87\xd0\xfa\xec\xc6\x59\x5d\xab\x16\xd9\x43\x97\xd0\x0a\xd2\x93\x63\x21\x3e\xd1\xd3\xc3\xd2\xd3\x16\x7e\x28\x7a\x12\x90\xee\x47\x4f\x2a\x9f\xbe\x03\x3d\xed\x85\x41\x1a\xf8\xe3\xe0\x77\x9c\x20\x1f\x85\xf8\x6a\x7c\xc3\x30\x1c\xb0\xe1\x98\x4f\x4b\x7c\xd7\xb8\x1e\x46\xf1\x64\x3f\x1a\x60\xb4\x4d\x7d\xd5\x20\x4c\x73\xc6\xe9\xa2\x58\xa6\x53\xb0\xae\x06\x37\x3f\x4e\xb5\x62\x93\xb1\x93\xe1\x77\x47\xb2\x0f\x46\x56\x25\xf3\x83\x8d\x53\xdc\x91\xe0\x82\x30\x50\x2c\x6c\xc4\x34\x49\xe4\x62\x51\x51\x6f\x4e\xa7\x84\x16\x60\xb4\x78\xba\xe9\xc4\x72\xcd\x40\x86\x78\x43\xfc\xe4\x9b\x22\xa5\x41\xf3\x54\x9c\x12\xc9\x99\x1a\xd6\x47\xf1\x84\x4e\xbb\x6f\xd3\xdd\x50\xfa\xce\x48\x6a\x23\x23\xaf\xd7\xb6\x92\xd4\x8e\x06\x6c\x65\xac\x67\xf1\x80\x12\x3a\xf5\x00\xb0\xf5\x03\xec\x8b\x4a\x85\x17\x0e\xd8\xe8\xa8\x7c\x18\x62\x39\x24\xa2\x25\xd0\x9e\xdd\x93\x7c\xd8\x12\x34\x71\x53\x66\x38\x2e\x62\x44\x45\x8d\x8a\x06\x7e\xea\xa3\x1e\xc8\x5e\x6a\x09\x87\x3c\x06\xa0\x69\xa6\x0b\xee\xed\xac\x03\x3e\xc4\x31\xcc\x65\x3f\x0a\xfb\x31\x4e\xf1\x0a\x1b\x8e\x71\x74\xa1\x30\x65\xe9\x5e\xea\x68\xb1\xb1\x86\x78\x1a\x80\x39\x75\x6f\x61\x3c\x05\x0f\x24\x96\x82\x07\x0b\x6c\x7a\x5f\x13\xe6\x0a\x43\x80\x32\x65\x27\xe1\x0d\xbc\x0d\xd6\x80\x04\xbe\xc0\xce\x25\xf1\x27\x01\x8b\x06\xcd\x62\xc1\x08\x82\xf0\xe2\x01\xb8\x49\xd6\xf9\x0d\x4e\x1e\x0c\x7e\x69\x89\xb4\xb9\xa4\x92\x49\x91\x7a\x57\x1c\x73\x27\x85\xb1\x92\x1d\x2d\xca\x2b\x1d\x3a\x07\xf7\xc0\xe1\xc0\x36\xfb\x3e\x7c\x91\xab\xdb\x68\x8a\xb6\x87\xfc\x4b\x3f\x18\xfb\xbd\x31\xa6\x66\x88\x89\x7b\x5b\x3c\xe7\x9d\x29\x4c\x55\x3b\x41\xc8\x36\xbe\xdc\x7d\x8a\xc1\x55\xf7\x99\x8f\x51\xca\xbc\xa3\x69\xd0\x34\x0a\x29\xdb\x35\x50\x90\x20\x3c\x1c\xe2\x7e\x1a\x5c\xe2\xf1\x0d\xf2\xd1\x00\x27\x69\x3c\x83\x67\x0f\xc5\xd8\x1f\xac\x44\x61\x1f\x17\xda\x67\x8a\x52\x2f\xa0\xf1\x58\x34\x4c\x81\x3f\x36\x25\xf3\x91\x2c\x15\x27\x62\x51\x65\x51\xea\x17\x15\xe7\x93\x3f\x2f\x5a\x9c\xfe\x77\xb2\xb9\x98\x41\x21\xb5\x44\x30\xcc\x05\x80\x72\x57\x8b\x52\xd4\x72\x51\xb2\x00\x43\x86\x78\x48\x04\x55\xb6\xe0\xf0\x80\xc5\xcb\xe4\x9c\x7a\x47\x9a\x10\xeb\xe2\x33\x6b\xcf\x55\x36\xd7\xea\xeb\xab\x8d\xba\xfc\x89\xaa\x44\x6c\x5f\x34\x39\xa8\x83\x6a\xca\x57\x55\xfe\xed\xa0\x7a\x91\xb3\x53\x62\x55\x65\xfb\xf3\x15\xd9\xc8\xb9\x36\xf9\xa9\x85\x8d\xf4\xc9\x08\x4b\x42\x01\x4b\xb4\xe5\xa3\x11\x68\x8d\x89\x90\x59\x60\x29\x72\x11\x76\x33\xe4\xf8\x40\x80\x01\xbe\xac\x89\xd0\xc4\xd6\xb5\xa5\x43\xdf\xe0\xb0\xc4\xac\xbd\x4d\x95\xa7\xa6\x23\x37\x64\x5b\xe7\x2a\x53\xea\x75\x9c\x7e\x53\xe4\x4f\x7c\x4a\xf0\x18\xf7\x53\xda\xf0\x71\x1a\xfb\x29\xbe\xb8\x29\xb9\xcc\xb5\x25\xed\x33\x88\x8b\x1b\x68\x89\xb2\xd2\x25\xa7\x79\x18\x9b\x8d\x43\x3f\x49\x08\x9b\x78\xeb\x27\x78\xa0\x78\xcc\xc9\x7f\xf9\xc6\x61\x0c\xd4\x31\x8e\xe1\xc0\x45\x76\x35\x37\xa4\xfc\x45\xae\xe7\xf6\x63\xf7\x19\x39\x36\xea\x2e\xa4\x18\x39\xc9\x8c\xcd\xbc\x61\xc9\xb3\x1b\xcd\x82\x80\xd9\xe7\x41\x5c\xdc\x50\x14\x3d\xe4\xbe\xc0\xd1\xc7\xc0\x73\x58\x7a\x32\xb2\xef\x18\xfd\xd7\xee\x73\xee\x85\xb6\x7a\x53\xe4\xa1\xdc\x1b\x23\x1d\x73\xcb\x84\xea\x6c\x5b\xe6\x92\xa5\x32\xd3\xf0\xda\xaf\xde\x54\x1d\x76\x92\xc6\xd8\x9f\xdc\x49\x95\x0d\x32\x14\x53\x3e\xcb\x36\xf8\x8d\xfa\x4a\x2f\xa0\x06\xdb\xea\x89\x86\x4a\x27\x10\xc6\x5a\xd2\x4c\xd7\x50\xa9\x51\x57\x15\xd3\x92\xc2\xf7\x18\xf0\xd3\xd4\xbe\xfa\xcb\x1c\x8f\x90\x1d\xcb\x5e\x6b\xdb\x61\xb9\x88\x38\xf5\x63\x38\x6e\xd9\x04\x44\x73\x7b\x83\xe3\x4d\x66\x5d\xc5\x85\xc6\x1f\x7e\x58\x1a\x8e\x67\xc9\x68\xa9\xd8\x36\x47\xa1\xb8\x36\x3a\x31\xcc\x1d\x54\xcb\x9b\x57\x38\xd7\x42\x56\xd3\xa9\x7c\x5b\x2a\x2b\xcf\xcf\x27\xf4\xec\xdb\xad\xb0\x1f\x7f\xdc\xce\xa7\x10\xc5\x63\x07\xea\x19\x54\x22\xb5\x21\xdd\x6e\xb2\x83\xb6\xe1\x1c\xcc\xde\xcb\x4a\xef\x3c\x05\xbd\xac\xa2\x9c\xf0\xe4\x5c\x99\x7c\xbd\xf0\x6e\xba\xa9\xf6\xc8\xaa\x10\xd4\x33\xcb\x64\x0a\x7e\xa0\xea\x6f\xb0\x1f\xf2\x99\xe2\xdb\x1d\xe8\x61\xbb\x6f\xbb\x86\x2a\x9a\x73\x94\xe0\x92\x7a\xed\xdc\x45\xf3\x9c\xc1\xc8\xd5\x15\x8a\xba\x5c\xd1\x24\xd5\xbb\x93\xc6\x59\x4c\x67\x76\x40\xfa\xcf\x9c\xce\x4c\x13\xbc\xe0\x74\x5a\x15\xbf\x05\xa7\x53\xd4\xbd\xc7\x74\xe6\x29\x7c\x8b\x5d\x1d\x7c\xd3\xe9\xbc\xf7\x74\xe5\x2c\x81\x39\xf3\xa5\xeb\x4d\x73\x26\x89\x6e\x26\x42\xcf\xdb\xb7\x89\x75\xcc\xea\xfa\x12\x6d\xa0\xe0\x52\x9e\xad\xbc\x2d\x82\xed\x98\x34\xae\x74\x77\xe4\x07\x21\xa4\x3c\x71\xdd\xb5\xbe\x05\xbb\x81\x73\xde\x79\xb4\xe1\x0e\x3e\xa0\xab\xd8\x94\x1d\x84\xd4\x35\x88\x41\x1a\x9a\xac\x31\x6d\x97\x10\x77\xa2\xaf\xf3\x38\xca\xdb\x2e\xdf\x0e\xb4\x93\x90\xd4\x84\x32\x77\xa4\x57\x6f\xbb\x96\xbd\xc7\x04\x4f\x9b\x38\x14\xe1\x3f\x53\xae\xc6\xa0\x54\xea\xa7\xcc\xa8\xbb\xa2\xd7\x31\x60\x68\x34\x4b\xa5\x23\xa1\x15\x61\xc2\x52\xc4\x65\x24\xa4\x72\x42\x64\xbd\x21\x61\x76\x59\x04\x08\xfb\x79\x35\xc2\x2c\xf2\x3e\xc5\x0f\x02\x79\x26\x05\x90\x33\x17\x86\xbd\x20\xf9\x83\xa9\x64\xa2\x0e\xf5\x06\x80\xf4\x78\xd0\x05\xe1\xda\xa0\xcb\xb2\xf2\x64\xa0\x4c\x05\x68\x98\xc9\xab\x50\x9c\xb6\xd0\x56\x07\x58\xa4\xdf\x90\xc8\x0b\xc9\x61\x38\x9b\x09\xb1\x42\x93\x23\x5e\x39\xcc\x59\x7f\x3d\x38\x82\xf3\x32\x23\x3a\xb3\xcc\x75\x14\x43\xbf\x32\x45\xb7\x87\x94\x7e\x79\x59\xb3\x36\xa1\x9f\xe1\x21\xfb\xba\x94\xf4\xd1\xb5\x62\x76\x84\x27\x18\xa4\x70\xd8\x5d\x29\x09\xb0\xab\x28\x38\xed\x83\x43\x3b\xbc\x36\xab\x73\x09\x16\x5f\xf2\xb0\xf3\x94\x99\xd2\x7c\xf2\x1c\x6f\x61\x0a\xe8\xec\x80\xec\xb9\x33\x77\xdd\x0e\x70\x81\x75\x2b\xf6\xa9\xa7\x75\xfb\xb4\x6e\xd1\xdd\xd7\xed\x7d\x56\x07\x58\x08\x8f\x82\x64\xe1\xb5\x61\xc5\x84\x51\x34\x70\x91\x5f\x0f\x8e\x9c\x1c\x40\xf6\x20\x33\x38\xc0\x7d\xd9\x8e\x15\xb3\x93\x6c\x68\x7a\xb8\x1f\x4d\xd8\xd2\x21\x6c\x21\x88\x66\x49\x71\xe6\x21\x06\xab\x28\x7b\x10\xa4\xc4\xbb\x51\x72\xe2\xbe\x90\x07\x14\x88\x48\x5c\x5a\xb2\x79\xf8\x8f\xa2\x28\xc1\x68\x12\x5c\x13\x59\xc8\xd2\x3f\xf0\x04\x35\x85\x34\x24\x13\x22\x93\xc2\x5c\x64\x17\x5d\x82\x74\x4a\x4e\x3a\xc9\xac\x97\xe0\x7f\xcf\x70\x98\x5a\x55\x0c\x48\x15\xed\xa4\xac\x1e\xea\x28\x3a\x55\x83\x32\x4a\xda\xac\xcc\x57\xf5\x93\x9d\xcd\x86\x95\x2d\x46\x52\xb6\xda\xac\x91\x92\xc8\x1f\x4c\x60\x66\x3d\x1e\x9c\xa1\xdf\x36\x68\xbd\xd3\x20\x37\x74\x49\xf6\x9b\x9b\x40\xbf\xed\xb2\xf2\x4a\x40\x13\x49\xb4\x3d\xf4\x07\x03\x32\x81\x73\x14\x20\x53\xc8\x72\xd5\xad\xd0\x7f\xed\xea\x8f\xc3\xf7\xdd\x63\xf4\x7f\x5a\xab\x6b\x68\xca\x80\x26\x4c\x97\x67\x83\x79\xf8\xa5\x9f\xac\x81\x9c\x3c\xf5\x07\x15\xfe\x94\x23\x1b\x1f\xfa\xfc\xfa\x79\x96\xf0\xd0\xf9\x22\x10\x0a\x33\x57\x86\xb8\xc9\x02\x8f\x85\xec\xaf\x00\xb2\x7c\xfb\x4c\xd0\xb2\x56\xb2\xeb\xf1\x58\x08\x28\xe9\x3e\x12\x00\x25\x22\x98\x25\x19\x14\x08\x67\xf9\xc8\xc7\x66\x71\xf8\x12\xe3\x4a\x7e\x65\xd7\x6b\x9e\x16\x37\x4b\xb9\x60\xf6\x07\xfa\xe5\xda\x9d\x19\x88\xa8\x46\x63\x9d\x6c\x48\xe3\xe5\x8a\x19\x32\x0b\x53\x41\x3b\xe0\x57\x64\x42\x0d\x19\xc1\x1a\x40\xe9\x8b\x15\x9a\x72\x5a\x44\x58\xf9\x87\x56\xc0\xd6\x2c\xbd\x17\xe2\xed\x9a\xa1\x17\x68\xa6\x37\xf8\x4a\xe8\x05\x22\xa0\x28\x58\x64\xbe\x2e\xc6\x7b\xe6\xe0\x62\xbc\x07\xb7\x16\xe5\xed\x5c\xcc\x72\x91\x4a\xf2\xc3\x17\x64\xec\x47\x6d\x13\x05\x68\xd9\xe5\x96\x2f\x43\xa7\x61\xee\xa5\x37\x39\xd2\xab\x86\x1d\xda\xc8\x6c\xdf\xf9\xe1\x5f\x06\xed\xa9\x28\xd9\xcc\x10\x36\x07\x03\xfb\x20\xc0\x5c\xf7\xa3\xb0\xef\xa7\x1c\x66\x61\x0d\xcc\xa7\x70\x2a\x18\x0a\x2c\xd9\x91\x3f\xa0\x81\x8c\xd8\x42\xfd\x36\x5c\x66\x16\xea\x7c\xe6\x9b\x70\x04\x68\xb6\xc0\x95\x3b\x94\xd3\x59\x82\x8d\x0f\xbc\xc3\xa9\x92\xb8\x58\x5a\xc4\x10\x03\x16\x8d\xfd\x24\x85\xe7\xf9\x6b\x3a\x13\xaf\x4f\x4b\xea\x72\x5e\x41\xb5\x32\x75\x31\x3b\x63\xce\x60\x36\x4f\x62\x2a\x38\xb8\x29\x26\x03\xb7\xa1\xaf\x41\x69\x33\xa5\xdb\xe6\x82\x7a\xfe\x3f\xe3\x22\xc8\xe6\xa2\x60\xbf\x59\xb0\xdd\x2a\xe4\xdd\x03\x3d\x9c\xd1\xff\x7e\x34\xc0\xb7\x54\x3d\x78\x22\x4e\x6b\xf4\x52\x04\x4e\x12\x52\x77\xba\x6f\xbb\x2e\x28\x6c\xae\x6e\x05\x7d\x11\x58\xba\xb0\x61\x42\x04\x92\x77\x10\x38\xf8\x11\xb0\x01\x90\x0c\x27\x35\x02\x27\x98\x02\x66\x9e\x76\xaa\xa3\x6d\x1b\x4d\xdc\x2a\xde\x08\x0b\x18\x06\xd2\x89\x56\x3f\x76\x25\xeb\xc3\x7c\x1b\xc0\x9c\x00\x67\xaa\x7d\xa8\xc5\x8f\x13\xe4\x66\x32\x02\x8a\x5a\x14\xa9\x8a\x5d\xf2\x7d\x02\xb6\x9f\x0e\xfc\xb3\x89\x35\x0f\x03\x86\x2d\x29\x97\xb4\x55\xe3\x12\xe7\x89\x81\x40\x85\x2d\x11\x34\x1a\x70\x2a\xd7\xee\x66\xec\xd2\xfe\xea\xcb\xfc\xe6\x55\xeb\x95\x32\x7a\xb9\xba\x30\x06\x42\xd5\xe2\x38\xcb\xbc\xc7\x78\x8a\xfc\x14\x8d\x31\xe1\x82\x51\xc8\x57\x00\xcb\xf2\x41\x2d\x41\x61\xbf\x06\x86\x6b\xf2\x2d\x24\xce\x37\x93\x20\xa4\x46\xa2\xec\x10\x6f\x84\x4b\x54\x1f\x59\x25\x3a\x7d\x12\xfe\x94\x90\x26\x60\x7f\x4c\x8f\xbc\xc1\x25\xfa\xf1\x47\xab\x3e\x5e\x0f\xd4\x71\x78\x27\x5d\x46\x86\x89\xaa\x4c\x71\x9e\xcf\xf5\x66\x8b\x5e\x49\xbb\x45\xd2\x4c\x24\x11\x86\xd2\xec\x95\x85\xa0\x79\x73\x0f\x4b\xc8\xab\xab\xe4\x20\x43\xd3\x7d\xb9\x44\x2e\x90\xd7\x99\xe9\x17\x48\xe0\xf0\x7b\xae\x0e\x82\x5f\xc5\x53\x1b\x41\xd7\x29\xf9\x4e\x97\xf1\x8f\xb7\xac\x1e\x17\x6f\x6b\x7b\x20\xf9\xcd\x99\x01\x2a\x1f\xd9\xda\x9b\x67\xf9\x77\x4f\x4b\x05\x30\xbd\x63\xb2\x87\xdd\x0c\x05\xf5\xa3\xf1\x18\x53\xfa\x8f\x86\x5c\x34\x00\x51\x13\x43\x2e\xbd\x3c\xd1\x43\x12\x45\x25\x27\x6f\xb2\x8d\xc6\xfe\x95\xf4\xca\xea\x97\x68\x77\xfd\xa0\x0e\xe8\x42\x48\x29\x52\x3b\xbb\x78\x84\x0c\x0f\x8c\x0b\xd2\xfa\x64\x7d\x1a\xe6\xb8\x2e\x40\x89\x3f\xa6\xd8\xc3\x0f\x00\x06\x2a\x49\x9f\x86\x1f\xc5\x71\x70\x49\x65\x15\xce\x31\xac\x00\xf9\x55\x6a\x26\xe7\x4b\x96\x83\x66\xac\xd5\x62\x72\xcd\x5d\x7a\x96\x2f\xdf\xf4\x47\x78\x72\x37\xb8\x76\x81\x93\xa9\xcc\xc1\x62\x7a\x28\xc1\xb3\x82\xa0\x39\x19\x6f\xb3\x9c\x8d\xf4\x14\x43\x45\x2c\xfe\x56\x17\xc3\xfa\x51\x78\x89\xe3\x54\x91\x61\x69\xb6\x3b\x6e\x4c\x09\x16\x9f\xd4\xfa\xcf\xed\xb6\x7a\x48\xab\xa8\xce\xab\xe2\x65\x41\x7b\x98\xf9\x2e\x56\x2a\x6a\xf3\x8f\x75\xc2\xbb\x49\xc6\x47\xb3\x13\xf5\x43\x91\xc4\x6a\x1a\x25\x49\xd0\x1b\x63\xf7\x8a\xb5\x34\xb5\x98\x73\x53\x36\x50\xa6\x3d\x28\xfd\xc6\x4f\xe0\x7f\x18\x50\x90\x50\x9f\x93\x15\xdc\x91\x7e\x67\x0e\x4f\xd6\x4a\x5f\xf0\x4d\x47\xf5\x8b\xb2\x16\xd3\x3c\xa5\xec\x85\xc8\x32\xee\xc0\x7f\xe7\x14\x14\xab\xb2\x63\xba\x73\xd9\x6b\x30\x11\x5e\xb7\x4c\xb0\x17\x16\x72\xbd\x7a\x74\x7e\xdf\x3d\x5e\xb3\x57\x90\x58\x78\xd3\x5e\x42\x2c\x1c\x09\x28\x7d\x57\x39\x98\xe2\xf0\xf8\xf8\x83\x51\xad\xb8\x33\x99\x3c\xfd\x76\xc1\x6b\x12\x5c\xef\x85\x6a\xb9\xc2\xa6\x47\x74\x15\x27\x8b\x2d\x63\xe4\x5c\x37\x26\x2b\xd1\x7c\x03\x1d\xdc\x84\x1c\xea\xdc\xc0\xb9\x81\x2d\xf7\xca\x80\x5d\x01\x7e\x07\xc3\x40\x5f\xe3\x39\x70\x20\x09\x58\x42\x33\x80\x41\xf6\x38\x9c\x79\x51\x66\x18\x87\x11\x7d\xa3\x31\x40\x96\xb3\x1f\xe7\x71\x8f\xa2\x4b\x9a\x22\x2f\xae\xe9\xd8\xda\x5e\x46\x4b\x4b\x76\xdf\x0a\x6b\xf9\x4a\x1a\xd1\x7c\x43\x2e\x57\x8e\x39\xb5\x1c\xa4\xea\x24\x4c\x5e\x51\x26\x4e\x31\x36\x2e\xab\xaa\xac\x04\xfa\xfa\x95\x92\x6b\x56\xa7\xc2\x27\xf1\x86\x1f\x7b\x0d\x1d\x8d\x55\x4e\xa2\x54\x36\xef\x5e\x83\xb6\x03\x57\x1b\xe2\xa7\xfd\x76\x83\xf5\xdc\x46\x9c\x36\xd0\xac\xb8\x48\x65\x0c\xbb\x97\x3a\x88\xf9\xd7\x1d\x62\xd5\xf9\xee\x25\x17\xf2\x66\x56\xfa\xd1\x64\xea\xa7\xb0\xbd\x14\x5d\x86\xf2\xb6\xa0\x6d\x62\x92\xf8\x53\x74\x4f\xb4\x2d\xbf\xbb\x20\xf7\x50\x86\x83\x11\x6d\xfb\x98\x93\xb7\x83\x90\x25\xea\x72\xf1\x46\x85\xbe\x45\xf1\xc2\xdc\x77\x8e\x5a\x46\x8e\xb4\xa4\x2c\xc1\xec\x8b\x2d\x50\x23\x11\x77\xb5\x0a\xe4\x9d\xed\x18\x0b\xfd\x35\x0f\xb1\xa4\xb8\x53\xd5\x72\x25\x45\xab\x31\xb4\xf7\xa7\xd5\xeb\x56\xa3\x5d\x6b\xf7\xd7\x20\xb1\x41\xbb\xd5\x6e\xb6\x86\xad\xe1\x59\x99\xab\xe2\x01\x34\x7f\xc8\xfa\xe1\x38\x47\x16\x40\xc1\x39\x16\x8e\xc3\x97\xa8\x9b\x31\x32\x1a\xd6\x66\xf1\x3d\x2f\x6f\x8d\xc9\xfe\x4a\x8b\x0a\x8f\x7c\x9d\x64\x74\x7a\xe7\x25\xa3\xc6\x6c\xe0\x0b\xfa\x0e\x6b\xf8\x61\x03\x38\x98\xc2\xa8\xb6\xf4\xa6\x7e\x9c\xe0\x92\xb2\x50\x73\x2e\x26\xe3\x44\x51\xfc\x64\xd5\xac\x5e\x09\xa4\x38\xa2\x31\xbc\xe6\x2c\x3a\x4a\x18\x06\x32\x79\xea\xd5\x3c\x88\xfc\x32\x4e\x3a\x0c\xb3\xa4\x10\x06\xb8\x13\x9c\xa4\xd4\xb6\xc1\x1f\x5b\x16\xa8\x06\xf3\xb4\x7a\x86\x36\x36\x50\xb6\xf6\xd0\x8f\x3f\xea\xed\x9e\xd6\x58\x19\xbe\x26\x5d\x2a\xa8\xed\x6b\x7a\x81\x61\xb6\x8c\x54\x0e\x63\x2c\x7e\xad\x45\x66\xca\x53\xf7\x50\xb3\x9c\x63\x5d\x17\x5d\xb2\x23\x3a\x5c\x05\x65\x30\xcc\xf2\x06\xfc\x29\x34\x50\xd5\x6f\xad\x8d\xe2\xca\xad\x4e\xad\x5d\x8c\x51\x58\x8f\x46\x8e\x63\x90\x27\x9d\x4e\x54\xd1\x3c\xf7\xae\x88\x2f\xc2\xab\xd8\x9f\x4e\x41\x8e\xf4\x53\xd6\xbc\xac\x32\x41\x3e\xd9\xe9\x13\xc9\x2b\x2d\x77\xf5\x2a\xae\x3e\x86\x2b\x5b\xe6\xf0\x63\xfb\x54\xd4\x81\xe4\xce\x97\x3d\x42\xe8\xe1\x32\x7e\x9e\x54\xcf\x75\x04\x72\x6f\x59\x67\xa9\x43\x68\x38\xa0\x54\x23\x0e\x18\xd9\xc5\x8e\xe5\xe0\x94\x17\x22\x4a\xf7\x5e\x04\x84\x3a\x86\xa8\x26\x4d\x6c\x6e\x50\x29\x76\xed\x40\xe6\x8d\x79\xd3\xdd\xc7\x43\x35\x53\x3e\x59\x8e\x3a\x39\xde\xe7\xac\x69\x6a\x83\xc2\x7e\x67\x7e\xe7\x7f\x91\x18\x2e\xf6\x2d\x6c\xf3\xcf\xdd\xc0\xc8\xb2\xb4\x6b\x54\xcc\x65\x25\xfc\x2b\x4d\x6d\x84\xe2\x6a\xe9\x38\x85\x3d\x5e\x83\x59\x90\x1a\x5d\x9d\xf0\x4d\x1b\xf7\xc4\x6a\x73\x48\x03\x39\xca\x0e\x8b\x73\xac\xdb\x8b\xf5\x6e\x21\x74\x16\x8a\x9e\xb3\x6d\xb3\x5f\x97\xa2\x1b\x44\x99\xf3\x89\x2d\x00\x9a\xd5\x67\xd5\x10\x4b\x32\xcf\x0c\x11\x20\x81\x75\xf6\x2e\x92\x49\x17\xfa\x97\xc1\x84\x2b\x60\x03\x0a\xb3\x37\x22\x1c\x57\x38\xe6\xba\xf6\xa3\xe2\xdb\x69\xde\xa6\xad\xec\xaf\x66\x41\xae\x5a\xb4\x7c\x22\x64\x25\xfa\x56\x09\x2e\x2d\x45\x24\x1d\x21\xa3\x17\xb3\x0c\xd5\x0a\x66\x80\xe0\x42\xd4\x2c\x26\xf4\x81\x59\x49\xf6\xca\x52\x58\xd2\x05\xea\x16\xd6\x96\xd2\x92\x5e\x90\x90\xde\xd0\x72\x5c\xbb\x2d\x7c\x6c\x61\xf7\xd0\x89\x98\x38\xa1\xf8\x92\xaf\x65\xd0\xa3\x6d\x4f\x32\x01\x88\x1d\x4a\xbb\x68\x92\x1e\x21\xb5\xf7\x5f\x71\x9f\xd2\x02\xb4\x88\x48\xc7\xdf\x60\x6f\xca\xa2\x2a\xcf\x67\xd3\xdc\x7b\xde\xc2\xa6\x39\xd9\xb1\x30\x0a\x92\x47\xfd\x9d\x59\xf6\x43\xa3\xa8\xef\x4b\x0f\xb8\xa5\x38\x63\x17\x38\x22\x0c\x7c\x83\x5d\x85\x69\x1c\x24\xd5\x82\xbc\x98\x34\xc0\xf2\x4e\xc1\x6e\xbf\xe1\xfc\x2a\x23\x9f\x71\x13\x5b\x73\x8c\x53\x98\x1b\x86\x3c\x79\xca\x26\xa6\x44\x5d\xa4\xc3\x92\xed\x4d\x12\x93\x51\x14\x3e\xd6\x6d\x42\x34\xb1\xb0\x36\xc6\xca\xd6\xf4\xb1\x52\xef\x5f\x40\xc7\xe4\x27\xc9\x6c\x82\x07\xea\x7d\xa2\x3f\x8e\xb1\x3f\xb8\x91\xf6\x3b\xe5\x40\x36\x0b\x69\xda\xca\x02\x11\xcd\x16\x63\x7b\x76\xfe\xb5\xd0\xa1\x89\x30\x2e\x30\x51\x8f\x13\xbc\x30\xaf\x77\xeb\x8b\x66\xe1\xa2\xb0\xfe\x44\x89\xdb\x20\x79\xaa\x42\x3a\xe0\x54\x80\x04\xf1\xdb\x79\xc0\xb9\xa1\x53\x92\x57\x0f\xab\x6c\x4b\xe5\xcd\x62\xd7\xc8\x8b\x70\x4e\x08\x1b\x6e\x13\x42\xd9\x93\xb9\x54\xf5\x8b\x0d\x94\xab\x1d\x65\xd0\x72\x94\xa2\x86\x66\xc2\x7a\x43\xf2\xde\x6e\x22\x31\xef\xca\xe4\xcb\x60\x08\xf7\x25\xf4\xdf\xfc\xcb\x92\x79\x56\x18\xe6\x85\xc9\x7b\x0a\x9d\xb4\x52\xec\x9e\x64\x8b\x80\x87\x3b\x7d\xd2\x18\x59\xcb\x7b\x3f\x73\x85\xc1\x94\xc5\x0b\x2a\xae\x8e\xe5\x35\x98\xe5\x05\x7b\x00\x39\x85\x34\x03\x80\xf3\xbd\x42\xb2\x40\xe5\x98\xda\x56\x04\x21\xb3\xe4\x65\x76\x00\xcc\x64\xe6\x02\x87\x60\xcc\x9b\x0f\x4d\x44\x29\x77\x00\xa3\xa1\xb3\xf3\x61\x99\x3a\x03\x50\x61\x49\x42\xd2\x26\x6a\x37\xc1\xe4\x18\x3e\x70\xfb\xd9\xbd\x21\x8a\x26\x01\x91\x11\x3c\xe4\xd3\x4f\x57\xc1\x78\x8c\x7a\x58\x34\x38\x40\xb1\x1f\x0e\xa2\xc9\xf8\xe6\x81\x0e\xf7\xd4\x6a\x82\x0d\x93\x87\xf6\x7e\xf6\x60\x4a\x49\xe3\xdf\x80\x0b\xd1\x49\x1e\x98\x2c\x48\xa2\xc6\x0a\xbe\xc6\xfd\x59\x8a\x4b\x4b\x3c\x1a\xd5\x92\xc7\x12\x77\x78\xcc\x7c\xcb\x21\x16\x3d\x10\x74\x0f\x2d\x91\xe1\x20\xff\xbf\xe4\x3e\x33\x53\x30\x32\x77\xe3\xd4\xec\x71\x12\xf5\x18\x75\x51\xc5\xa6\xdd\xa8\x9f\x4e\x33\x9b\x65\x87\xa2\xfa\x07\xe7\x55\x92\xa1\x44\xa6\x70\x4a\xed\xe6\xaa\x91\xd6\xdc\xe2\x56\x47\x97\xb6\xb4\xae\x4d\x69\x85\xc6\x9b\xa5\x89\x07\x32\x05\xae\x88\x71\x97\xa5\x41\x66\x0b\xe9\xb6\x5c\x61\x89\xbc\xa5\xf1\x00\xfc\xad\x01\x6b\x09\x6d\xa6\xf9\x18\x80\xdd\xb4\xa1\x26\x17\xc9\xa0\x99\x82\x9c\x27\x93\xe5\x63\x8e\x5e\x9a\xfa\x6c\x25\x35\x74\x96\xc2\xd9\xee\x2c\x75\xc4\x44\xa9\x05\x0f\xe3\xd9\x91\x5a\x48\xd1\x77\xd3\x6a\xdb\x34\x03\x8a\x8a\x7b\xc0\xf8\x32\x67\x79\x1a\x4b\xf6\x04\x2c\x87\xf8\x75\x77\x7d\xb8\x25\x4a\x9c\x50\x88\xdb\xbf\xd9\x34\x5c\x8f\xa8\x1f\x7f\xbf\xb5\x73\x8b\xc8\xf6\xc9\x2d\x28\x6d\xbb\x70\x26\xe5\x71\x66\x9b\xbf\xc5\x2d\xa4\x15\xb7\x74\xd8\xed\xfc\xf0\x65\x30\xec\x48\xdb\xb3\x44\x21\x0b\xaa\xc7\x99\x4b\xd5\x22\xfb\xf2\xf7\xa1\x2f\xcf\x95\x0e\xbe\x03\x75\xc4\x5f\x44\x6d\x6e\x59\x7c\x85\x34\xc9\x4b\x7c\xa8\x5d\x61\x65\x1f\xbf\x61\x0f\xfd\xf1\xc8\x1a\xec\x6c\x3b\xfa\x46\x0a\x07\x6d\x77\x8d\x52\x97\x72\xd7\x26\xbb\x10\xf0\x44\x6c\xe1\xe2\x8a\x84\x3d\x1d\x5e\x21\x63\xb0\x67\xba\xed\xb9\xbc\x3b\xa9\x18\x4b\xfb\x66\x74\xa9\x02\x5b\xac\x82\x41\xc5\x1a\x92\xc0\xa9\x98\x57\xf4\x25\xee\xeb\x0c\x39\x00\x84\x31\x3f\x6a\xfb\x92\x1e\xdf\x40\x63\x3f\xb8\xa6\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x3d\xf4\xc1\x7f\x02\x3f\x7e\x60\x05\xf1\xf7\xce\x98\xbf\x47\x3d\xb1\x8d\x19\x2e\xaa\x28\xbe\x17\x63\x7c\x70\x14\x4d\x45\xf1\x43\x31\xee\x82\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\x3d\xdc\xde\x51\x48\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x16\xdc\x3d\xf2\x14\xe4\x99\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\xb7\x67\x5e\xab\xf1\xbd\x26\xa5\x7c\xf0\x1c\x94\x8b\xe6\x9e\x54\x72\x4e\x1a\x88\x99\xe9\x27\xb5\xb4\x93\xbc\xa2\x23\xf1\x24\xe8\x47\x33\xe0\xe2\xa7\x9a\x7c\x72\xdf\x4f\x47\x1e\xb2\xa4\xa0\xcc\x8e\xd7\x1f\xa2\xbe\x3f\x46\xd3\x68\x7c\x33\x0c\xc6\x28\x1a\x22\xba\x69\xb1\x53\xbc\xe5\xc8\xcb\x62\xdb\x6f\xa8\x05\xb5\x86\x15\xc6\x24\x5e\xef\x90\xf7\xb7\xaf\xcd\xd8\x41\x92\xad\x65\xef\xb3\xc1\xd4\xc0\x46\x70\xd6\x23\x33\xa8\x13\xf1\x4e\x65\x1a\x47\x69\x44\x3e\xa1\x0d\x72\xfa\xd0\x0b\xb0\x7a\x68\x03\x85\xf8\x8a\x20\x90\x0f\x21\x9c\x8d\xc7\x8e\x85\x22\x30\xc8\x96\x89\x14\xef\xc8\x16\xc9\x93\xcf\x49\xbe\x92\xdb\xa9\xd8\xfe\x10\xf4\x62\x3f\xbe\x99\xa7\x23\x97\xf2\x83\x3a\x41\x41\xb6\x50\xa6\xf5\x24\xc2\x05\xef\xb2\x3f\x46\x41\x38\xc2\x71\xa0\x04\x70\x55\x22\x3a\xe8\x79\x46\xcd\x08\xa3\xe6\x74\x16\x08\xfb\xc7\x63\x0c\x83\x7b\x9c\xf0\x33\x18\xf9\x29\x47\x88\x85\xf2\xa0\x62\x90\x71\xaa\x44\x28\x2f\x0e\x20\x97\xbb\xa2\x4b\x1c\xc7\xc1\x00\x27\xe8\x90\x2a\x44\x02\x9c\x50\x06\x3e\xbd\x41\x41\xc8\xb2\x19\x67\x08\x14\x68\x41\xcf\xd5\x70\xb2\x28\x00\x43\xe6\x72\x94\x5b\x24\x6a\x20\x99\xa8\xfd\x9b\x13\x4a\xc2\x8a\x74\x93\x63\x92\x28\xfb\x8b\x05\x78\x3c\xe8\xa0\x25\xc8\x94\xb5\xa4\x1b\x8e\xd8\xdb\x24\x7f\x13\x9c\x8e\xa2\x41\xae\x8f\xbc\x54\x5a\x8f\x91\x6f\x73\x3c\x43\xc8\x0c\x67\x48\xd1\x57\x0c\xb2\xf9\xbc\x3a\x83\x18\x4e\xfd\xab\xd0\xfc\x22\x31\x12\x22\x2c\x64\x69\xf5\x5c\xe6\xc4\x9b\xb3\x8b\x09\x0e\x2d\xa6\xc3\x64\x47\xc9\xc7\x02\x65\xcc\x87\x9d\xbb\xb2\xf2\xd6\xf4\x0f\x56\x04\x98\x99\x14\x77\xfd\x0a\x84\x63\x69\x6c\xc7\xe9\x07\xde\xe4\xc8\x4f\x0e\xae\x42\x46\xf6\x37\xa5\x25\x52\x73\xa9\x2c\x7c\x9e\xc8\x23\x6c\x82\xbc\x3c\x79\x31\xb7\x1f\xb4\x56\xee\x74\x5b\x6a\xfd\x3f\xc9\x6c\x4a\x44\xad\x30\x48\x2b\x3e\x11\x4e\xd9\xd6\xe7\xc7\x17\x33\x32\xba\xd6\xf1\x40\x96\x0c\x0a\x39\xe3\x94\x79\xdc\xc6\x4b\x09\xca\x38\x7a\x40\x95\xc2\x7c\xd2\xe9\x2a\x35\x21\xc8\x1d\x54\xf6\x03\xc7\xb6\x83\xb8\x62\x7c\x88\x63\x1c\xf6\x49\x03\x30\xce\x53\x7d\xbd\x1a\xc3\xc0\xe4\x62\x1b\x40\xe7\x3e\x83\x6c\xa9\x31\x6c\x4c\x75\x1b\x56\x4a\x22\x33\x4d\xaa\xf2\x9e\x85\x74\x1c\x60\x02\xe9\xaa\x35\x43\xa0\x6e\xf2\xf9\xc8\x32\xd8\x94\xca\xe2\x1a\x8e\x88\xd2\x10\x52\x0e\x80\x94\xca\x7f\x65\x5e\xc9\x23\x96\xa3\x0d\xc6\x36\xf9\x9d\xc5\x5c\x5e\x44\xcb\xe5\x73\x3c\xb3\x11\x58\x72\x59\x9c\x6c\x73\xe5\xf2\x08\xea\xd2\x1a\xe1\xef\xd4\x75\xe2\xa4\x1a\x5e\xfc\x2e\x64\x93\xe7\xae\xee\x98\x2b\x74\xc0\x98\x19\x4b\x12\x00\x24\x05\x26\xf4\x83\x01\x4a\xa2\x09\xa6\xa9\xa7\xd0\xd5\x08\x87\xe8\x26\x9a\xc5\xc2\xcc\xde\x27\xe2\x2c\x05\xfe\xc0\xb1\x73\xef\xbb\x0b\xea\x8e\xce\x79\x7b\x19\xa2\x0c\xa0\x52\x31\x47\x46\x0c\xfd\x1d\xb7\xbb\xb9\x68\x14\x9a\xd3\x6e\x34\x25\xc2\xce\x34\x93\x7b\x98\xbc\x73\x0f\x71\x4a\x02\x06\x1a\x26\x45\xa6\x9a\x80\x26\xf2\x81\xa7\x94\xad\x4e\xba\x7f\x16\x95\x5f\xee\x38\xee\xd0\x88\x72\x89\x2d\xfa\x67\x5d\xe3\x22\xe2\x21\xbf\x6c\xfb\xe8\x4f\xc0\x68\x62\x4e\x3d\xc4\xb6\xea\xac\x98\xbe\x59\xcb\x00\xcb\xb9\x5b\x2c\x99\xce\x53\xb9\xf8\x19\xda\x90\xda\x57\x3f\x2d\x90\xba\xc8\xb1\xc9\x6e\xa3\xab\x28\x5c\x4a\xa9\xfc\xcc\xdd\x1d\xa5\xe0\x85\xe3\x28\x9a\x22\xbf\x17\x5d\x5a\xb6\xc1\xfc\x2e\x2f\x71\x68\x4b\xee\x0e\x03\x17\x15\xad\xca\xfd\x14\x6f\x0b\xe4\xd5\x2a\xb4\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\x8b\xac\x1b\xdb\xc6\xd7\x1f\x47\x21\x7e\x04\x8e\x07\x70\xd1\x46\xb6\x87\xc0\x8b\x02\x3b\x19\x29\x36\x77\x23\x93\x73\x91\xa8\xc2\x11\xe7\xa7\x56\x7b\x32\xfb\x19\xd9\x7a\xbb\x1f\x22\x1f\x3c\x6f\xb5\x58\x84\xb9\x91\x85\x8c\x38\xef\xf9\x20\x6c\xe1\x69\x84\xf1\x83\x1a\x0e\x31\x09\x2e\xc2\x60\x18\xf4\xfd\x30\x65\x01\x25\x03\xda\x7b\x00\x49\xdb\xb1\x1d\x93\x7f\x91\x3c\x88\xe9\x59\x59\x7e\xf3\x00\x61\x63\xcc\xe6\x75\xb2\x70\x84\xc1\x97\x4d\xaf\xe6\x8c\x35\xb2\x9a\x85\x89\x91\xd2\x6e\x30\xe6\x0e\x1a\x7e\xb0\x54\x2f\xb2\x7f\xb6\xb2\xb1\x1b\xb6\x30\x0e\xed\x7f\x71\x00\xa7\xd5\xeb\x6a\xb5\x5a\xab\xd6\xab\x0d\x0f\x55\xaf\xab\xcd\x6a\xab\xda\xae\xae\x9d\x3d\x1a\x60\x0f\xb5\x0b\x87\x5e\x61\xe1\xeb\xf8\x8c\x18\x2b\xf6\x8a\x39\x04\xc3\x72\xe5\x0f\xf4\xdf\xaf\x5f\x21\x66\xaf\x26\x6a\x0c\x51\x49\x4c\xef\x0f\x1b\x16\x45\xa1\xfc\x07\x50\x25\xa3\x21\xfe\xb3\xb0\x31\xa9\x0e\x80\x92\xc7\x18\x87\x17\xe9\x88\x9a\x1e\x39\xb9\x48\xf1\x98\x31\xd9\x42\x59\x2c\x52\xcc\x76\xd8\x8f\x06\x84\xde\x31\xfd\xa1\x93\x3b\xbc\xce\x8f\xfd\x29\x08\x00\x87\xfd\xca\x2e\xbe\x76\xb7\x39\x2f\x80\x4c\xa1\xd5\xbe\x70\x70\x97\x8c\x58\x0b\x44\x76\xb1\xc4\x35\x98\x17\xd6\xc5\x52\x45\x19\x92\x4f\xe9\x70\x7d\xa1\x68\x2e\x6c\x2a\x9c\xb1\x5c\xf8\x54\x7d\xfd\x8a\x76\xf1\x75\x6e\xf8\x96\x39\x04\xd4\xf7\x53\x1c\xb2\x3d\x5f\xa5\x20\x07\xf3\x77\x13\x92\x74\x0f\x9b\x0d\xf8\x09\xe3\x86\x12\x65\x42\x9a\xdf\x45\xef\x75\x8b\xe2\x52\x84\x36\x04\x76\x35\x1e\x3f\x43\xbc\xa9\xbb\x53\x9a\x41\x49\x9d\x29\xd1\xc0\xce\x8b\x85\x23\x21\x03\xfb\x8b\xc1\xb0\x2c\xbe\x8a\xe9\xc8\x17\xa1\x0e\x32\x12\x73\x97\x0e\x92\xe3\x8c\xc7\x28\x3c\xc7\x01\xfc\x58\x65\x49\x14\x7e\x56\xc7\xe8\x54\x77\xec\x4f\xa6\x08\x5f\x43\x24\xc9\x5e\xa0\x77\x8e\xde\xab\x92\x32\xe6\x6d\x03\xbd\x4f\xed\xdb\x82\xa4\x28\x88\xff\xc3\x11\x28\x1d\xea\x13\x91\x34\xc4\xb0\xd5\x22\x3f\x45\x3e\x4a\x83\x89\x45\xe2\xb6\x85\x64\x97\xbb\xeb\x4e\x0a\x21\x0f\x0e\x29\x8a\x36\x08\x7a\x6c\x16\x4e\x03\x1e\x15\x9b\xfc\x53\xaa\x37\xd1\x0a\x2a\x05\x14\xe3\x97\x68\xbd\x5c\x16\xd1\xb2\x9d\x52\x3c\x85\xa3\xf6\x78\x19\x05\x22\xdc\xf6\xd7\x8d\xac\xe9\x37\x6f\x78\x1b\x96\xf2\xa2\xd1\x02\x82\xbf\x73\x5b\x92\xc7\x94\x2e\xae\x7b\x8d\xa9\x3b\xca\x7d\xd1\xee\x6f\x20\x73\xb0\x8b\x64\x0c\x36\xa9\x50\x6c\xb6\xcb\x1b\x2a\x9a\xb6\x1c\x2b\x7e\x10\xfa\x3d\xfd\xe4\x21\x1d\x00\x8a\xb2\x53\x1a\x83\x83\x08\x81\x8a\x60\x18\xa4\xf7\x15\x05\xb3\xc5\x29\x56\x97\x83\x49\x91\xcf\x45\x43\xf7\x5a\x58\x93\x29\x47\xd9\xe2\x22\x39\x99\x8c\x9d\x61\x58\x44\xb5\x53\x01\x83\xc7\x99\xdf\x80\xa5\x43\xff\x80\xf4\x1b\x75\x42\xfa\x89\xc2\x17\x2c\x04\xaf\x88\x52\x1b\x68\xdf\x4f\x47\x95\x3e\x0e\xc6\x59\xcd\x55\xb4\x40\x44\x22\xfb\xf9\xb7\xd0\xce\xe3\x30\x47\x32\x8e\xbf\x77\xb5\xfb\x64\xc7\x5d\x99\x16\x8c\xf3\xae\x4a\x0b\xf3\xce\xb9\x32\x58\x38\xa9\x51\x5c\xe5\xe8\xe7\xe6\xc9\xb9\x62\xd2\x08\x33\xbf\xaf\x3a\x4d\xea\x48\xbd\xc5\xa7\x40\x12\x1b\x86\xc1\x78\xcc\xc3\xce\x32\x37\x09\x38\x6f\xcd\x17\x4a\xf8\x61\x2e\xb4\x1d\x7a\x65\x50\x4e\x17\x9f\x42\xb3\xcc\x20\x15\x22\x94\x87\x32\x3e\x2b\x70\x04\x63\xae\x20\x35\xf7\x49\x8b\x96\x90\xc9\x24\xb4\x1f\xb1\x64\xf6\x60\x1e\xa8\xc8\xd7\x58\xbd\x21\x9f\x9c\x5f\xb9\xa3\xcc\x9f\x5f\xa1\x0d\xf2\x5f\x47\x02\xb5\xc9\xf9\xef\x64\x9b\xb9\x6e\xf8\x03\xdc\x5e\xef\xe9\xe1\xd7\x45\x31\x3f\xf9\x82\x64\xce\x91\x73\x4f\x50\xe0\xee\x8e\xb6\x5a\xaa\x5e\xbf\xaa\xb6\x5f\xa1\x97\xa4\x0b\xbf\xc3\x9e\xbe\xb3\xb3\xb3\x53\x46\xcb\xf4\xc5\x4f\x3f\xa1\xea\x75\xad\x0a\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\x96\xaa\xd7\xcd\x76\xab\x4a\x81\x5d\xe9\xc0\xae\x8a\x02\x83\xe1\xc5\xc9\x0c\x3c\x7d\x4a\x80\xc6\x9b\x37\xb4\x26\x5a\x46\x30\xd2\xb9\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\xf2\xcb\x2e\x6f\xa0\x6a\xa5\xe5\x2c\x03\x63\xca\x8a\xbe\xa4\xf6\x36\x9c\xda\xca\xe8\x27\x54\x69\xa1\xff\x42\x35\xd4\x41\x2b\xb5\x22\x22\x8a\xc1\x39\x54\x71\xc3\x43\x71\xdf\xef\x8f\x30\xcb\xae\x33\x5f\xe0\x20\x35\xcf\x09\x3d\xc6\xa5\x12\xad\x4a\x8e\x4a\x0a\x92\x64\x37\x91\x06\xc3\x7e\xc5\x44\xab\x6e\xa0\xf3\xb8\x44\xcb\x03\x41\xae\xf5\xd6\x2c\x7d\xba\xca\x72\xf8\x94\x44\xf9\x0c\x3e\xfa\x8a\xaa\x05\xc3\x9a\x87\xf8\x4a\x72\x76\x82\x5b\x47\xa6\x00\x09\x79\xfa\x9e\x67\xda\x48\xda\x9d\x4f\xd9\xd1\x7e\x9e\x21\x0d\x0e\xfb\x60\x48\x43\xff\xb5\x1b\xd2\xec\xe2\x6b\x53\x13\x60\x03\x47\x0a\x6e\x50\xa0\x15\xfa\xbb\x58\xfc\x4d\x5d\x7d\x31\xc2\xd7\x85\x55\x18\x05\x4e\x9e\x0b\x46\xd5\x2c\xd4\xfa\x43\x31\xf2\x11\xbe\x36\x43\x68\xb2\xf1\x93\x8e\xf6\xf3\x13\x09\x59\x03\x67\xde\xf5\x98\x7a\x55\xf8\xe4\x99\x2c\x7a\x8c\xa4\xb3\x6e\x02\x1a\xe1\xeb\xee\xc8\x8f\x0b\xe7\xd9\x4a\xe6\x1e\xe8\x20\x47\x5a\x40\x0f\x72\x57\xf7\x3c\xc4\x71\xec\xd8\x1a\x07\xb0\x04\x48\xb3\x9c\xa9\x7d\x6a\xed\xb2\x8d\xdf\xd9\xaa\x92\x76\xaa\xc3\xfc\xba\x0e\x06\x21\xc0\x7d\x8e\x82\xb0\xb4\xb4\x74\x87\x88\x9b\x12\x85\xd3\xf5\xb6\x88\xa6\x87\xaf\x14\x4a\xb8\xc5\x17\x8c\x43\x78\xfa\xf3\xa5\x26\xbe\xd8\xa8\xcd\xb6\x58\x8f\xc5\x23\x65\xd2\x2a\x8b\x25\x4a\xa1\x75\x3e\xf0\xa3\x0b\x7d\x64\x47\x99\x45\x56\xcd\xd5\x22\xa9\xe9\xe4\x46\xd9\x16\x5a\xcf\xc9\x8f\x49\x57\x4b\x03\x34\x13\xd0\xe9\xbd\x30\x65\x9d\xad\x24\xb3\x5e\x92\xc6\xa5\xc0\x43\xf5\xb2\x07\x49\xf8\x32\x95\x05\x59\x51\xeb\x65\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x5e\xd4\x7d\xf6\x83\x9f\x06\x61\xad\xd8\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6d\xeb\x62\xd5\xff\xbc\xdd\xab\x28\x02\x0f\xb5\xa6\xc6\xd0\x9e\x7d\x0f\xa3\xb8\xfc\x47\x6d\x63\x74\x38\xbe\xe3\x9d\x4c\x42\x90\xee\x48\x74\xea\x2a\xc3\x38\x9a\x90\xb7\xdd\x68\x80\x61\x93\x2a\xba\x21\xc9\x00\xef\xb1\x27\x29\x74\x7b\xf7\x6d\x49\x90\xe3\x42\x8b\xe1\xbb\xde\x9c\xd8\x2a\xa2\xfb\x93\xbc\xdc\x8a\x6f\x51\xa2\xd6\x62\xbb\x94\xa8\x26\x36\x2a\xf1\xe6\xb1\xf7\x2a\xad\xe9\x79\xb9\x9c\x03\x49\x8b\x9e\xf5\xb6\xd2\x67\x04\xbd\x99\x96\x02\xbe\x26\xf4\xad\xca\xae\x5b\x5c\x78\xab\xd2\x10\x2e\xba\x53\x7d\x3a\xd9\x59\x59\x2f\xb6\x51\x7d\x4a\x87\xeb\x62\x9b\x62\x0f\x77\xdb\xa4\x68\xa3\x7f\xde\x1e\x55\xb0\xfd\x87\x5a\x59\xb3\x74\xb8\x6e\xdf\xa0\xc8\x28\x3e\xe6\xf6\x94\xc6\x37\x39\x06\x46\x03\x4c\x8e\xe8\x9f\x8e\xf6\xba\xdc\xd3\xa9\x84\x93\xbe\x3f\xc5\xa5\x9c\x8d\xd3\x64\xcb\xa8\xef\xa7\xfd\x11\x2a\x99\xe9\xa3\x01\x85\x51\x1c\x5d\x01\xdd\x42\xc6\x95\xd2\xd2\xbe\x3f\x1e\x46\xf1\x04\x0f\xd8\x34\x0c\xfc\xd4\x37\x53\xd0\x2d\xce\xc0\xe5\x49\xbd\x3b\xff\x66\x73\xb5\x08\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\xee\x8c\x0c\x8b\x33\x6e\x56\xc7\x65\x0c\xa0\x6c\x0d\xb3\x90\x51\x0f\xb5\x10\x50\xe8\x8a\xc3\x29\x17\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x3c\x60\xd9\xcc\x24\x2f\x74\x67\x26\x5e\xc9\x4e\xf6\x46\x4a\x89\x36\x99\x25\x29\xea\x61\x14\x90\x11\x9d\xe0\x30\xa5\x79\xd6\x7c\xb8\x5e\x8f\x71\x2a\x3c\x16\x0a\xe5\xf6\xd5\xf2\x74\xaa\xca\x7d\x9a\xe3\x90\xba\x56\x65\x09\xe2\xbf\xe0\x69\x8a\x66\xe1\x94\x27\x0d\x54\xb3\x83\x4a\x36\x2d\x55\x0b\xf7\x7d\xcb\xc6\x01\x32\x0d\x6e\x8a\x51\x10\x5e\x62\xae\xcf\x05\xcd\xe0\x20\xbb\x2b\xb3\xe6\xd1\x46\x7a\x89\x25\xd1\x66\x49\x4c\xd3\x08\x05\x69\xc2\xbd\x62\x10\xa1\xe0\xfb\xde\x31\xf5\xac\xc8\xd3\x84\xb8\xee\x4b\xa6\x42\x59\x77\x99\x79\x1f\x02\x2b\x65\x9b\xcd\x00\x64\xe0\x64\x9e\x8a\xda\xce\xaa\x33\x25\x5a\x3e\xdc\xf2\x53\x9f\x0b\xeb\xd5\xa2\x92\xe6\xe6\x60\x90\x40\x1b\x3c\x2f\xb8\x63\xa4\x19\x2d\x14\xdf\x14\x45\x90\x05\x23\xf3\x38\x33\x76\x41\x74\xcd\x33\x27\x00\xca\x2f\xa9\x4f\x89\x2f\x59\x50\x52\x7b\x62\xe0\x78\x8f\x33\x99\xe7\x14\x9d\xd2\x92\xc9\xef\x0b\xd5\x9b\xbf\x37\xb2\x92\x45\x92\x99\x9b\xee\xf5\x59\x3a\x3a\x39\xa0\xa8\x34\x40\x2c\x98\xa8\x0a\x4a\xf6\x71\x06\x32\x9a\x13\x27\x92\xd1\x9a\xc4\x94\x01\xc3\xf9\x91\xd2\x36\xa1\x6b\x2e\xf2\xe5\xa6\x44\x36\x60\x06\xd1\x2e\x6f\xa8\x49\xd2\x8b\x52\x30\xcf\x75\x9a\x20\xff\xd2\x0f\xc6\x10\xb1\x8b\xf2\x05\x60\x76\x6e\xaa\x39\x91\x9c\x55\x82\xf0\x32\xfa\x82\x13\x3d\xc9\x70\x89\x25\x07\xf6\xd0\xd5\x28\xe8\x8f\xac\xac\xba\x77\x93\xc3\xaa\xcd\x56\xf9\x42\xe9\x45\xd1\x18\xfb\xe1\x2d\x1a\x44\x3b\xe3\x59\x32\x42\xbf\x8c\x70\x4a\xe3\x99\xf0\x5c\xb4\xe0\xae\x35\xf5\x63\x60\x14\xec\x55\xc6\xb5\x05\xbb\xbe\x43\x38\x10\xc1\xe9\x61\xc4\xef\xbf\xcd\x0b\x80\x5b\x94\x90\x5c\x6b\x86\xa7\xca\x75\xc5\xe5\x58\x10\x8c\x3d\x53\xb0\x1a\x6b\x95\x16\x55\x16\x1f\x1d\xf0\x05\x75\x26\x6c\x89\x64\xc4\x6d\xd1\x96\x90\xd7\xdc\x38\x0d\x46\xd6\xa5\x56\x21\x1f\x25\x43\x33\x17\xdd\xf3\xe2\x99\xac\xb0\xa1\xa5\x64\xce\x2b\xcc\xa1\x67\xb5\xed\x11\xfd\xba\xd1\x2c\x4c\x39\x7d\x59\x98\x09\x01\x1a\xd2\x44\xc2\x47\x10\xb7\x78\x43\xc5\x7f\x55\x6b\xf2\xb5\xc9\x8b\x5c\x43\xce\x30\x38\x8a\x66\xe1\x00\xcd\xa6\xd4\xa1\xb0\x3f\x9e\x0d\xb0\x46\xf7\x66\x35\x0d\xa3\xcc\xc8\x45\xfe\x50\x3c\xb6\xad\xc0\x62\x10\x5d\x85\x32\x1e\x51\x38\xbe\x41\xc3\x99\x58\x94\x96\x48\xfa\xab\xab\x68\x8c\x13\xea\x54\x69\x97\xb5\x80\x6f\xc4\x78\xe2\x07\xa1\x2a\x5c\x15\xeb\xd7\xc4\xbf\x2e\x29\xfd\x82\x8b\x53\xb4\x62\xcb\xcc\xee\xcd\xbf\x52\x15\x73\x4e\x35\x0f\xae\x29\x07\x4a\xe6\x78\x28\xad\xbf\x44\x12\x01\xba\xe8\x09\x68\xc3\x49\x4e\xe4\xab\xda\xc7\x20\x2c\xc9\x4d\xbe\x44\x4d\x4f\xa1\x33\x9b\xf9\x24\xcf\xe0\x6d\x23\x12\x42\x77\x12\xc0\x7c\xb7\x2d\xca\xe7\xa9\x9a\x85\xfd\x7e\x23\x8f\x80\x78\xbb\x2c\xad\x27\xa7\xd1\x04\xc1\x0c\xc7\xe4\x34\x29\x36\x86\x95\xec\x80\x00\xce\x90\xf6\x8a\x8c\xbb\xa8\x7b\x90\xe0\x2a\xb6\x5c\xf5\xae\x39\x46\x4a\x0a\xac\x8c\xe1\xc3\x94\x9b\x45\x15\xee\x2b\xb3\x30\x3d\x19\x96\x3c\xa2\x16\x34\x14\x4e\x86\x56\x36\xe4\x99\x9e\x4f\x95\x3c\xb6\x68\x1e\xb6\x6e\x85\x93\x8a\xbf\x27\x37\x7d\x5f\x63\xb7\xc2\x59\x28\x73\x9d\xbc\xee\x69\xe5\xe6\xd8\x0d\xff\x24\x93\xb7\x73\x63\x43\xcc\x30\xb1\xce\x58\xae\xc5\x9b\xca\xc3\xc4\x49\xd3\x91\x89\x9e\x9f\xc1\x47\x7e\x02\x19\x72\x9d\x27\xee\xb9\xa9\xc8\x33\x76\x2d\xfb\x40\xd1\x49\x67\xd0\x69\xd8\x35\x9c\xa0\x28\x94\x8e\xc2\xb5\x36\x2a\xb5\x6a\x75\xb0\x64\x2d\x5b\x8e\xc5\xbb\xb4\x32\x3f\x06\x8b\x47\xfb\x79\xf8\x41\xa2\xbe\xe6\x65\x20\xcb\x0d\x98\x9a\xe7\x6a\x46\x07\x61\x81\x9c\xe4\x77\x8d\x6e\x47\x1a\x42\x34\x44\xf2\xbc\x20\x77\x85\x6d\x48\xc4\x1c\x28\xa1\xdb\x8e\x77\x37\xeb\xad\xb6\xdd\x49\x2c\x2f\xd5\xf5\x9d\x23\xac\xf1\xd8\x6a\xc5\xc3\xac\x1d\x63\x11\xde\xc3\xad\x21\x30\xd5\x10\x73\x2c\xb1\x33\x4d\x0a\x5f\x38\x0f\xaf\x32\x61\xf4\xf2\x10\x2a\x12\x40\x58\x56\xf1\xa8\x25\x1c\x2b\x09\x40\x2b\xcc\xcb\x94\x1a\xf4\xbd\x99\x0d\x87\x65\x63\xe6\x1b\xf2\xd1\x62\x63\xfd\x69\x3a\x00\x96\x21\x0f\x36\x4d\xcb\x5f\x3c\x63\x9f\x33\x82\x30\x05\xae\xc7\x11\x2e\xec\x42\x44\x59\x11\xf3\x1f\x9a\xbb\xbc\x17\x98\xf3\x19\xe0\x55\x5a\x62\x48\xd9\x74\x29\x6a\xc9\xf9\xaa\x13\x5a\x50\x26\x14\x65\x0c\x1c\xeb\xd1\xa1\x91\x60\x0a\x1b\x15\x82\x85\x3c\xd8\xf8\x12\x21\x9d\xe0\x6b\x03\x25\x9d\x63\x4d\xf1\xf7\xc1\x7c\x27\x76\x58\x92\x9b\x44\xe0\xe2\x64\x90\xe8\x63\x04\x28\xfb\x29\xcd\x17\xcf\x6a\x66\x31\x43\x51\x90\x20\x3c\x1c\xe2\x7e\x1a\x5c\xe2\xf1\x0d\xf2\xd1\x00\x27\x69\x3c\x83\x67\x0f\xe4\xf4\x95\x28\xec\xe3\x42\x51\x46\x0b\x52\xa8\x92\xe8\x01\x50\xca\x02\x72\x43\x89\xc5\x35\x17\x64\x10\x1e\x68\x67\x40\x1b\x9c\x1c\x45\x32\x21\x87\x5a\xc2\x51\x3a\x8f\xd0\x73\xaa\xcd\xa7\x7a\x5e\x74\x21\xba\xdf\xb1\x8c\xaf\x79\x20\xca\x07\x83\xe6\xad\x95\x79\x02\xfc\x02\x9c\x55\x1a\x21\xce\x64\x77\xa4\x79\xb0\x2e\x1e\x52\xde\xb5\x78\xa4\xe4\x77\xad\x5a\x7d\xb5\x51\x2f\x26\xe6\x27\x4c\xe3\xa3\xc4\xbf\xf7\xd9\xa4\x2d\x89\xc0\x49\x41\x98\xe2\x78\x28\x59\x0b\x23\xe7\xaa\xe0\xfc\x95\x75\x9d\x53\x2d\xdd\x6e\x59\x7c\x44\x1f\x8d\xf0\x78\x8a\x63\x22\xfe\x14\x58\x04\x3b\x0c\x37\xe6\x1b\xac\xa3\xfc\x0d\xee\xf1\xa8\xcc\xa4\x3b\x55\xd0\xae\x56\xce\x69\xaf\x76\xa1\x4b\x25\x9b\xb0\xe5\xd6\xcf\xc9\x55\x15\xe3\x41\x00\xed\xba\xdf\x33\xd6\x85\x3d\x00\x2e\x52\xcf\x8b\x6c\x25\xc2\x61\x51\xcd\x22\x96\x65\xb8\x54\x29\x7c\xf1\x63\xa3\x95\x9e\x08\x4b\xde\xdd\xdf\xec\x3e\x3c\x3d\x11\x11\x9a\x07\xa5\x20\x2d\x30\xba\xfa\x4b\xd0\xd4\xee\xc4\xef\x17\xa2\xab\x89\xdf\xbf\x0f\x6d\x89\xea\xf7\xa2\xaf\x2f\xd8\xae\x42\x92\xe8\xab\x7b\x0e\x68\x91\x79\xa0\x44\x46\x1b\xa1\x75\x17\x23\xb6\xdc\xe3\xaf\xd0\x24\xcd\xf1\x61\x20\xd8\x80\x13\x03\xfb\x91\x79\x31\xf0\x4c\x2d\x10\xd2\x77\xdf\x4f\x47\x34\xac\xef\x33\xfe\x9e\x0d\xf3\xeb\x2c\xd2\xef\xed\x99\xd7\x6a\x7e\xaf\xe1\x7d\x19\x32\x25\x1e\x8e\xb8\xfc\xe0\xf1\x7e\x39\xe4\x45\xe3\xfe\x0a\x0c\xe5\xf8\xbf\xae\xa0\xbf\xe2\x3b\x04\xff\xb5\x05\xd0\x35\xaf\x28\x78\xd4\xd8\x6c\xca\x24\x02\x90\xa2\xc1\x4a\xef\x73\xc2\xd3\x28\xb5\x25\x17\x18\x57\x18\xd9\x76\xb3\x98\x89\x16\x2b\xcb\x8d\xb4\xc4\xe3\xdd\xcc\xb4\x58\xf5\x3f\xcf\x4e\xab\x28\x02\x0f\xc5\x29\x7b\xd0\x9e\xdd\x54\x8b\xe2\xf2\x37\xb0\x25\x36\xca\x4f\xfc\xa9\x10\x0e\x27\xfe\x74\xf1\xd8\x0b\x16\x17\x71\x13\x84\xcb\x2a\x93\x8e\xf9\x5d\x0d\x96\xd1\xf2\x06\x6a\xb8\x6d\x96\x6f\x52\x5c\xb3\x18\x2d\xd3\x3f\x97\xe9\x32\xfd\x73\x1a\x30\x73\xc0\xf5\x0c\x70\x29\x40\xcb\xa8\x56\xb6\xd8\x44\xf3\x2f\x45\x2c\xa3\x39\xe0\x86\x06\xb8\xee\x04\x5c\xb7\x02\xb6\x43\x4e\xe3\x60\x3a\x86\xab\x97\x12\x1d\x96\x37\x6f\xc0\x6f\xe2\x2b\x7d\xae\x93\xe7\x75\xf2\x08\x28\xd8\xa0\x88\xa9\xf8\x4c\xa7\xa2\xf4\x19\xbd\x21\xad\xff\xf8\x23\x02\x6c\x3e\xa3\x97\xa8\x5a\x59\x6b\x49\x33\x54\x7e\x8d\x3e\xe7\x84\xbb\x90\xe6\x9e\xda\x82\x4f\xfc\x29\xd8\xcc\x6e\xa6\xa5\x12\x47\x18\x3a\xdd\x46\x2f\x51\xa9\x81\x56\xd0\xe7\x32\xeb\x69\x63\x68\xf5\x76\x32\xe2\x33\x98\x8a\x8b\xc1\x80\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x0d\x24\xa1\xd3\x36\x9c\x49\x20\xb6\x5e\x56\xdc\x6e\x1c\x3c\x0a\xc6\x18\x95\xe4\x7e\xb2\x70\x01\xae\x58\x23\xd6\x61\x91\x9b\x59\xbc\xcf\x8c\xb3\xca\x50\xef\x61\x27\xaf\xf0\xe4\xbb\xdb\x59\x0a\x56\xbb\x10\xa3\xff\xae\x4d\x2d\xd9\x0e\x41\xed\x7a\xe4\xad\xa4\xb8\xb9\xa5\xa8\xb5\xe0\xe6\x20\xea\x09\x43\x79\xf1\x46\x18\xca\xcf\xe7\xfb\x46\x89\x18\x5f\xe2\x38\xc1\xfb\x52\xc1\xec\x95\x2d\xae\xd9\x0f\xd9\x67\x27\x75\xe7\x02\xb5\x6d\x01\xfc\x4f\xe7\x3f\x84\xfd\x90\x15\xca\x3a\x98\xcb\x69\xd4\x86\x4f\xf9\xc2\x66\xb6\xf9\x9f\xcb\x67\x68\x03\x7d\x2e\x16\xab\xd3\xc2\x52\xf6\x2e\xc2\x28\xc6\xdf\x8c\xab\x48\x20\xf7\xc2\x01\xf8\x39\x67\xd3\x1d\x90\x37\x07\xc3\x79\x3c\x43\x6a\x87\xc2\xf8\x61\x63\x03\xad\xd4\xe6\xf0\x24\x99\xc2\xe4\xda\x77\x62\xc4\x56\x91\x20\x16\x69\x2f\x13\xfc\x21\x8a\xa6\xd9\x92\xf0\x74\x1c\x3c\x69\x46\x15\x91\x43\xbb\xf1\xf4\xa7\x1d\xb4\xb4\xf9\xb6\xbb\xb5\xbd\xf3\x6e\x77\xef\x9f\xef\x3f\xec\x7f\x3c\x38\xfc\xbf\x47\xc7\x27\x9f\x7e\xfe\xe5\xd7\x7f\xfd\x8f\xdf\xeb\x0f\xf0\xf0\x62\x14\x7c\xfe\x32\x9e\x84\xd1\xf4\xdf\x71\x92\xce\x2e\xaf\xae\x6f\x7e\xaf\xd6\xea\x8d\x66\xab\xbd\xb6\xfe\x6a\x79\x75\x83\x45\xb8\x15\x47\x3b\xb1\x68\x17\x46\x35\x1b\x62\x87\x57\x4a\x66\xb9\xa1\x58\x98\xda\x44\x21\xad\x1d\x9b\x9b\x0a\x99\xe9\xc0\xb1\xdf\x30\xc7\xae\x84\x08\x49\xd2\xf2\xc8\xa8\x49\x76\x60\x41\x2b\xa8\x56\x3e\x03\xef\x95\x4c\x60\xaa\x9b\xc4\xc5\x81\xd6\x8b\x00\x2d\x9f\xf1\x0d\x5e\x16\xc3\x2c\x50\xa9\x40\x14\x2a\x91\x7b\xbe\x12\x61\x06\xd0\xff\x4a\x5b\x94\x7d\x6b\xc2\xfc\xe0\x3d\x88\x0d\xf1\xf2\xb2\xf2\x41\x90\xad\xf8\xc1\x28\xd2\x88\x2d\x69\x0d\x8b\x70\x9b\xe5\xee\xd1\x0f\xf9\xd2\x1e\xf1\xda\x99\xd9\xa7\xf5\x74\xf4\x7f\x3a\xfa\x8b\xa3\xff\xa7\x93\x9d\x95\x5a\x1b\xbd\xdd\x2e\xec\xa0\x55\x6b\xbf\xdd\x96\x7d\xb4\x6a\x6d\xf5\x09\xbe\xde\xdd\x69\x8b\x22\xf3\xe7\x3a\x6e\x15\xc4\xe1\x01\x9d\xb7\x6a\x6d\xa7\xf7\x56\xad\xfd\x37\xd0\x08\x14\x3f\xac\xc3\x60\xdc\xe7\xac\x6e\xf7\xf7\x07\xcb\xa8\x68\x80\x0f\xa3\x20\x4c\x5d\x4e\xc6\xb5\xb6\xc3\xc9\xd8\x7a\x98\xce\x30\x75\x7b\x19\x8b\x26\x8b\xba\x1a\x4b\x40\xef\x71\x82\xd2\x89\xf8\x5e\xce\x6a\x40\x9b\x8b\xae\x8d\xef\xfa\x18\x45\x57\x95\x70\x59\xe3\x8b\x6f\x21\x9f\x35\xa8\xb4\x98\xaf\x31\xaf\x25\xe4\x5b\xfe\xe2\xb1\x3d\x8d\xd5\x86\x8b\x39\x1a\xd7\x40\xf6\x11\x18\xaa\x6e\xc6\x44\x04\xca\x16\x4b\x9d\x2c\x16\x2d\x08\x9b\x9b\xc2\x5d\x52\x8e\x36\x3a\x2f\x8b\x87\xc2\x60\x64\xf9\xa1\xc0\x1e\x26\xed\x53\x1f\xee\xbd\x4f\x7d\xf8\x0e\xf6\xa9\x22\x38\x3c\xf4\x3e\x65\x5d\x4e\x1f\xb6\x9f\xb6\x29\xf1\xf7\x60\xdb\x54\x72\xe5\x4f\xb7\xc3\x41\xe0\x87\xa5\x45\x77\x2c\xdb\x91\xfc\xfb\xdf\xb2\x3e\x3c\xce\x96\x55\x64\x99\x7c\xff\x5b\xd6\x87\x6d\x6d\xd3\x7a\xda\xb1\x8c\x1d\x4b\x5a\x31\x0b\x6d\x5e\xdf\x74\xf7\x12\xf3\x22\x61\x4b\x00\x29\x7d\xe4\xd1\xf0\xe1\x0b\xbb\x3b\xa1\x8b\xbb\x5a\x25\xff\x0f\x17\x2b\xf4\x23\xe9\x3e\xfb\x4a\xbf\x65\xcb\x7f\x9e\xba\x00\x08\xcb\xad\x2d\x68\xdf\x4b\x5b\xc0\x72\xd4\x7e\x4b\xa5\x81\x87\xa4\x57\xc9\xc8\xaf\x69\xaf\x46\x13\xbf\xff\x88\xaa\x05\x0f\xf1\x66\xe1\x17\xb4\xf6\x77\x50\x37\x18\xf9\x62\xef\xa0\x8a\x50\x8c\x58\xa4\x2f\xfb\x5b\x2d\xa8\x09\x26\x37\xfb\x5b\x2d\x9b\x8c\x07\x26\xce\x5f\xf0\x0d\xcd\x82\x4d\xed\x60\x45\x5f\xc1\xf9\xd7\x0f\x53\x9e\xc4\x3b\x8a\x27\xd4\x46\x7b\xfb\xe7\xc3\x73\xd8\x74\x4f\xa2\xf7\x38\x13\x06\xd1\xd5\xd5\x55\x25\x9a\xe2\x30\x49\xc6\x95\x28\xbe\x58\x1d\x44\xfd\x64\x15\x92\x70\x47\xab\x5a\x9d\x51\x3a\x19\x5b\x14\x21\xdb\x97\xd3\xf7\x5b\x3b\x19\xda\xe2\xb9\x60\x30\x84\xf9\x3e\x20\xda\x1e\x67\x78\xbf\xb0\x94\xe7\xb0\x47\x91\x81\x49\xc8\x43\x10\x72\xb7\x17\x29\xdc\x73\xe6\xea\xd2\x44\xa5\x5a\x7d\x5d\xf1\x74\x31\xe0\x3b\x8c\xd4\xe4\xb0\x18\x7a\x82\x94\xfd\xad\xd6\x3c\x6c\x83\x94\xd9\x22\xeb\x41\xaa\xa5\x0f\x69\x84\xa6\xd4\xea\x54\xf6\xce\x71\xec\x70\x86\x5f\x8c\xb6\x3b\xb0\xe1\xe9\xa0\x5a\x7d\x1d\x4c\x48\x95\xaf\xb4\x73\x80\xb9\xf6\x25\xc3\x47\x69\xfb\xf6\xce\x6e\x37\x0e\xa2\x7d\x6c\x3f\x1c\x2c\x35\xfa\x00\x66\xd6\x5f\x06\x43\xc3\xfb\x86\xd2\xfc\x9c\x14\x4d\xf3\x2b\xfe\x91\xcd\xd5\xba\x96\xcf\xef\xae\x60\x3c\x75\x1a\xab\xd5\xaa\x0e\x78\x41\xef\xa0\xb9\x7e\x3f\xc5\xe4\xdd\x2d\x48\xe1\x4f\x68\x84\x50\x05\x24\xc2\xf6\x21\x03\x2b\x59\xb4\x77\xb1\xd2\xe7\x75\x69\x2c\x00\x1b\xa0\x9c\xca\x89\x3f\x4e\xd1\x26\xfc\xb3\xb8\x58\x0c\xd4\x45\xc9\xfb\x21\xc8\x0b\x93\xcd\xe3\xcb\x60\x58\xa1\x6e\x11\xb8\xc4\x3b\xe3\x01\x7e\x39\x79\x6b\xa0\xb8\x92\xdf\x51\xad\xb9\x90\xc0\xab\x4e\xb1\x45\xbc\x25\x2b\x9d\x71\x0f\xb3\xb6\xf0\x52\x23\xe4\xc1\x4c\x94\xb3\xd5\x61\x85\xe5\x72\x0b\x83\xd0\x02\x74\x88\xdf\xc3\xd8\xd8\x52\xa2\x2d\x72\x46\xce\x80\x09\x9f\x60\xf1\xc6\x79\x5c\xe6\x7b\x0c\xed\x11\x7b\xb2\x94\x93\x98\x38\x2d\x9a\xbd\xb0\x60\xf9\x8e\x6d\x4c\x04\xbc\xfa\x91\x19\xb3\x68\xb8\x72\x83\x96\x37\x1c\x1f\xeb\x51\x80\x88\x71\xe0\x39\xe0\xbc\x60\x56\x5d\x96\x68\xd9\xf9\xd7\xca\x48\x0e\xc6\x90\x39\x81\x30\x28\x9c\xd8\x24\xa3\x60\x83\x5e\xd5\xe6\x85\x3f\x9d\x59\x82\xd0\x84\x18\x38\xf3\xb3\x72\x50\xaa\xd1\x83\x92\x34\xd0\xb9\x69\x7f\x34\xec\x05\xb2\xce\x51\xb0\x61\x6c\x19\x2a\xf3\x9d\x44\x56\x2c\x66\x8c\xb5\x0d\x6d\x94\xa5\x5a\x92\x8e\x86\xd3\x9f\x25\xda\x85\x08\x30\xc7\xeb\x15\xb5\xb9\x2e\xc4\x83\x65\xbf\xe3\x3b\xf1\xde\x05\xf9\xee\x03\x7a\xdf\x5a\xfc\xca\xa4\xde\x14\xe7\xe6\x52\x25\x45\xbb\x21\xbd\x57\xb9\x7b\xf6\x01\x29\x5c\x5d\x6c\xda\x74\xbf\x76\x71\xf6\xc5\xaa\x79\xc8\x21\x36\xdc\x07\x4c\xae\xd8\x20\x54\xc8\x99\xac\xef\xda\x73\x4c\x17\x16\x36\xec\xaa\xc4\x02\x8e\x2b\xf9\xfb\xdd\xed\xeb\x9c\xe3\x3b\x85\x66\x3f\xbb\x7b\xfc\xf0\xd9\x69\xad\x7b\xfc\x48\xda\x59\x5b\x23\x67\xfa\xb5\xbf\xf4\x99\xbe\x1f\x4c\x47\x38\x5e\x79\x64\x13\x01\x38\xbd\xcb\x4d\xfd\x39\x87\x78\x33\x73\xe7\x83\x9c\xe6\xbb\xd0\xb1\x43\xc2\x71\x12\x71\x68\x97\x5f\xba\x4d\x08\xc4\x7b\x2d\x13\x86\x52\x83\x9c\xe1\xfc\x14\x2a\xd1\x9f\x9c\x11\xb3\x8a\x3b\xf0\x32\x65\x51\x15\x68\x91\x05\xd2\x69\x90\xd3\x0d\x9d\x9b\x14\x5f\xa7\xe4\x14\xe9\xb3\x67\x34\xa5\x7d\x62\xbe\x59\x3c\xd5\x86\x3f\xc0\xfd\x60\xe2\x8f\xc7\x37\x2c\x0d\xe8\xa0\xf0\xcd\x8d\x3c\x2a\xb7\xac\x15\x36\x70\x27\x02\x0d\xb5\xd9\xc5\x93\x71\xdc\x05\xbf\x47\x4d\xcf\x91\x4d\x89\x74\xab\x23\x77\x7e\xb1\x8b\x1d\xa5\xa6\xc3\x51\x4b\x2e\x53\xc9\x66\x37\x4b\x20\xb1\x8b\xaf\xef\x98\x09\xc2\x32\xbc\x12\xf9\xc8\xf7\x0d\x0b\x4e\xa7\x76\xf3\x10\x84\xd3\x59\x7a\x9f\x39\xe5\xe4\xa1\x12\xdd\x1d\xe8\xec\xa1\x88\xa3\xaf\x31\x0a\x0b\x7d\xdc\x39\xa9\x04\x8c\x96\x3d\x84\x4d\x36\x39\x1b\x28\x6b\x83\x56\x78\x6d\xa5\x9e\xae\x42\x3d\x5c\x23\x90\x01\xea\xc8\x40\x6f\xed\xba\x79\xf7\x4e\x9b\x75\x57\xdb\x6d\xa5\x0d\xa2\xd3\xaa\x7b\x9a\xf2\x7c\xfd\xc9\xd4\xee\xef\xae\xfb\x76\xed\x8e\x46\x24\xf3\x3c\x4d\xb8\x79\x48\x01\x07\x60\xa1\x71\xb5\x26\xa2\x22\x25\x36\x64\x47\xd5\x87\x49\x48\x0f\x2e\xaf\x73\x39\x5e\x61\x25\x71\x41\x55\x14\x91\xd5\xc1\x79\x19\xf7\x63\x9c\x3e\x90\x52\x89\xc8\xbf\xbb\xf6\xc0\x41\xd0\x4b\xc6\x26\x6c\x9e\xc8\xd4\xd1\xb7\xa8\xc6\x50\x76\x0e\x76\x04\x08\xb6\xea\x8c\x84\xbe\x88\xfa\x28\x88\x47\xdd\xc3\x3d\xc7\xdb\xed\x21\xe3\xcb\xc2\x81\x69\x4e\x78\x59\x7a\xa8\x92\xa2\xcb\xea\xe3\x64\x37\xc4\xcf\x51\x4c\xd1\x8e\xbe\x95\xe2\x62\xb2\xae\xe7\x45\xc6\xd4\x2a\x71\x7d\x81\x0e\xcb\x1e\x25\x73\x73\x3c\x8e\xae\x90\x1f\xf7\x82\x34\xf6\xe3\x1b\xc4\xd4\x4b\x5f\xf0\x8d\x25\xee\xe0\x17\x59\x23\xf1\x93\xb5\xe1\x9c\x81\xd2\xd5\x2d\xc5\x46\x6b\x8e\x33\x24\x41\x29\xc7\x0d\x12\xe2\xbf\x81\x6e\x23\x8a\x51\x10\x86\x38\x86\xe8\xb3\xd1\x2c\x05\x01\x42\x8f\xc2\x07\x31\x13\xa9\x8e\x91\x92\x21\x7b\xa0\xad\x18\x01\xe9\xb8\xc6\x4f\xae\x11\x58\x6a\x2c\x42\x02\x91\xa4\x95\x8c\xf2\xf4\x91\x81\x54\x30\x90\x0a\x1a\x8d\xfd\x7a\x70\x04\xf3\x49\xaf\x01\xa7\xfe\x00\xf5\xa3\x30\x49\xfd\x50\x6f\xde\x9a\x44\x4a\x9d\x63\xb7\x62\x4d\xe0\x7d\x1a\x9c\xa1\xdf\x36\x50\xf5\xba\xd5\xa7\xff\xb3\xb9\xc3\x18\x85\x1b\x6d\xfa\xbf\x7c\xcd\x58\xa4\xe9\xc4\x02\xed\xd9\x46\x91\x7f\x42\x1c\x32\xd8\x81\x1e\x23\x0a\x99\x60\xe2\x0f\x12\x89\x2c\x27\x5f\x99\x8d\x19\x5b\x06\x12\x3a\x6d\xe3\xe3\x0e\x3d\xa9\xaa\x2f\xce\x16\xcc\xdd\x22\x90\xc1\x30\x7f\x37\xf1\xc7\xf6\x37\xbb\x2c\xfa\x18\xe0\x15\xc0\x12\xcb\x8d\x84\xb2\xe0\x94\x17\x09\x44\x66\x94\x7e\xf8\x60\x64\x32\x49\xf0\x56\xe6\x06\x1f\x7b\xac\xe8\x61\x30\xd4\xff\xe9\xd1\xc3\xe6\x88\xa9\x8b\x88\x88\x84\x87\x66\x34\x34\x37\x82\x98\xbb\xc6\xdc\x28\x62\xee\xaa\x8f\x14\x49\xec\xfe\xdc\xae\x4b\xd5\xd3\x30\xde\x96\xfd\x98\x48\x17\xbb\xf6\xe0\x68\xb9\x01\xc7\x72\x39\xa6\x3c\x56\x1a\xd0\x4c\x42\xe1\x92\x06\xbf\x64\x12\xa8\x94\x9d\x21\xc7\x26\x7e\xdf\x7e\x49\x24\x0e\xfe\x0e\x23\xb8\x57\x7f\x69\x85\xf9\x75\xbb\xb9\x62\x79\x3d\x0e\x7a\x2b\x04\x95\x01\xd8\xb6\x26\xda\x57\x1c\xf6\x57\xc0\xa6\xd1\xf2\x9e\xba\x59\x6a\x1f\x26\x83\xd6\x7c\xe3\xbb\x64\xe4\xd7\x5b\x3a\x48\xf2\xb2\xae\x83\x4b\x46\x7e\xab\x56\x37\x5f\x36\xd6\x2d\x25\x1b\xda\xab\x38\x98\xe2\xc9\xa0\xd6\xae\x5a\x6d\xff\x94\x57\xd3\xde\x97\xc1\x50\x6f\x07\x5f\x4e\xbf\x0c\x86\x79\xf7\x0e\x6a\xd7\xa3\x01\x5e\xe9\x0f\x7b\xd6\xd7\x69\xec\x78\xbd\x72\x31\xf6\x07\x13\x3f\xb4\x7d\x8e\xec\xc0\x70\x5f\x7f\x3d\xf5\x07\x2b\x7e\x98\x04\xd7\xaf\xea\xfa\x20\x90\x4f\x41\x12\xd5\xaa\xb5\xba\x3e\xe2\xec\xd3\xab\xb5\x57\x6b\xfa\x0c\x91\x4f\xbf\xe3\x38\x62\xae\xd7\x96\xaf\xa1\xe3\x1b\xd5\x91\xad\x8c\xf0\xb5\xf6\xc1\xc7\x3a\x71\xd1\xb8\x1b\x03\xe3\x7d\xdc\xd7\x27\x37\xf6\x7b\xbd\x20\xb5\xbe\x5c\x19\xe3\x0b\xbf\x7f\xf3\xd8\x77\x40\x62\xf5\xc0\x93\xbe\x68\xe0\x65\xb6\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x66\x16\xca\xd6\x81\xf8\x5d\x6f\x8a\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x95\x91\x76\x66\x5f\x0a\xbf\x18\x21\x53\x0c\x28\xfd\x1a\x77\x58\x14\x1d\x4e\xad\xd2\x53\x1a\xab\x4f\x82\x36\xb3\xb7\x91\x52\x83\x50\x22\x6d\x56\x26\x40\xf1\x46\xd0\x9d\xfc\x86\x92\x9b\x78\x23\x53\x99\x78\x19\xaa\xaf\x24\x9a\x82\x67\x42\x4a\xf0\x23\xa3\x20\x3a\x2a\x7d\x36\x50\x8c\x5e\xa4\xdf\x9c\x4c\x16\x55\x44\x2a\x0a\x48\x99\xd7\x2e\xae\x98\x74\x87\x62\x63\x5d\xea\xb4\x6a\x5e\xbe\x36\xd9\x53\xe9\xaa\xd3\x6a\x7a\x0a\xe1\x75\x5a\x2d\x2f\x9b\xf8\x4e\xab\xed\xa9\xa3\xd7\x69\xad\xe9\x37\xc2\x3a\x29\x77\xda\x55\x8f\x51\x6b\xa7\x0d\xf8\x08\x4a\xe9\xb4\xeb\x9e\x4c\x2b\x9d\x76\xd3\xb3\x51\x4b\xa7\xdd\xf0\x64\x0a\xe9\xb4\x5b\x9e\x4c\x3f\x9d\x36\xe0\xa5\xd0\x4c\xa7\xbd\xe6\xe9\x54\xd3\x69\xaf\x7b\x3a\xdd\x74\xda\xaf\x3c\x83\x48\x3a\x6b\x55\xcf\x42\x4e\x9d\x35\xc0\x9f\x2d\x89\xce\x1a\x60\xcf\x48\xa3\xb3\xd6\xf4\x0c\xe2\xe8\xac\x01\xe2\x84\x8c\x3a\x6b\x80\x73\xb6\xce\x3a\x6b\x6d\xf9\x02\xdd\xcb\x96\x6c\x67\x8d\x5f\xad\x93\xc5\xdc\x59\x7b\xe5\xf1\xa5\xda\x59\xaf\x7a\xd9\x12\xee\xac\xd7\xbc\x6c\x71\x77\xd6\x01\x9d\x8c\x82\x3b\xeb\xd0\xb8\x60\x34\x9d\xf5\xe6\xed\x99\xd7\xae\x3e\x5d\x1e\xfc\xf9\x97\x07\xdd\x11\xee\x7f\x21\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x64\x36\x25\x03\x83\x59\x7c\x6a\xa9\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xb4\xc4\x21\x2f\x59\x2c\x42\x84\x93\xc6\x03\x5e\x57\xe4\x9a\xe3\x8b\x76\x8e\xf0\x10\xc7\x18\x0e\x7a\x71\x70\x01\x67\xb2\x20\x0c\xd2\x0c\x4c\x32\x9b\xe2\x18\x54\xd7\x1b\x5a\x7a\x0e\x09\xca\xe6\xec\x62\x82\xc3\x54\x2b\x80\xd2\x08\x8d\xfc\x70\x30\xc6\xca\xb8\xc9\xb0\x7b\x56\xc8\x8a\x4d\x0d\x54\x35\xdd\x01\x25\xdd\x37\x8d\x25\x4f\x4d\xa0\x82\x30\x5d\x97\x34\xf4\x43\xb9\xbe\x50\x4c\xa8\xb3\x63\x1e\xf3\xb3\x1a\x54\x09\xff\x89\x40\x85\x17\x32\x36\xca\x21\xc2\x8a\x58\x44\xd3\x7f\x01\xa4\xcb\x00\x5f\xb9\x50\x74\x36\x2f\x21\xbc\xc7\x51\x40\x5f\xbf\xaa\xe5\x39\xc1\x01\x96\xa0\x33\xe6\xd5\x7f\x20\x6b\x4e\xd8\x8e\xc0\xa2\xb3\x03\x37\xaa\x96\x8d\x56\x9c\x58\xd5\xda\x76\xb4\xdc\x2d\x2d\x56\x63\x2f\x4c\x1b\xf5\x45\x9b\x58\xac\xc6\xce\x38\xf2\xef\x52\xa5\xdd\x84\xf7\x59\xf9\x3b\x92\x52\x85\x52\xb0\x87\xe4\x57\x37\x29\x3e\x80\xe4\x40\xc6\x6b\x5b\xde\x65\x85\xfe\x76\xe9\xa2\xcb\xda\x2a\xb2\x22\xb2\xd2\x8b\xa9\x10\x32\x68\x6f\x05\x6e\x68\xc3\x8e\xb3\x45\xb3\xb0\x7d\xcd\xb2\xaf\xde\xa4\x36\xe3\xe7\x85\xdc\x05\x6d\xa8\x2c\x92\x4f\x3b\xab\x7f\x1a\x9c\xdd\x29\x79\x76\x66\xce\x1d\xfc\x8e\xa9\xaa\x36\x73\x1c\x55\x8b\x0a\xc6\x9a\xa5\xb6\xf0\x10\x73\x23\xb4\x75\x44\x99\x6f\x6b\xd6\x33\x32\x9a\xe4\x35\x81\x87\x42\x22\xf5\xc9\xcc\xdc\x6c\xd7\x9f\x4e\xc7\x37\xac\x61\x3f\xbe\x98\x11\x16\x9e\xe4\xf9\x2b\x32\x7e\x5d\x99\xc6\x51\x1a\x11\x1c\x65\xce\x9d\x67\x38\xa1\xef\x3e\x76\x05\x4b\xbb\xf6\x24\xeb\xfc\x39\xb2\x0e\x04\x8c\xfe\x13\xe2\x12\x59\x73\x2a\x15\x30\x91\x80\x2d\x96\xde\xe3\xa1\x34\xd3\xad\x93\x2a\x27\x8c\x59\x48\x25\xa9\xea\x52\xbb\xf9\xb3\x49\x7a\x2e\xbe\xd2\x6e\xda\xb9\xc8\x09\x61\x13\x1b\x74\xf8\x2a\x7e\x2f\xa1\x3f\x92\x20\x64\xc1\x58\x09\xcb\xa8\x5e\xd7\xaa\xec\xaf\x8c\xbe\xaa\x69\x7c\xd9\xf2\x2a\x95\xad\x16\xea\xfb\x5b\x2d\xcd\x9a\xc2\x66\x00\xa2\x7b\x4d\xa2\x0d\x36\xaa\x16\x03\x10\x9e\xf6\x26\xf7\x76\x2c\xd3\x04\xdb\x73\x15\x9f\x9a\x9c\xb4\x7a\xdd\x5e\x6b\xb6\xea\x8d\x6a\xcd\x43\xd5\x6b\x3c\xec\x0f\xfc\xde\xfa\x2b\x4b\x5e\xc5\xea\xf5\xab\xf5\x9e\x3f\xe8\x0f\xb1\x07\x03\xd3\xa8\xb7\x9a\x6b\x6d\xb5\xdc\x99\xf3\x46\x4c\x4b\xa3\x27\xf7\x62\x5f\x64\xd2\xb3\xed\x5d\x57\xfe\x14\x61\x70\xaf\x9e\xbf\x87\xd4\xda\xee\x1d\xc3\x7d\x7d\xcd\x67\x83\x22\x71\x4e\xe0\xf1\xf4\x82\x28\x70\x44\xe0\xdd\x3f\x97\x4a\xef\x9f\xf2\x87\x33\x9b\x4b\x88\xf4\x99\x10\x9c\x59\x80\xfc\x95\x4a\x25\x09\x26\xf5\x14\x47\x5f\x91\xfc\x12\xf6\xba\x66\x59\xf3\x11\x47\x5f\x0b\x02\xac\x37\xcb\x16\x80\x10\xca\x58\x71\x49\x37\xc1\xdd\xcf\x38\x64\x57\xb9\xa1\xb0\x5f\xf7\x2b\x43\x5a\x45\xd2\x98\xa2\x65\x54\xd5\xc5\x07\xa5\x74\x4d\x2b\x5d\xcb\x2d\x5d\xd7\x4a\xd7\x73\x4b\x37\xb4\xd2\x8d\xdc\xd2\x4d\xad\x74\x33\xb7\x74\x4b\x2b\xdd\xca\x2d\xdd\xd6\x4a\xb7\x73\x4b\xaf\x69\xa5\xd7\x72\x4b\xaf\x6b\xa5\xd7\x73\x4b\xbf\xd2\x4a\xbf\xca\x9f\x9d\xaa\x36\x3b\x73\x26\xb3\xa6\x15\xcf\x9f\xcd\x5a\x5d\x2b\x9e\x3f\x9d\xb5\x86\x56\x3c\x7f\x3e\x6b\x4d\xad\x78\xfe\x84\xd6\x5a\x5a\xf1\x96\xc1\x0d\x56\x57\x09\x43\xfe\x12\x84\x17\xa4\x6a\xe0\x8f\x7b\x36\xb1\xd9\x27\xdb\xc0\xa9\x75\xa0\x7a\xf0\xc9\x3a\x28\x7d\xf8\x64\x1d\x80\x01\x7c\x6a\xd8\xd0\xe9\x66\x77\xd0\xea\x37\x82\xc4\xce\x4e\xc9\xf7\x50\xcf\x43\x7d\x0f\x0d\x3c\x69\x81\x7a\x08\xad\x79\x64\x0b\xad\x9e\xe9\xbc\x61\x40\xeb\x0d\x3c\x24\xaa\x66\x23\xe4\x21\x54\xab\x7b\xe8\xe4\xb4\x66\xd4\xeb\xd3\x7a\xb4\x25\x5a\x35\x5b\xb4\xa4\xde\x1a\xa9\x57\x37\xea\xf5\x68\x3d\x81\xa4\x2f\xd5\x6b\x78\x08\xd5\xa1\xbd\x86\x51\x2f\xaf\x7f\x4d\xd1\xbf\xe6\x42\xfd\x6b\x89\xfe\xb5\x16\xea\x5f\x5b\xf4\xaf\xbd\x50\xff\xd6\x44\xff\xd6\x16\xea\xdf\xba\xe8\xdf\xfa\x42\xfd\x7b\x25\xfa\xf7\x6a\xa1\xfe\xd5\xaa\x1e\xeb\x5f\xcd\x24\x98\xbc\x0e\xd6\x6a\x1e\xeb\x60\xcd\xa4\x98\xbc\x1e\x12\x2c\x69\x0f\x6b\x26\xc9\xe4\x92\x68\xc3\xe3\x24\x6a\xd2\x4c\x6e\x1f\x9b\xa2\x8f\x26\xd1\xe4\xf6\xb1\x25\xfa\x08\x54\x63\x76\xf2\xdd\x3b\x47\x27\x3d\x84\x5a\xb4\x93\x26\xdd\x0c\x68\x45\x6b\x27\x09\xbd\xbd\xa2\x15\x4d\xc2\xe9\xd3\x8a\xf6\x4e\xd6\x3c\x44\x3a\x7a\x72\x5a\x33\x29\xa7\x47\x2b\x5a\x3b\x49\x38\x46\xbd\x0a\x15\x4d\xd2\xc9\xeb\x63\x4b\xf4\xb1\x6e\xe7\x35\xae\x3e\x12\x9a\xa3\x7d\xac\xdb\x99\x8d\xb3\x8f\x2d\xde\xc7\xba\x9d\xdb\xb8\xfa\xd8\x14\x7d\xac\xdb\xd9\x8d\xab\x8f\xaf\xb2\x3e\xda\xf9\x8d\xb3\x8f\x4d\xd1\x47\x3b\xc3\x71\xf5\x91\x30\x46\xd6\x47\x3b\xc7\x71\xf5\x71\x3d\xeb\xa3\x9d\xe5\x38\x69\xb5\xe1\xf1\x3e\xda\x79\x8e\xab\x8f\x75\x41\xab\x75\x3b\xd3\x71\xf5\x71\x4d\xf4\xb1\x61\x67\x3a\xae\x3e\x92\xe5\x4f\xfb\xd8\xa8\xd9\x17\xe4\xee\xae\x9b\x58\x9b\x80\x6b\xc3\xce\x75\x76\x77\xed\x9d\x24\xc3\x4a\xd6\xd6\xc9\x69\xc3\xce\x75\x76\x77\x73\x16\x64\x1b\x2a\xda\xb9\xce\xee\xae\xa3\x93\x4d\x0f\xd5\x1b\x50\xd1\x24\x9d\xbc\x3e\xd6\xb2\x3e\xda\x99\x8e\xab\x8f\xcd\xac\x8f\x76\xa6\xe3\xea\x23\x4c\x24\xed\xa3\x9d\xe9\x38\xfb\x58\x15\x7d\xb4\x33\x1d\x67\x1f\x1b\x1e\xeb\x63\xd3\xce\x74\x5c\x7d\xac\x8a\x3e\x36\xed\x4c\xc7\xd5\xc7\x86\xe8\x63\xd3\xce\x74\x5c\x7d\x24\xac\x9c\xf6\xb1\x69\x67\x3a\xae\x3e\xbe\x12\xf3\xd8\xb4\x33\x1d\x57\x1f\xc9\xf2\x60\x7d\xb4\x33\x1d\x27\xad\xb6\x38\xad\x36\xed\x4c\xc7\xd5\xc7\x7a\xd6\xc7\x35\xfb\x82\xdc\xdb\x73\x0b\xaa\x6d\xda\x49\x3b\xd7\xd9\xdb\xb3\x77\x12\x68\x0e\x78\x40\xd3\xce\x75\xf6\xf6\x72\xc4\x80\x16\x88\x80\x76\xae\xb3\xb7\x67\xef\x24\xe1\x1d\x75\x18\xd6\x96\x5d\xd4\x71\xf5\x91\xcc\x07\xed\x63\xcb\xce\x74\x5c\x7d\x6c\x88\x3e\xb6\xec\x4c\xc7\xd9\xc7\xaa\xe8\xa3\x9d\xe9\xb8\xfa\x58\xcb\xfa\x68\x67\x3a\xae\x3e\xae\x8b\x79\x6c\xd9\x99\x8e\xab\x8f\x40\x73\xb4\x8f\x76\xa6\xe3\xea\x23\x88\xe4\xb4\x8f\x76\xa6\xe3\xec\x63\xc3\xe3\x7d\xb4\x33\x1d\x57\x1f\x9b\xa2\x8f\x6d\x3b\xd3\x71\xf6\xb1\xc6\xfb\xd8\xb6\x33\x1d\x57\x1f\xeb\xa2\x8f\x6d\x3b\xd3\x71\xf5\xf1\x95\x98\xc7\x76\xc3\x5c\x90\x70\x8d\x92\xe2\x78\x82\x07\x81\x9f\x32\xa7\x32\x70\x57\x50\xcb\x91\x23\x2e\xda\x40\x25\xf8\x77\x19\xf9\xba\x86\x95\x96\xa9\xb1\x32\x35\x52\xa6\x67\x2f\x53\x67\x65\xea\xa4\x4c\xdf\x5e\xa6\xc1\xca\x34\x48\x99\x81\xa1\xcd\xd5\x54\x95\x3b\x16\x4b\xdd\x05\x03\xda\x42\xa6\x74\x91\x4d\xd7\x4f\x7d\xdb\xc1\xdc\x4f\x7d\x11\xca\xc7\x4f\x7d\xb7\x72\x2c\x7c\x1b\xa4\xc9\x49\x94\xfa\x63\x01\x33\xdc\xf2\x53\x9f\x7a\x90\xbc\x44\xeb\x16\xe8\x50\xe7\x03\x1e\xa6\x1c\xba\xf0\x38\x81\xf2\x46\x67\x9c\x29\xaf\x04\x9a\xa7\x19\xc8\x9f\x7e\xfa\x09\xb5\xe0\xe2\xad\x7a\xbd\x5e\xcd\xee\xdb\xb2\x12\xff\x40\x8d\xba\x41\x1c\x6a\x5f\x76\xd1\x06\x02\xb5\xfb\x70\x1c\x45\x71\x49\xea\xe4\xaa\xa2\x7b\x77\x75\x0e\xca\x7e\x40\x1b\xd2\x93\xbe\x70\x04\xea\xa5\x52\x29\xc3\x6d\x19\xb5\x9b\x34\x5f\xda\x2b\x08\x26\xda\x2c\x53\x85\x8d\x5d\x3f\xcb\xab\x32\x9c\x33\xe5\xac\xfc\xb6\xb8\x76\xd6\x04\xc7\x54\xb3\x3a\xb8\x79\xba\x59\x83\x4b\x2c\xd2\xd9\x66\x91\xce\x7e\xb0\x76\xf6\xc3\x5d\x3b\xfb\xc1\xda\xd9\x0f\x45\x3b\x6b\xf6\x56\x76\xa2\x2a\x89\xee\xf3\x60\x53\x90\x53\xcf\xee\x3f\x08\x06\xef\xd4\x8d\x01\x7c\x14\x6d\x9e\x54\xb9\x79\xe5\xe7\x78\x43\x2a\x3a\x6f\x0b\xf9\xee\x32\xc3\x78\xa7\xf7\xdb\x42\xf7\x1e\x8e\x2b\x2e\x94\x77\xfd\x2f\x30\x81\x2b\x8c\xdd\x53\xfb\xdd\xc5\x2e\xbb\x25\x2b\x95\x76\x95\x6b\x89\xdd\x85\xef\x23\x28\x2d\xec\x2a\x77\x11\xbb\xce\x4b\x88\xf9\x37\x0e\x47\x2c\x37\x30\xcc\x21\x8b\xc0\x33\x80\x31\x55\x8b\x16\x48\x56\x0e\x6e\x08\xb9\xac\x1e\x14\xac\xe0\x94\x29\x6e\xe8\xe0\x31\xbb\xfe\x37\x36\x5e\xf8\x7c\x6e\xd0\x82\xcb\xbb\x92\x47\xd0\x20\x5f\xed\x1e\x0e\xf4\x97\x40\x52\x53\x7d\x5d\x7b\x28\xf1\x90\x7a\x85\x06\x7c\x12\x6d\x20\x1f\x2d\xa3\x52\xa9\x87\x7e\xa4\x9b\x63\xe9\x7f\xc9\xcf\x41\x99\xb0\x81\x6b\xb4\x8c\x52\xa9\x3d\x11\xb0\x38\x24\xd3\x94\xd0\x95\x4a\xe3\x94\x37\xea\x68\x05\x25\x65\xa8\xd6\xd3\x8c\xde\x04\x56\xda\xf9\xbf\x18\x56\xb0\x1d\x97\xfa\xe8\x47\xf4\xbf\x8f\x83\x95\x76\x08\x9a\x8b\x55\x0f\xfd\x86\xfa\xe8\x37\x82\xd8\xc3\x23\xa3\x09\x80\x73\x91\x21\x88\x94\x7a\xe8\xeb\x03\x0f\x8e\x7c\x5b\x7d\xec\x4a\x93\x3e\x37\xf1\x7e\x91\x20\x6b\xdc\x4f\x4c\x73\x51\x84\xd5\x60\x82\x71\x38\x8b\x39\x4a\xdf\x35\xac\x19\x5b\x97\xc2\xc8\x65\x7f\xab\x65\xf1\xfd\xca\x2f\x6f\x3a\x7c\x65\xf1\xc5\x94\xcb\x7c\x35\x23\xff\xfe\x56\xcb\x6a\x32\xe0\x9c\x84\x39\xb9\xea\x1f\x6a\x0a\xee\x14\xda\x61\xfe\xc4\xc9\x5e\x7e\x0f\x31\x71\xd4\xa9\x4c\x4c\xc4\xee\xc4\xef\x93\xc9\x50\x32\xc3\x9b\xf3\xc1\x8a\x99\x73\x92\x65\xb3\xa7\xf3\x92\x9b\x81\x9d\x45\xb6\x76\x58\x40\xd5\xff\xd2\x2e\x66\x7f\xff\x98\x6c\x74\xb1\xbd\x64\x71\x86\xd0\x0e\xc6\x83\x9e\xdf\xff\xc2\xe2\x6a\x4e\xa2\x01\x2c\x29\x42\x33\x62\xbe\xe1\x65\x77\xe7\x2d\x11\x81\x2c\xe2\x01\x98\x39\xc1\x57\xc5\x5a\x0e\x2c\x5c\x68\x2b\xfb\x04\x00\x33\xe6\x11\xab\xbe\xbb\xf3\xb6\xb2\x1d\xd2\x58\xe5\x60\x40\xb5\xf3\xd6\x62\xf0\x33\x75\x98\xcb\x30\x33\xc3\x1c\x93\x19\xb7\x68\xca\x42\x50\x71\x81\x84\x3e\xda\xee\x99\xa5\x50\x1e\xb4\x90\x1c\xca\x43\x2d\xcf\x63\x94\xbf\xc7\x37\x49\x1a\x63\x7f\xb2\x19\x0e\x58\xef\x2c\xd6\x91\x11\x33\x8b\x15\xe0\x3c\xd6\x80\x4d\xc8\x3e\xc2\x13\x0c\x41\xc6\xc1\x18\x93\xce\x13\x8b\x95\x09\xfe\xf3\x21\xbe\x4e\xe9\x6b\xbb\xf8\x8e\x2f\xdf\xb2\x98\xa9\xd0\x7a\x25\x19\x07\x7d\x5c\xe2\x28\x88\x9b\x7a\x81\x8b\xcd\x7e\x52\x99\xb5\x2d\xfc\x77\x99\xb5\x7b\x8c\x2e\x18\x0e\x8f\x82\x64\xe1\xb1\xfd\x66\x74\x73\x92\x75\xa8\x87\xfb\xd1\x84\x79\xdd\x13\x82\x08\xa2\x59\x52\x8c\x64\x44\x17\x0b\x89\xe3\x39\xbd\x29\xcd\xed\x82\xe6\x1b\x61\x1e\xd8\xe0\xbc\x77\x99\x05\x6b\xb9\x7c\xad\x1a\x8d\xcb\xe1\x98\x69\xf3\xd9\x67\xc8\xec\x7a\x69\x3d\xd2\x88\xd2\x68\x03\x05\x97\x6c\x0a\xab\x8e\x95\x18\x5d\x62\xb4\xf7\x33\x9c\x3f\x93\x59\x2f\xc1\xff\x9e\xe1\x30\xcd\x39\x3d\x03\xbe\xc2\x81\x61\xae\x01\xb4\x8e\x8f\x36\x21\xe6\x24\x90\x3f\x46\xe5\x98\x0e\x34\x14\x2c\x09\x20\x1e\x52\xbb\xb2\xba\x8a\xd8\x8c\x64\xef\xac\xd9\x72\xf3\xa3\xc6\x50\xd3\xf3\xcc\x42\x10\x22\xc1\x88\x46\xe1\x1c\x6d\xd0\x0b\xc3\x82\x8b\x13\x3b\x6f\xf3\x0c\xae\xf9\xa6\xb3\x48\x9c\xba\x76\xe3\x49\xf8\xf8\xde\x85\x0f\xf4\xdf\xd3\x18\x27\x38\xbe\xc4\x54\x0c\x89\x66\x44\x94\x97\xc4\x0f\x50\x63\xf8\x69\xd0\x1b\x33\x0e\x8c\xb6\x62\xf4\x36\x0e\xfc\x10\xbd\xa3\xee\x99\x68\x18\x8c\x31\x0e\xfb\x95\x3e\x80\xe0\x21\x9f\x21\x02\xb6\x46\x3f\x27\x47\x50\xe4\x9f\x7e\x88\x76\xe3\x59\xef\x06\x7d\x1e\x91\x7f\x2a\x57\xb8\xf7\xdf\x17\x13\x3f\x18\x57\xfa\xd1\xc4\x2e\xef\x9c\x1c\xf1\xe6\x72\xc4\x1e\xb9\x50\x61\xe9\xe7\x59\x96\xef\x25\xec\x93\x83\x02\x4d\x99\xf4\xfc\xd9\x33\x32\xe8\x40\x7a\x22\x1d\x12\x28\x89\xa8\x52\xa8\x0c\xb3\x4e\x7f\xfd\x81\x56\x57\xa3\x4b\x1c\x0f\xc7\xd1\x15\xa9\x03\x1b\x5f\x8d\xa7\x03\x25\xf5\x6a\xed\xf2\x8f\xa4\xec\x6b\xf1\xb9\x2e\x7f\x5e\xd7\xbf\x36\xd8\x1e\xc6\x1a\x03\x3c\x01\x15\x02\x56\xb4\xbb\xba\x8a\x78\xb3\xa8\x57\x23\x45\x00\x65\x68\xba\xfa\x5a\x54\xa9\x67\x55\x44\x99\x67\x80\x00\x2d\x44\x4b\x35\xd4\x52\xac\xd8\x33\x40\x85\x95\xbb\x85\xff\x12\x82\x94\x4b\x2c\x2f\xf7\x1a\xd2\x77\xf8\x0f\x2f\x43\x8b\x2c\x2f\xf7\xea\xaf\x9f\xbb\x0b\x2c\x2f\xf7\x6a\xec\x3b\xf9\x2f\x74\x9c\x37\x0a\x0f\xcb\x1b\xd0\xf3\x37\x6f\x58\x3e\x48\xf9\x75\x9d\xaa\x00\x95\xb7\x0c\x21\xb3\x25\x51\xad\x7a\x5d\xad\x31\xad\x5f\x56\x94\x71\x3d\x52\x88\xbc\xbc\xd5\xa9\x83\x2d\x8f\x52\x9f\xfe\xab\xd2\x08\x7b\x49\x6f\x90\x38\x29\x65\x2f\xcb\x8c\x60\xa4\x29\x58\x5d\x45\x64\x97\x80\x9b\x18\x14\x48\x0b\x89\x2e\x1e\x63\xa5\x2d\x25\x08\xe0\x25\x28\x0a\xc7\x37\x74\x39\x6e\xfd\x72\x70\xb4\x85\x3e\xa3\x37\x68\x1d\x60\xf2\x06\x6b\x36\x2c\xe8\x5d\x9c\xda\x59\xf6\x8d\xf7\x97\xaf\x25\xe5\x2c\x20\xd6\x55\xc5\xf1\xfa\x4f\x94\x39\x17\x15\x39\x8d\xe2\x9a\x0c\x63\xb6\xca\x78\xa2\x68\x96\x0f\x98\x81\x7a\x9e\xc4\x83\xdc\x52\x0f\x08\x0d\xf6\x46\xf2\x65\x20\x74\x07\x39\x08\xcd\x97\x85\xb8\x74\x40\x08\xdb\xa4\x79\xca\x8a\x9e\xe9\xa2\x11\xfb\x2c\xe1\xaa\xaa\x9e\x17\x11\x8a\x90\x43\x30\x42\x77\x13\x8e\xd0\x82\x02\x12\x52\xe5\x39\xf3\xd0\x95\xd1\xbd\x7c\xf6\x12\x4b\xe3\xb5\x26\x59\x89\xe2\x92\x80\xe5\x14\xb1\xa4\xc2\x0b\x48\x5a\xcd\x27\x49\xeb\x7b\x97\xb4\x1c\xf2\x95\x43\xbd\x73\x72\x94\x2f\xe7\x2c\xaa\xde\xb1\xb0\x74\x9d\x97\x3f\x31\xf1\xbf\x1f\x13\xcf\x3d\xcd\x3e\x02\xcb\xde\x0b\xfb\x31\x86\xc8\x0d\x0c\xb8\x06\x92\xc9\x21\xd9\xe4\xae\x20\x6a\x4c\xe3\xf8\x02\xb7\xe5\x5f\x51\xf5\x2f\xb5\x39\x14\xdd\x15\xe6\x9f\xb7\x49\x99\x05\x76\x81\xd6\xd3\x2e\xf0\x97\xd8\x05\xb6\xc7\xb8\x9f\xc6\x51\x18\xf4\x51\x37\x1a\xe0\x5e\x14\xcd\x57\xf8\x6f\x77\xf3\x14\xfe\xf4\xeb\x42\x3b\xc2\x76\x57\x55\xf8\x93\xe7\x87\xda\x01\x64\xd6\xae\x32\x10\xb5\x5e\x9e\x16\x93\xe0\xa3\x2c\xa4\xc7\xc2\x6f\x80\xef\x84\x1f\x4f\xbd\xd4\x9d\xaf\x37\x83\x32\x0b\xac\xe3\xbf\x76\x72\xe4\xff\x9c\x75\x7c\x30\x4b\xa7\xb3\xb4\xf8\xa5\xdd\x41\xee\xa5\xdd\xc1\xe2\x97\x76\xba\x54\x77\xa0\x5d\xe2\x1d\xfc\xb9\xd7\x41\x8f\x2e\xd5\x99\xba\x79\xf1\xe6\x61\x25\xbb\x9c\x86\xbe\x17\xe9\xee\xef\x74\xc2\x3e\xd0\xae\x35\x5d\x42\xd4\x41\x81\x4b\x8b\x83\x05\x2f\x2d\x9e\xb2\xd8\xfd\x35\x98\xef\xe6\xc7\xe3\x3d\xf4\x6b\xe5\x55\xbd\xc1\x0d\xc4\x51\x92\x92\xe5\x7d\x71\x63\x70\xdf\xa9\x3f\xa8\x6c\x86\x49\xf0\x2b\x29\x2d\x72\xc1\x4d\xfd\x81\xcc\xfe\x06\x7e\xea\x4b\x17\xa1\xae\x0b\xd0\x44\xbd\x01\x25\xb5\x8e\x33\x83\x5f\xc5\x00\xf8\xb5\x5a\xb4\xa7\xa7\x15\xe9\xb9\x12\x8a\x00\x51\xcc\xc2\x54\xf4\x4c\x0b\x66\x05\xb6\x78\x87\xf4\x9b\x01\x8c\xbe\x58\x51\x31\xfb\x87\xf6\xdd\x68\x8d\xc6\xb4\x19\xfb\x09\x8d\x9c\x85\xa6\x51\x12\xa8\x1e\xf8\xa4\x51\xf2\x9d\xd4\x3f\x8c\x78\x67\x45\x0b\xcb\x1a\x46\x2b\xa8\xa6\x35\x72\xe8\x0f\xb2\x67\x18\x28\x91\x6d\x44\x7d\x4d\x59\x89\xdc\x56\x16\x52\x4b\x6d\x24\x0b\xa9\x25\x97\xb6\x05\xd7\x52\x2d\xb3\x97\x35\x40\xdc\x0e\x91\x5b\xe0\xce\x42\x0b\x71\xe8\x14\xf1\x0e\xa7\x52\xc2\x79\x65\xaa\xa8\x02\x5f\x8c\x66\xfe\xcc\x49\x7d\x2e\xa9\x68\xae\x90\xe3\x2f\xeb\x7b\x76\x11\x24\xa1\xc0\xf6\x15\xc3\x43\x42\x03\xe3\xe8\xed\xf3\x67\xb7\x56\xbe\xc9\x97\xcb\xf5\xab\x7a\x63\x21\xde\x79\xbf\xc4\x64\x4f\xbc\xf3\x5b\xf1\xce\xbd\xe3\x03\x04\x21\x71\x8b\xb1\xce\x3d\x16\x40\xf7\xbe\xac\xf3\x4f\x67\x87\xd9\x92\x98\xc3\x0f\x2d\xac\x8a\xa6\x03\xb0\x47\xa0\xab\xc4\x7e\x38\x88\x26\x25\x83\x03\x96\xcb\x15\x4d\x52\xca\x87\xc3\x52\x87\x9d\x1a\x5c\xae\xde\x3c\xf3\x08\xb8\x27\x46\xa5\x33\x2a\x4e\x9c\x0b\x31\xaa\xbf\x76\xe6\x85\xff\x28\x46\xb5\xba\xb7\xdd\x45\xaf\xd6\x5e\xad\xad\xd4\x10\xa3\x0d\xb4\x8f\xd3\x51\x34\x40\x75\x17\xb7\x82\xd0\xde\x77\xe5\x56\x9b\x83\x01\xf5\x1f\x54\x17\x44\x01\x2e\xc0\x57\x2f\xa9\x4d\xff\xf8\xa2\x55\x1a\xf8\x1f\x1c\x47\x90\x3b\x2c\x1d\x61\x14\xe3\x44\xe2\x8b\x4a\x47\x48\x39\xd6\x63\xf2\x6c\xe0\x7d\x27\x5e\xc0\x16\xe2\xef\x0c\x07\x75\x35\x3a\x9b\x07\xd0\x14\x9e\x7d\x61\x47\x21\x46\x93\x28\xc6\x54\x78\x5c\x59\x81\xbe\xb9\x46\x91\xaf\xf7\x95\x95\x82\x0b\x1c\xe6\x73\x91\x05\xbe\x76\xbf\x28\xe7\x4f\x0b\xfc\x9b\x9d\xe2\x50\x18\x45\xd3\x62\x62\xc8\x47\x4e\x8e\xce\x95\x2d\x88\xdd\xbd\x26\xb2\x22\x79\x34\x27\x9a\x5a\x88\xe8\xee\x17\x6e\xf6\x89\xe8\xbe\x15\xd1\xfd\x8f\xc4\xfc\xf2\x49\x4e\xe2\x81\x7f\xa2\xf0\x5b\xf8\xe0\x2c\x9f\x6f\x0d\x01\xb8\x54\xca\x17\x81\xcb\xe8\xeb\x57\xfd\xd5\x9d\xb6\x18\x7b\x8f\xe7\xc7\x15\x58\x5d\x45\x9f\x08\x7c\xb5\x5e\x60\x44\x0a\x00\xcd\x82\x28\x73\x35\x0a\xc6\x18\x95\x7e\x28\x65\xbe\xd6\x59\x0c\x6e\xf0\x38\x34\x62\x6e\x0b\x13\x4e\x43\x91\x19\x88\x2d\x09\xa9\x2a\x4a\xdd\xb1\x1b\xe2\xf1\x16\xd9\xbd\x24\x0a\x5a\x88\x97\xfc\xb5\x1d\xb7\x2c\x39\xba\x68\x92\xac\xc7\xe5\x2b\x59\x26\x24\x68\xed\xcf\xcf\xf3\xf1\xb8\x49\xc2\x8b\xc5\xc4\x36\x62\x5e\x8b\x2f\xc7\xbb\x9b\xb5\x2c\xd6\x33\x79\x92\x3e\x9a\x89\xc0\x6d\x0e\xa2\x87\x7e\x92\x90\x85\xbc\x42\x50\x1b\xa0\xf7\xf8\x06\x6d\xe1\x38\xb8\xa4\x39\x21\x77\xf8\xa0\xd4\xf3\x63\x4e\x1f\xbe\x7d\xbf\xb5\x53\xcf\x5a\x13\xcf\x05\x13\x8f\x77\xa3\x70\x18\x5c\xcc\x58\x26\xca\x08\xb2\x42\x26\x79\xf9\x25\xe3\x68\x8a\xe3\xf4\x06\xfd\x41\x8f\xc5\xe0\x4d\x0a\xcc\xf7\x64\x44\x73\x1c\x27\xe4\x21\x08\x59\xba\x80\x34\x12\xbe\x34\x15\xb4\x85\x87\xfe\x6c\x9c\x76\x50\x13\x95\x6a\xf5\x75\x48\xa4\x5c\x76\xc1\x77\x24\x34\xc7\x31\x4f\x64\x9e\x81\x23\xe3\x3f\x0f\xcd\x20\x65\xc9\x33\x13\x00\x95\x1d\xea\xa5\x0f\x69\x84\xa6\x38\x1e\x46\xf1\x44\x02\xae\x40\x96\xd2\x3f\xf6\x87\x17\x1d\xd7\x28\x23\x7a\xf1\x75\x0c\x31\x67\x6a\xf5\xf5\xd5\x46\x5d\x0b\xc1\x4d\xbb\x42\x51\xd7\x3e\x65\x08\x29\x8d\xdf\x96\xf3\x12\x92\xe6\x25\x90\x27\xb3\x32\xc8\x48\x8b\xaf\xb7\xf9\x59\x44\x0f\x80\xcf\xdd\x92\xae\xca\x19\x43\xc9\xf8\xf5\x6d\x74\xc3\xfd\xcd\x86\x51\x0c\xa7\x98\xac\xd1\x07\x48\x0c\xfa\x65\x30\x34\x92\xc6\x53\x6a\xe7\xa7\x47\xc5\x0c\x6b\x91\x8a\x7f\x64\x93\xb5\x4e\xd3\x4f\xde\x1b\x8c\xa7\x4e\x63\xb5\x5a\xd5\x01\xe7\x64\xaf\xef\x0f\x2f\xec\x86\x17\x64\x22\x36\xc4\x4f\x4e\x78\xa4\xb8\x2b\x18\x86\xb9\xde\xe1\xba\x82\x7a\xd0\x15\x65\x41\x77\xc9\x37\x3b\x65\xb0\x81\x5a\xf8\x43\xa5\x60\xe5\xc4\x1f\xa7\x68\x13\xfe\x59\x3c\x11\x2d\x77\xa3\x91\xfc\xda\xef\x43\x76\x34\x91\xfa\x60\x58\x61\x51\x49\x4a\xbc\x33\x1e\xe0\xe7\x9c\x54\x56\x5c\x9e\x57\xad\xe6\x42\xb9\x5d\xd4\xa9\xb7\x1a\x10\x06\xa9\x23\x29\x2c\xf3\xb2\x07\xdf\x7d\x46\xab\x84\x7c\x28\x0f\xf2\xc4\xec\xd8\xcd\x12\xdd\x09\xca\x41\x36\xa5\x83\x4d\xd3\xcd\x1b\xfa\x1c\x5b\xa8\x27\x90\x93\xf7\xc2\x01\xbe\xb6\xd5\x38\xad\x5e\x33\x05\x90\x25\x5a\xe7\x9c\x10\x5d\x02\x15\x21\x2c\x8b\x37\xce\xfc\xf5\x19\x36\xbc\x52\xf6\xc6\x59\x89\x6f\x79\x1b\x64\x56\x2a\xec\xc9\x66\x84\x91\x6d\x2d\xb4\x68\xf6\x62\x8e\x91\x85\xfa\x91\x09\xea\x5a\x07\x79\x5c\xa4\x37\x1c\x1f\xab\x71\x81\xe8\x24\xcb\x73\xcc\x93\x65\x03\x05\x66\x69\x7c\xb3\x5e\xeb\x73\x86\x58\x46\xef\x2c\x35\xb0\xf9\x7d\x7e\x36\x06\x80\xaf\x0c\xb1\x75\x74\xcd\xe2\x22\x8b\x51\xf6\x8a\x75\xdc\x81\xc8\x9e\x18\x63\x3b\xe8\x40\x8e\x66\xc7\xc0\x5a\xb0\x50\x6c\x39\x6a\xd4\x96\x43\x9a\x3e\xa7\x31\x07\x02\x7e\xae\x34\x01\xa3\x27\x46\x5a\xfe\x68\x1b\xeb\x22\xe3\x8d\xe6\x85\x82\xb2\x75\x96\x8f\xbe\xfc\xce\x1e\xb0\x4a\x6a\xe2\xd7\x83\x23\xb5\x3b\xe0\x3a\x65\xf1\xb8\x36\xc6\xed\x33\xb5\x81\xf9\xcc\x6d\x60\xa4\xd9\x7c\x8d\x3e\xe7\x8c\x1e\xf9\xcb\x6a\x9c\x7e\x06\x73\x18\xa3\x23\xa7\x9f\x75\xb3\x18\xfe\x77\x6b\xbe\xd6\x03\x4e\x91\x3f\x89\x39\x30\xdd\x34\x34\x6a\x9b\x12\x8d\x49\x9c\x56\xcf\x96\x97\xf3\x4d\x8a\x24\xe0\xd2\xd1\x97\xf3\x0d\x4b\x10\x33\xb6\x97\x65\xf5\xf2\x0c\x28\xe5\x63\xc4\xbd\x36\xf4\x22\xc1\x66\x72\x37\xf2\x05\x37\xf1\x87\x12\x2d\x83\xc4\x96\x6e\x7f\x7e\xf4\x1a\x8b\x68\xf0\x00\x41\x6c\xa8\x88\x20\x24\x43\x2a\x14\xba\xc4\x84\xc5\xaa\x79\xc8\x21\x9b\xde\x07\x4c\xae\x6c\x9a\x05\xd9\x11\x47\x49\x97\x00\xe3\x21\x5d\x50\x65\xc3\xae\x8a\xc5\xa4\xd0\x1c\xe1\xe9\x36\xcf\x16\x8d\x42\xb3\x07\xea\xd1\x53\xe8\xf2\x9c\xb0\xb7\x67\xde\xda\x5f\xdb\x87\x7e\x81\xb4\xee\xf3\x93\xa3\x3f\xae\xee\xc8\x99\x5e\xdb\x95\xf5\xfa\xef\xa0\x5d\x3a\x06\xe3\xcc\x2e\x37\xde\xa5\x4a\x24\xf9\x65\x9e\x1e\x49\xe0\x71\x84\x67\x89\xdf\x1b\x63\x16\x0e\x4c\x42\xe7\x18\xc9\xa9\x16\x29\x14\xfd\xcd\x3b\xa4\x66\x58\x93\xb6\x85\x23\xc8\xa6\x8c\x98\xa1\x2d\xb3\x31\x36\x35\x49\xa2\x3c\xc4\x58\x09\x12\xe4\x23\x9a\x80\x19\x5d\xe2\x38\x81\xa8\x65\x23\x3f\x45\x21\xbe\x18\xe3\x7e\x8a\x07\x84\x0d\xf7\x59\x4a\xd5\x94\x29\x7c\xd2\x08\x8d\x83\x34\x1d\xe3\x15\x1a\xe0\xb2\xa2\x02\xc5\x71\x1c\xc5\x68\x10\xe1\x24\x5c\x4a\x91\x3f\x1c\xe2\x3e\xad\x4b\x91\x5a\x4a\x50\x82\xfb\xb3\x38\x48\x6f\x3c\x51\xb1\x37\x4b\x51\x90\x42\x25\x5e\x23\x48\x13\x11\x50\x21\x18\x07\x29\x73\xe2\xa6\x79\x5d\x03\xc2\x9f\x27\x38\xa4\xfb\x41\x62\x53\x94\xd1\x01\xf9\x40\x3b\x27\xd4\x65\xda\x5b\x79\xfe\xee\x9a\xb4\x2d\xff\x90\xf2\x5e\x36\x83\x76\x1e\x30\x32\xeb\x6d\x38\x35\x5c\xe6\x9d\x16\x02\x76\x42\x23\xbb\x17\x76\x9e\xd3\x7e\x15\xed\x92\x5f\x96\xc4\x71\xef\x4f\xab\x67\x1e\x2a\xbd\x3f\x6d\x9c\xb1\x60\x01\xe8\x2b\x79\x64\x57\x01\xb5\x76\xd9\x92\x44\xee\xfd\x69\x8d\x56\xaa\xaa\x95\x1a\xf9\x95\xea\xb4\x52\x4d\xad\x54\xcd\xaf\xd4\xa0\x95\xea\x6a\xa5\x9a\xa8\xa4\xd6\xb1\x65\x47\x32\x86\x8c\x7b\x19\xba\x06\xad\x2b\x06\xad\x6b\x1f\x34\x13\x1f\x69\xb8\x58\x9f\xe8\x85\xc9\x70\xc8\xd3\x0e\x52\xa4\x69\x90\xd5\x6a\x95\x7c\xb1\xf5\xd7\x9c\x88\x86\x0a\xb9\x66\x85\x5c\x2f\x04\xb9\xea\x1c\x78\x09\x86\x06\xb9\x51\x08\x72\xcd\x35\x3b\x9e\x04\x43\x83\x5c\xd5\x20\xcf\x9f\xc8\xae\x1f\xc7\x37\xa8\xa7\xa7\x53\xa5\x53\xd5\xa3\xf1\x2f\x4c\x4d\x46\x4a\x27\x9f\xb0\x9e\xe4\x26\x49\xf1\x04\x0d\xa3\x59\x8c\xd2\x60\xa2\xcf\xfd\x82\x41\x79\x43\x7c\x9d\x1e\x93\xd5\xe7\x8e\x1f\x6b\x89\x78\xbb\x1f\x0d\x82\xe1\x0d\xe5\x84\x94\x0e\x0b\x60\xb1\xee\xc6\xa2\x7b\x4a\x1d\x07\x7e\x3d\x85\x94\x97\x10\x6d\xc5\xc8\x14\x67\x4b\x92\xfb\x33\x4a\x70\x3a\x9b\xaa\x1f\x72\x3c\x3a\xe6\x1f\xf6\xf7\x7e\xa6\xae\x1d\x79\x27\xfc\xbd\x9f\xcf\xab\x68\x03\xed\xfd\x6c\xa6\x46\x93\x8a\xd4\x68\x91\x9a\x35\x9a\xb1\xbc\xa4\x61\x2a\x93\x59\xef\x12\x13\x51\xc1\x75\xf4\xaf\xd2\xe0\xc7\xd0\x36\x8d\x7e\xfc\x15\xd1\x27\x57\xf4\x63\xb9\x38\x0b\x73\x2c\xca\x67\xd7\xa1\xf6\x30\xc7\xa2\xd9\xba\x68\xb6\xa6\x34\x5b\x9b\xd7\x6c\x4d\x6d\xb6\xb6\x58\xb3\x10\x46\x27\xa8\xf2\x25\x48\x80\x04\x75\x75\x05\xba\xaa\x36\xa0\x6a\x9d\x2f\x66\xa8\x5a\x55\x97\xa9\x63\x46\x18\x59\xe7\xb1\x56\x04\xd4\x5a\xa5\xe7\x7a\x3d\xb6\x3f\xfd\x58\xa3\x1f\x6b\xd6\x8f\x75\xfa\xb1\x6e\xfd\xd8\xa0\x1f\x1b\xd6\x8f\xcd\xbc\x36\x5b\x79\x6d\xb6\xf3\xda\x5c\x13\x6d\xe6\x68\xa4\x0a\x71\x1e\xb4\x38\xf7\x41\xc5\x38\x10\x32\x95\x14\xb2\x1f\xd1\x83\x24\x77\x75\x2a\xaf\x25\xe9\xa3\x10\x67\x56\x8b\xd8\x7b\xe7\xde\xde\x61\x70\x33\x2f\x33\xe0\x42\x6a\xe9\x63\x1a\x6a\xe8\x57\x20\x42\x54\xfa\x95\xcc\x3d\x5f\x25\xf0\x2c\xf6\xde\xd7\x7a\xc5\x1a\xad\x58\x67\x15\xd7\xb4\x8a\x2d\x67\xc5\x3a\xad\xd8\x64\x15\x6b\x5a\xc5\x35\x67\xc5\x06\xad\xd8\x3e\x13\xa8\x29\x15\x6b\x59\xc5\x7b\xed\x62\x79\x51\xea\x29\x22\x3c\x76\xfc\x31\x4b\xc9\xce\x82\xc7\xc3\xe3\x5d\xa2\xc7\x73\x38\x8c\xc1\x09\x38\xb6\xf8\xf1\x56\x7c\xad\x4e\x78\x48\xca\xd1\x2b\xbc\xe9\x8e\xf3\xbd\xe8\x64\xea\x17\x76\x3c\xd9\xcd\x6d\xf6\x31\xb8\xa4\x5f\xda\xcd\xd5\x46\x5d\x57\xcb\x89\x65\x22\x08\xb6\x54\xd0\x15\x4a\x59\x1f\xca\x17\x49\x04\xd5\x0c\x7e\x8e\xfd\x4b\x8c\xa2\xf1\xc0\xc9\x6a\x17\x90\x1f\xba\xe7\x74\x72\xbb\x7a\xbc\x43\xa5\xc5\xae\x3f\xee\xcf\xc6\x64\x85\x85\xf8\xca\xd9\x6c\x97\x25\x82\xe9\xd2\x44\x30\xd5\xeb\xe6\xa0\x01\xff\x87\x96\xb9\x84\xa6\xe7\x6b\xe9\xb2\xbc\x30\x5d\x9a\x17\xa6\x7a\xcd\x6a\x34\x20\xa6\x7c\x97\x0b\xa8\xd5\x32\x7a\x83\x4a\xdd\x73\xe9\xf9\xbf\x50\x0d\x75\x50\xb5\x6c\x42\xac\x33\x88\x75\x0a\x91\x01\x6c\x32\x88\x35\x0d\x62\xad\x00\xc4\x06\x83\xd8\x30\xba\x55\xa2\xed\x28\x10\xeb\x05\x20\x36\x19\xc4\xa6\xb5\xd7\x0d\x0d\x62\xa3\x00\xc4\x16\x83\xd8\xb2\xf6\xba\xa9\x41\x6c\x16\x80\xd8\x66\x10\xdb\xd6\x5e\xb7\x34\x88\xad\x02\x10\xd7\x18\xc4\x35\x6b\xaf\xdb\x1a\xc4\xf6\x5c\x88\x99\xd8\x4f\x81\x2a\xd5\xd7\xf4\xea\xba\x77\x8c\xa0\x69\xb2\xfb\x5c\xac\xdc\x63\x11\x91\x52\x17\xd7\xc0\xab\x03\xd2\xb5\xae\x25\x09\x07\x4f\x97\x1f\xcf\xfa\x29\x1a\x05\x17\x23\xe4\x87\x03\x34\x8e\xae\x90\x1f\x5f\xcc\x20\xfc\x0b\xb8\x39\xff\x7b\xe6\xc7\x46\xe2\x1e\x68\xc0\x47\x1b\xa4\x15\x2e\xc5\x59\x94\x07\x17\x3d\x5a\x84\xee\x12\xd6\xe3\x13\xef\xb3\x82\x41\x8c\x93\xd9\x38\x45\xd1\x30\xaf\xf9\x11\xdd\x02\x4a\x17\x3e\x7a\x89\x2e\x7c\xea\xba\x52\x5b\x2b\xa3\x65\x44\x5f\xf5\xd8\xab\x16\xbc\xea\xc1\x2b\x1b\x92\x63\x0a\x48\xea\x0a\x3d\x12\xbe\x44\x17\xd7\x30\xc3\x65\x20\x08\x5e\x40\x88\x9d\x52\x01\x5b\x22\x18\xd2\xa1\x5f\x0f\x8e\x10\x84\x93\x94\x3f\xbe\xa3\x1c\xee\x62\x84\x7e\x43\x17\xe3\xa2\x4c\xce\xae\x54\xf9\x95\xb1\xb8\x77\x94\xc5\x95\x4a\xef\xb2\xed\x9b\xec\x64\xef\x24\xb1\xa0\xcc\x0a\xb4\xd5\x02\xed\xac\x80\x4e\xcf\xbf\x32\x6e\xf8\x8e\x72\xc3\x12\x6d\x26\xdb\x6f\xdf\x71\xfe\x07\xfb\xed\x32\x22\xad\x99\x30\xea\x0c\x46\x9d\xc3\xa8\xa9\x08\xd4\x0c\x0c\xab\x6a\x81\x6a\x1e\x86\x0d\x06\xbd\xc1\xa1\xd7\x55\x0c\xeb\x1a\x86\x35\x0b\x86\x4d\x06\xa3\xc9\x61\x34\x54\x04\x1a\x06\x86\x75\xb5\x40\x3d\x0f\xc3\x16\x83\xde\xe2\xd0\x9b\x2a\x86\x4d\x0d\xc3\x86\x05\xc3\x36\x83\xd1\xe6\x30\x5a\x2a\x02\x2d\x03\xc3\xa6\x5a\xa0\x99\x87\xe1\x1a\x83\xbe\x76\xa6\x90\x88\xc0\xb0\xad\x61\xd8\x52\x30\x2c\x94\xf8\x23\xe1\x49\x27\x84\xae\xb5\x40\xda\x89\x79\xd7\x5d\x14\x56\x8a\xaf\x53\xf9\xde\x49\xd6\xa4\xf2\x50\x0a\x4a\x1a\x07\x7a\x5b\x64\xde\x5f\x4d\xc7\x3e\xc1\xe6\x3a\x45\x4e\x70\x2c\xce\x4c\x29\x6b\xd9\x06\x51\x5c\x5c\xe5\x29\x75\xd5\xe4\x1d\x72\xc9\x72\xde\x1d\x94\x5c\xb0\xb0\x31\xb2\xa7\xde\x8d\x74\x5a\x4d\x2f\xbb\x14\xe9\xb4\xda\x1e\xbb\x2b\xe9\xb4\x6b\xb7\x67\xde\xda\x5f\x3b\x12\xe1\xd3\x7d\xd5\xd3\x7d\xd5\xa3\xdd\x57\x69\x4b\x3c\xbb\xcf\xd1\x6f\x72\xfe\x5a\x77\x38\x0f\x95\x15\xee\xbd\x38\x9a\xbf\x57\x8f\xe6\xef\xef\x7a\x34\x7f\xaf\x1e\xcd\xdf\xe7\x1d\xcd\xe7\x29\x98\x9f\x6e\xaa\x9e\x6e\xaa\x9e\x6e\xaa\x94\x2f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\x59\xb3\x4f\x37\x55\xfa\xc7\xa7\x9b\x2a\xc7\xe3\xd3\x4d\xd5\xd3\x4d\xd5\xd3\x4d\x15\xfc\x3d\xdd\x54\x15\x53\xe2\x3e\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\x49\x7f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\xff\xc9\x37\x55\x0f\x76\x47\x75\xb7\xdb\xa9\x22\xf7\x52\x05\x6e\xa4\x1e\xeb\x2e\xea\xaf\x9d\x0f\xe5\xe9\x2e\xea\xef\x7f\x17\x25\xdf\x1d\x75\x9b\x73\x1d\x9d\xe4\x9b\xa3\x6e\x53\xba\x36\x82\x87\xc7\xbf\x33\xa2\x5e\x9a\xe2\xd6\xc8\x1e\x54\x80\x7b\x68\xe7\x5d\x2b\x81\x1b\xa7\xec\x51\x2c\xc5\x4c\x37\xf5\x15\x61\x90\xa2\xa4\x17\x5d\x9b\x70\x8e\x05\x3a\xc7\xf2\x35\x1d\xff\xb3\x49\x93\xf5\x56\xdb\x7d\x28\x67\x87\xee\x60\xbe\x1a\xf7\x3d\xbe\xb1\xe9\x71\xd5\x16\x3d\xee\x3f\x3e\xb7\x61\x36\x28\x64\x08\x78\x54\x89\x00\xfd\x43\x1e\x27\x87\xea\x90\x55\x22\x5b\x1b\x1f\xfb\x53\x05\x90\x19\x09\x4d\xf9\x6c\x04\x45\xb3\x9d\xfd\x49\x2f\x4a\x9f\xd1\x32\x1d\x9f\x65\xde\x68\x19\xfd\x03\x7a\xe5\x88\xa5\x70\xe5\x4f\xed\x38\xc3\xbe\x61\x6a\x08\xa4\x09\x38\xb6\x3b\xc6\x93\xd7\x64\xc6\xe7\x4f\x4f\xd7\xaa\xe2\x67\x59\x35\x04\xd1\x7c\x66\x59\x66\x05\xa0\x7b\xab\xe5\xb8\x26\x04\xb4\x20\x46\xfe\x75\x32\x3d\x76\x95\xa1\xd2\xb2\x70\x72\xae\xb7\xda\x0e\x85\x48\xd5\xa9\x0c\xb1\x36\x5a\x54\x31\x22\xad\x27\x4d\x31\x92\x0d\x5a\xa0\x7d\xf9\x9c\x0d\xe7\xdc\x0c\xf0\xa0\x1c\x54\xab\x7f\x91\xf1\xd4\xe6\x43\xac\xa6\x90\x2e\xa3\x90\xaa\xd4\x42\xcb\x22\x0a\x40\x83\x4e\x13\xc6\x31\xaa\x54\xbe\x2b\x24\xec\x20\x5c\x2b\xd1\xe6\x10\xac\x9b\x58\x33\x42\x55\xdf\xab\x9d\xfd\x4a\xea\x96\xd8\x9a\x22\x55\x18\x5e\x67\x59\x5e\x83\x50\xcf\x63\xa0\x1d\x9f\x3e\x41\x1c\x14\xcb\x8d\x56\x46\xea\x81\x71\x76\x27\x63\xa1\xcc\x15\x13\xcb\x14\xec\xbe\x57\xb9\xb7\xdb\x7c\x08\xa1\xb7\xdb\x5c\x58\xe2\x35\xf7\x58\x4d\xdc\xed\x36\xad\xb1\x2d\xe0\x86\x26\xc0\x83\x3b\xec\xf0\x5b\x71\x34\x55\x76\x79\xf6\x02\x06\xe1\x1b\x44\xc5\x1b\x90\xe6\xd4\x40\x73\x9a\x9e\x9f\x4c\x3c\x29\x25\x42\xcd\xa1\xda\xab\xba\x0c\x56\x8f\x35\x47\x50\x97\xa2\x7e\x69\xab\x98\x80\xea\xa8\x20\xd4\x88\x71\x85\x84\x18\xd2\x06\x2f\x98\x7f\x87\x41\xc6\x33\x67\x03\x17\x86\x2f\x04\x2f\xb2\x8b\xff\x04\x9b\xf9\xca\x8a\x75\x0f\x5f\x80\xdd\xa3\x39\x09\x90\xbe\xa3\xd5\x46\x86\xe8\x61\x56\x1c\x40\x5a\x7c\xd5\x31\x9a\xcf\x5f\x79\xa4\x50\xfe\x49\xb3\xdb\x7c\xac\x63\xe6\xfd\xd2\xf5\x7d\xcb\xf3\xe5\xa3\x9d\x02\xbf\x6d\x10\x67\xc2\xaa\x70\x82\xe3\x4b\xfc\xfc\x59\xa9\x5f\x46\xf5\x6a\xad\x8e\x7a\x37\xa8\xfb\xff\xfd\xbf\x83\x38\xe8\xa3\x7d\x9c\x84\xc1\xb8\x82\x36\xc7\x63\x14\x07\x17\xa3\x34\x41\xac\xfc\xa0\xf2\xfc\xf9\xb3\x23\x3c\x08\x92\x34\x0e\x7a\x33\x80\xef\x87\x03\x08\xca\x13\x84\x28\x89\x66\x71\x1f\xc3\x9b\x5e\x10\xfa\xf1\x0d\x61\x07\x93\xc4\x63\x51\x1a\x62\xf8\x37\x9a\xa5\x68\x02\x3c\xbd\x0f\x9c\xd5\x43\x7e\x8c\xd1\x14\xc7\x93\x20\x4d\xf1\x00\x4d\xe3\xe8\x32\x18\xe0\x01\x0d\x3a\x41\xd6\xe9\x30\x1a\x8f\xa3\xab\x20\xbc\x40\xfd\x28\x1c\x04\x74\x0d\x93\x4a\x13\x9c\x76\xd8\x8a\x5f\x41\x2a\x5a\x09\x28\x86\x29\x3e\xfd\x68\x80\xd1\x64\x96\xa4\x64\xa3\xf6\x83\x10\x80\xfa\xbd\xe8\x92\x7c\x9a\xde\x40\x17\x51\x18\xa5\x41\x1f\x7b\x34\xae\xd0\x38\x48\x40\xb3\x2c\xb7\x17\x0e\x34\x64\x06\x41\xd2\x1f\xfb\xc1\x04\xc7\x15\x17\x0e\x41\x28\x0f\x04\xc7\x61\x1a\x47\x83\x59\x1f\x3f\x38\x1a\x88\x75\x6d\x10\xf5\x67\x22\x0e\x06\xa9\xb1\x1a\xc5\x2c\x46\xc6\xc4\x4f\x71\x1c\xf8\xe3\x24\x1b\x66\x98\x1b\xa8\x26\xa1\x4e\xe6\xf9\x64\x77\xef\x18\x1d\x1f\xec\x9c\xfc\xb2\x79\xb4\x8d\xf6\x8e\xd1\xe1\xd1\xc1\xcf\x7b\x5b\xdb\x5b\xe8\xed\xbf\xd0\xc9\xee\x36\xea\x1e\x1c\xfe\xeb\x68\xef\xdd\xee\x09\xda\x3d\xf8\xb0\xb5\x7d\x74\x8c\x36\x3f\x6e\xa1\xee\xc1\xc7\x93\xa3\xbd\xb7\x9f\x4e\x0e\x8e\x8e\xd1\x8b\xcd\x63\xb4\x77\xfc\x02\x3e\x6c\x7e\xfc\x17\xda\xfe\xf5\xf0\x68\xfb\xf8\x18\x1d\x1c\xa1\xbd\xfd\xc3\x0f\x7b\xdb\x5b\xe8\x97\xcd\xa3\xa3\xcd\x8f\x27\x7b\xdb\xc7\x1e\xda\xfb\xd8\xfd\xf0\x69\x6b\xef\xe3\x3b\x0f\xbd\xfd\x74\x82\x3e\x1e\x9c\xa0\x0f\x7b\xfb\x7b\x27\xdb\x5b\xe8\xe4\xc0\x83\x46\xcd\x6a\xe8\x60\x07\xed\x6f\x1f\x75\x77\x37\x3f\x9e\x6c\xbe\xdd\xfb\xb0\x77\xf2\x2f\x68\x6f\x67\xef\xe4\x23\x69\x6b\xe7\xe0\x08\x6d\xa2\xc3\xcd\xa3\x93\xbd\xee\xa7\x0f\x9b\x47\xe8\xf0\xd3\xd1\xe1\xc1\xf1\x36\x22\xdd\xda\xda\x3b\xee\x7e\xd8\xdc\xdb\xdf\xde\xaa\xa0\xbd\x8f\xe8\xe3\x01\xda\xfe\x79\xfb\xe3\x09\x3a\xde\xdd\xfc\xf0\xc1\xda\x4b\x82\xbb\xd2\xc7\xb7\xdb\xe8\xc3\xde\xe6\xdb\x0f\xdb\xb4\xa5\x8f\xff\x42\x5b\x7b\x47\xdb\xdd\x13\xd2\x9d\xec\x57\x77\x6f\x6b\xfb\xe3\xc9\xe6\x07\x0f\x1d\x1f\x6e\x77\xf7\xc8\x8f\xed\x5f\xb7\xf7\x0f\x3f\x6c\x1e\xfd\xcb\x63\x30\x8f\xb7\xff\xef\xa7\xed\x8f\x27\x7b\x9b\x1f\xd0\xd6\xe6\xfe\xe6\xbb\xed\x63\x54\x9a\x33\x24\x87\x47\x07\xdd\x4f\x47\xdb\xfb\x04\xe7\x83\x1d\x74\xfc\xe9\xed\xf1\xc9\xde\xc9\xa7\x93\x6d\xf4\xee\xe0\x60\x0b\x06\xfa\x78\xfb\xe8\xe7\xbd\xee\xf6\xf1\x6b\xf4\xe1\xe0\x18\x46\xeb\xd3\xf1\xb6\x87\xb6\x36\x4f\x36\xa1\xe1\xc3\xa3\x83\x9d\xbd\x93\xe3\xd7\xe4\xf7\xdb\x4f\xc7\x7b\x30\x68\x7b\x1f\x4f\xb6\x8f\x8e\x3e\x1d\x9e\xec\x1d\x7c\x2c\xa3\xdd\x83\x5f\xb6\x7f\xde\x3e\x42\xdd\xcd\x4f\xc7\xdb\x5b\x30\xba\x07\x1f\xa1\xab\x27\xbb\xdb\x07\x47\xff\x22\x40\xc9\x18\xc0\xe0\x7b\xe8\x97\xdd\xed\x93\xdd\xed\x23\x32\xa0\x30\x52\x9b\x64\x08\x8e\x4f\x8e\xf6\xba\x27\x72\xb1\x83\x23\x74\x72\x70\x74\x22\xf5\x11\x7d\xdc\x7e\xf7\x61\xef\xdd\xf6\xc7\xee\x36\xf9\x7a\x40\xa0\xfc\xb2\x77\xbc\x5d\x46\x9b\x47\x7b\xc7\xa4\xc0\x1e\x6d\xf6\x97\xcd\x7f\xa1\x83\x4f\xd0\x65\x32\x47\x9f\x8e\xb7\xe9\x4f\x89\x62\x3d\x98\x49\xb4\xb7\x83\x36\xb7\x7e\xde\x23\x68\xb3\xc2\x87\x07\xc7\xc7\x7b\x8c\x4e\x60\xc8\xba\xbb\x6c\xb8\x2b\xcf\x9f\xbd\x5c\x55\x75\x5e\xfb\x7e\x3a\x7a\x58\xbd\x57\xb1\xa8\xd3\x34\xf0\xb1\x28\x42\x1f\x0b\x59\x67\xc3\x85\x9d\x1f\xa6\x09\x4a\xfd\x1e\x97\x58\x48\x95\xf3\xdf\xc7\xd6\x60\x9b\x99\x1c\x55\xf5\x10\xaa\x79\x08\xd5\x3d\x84\x1a\x1e\x42\x4d\x0f\xa1\x96\x87\x50\xdb\x43\x68\xcd\x43\x68\xdd\x43\xe8\x95\x87\x6a\x55\x0f\xd5\x6a\x1e\xaa\xd5\x3d\x54\x6b\x78\xa8\xd6\xf4\x50\xad\x25\x59\x58\xae\xd1\xba\xe4\x1b\x81\x47\xca\x13\x18\xb5\x16\x85\x4b\xea\x41\x5b\xaf\x18\xfc\x3a\x83\x51\x83\x36\x32\x38\x0d\xd6\x56\x93\xe1\xf2\x8a\xc1\x58\x97\xf0\x5c\x63\xb0\xda\x0c\x97\x1a\x85\x59\x93\x63\x2d\xd7\x58\x5d\x8e\x4b\x95\xc2\x00\x3c\x38\x9e\x0d\x0a\x8b\xc0\xaf\xc9\xfd\x96\xe1\x34\x59\xdd\x16\xc3\x7d\x8d\xc1\xa8\x4b\x78\xd6\x18\xac\x75\x86\x0b\xeb\x77\xad\x71\x56\x7e\x2d\xcf\x45\x3c\x67\x2e\x38\x1e\x6b\xd2\x58\xd5\x19\x4c\x8e\x73\x5b\x1d\x0f\xe8\x5b\x43\xeb\x7b\x9b\xd5\x69\x64\xb0\xa0\x6e\x2b\xc3\x99\xc3\xe0\xe3\x01\x6d\xd5\xb4\xbe\x43\xa1\x96\xd4\xc1\x35\x86\x60\x3b\x1b\x5c\x01\xa4\x2e\x0d\x34\x45\x36\x03\xb4\xce\xea\x48\x83\x05\x13\xd3\xca\x06\x57\xc0\x68\x48\x03\x4d\x91\x95\x10\xaa\xb3\x91\xad\x4a\xc0\xf8\x68\xac\x89\xd9\x13\x14\x8a\xd8\xe8\x50\x64\xd5\xd9\x48\xe6\xad\x0c\x8a\x22\x1b\x2b\x40\x4f\x6e\x89\xd3\x56\x43\x1a\xcf\x76\xf6\x4d\xa1\xe9\x35\x0f\x3e\xc1\x50\x71\x7a\x7d\x95\xd1\x1e\xa7\xa9\x5a\x4b\x1a\xd6\x35\x56\x56\x99\x8f\x5a\x46\x04\x62\x2e\x5e\xb1\x82\x9c\x78\xd6\xa5\x32\x1c\xf1\x35\xf8\x2d\x9f\xa5\xc4\x5a\x6e\x66\x55\x79\xfb\x62\xcd\xcb\x6b\x62\x5d\x01\x99\x81\xe2\xeb\xb3\x95\xd1\xbe\xe8\x67\x3d\x43\x41\x8c\x13\x23\x19\x0a\x17\x69\x53\x32\x6f\x81\x30\xc4\x94\xc1\x6f\x65\x08\x40\x3f\xd7\xb2\x85\x08\x0d\x36\x19\x22\x6d\x0d\xe9\x86\x3a\xf8\xa2\xd3\xb5\x0c\x8e\x18\x3b\xb1\xa0\xe1\xbb\x02\x47\x30\x90\x9a\x34\x48\xed\xac\x5d\xb1\xf0\xd8\x02\xae\x35\x2c\xf3\x21\x3a\xa0\x21\xce\x01\x89\x05\x57\x97\xfe\x6d\x89\x55\xac\x0e\x50\xcb\x52\xae\xa9\xce\x8c\x98\xc9\xac\x53\xa8\x56\x43\x67\x4a\x96\xec\xf3\x11\x59\x21\x96\xf9\x40\x22\x54\x73\xd5\x43\xd5\xeb\xd6\xe6\x7a\x7d\xed\xd5\xab\x57\xe4\x77\x7b\x7b\xeb\xd5\xf6\xdb\xcd\x1a\xf9\xbd\xbe\x53\x7b\xfb\xb6\xbb\xd5\x25\xbf\x37\x5f\xb5\x1a\x3b\x5b\xcd\x6d\x75\xbe\x47\xb1\xb3\x81\x56\x75\xb3\xbe\xfe\x76\xbb\x0d\x0d\x74\x9b\x5b\x5b\xb5\x7a\x13\x1a\xd8\x5a\xab\x36\xb6\x77\x1a\xe4\xf7\xda\x66\x7b\x6b\xad\xbd\x0d\x0d\x73\x84\xce\xac\xfa\x80\xa3\xbd\xc3\xed\xfd\xad\x5a\xbb\x0a\xe1\xf7\xe7\xe8\x90\x44\xd9\x4c\x8b\x24\xbd\xa2\xbb\xf2\x5d\xef\x8a\xa8\x32\x11\x90\x70\x04\xc1\x6e\xaf\x35\x5b\xf5\x46\x15\x46\x70\x7b\xa7\xbb\xb5\xf9\x76\x1d\x3a\xf8\x6a\xfd\xed\xe6\x56\x77\x67\x9b\xfc\xae\x55\x1b\xf5\x56\x73\x0d\x06\xa7\xdb\xd8\xaa\x6f\xd7\x76\xaa\x67\x4e\xd5\x78\x51\xa5\xbc\x55\xb1\x5b\xd8\x4b\xa9\x96\x73\x53\x33\xdf\x1c\x9f\x62\x01\xba\xd7\xcc\x2c\xd2\x71\x7d\xb3\x7f\x2e\x95\xe6\x97\x07\xe7\xa6\x21\x13\xca\xbb\x53\x91\xea\xa1\x0d\x54\x32\x0b\x20\x6a\x00\x2a\x35\x96\x19\x3e\x48\x2f\x17\x33\x2a\x35\x00\x32\xbb\x52\x0d\xa0\x69\x5d\x6a\x82\xcb\x51\x8d\xa1\x79\xb6\xce\xbb\x48\xdc\x3f\x10\x52\x74\x5e\x39\x02\x03\x38\x1f\x8d\xdd\x05\x62\x28\x10\x3b\x0b\x80\xf8\x79\xfe\xbb\x1b\x02\xc8\x44\xe7\xbf\xbb\x21\xc0\x36\x7d\x9e\xb8\x21\xc0\xa6\x71\x9e\xc4\xf6\x88\xd6\xab\xab\x64\x95\x7d\x21\x87\xe6\x4b\x3f\x0e\x88\x74\x6c\xb9\xa4\xf5\xc7\x1e\xea\x8d\x3d\xd4\x1f\x7b\x68\x30\xf6\x10\x1e\x5b\x1a\xf2\x63\x0f\xf5\x62\x0f\xf5\x63\x0f\x0d\x62\x0f\xe1\x58\x6f\xcc\x27\xa8\xf8\x04\xe1\x5d\xd3\x65\xa4\x17\x43\xd0\x71\xf8\x58\xd3\x3f\xf6\xc9\xc7\x3e\xfd\x58\xff\xff\xd9\x7b\xfb\x2d\xa9\x6d\x6c\x51\xfc\xef\xf0\x14\x9a\xf9\xad\x81\x6a\xba\xe8\xb6\xe4\x2f\x19\xe8\xfc\x2e\x21\x70\x3a\x37\x10\x58\xc0\xdc\x70\x16\x0b\x32\xb2\x2d\x77\x39\x54\x57\xf5\xa9\x72\xd3\xd5\x49\xc8\xba\xaf\x71\x5f\xef\x3e\xc9\x5d\xda\x92\x6d\xd9\x96\xe4\xaa\xa6\xc9\x99\xcc\xd0\xb3\x86\x54\x95\xa4\xbd\xb7\xf6\x97\xb6\xbe\xb6\xfa\x85\xb9\x28\xcc\x65\xa1\xdf\x2f\x84\x09\x03\x97\x85\x41\xbf\xb0\x79\xa6\x9a\x75\xdf\xa5\xae\xbb\xd4\xdf\x15\x34\x1e\x25\x84\xff\xee\x1f\x21\x6c\xb4\xed\x4a\x98\x0f\x9b\xa3\xfd\xd6\xa6\xf6\x7f\x99\xbf\x29\xdf\xbe\xdd\xfb\xcd\x74\x89\x01\x6e\xed\xdc\xc7\xd1\xde\xaf\x37\xbe\xea\xba\x46\x81\x03\x15\x78\x92\xce\xa7\xd9\x7c\x9a\xcf\xf7\xd0\x3e\x9a\xcd\xcd\x77\x6f\x3e\xa2\x66\x41\xae\xbc\xef\x13\xb9\xd4\x66\x80\x46\xfa\xd0\x06\x9c\x1f\x40\x0b\xa8\x15\x9a\xdf\x87\x36\x10\xd5\x00\x5a\x14\x58\xa1\x05\x7d\x68\x03\xd9\x6a\xd0\x7e\x3d\x3c\x54\x10\xa9\x67\x85\x18\xf6\x21\x0e\x14\x02\x99\xd3\xa4\x0b\x21\x56\x46\x71\x89\x12\xb4\x5a\x56\xf3\x49\x35\x5d\x0b\xb1\x9a\x2e\x6d\x80\x0e\x54\xfb\x7c\x6e\x16\x39\x58\xc4\xc0\xa4\xc4\x1f\xe8\x6d\x6e\x2a\x01\x75\x07\xbc\xc2\x26\xb1\xf1\x1a\x10\xd8\x4b\x6a\x6a\x0d\x66\x36\xd8\x49\x6c\x48\x65\x2b\xb4\xaf\x69\xeb\xea\xea\xda\x1a\x4e\xd2\xd5\x34\x5b\x4d\xf3\x15\x70\x7c\xf5\x69\xda\x1a\xf4\xa1\x7d\xaa\xb6\x76\xa1\x7d\x92\xb6\x92\x3e\xb4\x4f\xd6\x56\xdc\x87\x78\xcd\xda\xba\x82\x5d\x6b\x87\xba\xae\x2c\xea\x0a\x1e\x75\x65\x52\x57\x70\xc4\xa6\x12\x70\xd1\x52\x5d\x57\x56\x75\x85\x01\xc0\xd4\x1a\x86\x86\xe1\x09\x8d\xbe\x2b\xff\x4e\x7f\x8e\x01\x62\x48\x38\xf5\xdb\x8b\x30\xc5\x3f\x47\x68\x72\x2c\x8f\xe6\x66\xc2\x33\xe7\x86\x9e\x1e\xab\x23\xbc\xc7\xf2\xf8\x6d\x2e\xea\x99\x38\x72\xac\x8e\xe9\x1e\xcb\x83\xb4\x5c\xd4\x63\xc6\x7a\xbe\xaa\x07\x87\x65\x61\x44\x48\x8d\xf5\x02\x55\x0f\x0e\x26\xa7\xa2\x5e\x66\xac\x07\x07\x98\x3b\x6c\xe9\x87\xb5\x8f\xd5\xd3\x1a\x9f\x70\x3c\x2b\x67\x15\x6b\x82\x21\xf1\xc5\x30\xf0\x8f\x3f\xc3\x58\xd7\x5c\x7c\x53\x56\xeb\x57\xcb\x0a\x3c\x9e\x84\xb9\xf8\x96\x55\x4c\x9e\xda\xba\x8d\xa8\x01\x3a\xb4\x79\xc2\x8b\x6a\xf0\x68\x23\xd4\x1f\x74\xe6\x41\x9e\x0f\x5f\x21\x46\xea\xbd\x45\x79\x98\xa9\x05\x29\xa2\xc9\xf0\x2d\xfa\xed\x48\x3e\x2c\xdc\x9e\x91\x68\x6a\xfc\x0d\xf9\xa4\xaf\xad\x2d\xa4\xc9\x64\xd2\x56\xdd\x47\xc2\x3f\x08\x90\xc9\x9e\x00\x15\x08\xbb\xc5\x81\x25\x80\xae\x9b\x4a\x76\xb4\xc1\xb3\xf6\xe3\xf6\xc1\xf3\x00\x98\x0a\x9c\x7b\xc0\xc6\x02\x67\x53\x47\xf5\x77\x3a\xda\xf7\x30\xeb\x37\x76\xe0\x70\x8c\xe1\xd9\x8e\xc3\x43\x98\x09\x22\x78\xdd\x45\x5e\xc8\x32\x1e\x9c\x3a\x93\x33\xaf\xe1\x6b\x2e\x6e\xb5\x04\xeb\xd6\x63\x74\x83\xe2\x1c\xa3\x23\xa4\x87\xef\x9f\x36\x7f\x0b\xb7\x9a\xbe\x99\x67\x64\xc7\x30\x15\x3b\x36\x5c\x26\x41\xae\x39\xd8\x71\x73\x5d\xef\xb8\x33\xbd\x3a\xde\x79\x5e\x25\x35\xe4\xb8\x33\xa7\x3a\xb6\x4e\xa6\xc6\x8f\xc2\xbd\x90\x3b\xe1\x52\xb8\xea\x05\x8b\x1c\x98\xdd\xad\xaa\x76\xcc\x7b\x02\xea\xb8\xa9\x6c\xbe\x5c\xb8\x1d\x14\x1c\x25\x10\xb5\xda\xd5\x05\xf8\x6a\x3f\x06\x21\x8b\x7f\x1a\x28\x89\x6c\x37\xd4\x35\x45\x26\x94\x76\xce\x45\xc1\xc7\x8f\x72\xf7\x1f\xe9\x27\xe2\x0a\x3c\xd9\x4c\xd1\xe5\x14\xfd\x62\x7a\xe6\x63\x32\xd9\xc0\xcd\xce\x4b\xf8\xf7\x97\xf6\xb5\xf6\x8f\x03\x38\xc4\x0d\x67\xb2\xd9\xbb\x39\xb9\xdc\x93\xd7\xc9\x7f\x17\x5f\x7e\xd9\xdb\xdb\xbb\x67\x83\xe6\x8f\x42\x13\x80\x7e\x17\x10\x5b\xd2\x2c\xb0\x82\x71\x58\x37\x01\x02\xd0\x76\xb9\x77\x73\xf2\x3b\x10\x67\x87\x18\x6e\xc3\x33\xc1\xb4\xdf\x5a\x50\x16\x58\x10\x4a\x6c\xa6\x0b\x23\xa4\xcd\xfd\xfb\x0b\xa0\x6a\xf3\xf5\xd7\x5f\x4f\x7c\x72\x67\xa1\x13\x25\x3f\x38\x4f\xc3\xd4\x87\x61\xe4\x3b\x70\xdb\x1d\x86\xb1\xbe\xf6\xa3\xce\xb7\xc0\x99\xa7\xfa\x73\xb5\x94\x9e\x69\x08\xc6\xf2\x3e\x8f\xa5\xf6\x55\x1f\xe6\x51\x96\xd1\x9e\x64\xa9\x17\xf0\x26\xb7\x14\x89\xb7\x0c\xa7\x70\xec\xad\x2e\x6a\x6a\x4d\xc7\x6d\x86\x8b\x83\xbd\xa3\x36\x75\x85\xed\x8e\x2a\xd5\xc2\x39\x7e\xfa\xe0\xe1\x1f\x20\x1a\x47\xf3\xf7\xfc\x12\x9a\xae\x79\xb6\xe2\x95\xe5\xed\x24\x8b\x40\xe1\xc9\xc1\x6b\x14\xa8\x7c\xc8\xb0\x11\xcd\xf1\x29\xcb\x5a\xf1\xe8\x47\xac\x0c\x12\xea\x54\x1e\x4a\xe9\x94\x65\x06\x49\x7d\xf5\x51\xee\x03\x5b\x8e\x46\xd5\x35\xcd\xaf\x13\x7d\x7c\x3b\x8d\xe3\x2f\x47\x9c\xfe\x15\xae\xac\x7c\xee\xad\xfb\x5e\x62\x35\x0d\xb1\x35\x65\xda\xcb\xe3\x07\x77\xf0\x16\x3b\x19\xc3\xb7\xaa\xaf\x73\xff\xe2\x08\x6e\x9f\xb6\x5b\x18\xe5\xa2\xac\x26\x86\x04\x54\xdd\x2d\x0d\x5e\x64\x39\x4b\x69\x62\xc8\xcd\xe4\x6d\x12\x9a\xb2\x3c\x2b\x78\x67\x8f\xc3\x54\x31\xf3\x73\xc2\x71\xe1\x75\xcb\x3e\x7d\x0b\xc4\x16\xa1\x9b\x83\xef\xe1\x0a\xfa\x00\xc0\x36\x6b\xcf\xe6\xe5\x62\x51\x94\x9a\x17\x8b\x21\x60\x34\x2f\x15\xc3\x74\xd5\xbc\x50\x2c\x8a\x78\xb3\x4c\x3c\xa0\xd4\xba\x4e\x6c\x5d\x13\xb6\xcc\x16\x60\xdd\x07\xc9\x1b\xa6\x96\x5c\x30\x3f\xca\xc0\xbf\x9b\x02\xa3\x7b\xf7\xb4\xfe\xab\x17\x94\xcc\x80\xea\x7b\x0e\x3f\xbe\x29\xd1\x1d\xe4\xbf\x45\xef\xd4\x47\xda\x7e\xc4\x81\xf6\x39\xb2\xbd\x1d\xa9\x48\x9a\x2c\xe0\x72\xac\x9c\x5b\xc2\xf4\xc1\xc7\xe6\x34\x35\xe6\x99\x10\x2c\x2d\x4d\x98\x00\x12\x02\x10\x26\x67\x32\x31\x5c\x90\xe5\x68\x1f\x10\xd9\x16\x1a\xd1\x7d\x44\x3c\x2b\xd7\x60\xd9\x6c\x32\x49\xd1\x4d\x94\xc9\x38\x57\x7c\xcc\x01\xb2\xb7\x09\x99\xdc\x85\x1d\x59\xe2\x43\xf7\x51\x30\x86\x22\x45\xef\x50\x86\xde\xa1\x5c\x42\x8e\x78\x9e\xf0\x94\x99\x92\x0e\xf5\x20\x47\x3b\x10\x2f\x69\x17\x9f\x32\xd5\x8b\x3b\xc8\xdb\xc4\x1e\x0f\x02\x9f\x04\x76\x5c\x87\xb7\x1b\x74\xd4\xdb\x43\xb7\x0f\xb7\xee\x8b\x80\xef\x87\x49\xee\x73\xd2\x5f\xe5\x41\x16\x91\x0a\x7b\xc9\x4d\xcb\x7d\xe8\x08\x65\xa6\x25\x3e\x04\x28\xef\xdf\x47\xbe\xa7\x7a\x09\xe2\x37\xbe\x2d\x8a\x8e\x90\x89\x0e\xb6\xdd\x6d\xad\xad\x16\x03\xd5\x22\x5a\xbd\xd8\xc6\xfa\x37\xbc\x51\x67\x21\x10\x16\x0c\x07\x99\x4f\x50\x67\x11\x10\x16\x0b\x33\x73\x1d\x5f\x5f\x28\xcc\xcd\x75\x02\x7d\x91\x90\xf7\xeb\x7c\x59\xe0\xfb\x67\x5d\xe0\x13\xb1\xf0\x41\x31\x5f\x2e\x57\xfa\x9a\xdb\x21\x0c\xd4\xea\xef\x93\x90\x40\x2e\x84\x16\xf2\xc8\x3a\xdd\x60\x99\xee\x33\xad\xd0\xed\xb8\x0e\x64\x5c\xae\xfb\x33\xae\x06\x7d\x59\x42\x18\x2c\x06\x88\xf0\x79\xa7\xd5\x03\x68\xe0\x5a\x38\xe8\x06\xe4\xdd\x35\x03\x51\xf6\x65\xb9\xe0\x5a\x97\x0b\x40\x1e\x5b\xac\x14\x98\xc5\xd2\x2e\x12\x28\xd1\xd8\xaf\x4d\x89\x0a\xf6\x65\x01\xfa\xa7\x4e\xb0\xb1\x9e\x31\x12\x46\x9f\x3b\x37\x86\xc2\xf2\xef\xb3\x7c\x30\x58\x1e\xd0\xe7\xf0\x24\x8c\x3a\xb3\x78\xed\x16\x76\x7f\x55\x80\x90\x60\xbb\x75\x01\x51\xb1\x03\x13\xbe\x4b\xe0\x7f\xe8\xda\x40\x86\xbd\x30\xe1\x39\x15\x53\x7e\x3f\x8a\xb3\x3c\xf4\x62\xf8\xec\xc5\x5e\x9e\x63\xf8\x5c\xc4\x1e\x0f\x13\xdf\xbc\x66\x50\x14\x99\xe7\xa5\x3e\x2c\x2e\x44\x34\xa4\x38\xc4\xf2\x73\x50\x24\xb4\x60\x00\x20\xe5\x05\x0b\x0a\x16\xec\xb0\x5c\xb0\x55\xe4\xa9\xb9\x7d\xc5\x3a\xad\xa5\xe3\x16\x2d\x78\xd4\x26\x9c\xb9\x73\x34\x0c\x5e\x2c\x1b\x4b\x5f\x86\xe8\x91\x11\x97\x90\x60\xd7\x41\x5a\x34\x19\x19\xa6\x3b\xd6\x31\x18\xa8\x09\x31\x5f\x62\xff\x32\x54\x7f\xc2\x50\x2d\xa4\xb2\xdd\x60\x6d\x14\x4e\x67\xb8\x96\x02\x72\x0e\xd8\x84\xf4\xaf\x3a\x6b\xf7\x9a\xd5\x70\x74\x37\x4e\xc4\x00\x9e\x7c\x59\xd7\xff\xef\x19\x98\xff\x7c\xd7\xf2\xbe\x93\x8f\x38\x94\xbf\x34\xb7\x72\xd1\x6a\x79\xbe\xc8\x51\xd6\xbd\xaf\xa7\xf5\xe0\xb8\xff\x74\xca\xf7\xdd\x6d\x80\x7a\xa1\x96\xb7\x30\x64\x89\x29\x82\x41\xfa\x96\x72\xb9\x7e\xbe\x2a\x4f\xf9\x64\x61\x1c\xc6\xd6\xff\xb5\xaa\x7e\xa8\xe7\xf9\xe2\xcb\x64\xd1\x9f\x67\x36\x0b\xc1\x52\x9c\xe8\x08\x91\x7b\xf5\xe7\xfb\x47\x12\x42\xfd\x83\x63\x6d\xf8\x2f\x93\x05\xfa\x9b\xaa\xb6\x67\x5d\x2f\x54\x36\x5a\xb0\xf9\x9a\x8f\x9f\x0a\xec\xaf\x8f\xd5\xf3\xf1\xd5\x79\x77\x86\x6b\x60\xcb\x09\xaf\x1e\xaf\x18\x7c\x66\xf3\x6f\xca\x6a\x6d\x60\x50\xb3\x85\xbf\x40\x77\xd0\x64\x01\x99\x3d\xf7\xd0\xed\xce\xe2\x47\x7f\x25\x4b\xc3\x55\xaf\x52\xeb\x99\xd9\xe1\x37\x10\x48\x2f\x7f\xcf\xc5\xac\x9c\x73\x34\x51\x65\xf7\x91\x3a\x92\xd9\xe7\x62\x2b\x4d\x2b\xa3\x1b\x10\xd4\xca\xe5\xe3\x37\xb2\x12\xa4\x1d\x1d\x30\x02\x74\xe1\x6c\x79\x31\x59\x4c\x11\x46\x87\x88\xec\x6d\x91\xb1\x1d\xc1\x4b\x28\xbb\x80\xf5\xf7\x8c\xc9\xb3\x25\x88\xfd\xfd\x91\xa5\xd0\x45\xa7\x46\x1d\x21\x4d\x5a\x98\x57\xdf\x63\x13\x81\xf7\x76\xd1\xf4\x30\x42\xff\xec\x3b\x6d\xc7\x07\xeb\x79\x99\xf1\x89\xb7\xf7\x65\xd7\x6b\xeb\x5d\xaf\x41\x51\x01\x45\xa1\xa9\xe8\x04\x8a\x06\x1b\x46\x10\xb3\x40\x51\xfc\xc9\xdb\x68\x91\x23\xd7\xfd\x1f\xbd\x8d\x76\xc2\x4e\x4f\x99\xb7\x69\x36\xd3\xf0\x80\x29\xc3\xda\x70\xd0\x78\x52\xb7\xbc\x7f\x1f\x11\xb9\xe9\x55\xff\xf2\xf5\xd7\x5f\xa3\x78\x6f\x0f\xa1\x77\x66\x48\xdd\xbf\x0e\x24\x1c\x0c\x20\x61\xba\xb7\xb7\x1d\xa4\x6e\x3b\xdf\xe8\x5e\x3a\x3d\xc1\x6d\xbf\x8d\x87\xe4\xbb\x95\xb5\x6e\x63\x49\xac\xd6\x6d\xbc\xa9\xf3\x4d\x6f\x49\x6c\x17\x92\x3f\x84\x94\xec\xd8\xed\xba\x9d\xf9\x4d\x02\xd4\x2a\x8e\x12\xe2\xbe\xea\x39\x24\xf9\x55\x3d\xdc\x77\x6e\x98\xda\x76\x3f\x33\xb8\xd5\x38\xe1\xe8\x26\x2a\xe0\xb0\xdb\xef\xe2\xe3\x89\xed\x09\x97\x53\x06\x19\xe6\x18\xba\x89\x52\xa8\xce\xe4\xee\xe0\x3b\xa4\xf6\x09\x4d\xf4\x43\xb0\x52\x9e\x08\xc2\x9b\xad\x56\xb5\xd9\xa6\xf6\x5a\xe5\xd1\x3f\x59\x82\x13\xad\x04\xfb\x9d\xa2\x4e\x23\xf3\xd8\xd6\x20\x83\x77\x6a\x26\x1c\x74\x5c\x66\x4e\xe6\xd0\x2e\x52\x10\x65\x09\xd6\x4a\x30\xd6\x8b\x62\x79\xb2\x55\x16\x91\xd0\x3c\xe2\xc1\x06\xb2\xc0\x34\x43\xfb\x35\xda\x7d\xc1\xd4\x7d\xf9\xd0\x9b\x75\xf3\x18\x1a\x12\x74\x54\x33\x66\x5f\xb0\xd6\x84\x41\x38\xae\x13\x03\x00\xe1\xeb\xfa\x79\xda\xc5\x9f\x70\x8f\xa6\xf0\x0b\x72\x67\xc2\x6b\x09\xd8\xb4\xcd\x87\x46\xb6\x48\xfb\xd9\xd6\xd1\xc8\x76\xe8\xa4\x12\x8c\xa8\x88\x09\xd7\xbf\xcb\xd6\xa8\xac\x13\xaa\x3a\x90\x32\xbc\x30\xd7\x89\x54\x1d\x48\x09\x7e\x62\xae\x13\xab\x3a\x60\xf3\xb3\x2f\xdb\xb0\x5f\xb6\x61\xbf\x6c\xc3\x0e\xa3\xcd\x2f\xdb\xb0\xff\x94\x6b\xbc\x61\xb4\xf3\x1a\x6f\x18\x8d\xae\xf1\xea\x73\xb6\xe1\x1a\x6f\x18\x7d\x59\xe3\xbd\xf6\x35\xde\x30\xda\x76\x8d\xd7\x24\x9c\xee\x1a\x2f\x08\xc8\x7d\x68\xbb\xd9\x3b\x33\x6f\xcd\x52\xef\x4f\xbd\x35\xbb\x89\x82\x3f\xe4\xe1\x82\x06\xcf\x97\x55\xe0\xee\x2a\xf0\x26\x82\x3d\xd5\x83\x4d\x14\x68\xbf\xbf\x8e\x02\x95\xa5\x1b\x6a\x1c\x68\x79\xa2\x77\xca\xe9\xa6\xf5\xef\xc5\xf1\xb3\x9f\x9e\x3d\x7e\xfc\xf2\xd1\xab\x97\xfd\xd5\xe2\xe7\xdf\xfd\xf4\xdd\x0f\xdf\x3e\x7a\xfd\x68\xf8\x2a\xf7\x8b\x67\x7f\xff\xe1\xdb\x9f\x1e\x3e\xfb\xe1\xe5\xab\x07\x3f\x34\x2d\x35\x74\x72\x59\xf9\xe1\x76\xcb\xca\x5a\x8b\xd5\x6c\x59\x27\x6d\xe9\xad\x49\xd7\xa8\xc5\xec\x1a\x4f\xd1\xa5\x2d\x55\x79\x25\x97\x44\x2a\x74\x1f\x91\xe0\x1e\xaa\x0c\x4b\x22\x5a\x9f\xdf\x6c\xd0\x3e\x0a\xd1\x6d\x74\x29\x6f\x0f\x56\xf5\x25\x4d\xf8\x44\xf6\x60\xa5\x12\xfd\x0d\x45\x83\x58\x04\xc2\x40\x7e\xf1\x1a\x1d\xa1\x4b\xf4\x37\x14\x9a\xa2\x44\x7e\xf1\x9f\x02\x2a\x41\xb7\x91\xc0\xe3\x0b\x3c\x7b\x86\xca\x1b\xb9\x2c\xf7\xba\xf7\xf3\xa5\xfc\xf9\x3f\x2d\x4b\xc1\x1a\xdb\xce\x4a\x54\xc2\x73\x02\x06\xa6\x35\x9c\xd9\x48\xce\x6c\xe4\x05\xcd\x8d\x81\x31\x4d\x55\xc9\x5d\x74\x29\xab\x5e\x5a\x96\x95\x5a\x05\xe9\xb2\xf1\x12\x1e\xf8\x19\xf6\x5a\xf0\xb5\xdf\xf5\x8f\xa3\x7d\xeb\xed\x72\x74\xb5\xe1\xc9\xe3\x97\x2f\x04\xad\x1b\x0f\x9b\x94\x41\x7f\x77\xc2\xb2\x3e\x26\xaa\x01\x8a\x5a\x59\x9f\xae\x2f\x7a\xba\x65\xac\xf6\xa4\xae\x66\x61\xa1\x7a\x79\xe2\x67\x74\x1f\xc5\xf7\xd0\xcf\x8e\x95\x39\xe8\x03\x5c\x4d\x35\x67\x45\xa9\xd1\xa7\x65\xf5\x7c\xb9\x86\x3c\xae\x42\xab\xe0\xb1\xdc\x9f\xf7\xd0\x1d\x64\x3a\x4d\x5d\x03\xd7\x1b\xdd\x47\x2a\x5f\x84\xa9\xb2\xf8\x1b\x74\xf0\xdd\x11\x02\x34\x1a\x14\x0b\xae\xee\x89\x6a\x1d\xeb\xd7\x47\x80\xd6\x7e\xb8\x7a\x80\xf9\xa9\x86\xb9\x03\xea\x8e\x61\xde\xd3\x10\xb0\xdd\xd2\x92\xa6\x58\x0b\xbe\xa9\x40\x81\x46\xc4\x42\xed\x27\xd1\x0f\x0f\xd1\xf3\x55\x79\x5a\x56\xe5\x07\x8e\xce\x96\xf3\xcb\xc5\xf2\xb4\x64\x73\xb4\xfc\xc0\x57\xe8\x3f\x1e\x4f\xc8\xde\x5d\xb4\x79\x47\xd1\x3e\xda\xbc\x8b\xe0\xdf\x10\xfe\x0d\x84\x9b\x31\x83\x54\x1a\x2d\xd1\xcb\xfb\x03\xef\x90\xb7\x89\x1d\x47\xe6\x2d\xc4\x29\x08\x47\x46\xfd\x18\xd9\xf4\xea\x39\x78\xb9\xc6\xa7\x86\x9f\x3a\xc1\x58\x5f\x66\xd3\x81\xfe\xec\xed\xba\x9b\xb2\x06\xfb\xa9\xf8\xe9\xd9\x72\xc5\x56\x97\x9d\x97\xe8\x84\x09\xbc\xd2\x07\x22\xeb\x2e\xa5\xf1\xd5\x19\xb3\xf5\xbf\x32\xf6\x6c\x8c\xee\xde\xde\x8e\xbf\xdd\xce\x8e\xdf\xd9\xd7\xf1\x5d\xbb\x3a\xd7\xff\x94\xc0\xf2\xbc\x3a\x3b\xaf\x9e\xc0\xd4\xba\x53\x17\x41\x90\x9e\xf3\x75\xb9\xe2\xb9\xf6\xd0\x40\x5a\x56\xeb\x3a\x21\xb4\x6c\xdc\x99\x2d\xd4\x8d\x9f\x2d\xe6\xb5\x98\xb4\x1c\xdc\x6c\xc5\xef\x22\x42\x82\x29\x22\x61\x34\x45\x3e\x0d\xa6\x28\xc4\xa4\xdf\x58\xbd\x59\x70\x57\x94\xe9\x45\xfd\x47\x0b\xea\x49\xb3\xf5\xdd\x02\xbd\x77\x3d\x68\x57\x78\xbf\x00\x56\x6a\xe1\x25\xc4\x7a\xee\x5d\x7f\x7b\xf3\xd6\xe2\xed\xb7\x50\x35\xf1\x07\x70\xa4\xca\x2d\xf8\x45\xa3\x76\xb0\x09\x37\x96\x4a\x00\x28\x69\x5e\xeb\x85\x11\x20\xf2\x3c\x74\x07\x89\x81\xb6\x79\x29\x41\xe7\x84\x88\x5e\x7c\xf2\xb9\x76\xf4\x0c\x0b\x73\x06\xa6\x19\x17\xcf\xea\x4e\x3c\x61\x0b\x58\xfb\xe9\x75\xed\x10\x11\xd3\x1a\x5a\xba\x5e\xae\xd2\x71\xfe\xf7\xc0\x7f\x4a\x26\xc1\xa7\xa4\x44\xdd\x4d\x31\xc1\x6b\xeb\xb2\xf9\x53\x02\x6f\xd0\xf7\xab\x0b\x5f\xef\x4a\x66\x61\x7d\x82\x5a\xa0\x77\xe6\x13\x24\x9d\x44\x82\xe4\x2a\x19\x04\x49\x27\x75\x20\xb9\x7a\xce\x40\x45\x30\x1e\xa3\x18\x77\x49\xc6\x57\xa2\x19\x77\x89\xc6\xbb\x50\x6d\x94\x83\x54\xae\x66\x69\xa4\x5c\x54\x4b\xa9\xcd\x66\x49\xcf\x19\x2c\xe6\xd5\xe6\x6c\x60\x85\xa8\x71\x00\xef\xcd\xbe\x3b\x02\xbe\xd8\xea\xcc\x97\x17\x48\xd5\x19\xdf\x8d\x78\x21\x06\xd8\xb5\xc5\x06\x64\xa0\x0c\x76\x20\x3f\xca\xa0\x17\x3e\xdb\x4d\xe0\xd5\x8c\x57\x6c\x58\xb2\xc3\xac\x41\x03\xf6\xb4\x14\x53\x90\xf9\xf9\xe9\x02\x3a\x67\x30\xab\x9a\x83\x75\x98\x3d\x45\x6d\x24\x6d\xac\xbc\xe3\x9c\x44\xc7\xd1\x91\x52\x3b\x43\xb1\x20\x12\x7f\x75\xe8\xd9\x48\xcf\x55\xf7\x89\x56\x77\xbe\xbc\xb0\xc6\xa5\x56\x6e\xbd\x32\xc6\x39\xa6\x9e\xbc\x12\x52\x78\xf5\x66\x63\xa3\xfd\xd5\x46\xea\xda\x11\xf4\xc0\x5e\x09\x94\xed\x08\x48\xdf\xee\xf4\xcd\xd5\xd4\xc0\xe1\x56\xdb\x1e\x05\xd0\xa5\x89\x90\x4b\x00\xd3\x43\xd7\x66\xf9\xab\x0d\x6e\xab\xe3\x6d\xaa\x4b\xfd\x7a\xb5\xc1\x2e\x39\xaa\xba\x4f\x9a\xba\x20\x47\xa7\x7a\xaf\xcf\x57\x60\x51\xf2\x39\x11\xa1\xea\xe3\x5a\xfe\x6a\x13\x28\x5f\x80\x26\x13\x45\x5b\x73\x35\x58\xe1\x57\xf7\x83\x6d\xd3\x1b\x80\xf6\xa4\x81\x26\xbd\x86\x84\xf6\xa4\x07\xed\xe9\x38\xb4\x3f\xd4\xa8\x3a\xae\xd0\xa1\x9f\xa8\xef\x12\x2d\x6a\x8a\x76\x9a\xed\xbd\x98\x2d\xd1\xf3\xd2\xa1\xd9\x02\x65\xfd\xe6\x23\xbe\xa7\x7d\x95\xa1\x5c\xf3\xfd\x93\x55\xbe\xc3\xb9\x06\xac\x4b\x8d\x45\x25\xa9\x41\x63\x0e\xa9\xae\xfd\xa4\xad\x6d\x77\x49\x30\x58\xcc\x96\xcf\x64\x94\x72\xd4\x59\x0f\xd3\xe9\xb2\x76\xf6\xc5\x12\x02\x3d\x87\x8b\x17\x13\xe8\x16\xc5\xe8\xc2\x83\x66\x2b\x93\xba\xd3\xf7\xef\xb7\x44\x82\x6a\xd7\xfd\x83\xa7\x34\x7d\x82\xee\x68\xe5\x36\x45\x47\x5d\xd3\x69\x60\x18\x81\x3f\xdd\x11\x78\x77\xcd\xa3\xed\xee\x56\x2b\x1e\xfd\x2e\x2b\xaa\x34\x30\xb0\xda\x31\x24\x2e\x0a\xae\xdc\xf3\xa7\x23\x38\x9e\xec\x88\xc3\x35\xb6\xad\xd8\x62\x7d\xb6\x5c\x3b\xb5\x04\xdc\xef\xf3\xf2\x89\x34\x8c\x57\x6f\xb4\x05\xc5\x56\x0f\xad\x63\x9e\x6c\xb8\xcd\xc0\xa7\x6a\x8e\x8d\x7e\x56\xff\x71\x56\x22\x56\xc1\x10\x08\xfe\xd2\x1c\x13\xbe\xf2\xa0\x0f\xc6\xa4\xad\xcd\xe4\xc8\x6b\x1c\x80\xb1\xde\x2b\xaf\xee\x8e\xac\x6d\x33\xf9\x57\x5e\xdd\x19\x55\xcf\x32\x6e\x1d\x1e\xa2\x87\x33\x97\xf3\xdb\x7e\x58\xbf\xe2\x90\x31\xee\x1a\x91\xe6\xbe\x6a\x3f\xdc\x8c\x2b\x23\xca\xbd\x9b\x4b\xad\x5b\xbd\x6a\x14\x6e\xfb\x26\x1b\xdc\x34\x9a\x68\x41\xc8\xde\x36\x03\xa0\x04\x40\x7a\x00\xc8\x00\x80\x93\x8b\x22\xf6\x58\x2d\x2f\x1c\x4c\x9c\x6b\xd6\xf0\xaa\x35\x8d\x77\x68\xf2\xbb\x22\x5f\xfe\x70\xb3\x26\x06\xbe\xba\xfc\xc7\x5c\xb3\x9a\x57\xad\x09\xe9\x10\xe1\x87\x16\xe2\x7c\x79\xf1\xe9\x0b\xb4\xdf\x2d\x4d\x33\x92\x81\xbc\xad\x96\xd6\x59\x86\x14\xe3\x5b\x6f\x31\x13\xca\x47\x27\x6d\x1d\x28\x36\x43\xec\xc4\x2b\xdd\x16\xc2\x24\x1d\x9b\x1d\xff\x5c\xc7\xa2\x0c\x8b\x34\xd7\x7e\x2a\x6a\x50\xbf\x59\xf1\x11\xed\x86\xcb\x40\xb7\x61\xf1\x6a\xb8\x0e\x74\xd5\xb3\x54\xf8\x2a\x47\xa9\xe0\x90\x54\xc6\xcb\x79\xf7\xbc\x13\xde\x43\x87\x5d\xfa\xf7\xd0\xed\xfe\x0f\x80\x1c\x36\x68\x9a\xd3\x5c\xff\x24\x87\xa0\x3e\x79\x0d\x4f\x5f\x66\xac\x89\x37\xae\x41\xa2\x43\xa3\xe8\xf5\x2a\xf5\x2a\xe0\x10\xe6\xa1\xf1\x30\xdd\xcb\xff\x3a\xe7\xfc\x17\x3e\x04\x3a\x63\xeb\x59\xad\xdc\x5b\xbd\x45\x3f\xa0\xe2\x53\x16\x0b\xc7\xd7\x84\xb6\x0f\xe9\x6d\xe1\xfc\xee\x6b\x88\x2d\x3e\xfb\xaa\x9c\x16\x1a\xaa\x85\x39\x3d\xe0\xdc\x69\x6d\x4e\x03\xa5\x96\xe7\x74\x50\x57\x5d\x57\x6c\x59\xe1\xee\xc4\x93\x41\x27\x9e\x5c\xb5\x13\x4f\x06\x9d\x78\xb2\x5b\x27\xcc\xa2\x92\xaa\xab\x8c\xac\x5a\xa2\x15\xaf\x56\x25\xff\xc0\x0d\x07\x10\x91\xba\xdc\x2d\xfd\xc1\xd9\xf9\x7a\x56\x93\x61\x62\x91\xa1\xe6\xd3\x61\xcd\x4f\x4f\x4f\x6c\xb8\x3d\xd4\xa0\x9e\x0e\x4d\xd8\x7a\x9f\xe8\x9a\x4e\x4d\xda\xfd\x97\x3a\x42\x69\x70\x67\xcd\x65\xa7\x2d\x3c\xc4\x96\x9b\x39\xf5\xc7\xf6\x7c\xa6\x93\xed\x5f\x8e\x6b\x5e\xf1\xb8\xa6\xbf\xeb\x61\x4d\x7f\xec\xa8\xa6\xef\x38\xa8\xe9\x7f\x39\xa6\x79\xdd\xc7\x34\xfd\x2d\x0f\x69\x1a\xc4\xd2\x39\xa2\xe9\x6f\x73\x40\xd3\xb7\x5f\xc3\x6f\x0e\x1e\xde\xa5\xc1\xc7\xb7\x53\x8a\xff\x45\x8e\x6b\xf6\x13\xec\x84\x98\xfc\x61\x67\x38\xeb\x74\x3b\x02\xe7\x9f\x2b\xdd\xce\x95\x4e\x5b\xaa\xe2\xf6\xb4\x67\x5d\x67\xa7\x84\x3c\x21\x26\x9d\x63\x21\x21\x26\xd6\x63\x26\x74\xcb\x84\x3c\xa2\x62\xe7\xa8\x09\x55\x59\x2d\x42\x4c\xae\xed\x0a\xb1\xde\x7d\x6b\x4e\x9e\xc1\x21\x07\x6f\x93\xa5\x69\x9a\xe4\x61\x3e\xd5\x12\xf6\xec\x4d\x4d\x35\x23\x92\x30\x92\x10\xa6\xa7\xf3\xd9\x33\xe4\xed\x31\x34\x4d\x70\x98\x78\x38\x64\x7a\xf6\x1f\x33\x12\x1c\x92\x82\x67\x32\x67\x50\x9d\x1b\x68\x4b\x24\x51\xec\xfb\x24\x8a\x64\x5a\x21\x95\x39\xc8\x8c\x84\xf2\x34\x08\x18\x8d\xf5\xbc\x42\x5b\x22\xc9\x53\x2f\x23\xdc\xcb\xf5\x34\x44\x66\x24\x41\x9c\x86\x01\xc5\xb9\x9e\xa4\xa8\x17\x9a\x5e\x77\x96\x22\xa1\x4f\x57\xcc\x52\x84\xa3\x2f\x69\x8a\xae\x29\x26\xa2\x3b\xa7\x29\x12\x4d\xc6\xe2\x22\xdd\x67\x0c\x23\x23\xfa\x25\x4d\xd1\xf5\xc7\x46\x74\xdb\x34\x45\x46\xe1\x74\xe3\x23\x3a\x9a\xa6\xc8\xa7\xee\x34\x45\x62\x18\xbf\x4b\x89\x29\x5a\x22\xff\x22\xd1\xd2\xbf\xf4\xe5\x96\xeb\xbd\xd8\xf2\x99\xae\xac\x5c\x3d\x88\x92\x45\x4d\x77\x15\xa0\x9f\xea\x13\xbc\x86\xb7\x6e\xba\x87\x7c\x0f\xd8\xd9\xd9\xfc\x72\xa2\x7e\x9c\x22\xb6\x3a\x39\x3f\xe5\x8b\x6a\xdd\x7f\x93\x47\xbf\x3e\xd3\xd2\x03\xa9\x94\x5a\x14\x3d\xf4\xde\x26\x20\x94\x91\x22\x81\xb8\x22\x8f\x09\x65\x9c\x90\xbd\xe9\xb0\x5e\x8c\xfd\x38\x08\x12\x48\x33\x48\x7c\x5e\x44\x61\x96\xeb\xa1\xc1\xa0\x41\x1a\x66\x5e\x91\x66\x05\x3c\x80\x90\x05\xb9\x9f\x92\xc2\x04\x98\x27\x69\x98\xa7\x2c\x84\xd7\xb3\x31\x4d\xf2\x34\xcd\x9c\x80\xfd\x24\x8c\x32\x12\xa6\x10\xce\xf8\x01\x4d\x43\x9f\x9a\x00\x87\x49\x81\x31\x2e\x80\xe2\x34\xf2\xc2\xdc\xc3\x89\x13\x70\x42\xfc\x82\x12\x06\x4f\x6e\xb3\x02\x27\x41\x91\xa4\x26\xc0\x2c\xc5\x59\xc8\x73\xa0\x38\x67\x51\x4e\x31\xa6\x4e\xc0\x39\xf5\x62\xc6\x24\x8f\x99\xef\xf9\x1e\x09\x8c\x3c\xc6\x84\xfa\x61\x2a\xdf\x8c\x08\xc2\xd8\x8b\x8a\x94\x3b\x01\x93\xc0\xc7\x34\x4c\xe1\xed\x88\x80\xf3\x20\x25\x34\x33\xb2\x22\xf4\xb2\x38\xcf\xe0\x01\xf1\x3c\x2c\x8a\x34\xe0\xc4\x09\x38\x26\x29\x0f\xf3\x18\x58\x51\x90\x38\xa5\x49\x64\x14\x1e\xf5\x72\x9e\x62\xf9\x78\x85\x9f\xe2\x28\x89\x52\xec\xe6\x71\x9a\x67\x5e\x24\x33\x54\x92\x30\x8b\x31\xf1\x43\x13\xe0\x0c\x27\x69\x81\x25\x01\x59\x11\x25\x24\x4a\x02\x27\x60\x1e\x24\x69\x94\x64\xc0\xbb\x84\x17\x38\x60\xb9\x91\xc7\xbc\x48\x79\x10\x53\x78\x46\xdc\xa7\x41\x41\x42\xee\x3b\x01\x7b\x45\x86\x93\x3c\x83\x06\x34\xa5\x59\x1e\xa6\x46\x8a\x49\xe0\x65\x0c\x67\x19\x3c\xd2\x1e\xb3\x2c\xc9\xa2\xd0\x2d\xbc\x9c\x27\x24\x8b\xc0\x40\xc2\x84\xa4\x1e\x89\x8d\x80\x03\x16\x07\x34\x60\x30\x47\x88\x38\x8b\x78\x40\xdd\x14\x87\x59\xea\xb1\x24\x07\x4a\xd2\x3c\xc0\x45\x9a\x07\x46\x93\x8e\x8a\x84\xd2\x1c\x00\x53\x1f\xe3\xd0\x4f\xdd\x14\x27\xd4\xe7\x21\x0e\x09\x98\x34\x8f\xa2\xbc\x60\x66\x03\xa1\x3e\xce\xa2\x08\x22\x7c\x92\xa7\x81\x4f\xb0\xe7\xf6\x15\x9e\xe7\x93\x38\xa3\xf2\xcd\xf7\x22\x25\xd8\x37\xaa\x5b\x5a\x84\x49\x5c\x64\x2a\xbf\x29\x2f\x3c\xce\xdd\x5a\x91\x45\xdc\xf3\xd2\x02\x14\xdf\xcf\x19\xa5\x45\x66\xd4\x8a\x3c\x64\x71\x82\x03\x00\x9c\xf8\x1e\x63\x31\x71\xb3\xc2\x8b\x32\x16\xf9\xa1\x7c\xde\xc5\xf3\x7c\x4a\xcc\x06\x82\x03\x92\x90\x44\xce\xbd\x3c\xe6\xf1\x88\xc7\x6e\x56\x90\x38\x8d\x3d\x46\xc1\xb9\x04\x51\x4e\x48\x51\x18\x4d\x9a\x70\x2c\xd8\x04\x2c\x0b\x33\x12\x65\x09\x89\x9c\x80\x83\x9c\x64\x51\x5e\x80\x56\x84\x2c\x0b\x08\xe3\xb9\xd1\x57\xf8\x3e\xf5\x72\x0c\x2c\x4b\xf2\x24\x4c\xfd\xbc\x70\x02\x8e\x42\x8f\xc5\x7e\x18\x48\x03\x61\x45\xe4\xe7\xdc\xac\x6e\x11\xf3\x58\x0a\x7e\xdb\xcf\xe2\x38\x25\xcc\xed\x36\x29\xce\x48\x96\x10\xe9\xdd\x62\x9e\x33\xce\x23\x13\xe0\x84\xc4\x84\x64\x92\x65\x38\xa0\xc4\x0f\xfd\xd4\x09\x98\x91\xb4\xe0\x94\x49\x3f\x9b\x15\xd8\xf3\x23\xa3\x81\x30\x8a\x59\x14\x05\x40\x71\x9a\x05\xc4\xf7\x3c\xb7\x77\xcb\x48\x90\xd2\x34\xf6\xc0\xcf\x7a\x05\x4d\xe2\x04\x1b\xbd\x5b\x1c\x65\x21\x66\xc0\x63\x2f\x0a\x83\x94\xfb\x6e\xad\xc8\x71\x42\x38\xc5\x09\x00\x8e\x78\x11\x12\x6c\x1c\xf3\xf2\x28\x49\xbc\x88\x80\x2c\xc2\x30\x0a\x59\x32\x62\x79\x45\xe0\x71\x3f\x94\xbc\x0b\xe3\x18\x13\x8f\x30\xa3\x1e\x7b\x11\x63\x9e\xec\x99\x4f\xd2\x34\xc7\xa9\x5b\x78\x38\x61\x41\x86\x31\xb8\xcd\x94\xe6\x24\xf7\x32\x23\xc5\x98\xfb\x71\x94\x79\x52\x8f\x71\x80\x59\x1a\xba\xbd\x1b\x89\x03\x1a\xc7\x01\xe8\x71\x5e\x50\xce\xd3\x24\x31\x01\xf6\x83\xd4\x4b\xb3\x14\x7a\xc6\x71\x92\x06\x74\x44\xdd\xfc\x04\x67\x5e\x96\x82\x50\xb2\x30\x4b\x42\x16\xf9\x46\x7f\xcc\x73\xca\x58\x00\x6e\x93\xfb\x01\xa6\x2c\x73\xab\x5b\x98\x26\x59\xc6\x82\x42\x8e\x0c\x91\xcf\xfd\xd8\x08\x38\xa2\x84\x47\x85\x74\x56\x79\x94\x92\x94\x32\x37\x2b\xe2\x80\x16\x94\x70\x30\x90\x30\xe7\x45\x4a\xcc\xbe\x22\xa6\x2c\x8c\x7c\x39\xd2\x04\x3e\x8e\x49\x11\xb9\xb5\x82\x06\x19\x8d\x29\x96\x91\x10\x2e\x3c\x96\xc6\x46\xb7\x49\xb3\x2c\xf6\x88\x14\x1e\x66\x51\xe0\x27\xdc\x1d\xbb\x25\x5e\xca\x8b\xa2\x60\x32\x8a\x8c\x7c\xcc\x89\x51\x2b\x58\x10\x7a\x51\xc6\xc1\xf2\x72\x4e\x49\x9a\x73\x77\xec\x96\xf2\x22\x61\x7e\x21\x47\x06\x92\x45\x71\x82\xcd\x71\x45\x14\xe3\x98\x16\x72\x08\xf3\x63\x12\xfa\xc4\x2d\xbc\x8c\x91\xd8\xe7\x19\xf0\x98\x33\x12\x45\x38\x31\xf2\x38\xc7\x34\x4a\xa9\x1c\x9a\x88\x50\x24\xd2\x5d\x04\x1c\x06\x22\x2c\x67\x71\x9e\x83\x81\x64\x39\xf7\x78\x8a\x8d\x6e\xb3\x08\xe3\x3c\x28\xe2\x42\x0d\xba\x3c\xc7\xb1\x5b\x8f\xbd\xa8\xf0\xa2\x58\xc6\x0b\x31\xc1\x71\x54\xa4\x46\x93\xf6\x58\xe4\xc7\x79\x06\x06\xc2\x48\x46\x13\xca\xdc\x23\x08\xc6\x7e\x91\x50\x2f\x50\x0b\x77\x89\x97\x33\x23\xc5\x38\x8d\xb1\x97\xfa\xd2\x1f\xfb\x38\x0b\x62\xec\xe6\x31\xa1\x79\x1a\xc7\x45\x28\xb5\xc2\x0b\xe2\x9c\x1a\xfd\xb1\x4f\x32\xc6\xd2\x18\xb4\x22\xf0\xb2\x98\x04\x89\xdb\x40\xfc\x2c\xe1\x29\xf7\x80\x15\x38\xcc\x92\x94\xa7\x46\xe1\x05\x3e\xce\xa3\x38\x83\x9e\x25\x19\xf6\xbc\x3c\x70\xeb\x71\x90\x65\x61\x1e\xc8\xc0\x3b\x4b\x7d\x1e\x90\xd4\x38\x34\x89\x70\x85\x24\x09\x38\xab\x22\x8b\xc2\x98\x0b\xf7\xea\xf2\x15\x45\x96\x46\x05\x93\x83\x24\xcb\xa3\x82\x71\x23\xc5\x51\x16\x04\x38\xa1\x00\x38\x60\x41\x1c\x52\x1c\xab\x45\xd4\xb7\x8e\x6b\xab\xed\xbc\xf0\xc7\xab\xde\x50\xb5\x3d\x83\xf6\x63\xe7\x86\xea\x4f\x57\xbb\xa1\x1a\x62\xb2\xdd\xd6\x81\x61\x3b\xe2\xfa\xb3\x8f\x5e\x75\xeb\x20\x62\x5e\xc2\xeb\x05\x77\x3f\xcd\xb2\xc4\xb3\x6c\x1d\xa4\x69\x14\x33\x2e\x87\x5f\x1a\x64\x8c\xc5\xdd\xd0\xc5\x81\xc4\xcf\x22\x5e\xf8\x31\x78\xb2\x82\x27\x41\x41\x85\x27\x33\xd5\x64\x61\x50\x14\xa1\x0f\x56\x10\x16\x38\xf7\xa3\x62\xdb\x55\xfd\x10\x7b\x3c\x24\xd2\xf9\xb0\x9c\x47\x94\xe4\x96\xad\x83\x24\xf5\xc2\x88\x4a\x85\x24\xa9\xcf\xa3\x0c\x17\x5b\x22\xc1\x05\xf5\xf3\x44\xea\x7c\x91\x06\x38\xcd\x23\x4b\x4f\xc2\x94\x7b\x59\x2e\xc3\x20\xec\xc7\x9c\xe0\x38\xd9\x65\xeb\xe0\xba\xef\x91\x6e\x93\x1a\x16\xea\x79\xf6\xcc\xaf\xc7\xd8\x9e\xfa\xf5\x98\xd8\x73\xbf\x1e\xfb\xf6\xe4\xaf\xc7\x81\x3d\xfb\xeb\x71\x68\x4f\xff\x7a\x1c\xd9\xf3\xbf\x1e\xc7\x96\x04\xb0\xb2\x83\x90\x1e\xd6\x78\x0e\x5c\x96\xcf\x65\xf9\xf0\xb2\x87\xe4\x01\x34\x37\x5e\x81\x92\xe5\x73\x59\x6e\x69\x4e\xa0\x39\xb1\x36\x27\x73\x59\x6e\x69\xee\x43\x73\xdf\xda\xdc\x9f\xcb\x72\x4b\xf3\x00\x9a\x07\xd6\xe6\xc1\x5c\x96\x5b\x9a\x87\xd0\x3c\xb4\x36\x0f\xe7\xb2\xdc\xd2\x3c\x82\xe6\x91\xb5\x79\x34\x97\xe5\x96\xe6\x31\x34\x8f\xad\xcd\xe3\xb9\x2c\x37\x1c\xeb\xdb\x32\xe9\xb1\xd4\x0c\x13\x70\x26\x95\xa2\x9f\x71\x0f\x8e\xdc\x4a\x85\x30\xb5\x4a\xa5\x2e\x98\x5a\x65\x52\x0f\x4c\xad\x32\xa9\x02\xa6\x56\xb9\x14\xbf\xa9\x55\x2e\x25\x6f\x6a\xc5\xa5\xd4\x4d\xad\xb8\x14\xb8\xa9\x55\x21\x85\x6d\x6a\x55\x48\x39\x9b\x5a\x9d\x48\x19\x9b\x5a\x9d\x48\xf1\x9a\x5a\xcd\xa4\x68\x4d\xad\x66\x52\xaa\x73\x53\xde\x41\xd7\xd5\xdd\x2d\x9f\x43\xb5\xe6\xd3\xae\xf1\xff\x58\xca\xdc\xc3\xb6\xeb\xe6\x8f\x60\x04\xaf\xb7\xcf\x86\x55\xb6\x48\x14\x2d\xd1\x08\x16\xfc\x58\xd6\xb7\x0d\xf4\xac\xd1\xe8\x36\x22\x6f\xa1\xa6\x39\x97\x6b\x0b\x63\x2e\x61\xa8\xfb\x05\x7d\x18\x70\x6b\xfe\x4a\x19\xa8\x0f\x0f\xd1\x7f\x40\x36\x62\x3b\xf2\x3a\xa5\xf3\x4e\x19\xaa\x37\xb3\x26\xcf\xf1\x66\xec\x2e\x9e\xaa\x36\xd7\x5a\xb8\xef\xe3\xc9\x5a\xb3\x4e\x16\xec\x99\x4c\xfe\xab\x27\xaf\x9e\x43\x8a\xe2\x3a\x1d\x70\xa7\x1e\x1d\xd4\x83\x43\xaf\xef\x50\xb7\x5a\xec\xba\x61\x2a\x6b\xce\x3b\x54\xcc\x87\x54\xcc\x4c\x54\xcc\x87\x54\xcc\x74\x2a\xba\xf5\xe2\x61\x3d\x4b\x26\x63\x5d\xa4\x96\x9c\x39\x1f\xb4\xdc\xdb\xbb\x24\xdf\x6e\x25\x8a\xb7\x93\x28\x6e\x25\x8a\xb7\x92\x28\x9e\x75\x12\x7c\xcf\xea\x2c\xdc\x5a\x62\xee\xb9\xca\xd5\xad\x31\x09\x2b\x0e\x77\xab\xc1\x39\xe6\x44\x13\x69\x0d\x2f\x1a\x15\x29\x9e\x77\xc8\x98\x1b\xc8\x98\x99\xc8\x98\x0f\xc8\x98\x75\xc8\xe8\x02\x8c\x06\xf0\x48\xe4\x94\xe9\x4e\xb9\xc3\x5d\xae\x24\x6e\xc5\x1e\xbb\xc4\xfe\x63\x19\x4b\xcf\x65\x1c\x98\x7b\x35\xe7\xaa\xa6\xe3\x4e\xb8\xac\x89\x23\xcd\x91\x58\x5f\x85\xae\xeb\x4a\x02\xb0\x31\xb2\xe8\xd7\x9d\xd7\x75\x47\x69\x68\x3d\xcd\x5c\x30\xad\x8c\xfb\x23\x57\xb7\x7a\xeb\xca\x66\xb2\xfa\x0c\x72\xb6\x09\x38\x42\x92\xde\x1e\xba\x5f\x5b\x67\xf3\xcb\xff\x8f\x30\xba\x8b\x06\xc7\xa6\x87\x74\x88\x7f\x6b\x09\x8e\x93\x21\xfe\xdd\x6f\xac\xc5\x42\x05\xbe\x2a\x15\xc0\xc5\x2d\x69\x90\xd2\x19\x52\x20\x25\x31\xc0\x6f\x06\xda\x8e\x8a\x3f\x96\x36\xf1\xb6\xa3\xde\x8f\xa5\x89\x38\x7b\x4e\x7c\x95\x14\x7f\x86\x6e\xa2\x62\xa6\xd2\xe2\x8b\x2f\xe6\x7b\x7c\xb2\x8d\xb4\x7d\x3e\x17\x6d\xe6\xaa\x8d\xf8\x72\x32\x77\x24\xd3\x9f\x41\x36\x7d\x01\x3a\x95\x78\xe0\x73\x26\x3f\xa7\xea\xb3\xbd\xf9\x1c\x9a\x0b\x2c\xa9\x44\x09\x9f\x33\xf9\x39\x55\x9f\xdd\x29\xf9\x67\x32\x27\xbf\x72\x38\x72\x5c\x61\x73\x99\x5e\x7a\x4f\x26\x3f\x60\xb3\x3a\x63\xbf\x2a\xec\xe4\xec\x9f\x69\xaf\x48\xb0\x7a\xd4\x71\x66\xe6\x87\xd9\xd4\xa4\x01\xa4\x70\xce\xba\x38\xe7\x1d\x9c\xb3\x2e\xce\xb9\x8e\x73\xb6\x0d\x4e\x2c\xfb\xc9\xd5\xd0\x20\xef\x9b\x70\x39\x28\xd0\x3a\xed\xff\xac\x7e\xb4\x42\x2b\x0c\xda\x42\x81\xd3\xaf\xcb\x64\x1a\x6e\x37\x4e\xd9\x4f\x55\xb9\xc6\x39\xeb\xe2\x9c\x77\x70\xce\xba\x38\xe7\x3a\xce\x59\x8b\xd3\x18\x75\x8e\xbf\x43\x60\xa6\xf5\x7b\xc8\xbe\xf4\xbd\xfd\x32\xd5\xf7\x60\xbc\xdf\x97\xae\x6b\x54\xdf\x83\x33\xf8\xbe\xb4\xb9\xd0\x0f\xf0\x50\x82\xa8\x33\x9b\x37\x24\x9a\x8c\x52\x56\x14\x08\x67\x6d\x5f\xa4\xbb\xa8\xb0\xee\x2e\x66\xdb\xf8\xaa\x16\xad\xf8\x57\x70\xc4\x8d\xb3\x02\x54\xd9\xcc\x84\x30\xbb\x12\xc6\xef\x8d\xae\xa7\x8f\xf1\xfb\xd2\x84\xf1\xfb\xf2\x2a\x18\xcd\xce\xae\x8f\xf1\x47\x23\xc6\x1f\x4d\x18\xcd\xda\xd6\x7f\xbc\xc2\x82\x12\x16\x2f\x6a\xb3\x87\x8a\x56\xea\x60\x1d\xa4\xf6\x4a\xfb\xd2\x3d\x02\x89\x44\x27\xb1\x86\xb5\x1d\x99\x7f\x3f\xcb\x59\xc5\xd1\x85\x7b\xa6\x2f\xfe\x60\xbe\x69\xd4\x6f\x98\x6e\x9e\x98\xc8\x86\x01\xa8\x30\xb5\x81\x89\x6d\x61\x6a\x03\x73\x68\x6e\x6a\x03\x53\x68\x6e\x6a\x03\x53\xf2\x49\x3e\x87\xe7\x3b\xe6\xb6\xf7\x3b\x60\x4e\x3f\xc9\x67\x50\x4b\xb2\x8e\xeb\x9c\xcb\x07\x4c\xb3\xbe\x04\x22\x20\x65\x26\x1a\x61\x49\x21\x33\xd1\x08\xab\x17\xa9\xa9\x0d\x2c\x5e\xa4\xa6\x36\xb0\x4e\xc2\x4c\x6d\x60\x99\x64\xf0\x9a\x81\xf8\x83\x65\x97\x89\x54\xf5\x8a\x58\x99\x01\x0b\x37\x13\xc9\x07\xa1\x59\xfb\xed\x88\x23\xb9\x51\x0d\x83\x9d\x6b\x7d\xac\x44\x5b\x33\x84\xc8\xe0\x18\xf4\x9f\x0d\xa2\x81\xe3\x26\x19\xc5\xe4\x18\xf4\x9e\x49\x62\x8f\x3d\x9d\x5a\x36\x24\xb6\x0f\x47\x5b\x65\x94\x08\x81\x45\xe9\x10\x21\x6e\x11\x02\x7b\x52\x85\xb0\xe3\x09\xd2\x71\x84\xda\xba\xa4\x44\x48\xc0\xc5\x0e\x11\x92\x16\x21\x99\xd5\xe3\xd2\x04\xea\x6b\xee\x75\x1c\xa1\xb6\x92\x29\x11\xfa\x02\x61\x3e\x44\xe8\xb7\x08\x7d\x81\x2b\x57\x08\xfd\x11\x73\xe8\xc3\xd1\xd6\x3e\x25\xc2\x40\x20\xe4\x43\x84\x41\x8b\x30\x10\xb8\xb8\x42\x18\xe8\x08\xf9\x38\x42\x6d\xb5\x54\x22\x0c\x05\xc2\x62\x88\x30\x6c\x11\x86\x02\x57\xa1\x10\x86\x3a\xc2\x62\x1c\xa1\xb6\xbe\x2a\x11\x46\x30\xa9\x18\x22\x8c\x5a\x84\x10\xbd\x9f\x28\x84\x51\x67\x12\x31\x8e\x50\x5b\x91\x95\x08\x63\x81\x70\x36\x44\x18\xb7\x08\x61\xda\xa4\xc6\x64\x51\xdf\x15\x04\x7c\xf2\xdd\x8b\x2f\x8f\xe2\x5c\xdf\xa3\x38\x58\x04\xf7\xea\x65\x33\x01\x0c\xf2\xb0\xf8\xde\x75\x3f\x8b\x63\x46\x83\xff\x29\x1f\xc6\x79\xb8\x5c\x7c\xe0\x2b\x99\xe5\x17\x55\x4b\xe4\x93\x3b\x69\x59\x89\x00\x25\x47\x0c\xce\x67\xa7\xbc\x58\xae\xb8\x3a\x4e\x3d\x90\x9a\x76\xd7\x44\xdb\xbb\xab\x96\xaf\x7d\x72\x1d\x0f\xf1\xfc\x59\x9f\xe0\xd1\xe9\x6c\xf2\x83\xdc\x45\xd8\x23\xc1\xa1\xaf\xf2\x14\x7f\xb9\xdd\x64\xbd\xaa\x14\x62\xb2\xeb\xed\x26\xd1\x64\xe4\x76\x53\xe7\x58\xc3\xe0\x76\x53\x88\xc9\x97\xdb\x4d\xd7\x7d\xbb\x49\x48\x65\xbb\xdb\x4d\x46\xe1\x74\x6e\x37\x49\x01\x39\x6f\x37\xc9\x7b\xb4\x5b\xde\xfe\xf6\xff\xd4\xf7\x99\xf8\x22\xbb\x93\xb2\x35\x8f\x82\x5e\xc1\x69\x1e\xf6\xab\x7e\x38\x7b\x9f\x17\xbd\x1f\xb3\xf2\x6c\xc6\x57\x7f\xc8\x95\x28\x8d\x54\xf8\x2e\x28\x94\x05\x92\x30\xf8\xac\xd3\xf3\xaf\x70\x75\xea\xc7\xad\xde\x04\x82\xc3\x33\x0f\xa1\xeb\x4d\x3d\xed\xb7\xf1\xab\x50\x87\x87\xe8\x39\x5f\x9d\xc2\x28\xfa\x70\xb6\x2c\x33\x8e\x70\xff\xd9\x14\xd1\xfc\xf9\x43\xdc\xbd\xbb\x14\xc6\x53\x14\x24\x53\x14\xe0\x29\xf2\xfd\x29\x22\xe1\x14\xe1\x78\x8a\x92\x29\x42\x58\x3b\x6a\x14\xd2\x29\x0a\xbd\x29\x0a\xc8\x14\xf9\xc1\x14\x91\x68\x8a\x30\x9d\x22\xec\x4d\x11\xd1\xeb\x25\x53\x14\xe2\x29\x0a\xfc\x29\xf2\xc3\x29\x22\xf1\x14\xe1\x64\x8a\xb0\x80\xaf\xd5\x8b\xbc\x29\x0a\xc9\x14\x05\xc1\x14\xf9\xd1\x14\x45\xfe\x14\x85\xe1\x14\x05\xf1\x14\xf9\x89\x56\xd1\xc7\x53\x44\xfc\x29\xc2\xe1\x14\xc5\x53\x84\x22\x32\x45\x61\x30\x45\x01\x3c\x2d\xa0\x57\x14\x94\x90\x29\xc2\xc1\x14\x45\xa2\x22\x9e\xa2\xd0\x9f\xa2\x20\x9c\x22\x3f\xd6\x2a\x92\x64\x8a\x08\x9e\x22\x2c\x50\x4e\x11\x22\x74\x8a\x88\x37\x45\x58\x90\x23\xab\xbd\x75\xf0\x95\x98\xf9\x4a\xba\x7c\x15\x54\x08\x3e\x8a\x7e\x13\xf1\x79\x8a\x50\xa8\x53\xab\x10\x8b\x6e\x09\x6a\x81\x20\x4f\xa7\xd2\x57\x8c\x13\x54\x89\x0a\xd1\x14\xe9\xdd\xc5\x91\xe4\x87\x60\x30\x50\xef\x77\x05\x21\x04\x2a\x18\x2c\xf8\xe7\xc7\x92\xb1\x61\xd8\xe3\x57\xe0\x29\x69\x85\x52\xfa\x81\x8e\x41\x88\x46\xa8\x86\x2f\x44\x1a\x49\xb1\x87\xba\x0c\x85\x08\x84\x3e\x08\xbd\x10\x32\x14\x8c\xad\xa3\x9a\xce\x8b\x50\xe7\xa7\xe7\x73\x06\xcf\xa4\x88\xa0\x72\x3d\x2b\x8b\xc1\x0b\x4f\x60\x05\xdf\xbd\xfa\xe9\xe5\xf1\x77\x8f\xe5\x9b\x52\x82\x63\x64\x8a\xa0\xf3\x82\x43\x54\x68\xa4\x12\x13\x70\x57\x69\x2a\x56\xe2\x24\x4a\x7b\x81\x21\x54\xc7\xff\xf2\x9b\x67\xaf\xf9\x1a\xb1\x45\xae\x72\xa3\x9f\x81\x48\xe5\x7b\x1a\x06\x3a\x44\xfd\x9f\x9e\x77\xe5\xd9\x0b\x29\xbd\x8d\x77\x17\x26\x23\x94\x78\xde\xb4\x5f\x56\xcf\x15\x64\x15\x43\x05\xd2\xa9\x40\x3d\x8f\x0c\xaa\xf8\x5a\x95\x61\x69\xa0\x97\x1a\x10\x84\x5d\x04\xc4\x80\x20\xea\x12\x69\xaa\x12\xf7\xfa\x61\x40\x44\x3b\x84\x0c\x41\x24\x7d\x2c\x43\x10\x4c\xaf\x62\xaa\x90\xf6\xb9\x35\xac\x92\xf5\xd0\x0c\x2a\xe4\xfd\xae\x0c\xab\x70\xad\xca\x10\x43\xd1\xa5\x72\xd8\x9c\xba\x5a\x63\x3a\x2a\x0f\x42\x47\x10\xf8\x74\x44\xab\x82\x3e\x12\x83\x5e\x50\xb7\xde\x44\x74\x54\x31\x63\xea\x52\x4c\x4a\x47\xe5\x9d\xd0\x11\x79\xb3\x3e\x11\x06\x95\xe8\xa3\x19\x52\x92\xd1\x51\x89\xe7\x74\x44\x6b\x38\x75\x6b\x77\xd1\xc7\x61\x90\xbc\x55\x5c\xca\x4b\x60\x33\x23\x89\x56\x6a\x11\xa6\xdf\xa9\x62\xc4\x1e\x74\xa1\x98\xfa\x18\xea\x55\x8c\x3a\xa1\xd3\x69\x28\x8f\xbb\x64\x38\x6c\x03\x3b\xd4\x3f\xe9\x53\x6a\x75\x14\xd8\x21\xd1\xb4\xdb\x19\x83\x56\x74\x3a\x63\xf5\x13\xd8\xa1\xbf\xbc\x57\xc5\xe6\x2a\xb0\xd9\x15\xd0\x51\x56\x60\x3a\xca\x0a\x42\x47\x45\xef\x53\xb7\xd8\x82\x1e\x08\x9b\xaf\x70\xb1\x3b\xa2\x2e\x15\x8e\xe9\x88\x30\x28\x1d\xe1\x64\x42\x47\x55\x8b\x51\xb7\x40\xd3\x3e\xbf\x0d\x83\x47\x1f\xcb\xb0\x4a\x4e\x5d\x22\xe5\x74\xc4\x84\x8a\xbe\x44\xf5\x37\xaa\xa6\x63\x51\x46\xe0\x79\x34\xf0\xb0\xd5\x83\xa8\x3a\xd6\x30\xa3\x11\xa0\xcd\x83\xd4\x48\x3c\x13\x92\xa0\x8b\xc4\x58\x27\xec\xc2\x31\x12\x13\x75\xe1\x18\xeb\xc4\x6d\x1d\x03\x16\xdd\xd9\x1a\x9b\x27\x7d\x14\x06\x20\xac\xdf\x1d\x7b\xc0\xa1\x10\x19\x80\x64\x1d\xc6\x1a\x2a\xe4\x6d\x05\xab\x03\x91\x24\x18\x1a\x17\x7d\xa9\x58\xe3\x2e\x27\x33\x31\x1d\xe9\x05\xa1\x2e\x6e\xfb\x7d\x14\x26\xdd\xa0\x3d\xb9\x9b\x74\x83\x8e\x33\x3c\xa2\x23\x8a\x1a\xd3\x71\x45\xa5\x74\x44\x28\x09\x75\x08\x85\x51\xb7\x2d\xa5\x7d\x0a\xec\x8e\xc4\x69\x2a\x39\x1d\x51\x62\xde\xe7\xa9\xdd\x9f\x58\x35\x48\x9f\x80\x18\x4a\xf1\x16\x66\x8f\xc9\x16\xc6\x84\xfd\x2d\x0c\x1f\x07\x5b\xe8\x33\x0e\x9d\xa6\x8f\xa3\x31\x93\xc4\xf1\x88\x33\xd4\x43\x70\x33\x84\x64\xcc\x5d\x62\x36\x66\xf7\x38\xdd\xc2\x5b\xe2\x6c\xcc\x91\xe1\x7c\x0b\x67\x89\xf9\x16\xae\x0c\x17\x7d\x09\x19\xd5\x65\xcc\x55\x60\x3c\x66\xa1\x98\x6c\x61\x20\xd8\x1f\xb1\x32\x1c\x6c\xe3\xd8\xc2\x2d\xdc\x0e\x8e\x9c\xde\x0d\xc7\x5b\xb8\x25\x4c\xb7\xb0\x45\x9c\x6c\x61\xf5\x98\x6d\xe1\x4d\x71\x3a\xe6\xc1\x70\xe6\x72\x61\x38\x1f\x73\x0b\x7c\x0b\x37\x8a\x8b\x9e\x87\xda\x25\x54\xc1\x5e\x60\x71\x46\x66\x92\x49\x87\x2b\xd8\x1a\xa2\x48\xd8\x26\xe8\x81\x56\xee\x19\xca\xc3\x9e\x70\x86\x35\xa2\x0e\xd3\x4c\x38\xe2\x4e\x8d\xf1\xe1\xd8\x1e\x9b\xb4\x58\x6c\x91\x49\xdd\x53\x5b\x54\xd2\x52\x31\xa4\x33\xeb\x71\x73\x58\x23\xef\x70\xcb\x16\x9a\x00\x04\x4b\x58\xa2\xda\x9a\x39\xe0\xea\x1e\xa6\x63\xe4\x13\x6a\x57\x14\x9f\x8e\x29\x4a\x40\xc7\x04\x1d\x52\x77\xe7\x23\xea\x56\xa5\x58\x2b\x1f\x96\x52\x6a\x67\x5d\x42\x5d\xac\x63\x74\x4c\xbd\x52\xea\x36\x82\x8c\xba\x55\x27\xa7\x63\x8a\xc1\xe9\x98\x11\x14\x74\x4c\xc5\x3b\x61\x85\x45\x09\xf0\x88\xb9\x62\x32\xa2\xa1\xd8\x1f\x75\x19\x38\x70\x6a\x2a\x0e\x47\x0d\x1e\x47\xa3\x5e\x03\xc7\x2e\x4f\x4c\x47\x2d\x11\x27\xa3\x2e\x03\x33\x87\x35\xe2\x74\xc4\x5d\xe0\x6c\xd4\x6b\x61\xdd\x1d\x18\x50\xf0\x11\xdf\x8b\x8b\x51\x97\xa4\x42\x0b\x67\x37\xb1\xd3\xae\x30\x19\x77\x2d\xbe\xc3\x73\xe0\x60\xc4\xac\x71\x38\xea\x5b\x70\xe4\x34\x60\x1c\x8f\xfa\x36\x4c\x47\x9c\x0f\x4e\x46\x2d\x10\xb3\x11\x37\x80\xd3\x51\x1f\x88\xb3\x51\x57\x80\xf3\x51\x7f\x84\xb9\xc3\xd9\xe1\xa2\xeb\x8d\x76\x89\x1f\xa8\x27\x51\x9a\x7d\x4b\x1d\x7d\x62\x2f\xb0\x84\x12\x35\xd1\x86\x72\xbf\x85\x10\x98\x15\x31\xb0\x2b\x51\xd8\xe5\x88\x39\x86\x68\x82\x63\x13\xfa\xd8\xeb\x84\x7f\xf6\xf1\xb3\xde\x51\x31\x47\x10\xad\x6c\xcd\xf1\x83\x2c\x37\xc7\x0e\x2d\xfb\x6c\x3b\x28\x2d\x7b\x0c\x30\x72\xcd\x4a\x2d\x91\x43\xad\xde\xe6\xd8\xa1\x15\xb0\xa5\xff\x4e\xf9\x62\x6a\xef\x1e\xa1\x63\xc4\xfb\x74\x8c\x01\x01\x75\x8b\x38\xa4\x63\x5d\x88\xa8\x55\x7f\x62\x3a\xa6\x7c\x94\xba\xf8\x97\x74\x91\xdb\x82\x08\x87\x76\xa4\xd4\x25\xbd\x8c\x8e\x69\x5f\x4e\xdd\xfa\xcb\xa9\xdb\xfc\x0a\x3a\x66\x21\xd8\x1b\x31\x11\x8c\x47\xac\x10\x93\x51\x33\xc4\xbe\x6b\xa4\x70\x6a\x38\x0e\x47\x4d\x04\x47\xde\x98\x9c\x70\x3c\xea\xc9\x30\x1d\xb5\x16\x9c\x8c\xba\x0b\xcc\x46\x1d\x1e\x4e\x47\x7c\x26\xce\x46\xfd\x06\xce\x47\xdc\x12\xe6\x0e\xbf\x84\x0b\xa7\xdb\x90\xd1\x83\xbb\x0f\x78\xd4\x2e\x31\xb1\x1b\x26\xf6\x47\xcc\x1e\x07\x23\x8a\x8f\xc3\x51\xdb\xc1\xd1\xb8\x77\x8b\x1d\xee\x0d\xd3\x71\xe3\x49\x9c\xfe\x03\xb3\x51\xff\x87\xd3\x51\x27\x8a\x33\xa7\x13\xc1\xf9\xa8\x97\xc2\x7c\xc4\x4d\xe1\xa2\xeb\x47\x76\x0b\x1e\x8c\x3e\xa5\xa6\xd7\xb6\x43\xd2\x50\x63\x0c\x19\xee\x6a\xc7\x35\x8c\x11\x83\xaa\x00\xeb\x29\xc6\xb8\xa1\x89\xf9\x0c\xe5\x51\x0d\xc0\x56\x21\x6e\x09\x34\x94\xea\x32\xb7\x85\x0c\x2d\x7d\x96\x98\xa1\xed\xa1\x01\x43\xda\x12\x68\x26\x21\xeb\x54\x30\x0d\x1c\x56\xdb\xe3\xba\x70\x0c\xa0\x8b\x0e\x73\xcc\x6b\x0e\xae\xf6\x98\x8e\x30\x97\x50\xcf\xa6\x38\x3e\x75\x2b\x4e\x40\x5d\x8a\x13\xd2\x11\xbd\x88\xe8\x08\xd7\x62\x3a\xa2\x7a\x94\x8e\x88\x36\xa1\x36\xbe\x33\x3a\x22\xd3\x94\xba\xb5\x36\xa3\x23\x5a\x93\xd3\x11\xc9\x71\xea\x56\xdc\x82\xba\xd4\x1e\x7b\x4e\xb3\xc5\xd8\xb3\xca\x15\x93\x31\x9b\xc6\xfe\x98\x4d\xe2\x60\xc4\xaa\x71\x38\x66\x14\x38\x1a\xf3\x1c\x38\x1e\xb1\xed\x66\xdc\xb3\x8a\x11\x27\x63\x06\x84\xd9\x88\x7f\xc4\xe9\x98\x07\xc1\x99\xd3\x43\xe1\x7c\xcc\xc3\x60\x6e\x1f\x9c\x8b\x11\x0f\x01\xf1\x81\x5b\x56\x78\x44\xd3\x30\x19\xb1\x74\xec\x8f\x19\x33\x0e\xc6\x8c\x15\x87\x63\xae\x2a\xb2\xbb\x22\x1c\x8f\x39\x0b\x4c\xdd\xe6\x92\x8c\x19\x3c\x66\x56\x67\x81\xd3\x31\x5b\xc6\xd9\x88\xbb\xc0\xb9\xd3\x59\x62\x3e\xe6\xca\x70\xd1\x73\x38\xbb\x44\x05\x8a\x6c\x6a\xf2\x22\x35\x4c\x53\x5c\x20\xdb\x12\x73\x9f\xfd\xb6\x9c\x98\x60\x07\x2d\x47\x8c\xf0\x43\xbd\x3f\xa6\xa8\xa0\x29\x1d\xc2\x8e\x3b\x0a\x6d\x1d\x15\x8d\xd1\x80\x46\xd4\x10\x30\xab\xd1\x1a\x49\x4e\x95\x82\x9a\x22\x00\x8d\x57\xc3\xf2\x5c\x03\x3b\x2c\xe5\x4d\x5f\x87\x65\x45\x87\xcb\xa6\x9e\x3a\x85\x84\xa9\x5b\x48\x84\x5a\x7a\xe4\x53\x97\x74\x02\xea\xea\x4f\x48\xdd\x5a\x17\x51\xb7\x66\xc4\xd4\xce\x0f\x4a\x5d\x7a\x91\x50\xbb\x3e\x33\xea\x16\x7d\x4a\xdd\x32\xcc\xa8\x45\xa7\x72\xea\x16\x11\xa7\x2e\x9d\x2a\xa8\x5b\x95\xb1\x37\x62\x47\x18\x8f\x28\x1f\x26\x23\x96\x8a\x7d\x87\x02\xe2\xc0\x69\xa7\x38\x1c\x31\x45\x1c\x79\x23\x3e\x28\x76\xda\x5c\x13\xc1\x5a\x68\x4f\xac\x5e\x9b\xd9\xac\x15\xa7\x23\xae\x0d\x67\x0e\xbf\x88\xf3\x11\x1f\x82\xf9\x88\xcd\xe2\xc2\xe9\xdc\xc4\x88\x6e\x21\x1c\x3b\x55\x09\x13\xa7\xd1\x62\x7f\xc4\x2e\x71\x30\x62\x98\x38\x74\x58\x26\x8e\x46\x7c\x0d\x8e\x47\x9d\xd5\x88\x25\xe1\x64\xc4\x46\x31\x73\x38\x00\x9c\x3a\xbd\x16\xce\x9c\xae\x05\xe7\x36\xfb\xc7\x7c\xcc\x84\x8b\xae\xeb\xd9\x7d\xe8\x36\xe8\x48\x4d\x6a\xe0\x61\xc3\xd0\xad\x42\x0d\xc3\xa0\xad\x80\x9a\x9a\x05\x4d\x90\x63\x2a\x0d\x2d\xdd\x8f\x24\x48\xc3\x18\xdd\x86\x4c\xc3\x52\xaa\x75\xc0\x34\x4c\x37\x7d\x1f\x36\x65\x9a\x92\x0f\x4b\x53\xad\x13\xa6\xa9\xba\x16\xc7\x19\x86\x69\xc9\xb7\x21\x54\xde\xf2\xcd\x34\x49\xd7\x22\xdf\x61\x4f\x5d\x6c\xc0\xd4\xcc\x54\x42\x5d\xf2\xf5\xa9\xab\x8f\x01\x75\x28\x4e\x48\x5d\xcc\x8b\xa8\xab\x27\x31\xb5\xb1\x87\x52\x87\x5a\x25\xd4\x25\x6a\x46\x5d\x12\x49\xa9\x43\x11\x32\x6a\x53\xf3\x9c\xba\x34\x99\x53\xb3\xc6\x16\xd4\x21\x64\xec\x39\xa5\x8c\xb1\xd3\x5c\x89\xd3\x5e\xb1\xef\xb4\x15\x1c\xb8\xcc\x01\x87\x4e\x53\xc2\x91\xd3\x20\x70\xec\xf2\x08\x6a\xbc\x31\x16\x25\x4e\x6f\x81\x99\xcb\x62\x70\x6a\x71\x1a\x38\xb3\x39\xd9\xdc\x69\xb9\x98\x3b\x9d\x02\x2e\xac\x1e\x11\x7b\x4e\xa9\x63\xa7\x21\x62\xe2\xb6\x6e\xdf\xa2\x69\x38\x70\x1a\x1a\x0e\x5d\x26\x8c\x23\xab\x1d\xe2\xd8\xe9\x19\x30\x75\x5a\x3f\x4e\x9c\xb6\x88\x99\xc5\x59\xe1\xd4\x69\x6e\x38\x73\x79\x07\x9c\x5b\xad\x18\x73\xa7\xe7\xc0\x85\xe6\x1c\x76\x19\x53\xa9\x18\xe0\x89\x01\x60\xc3\x9c\xa1\x3f\xbe\xdb\x6e\x6e\x0c\xdd\xb1\x6c\x37\x74\xc4\x0a\x9e\xa1\x28\x94\xf0\x88\x91\x8e\xa8\x29\x34\x39\x61\x45\x89\x79\x9c\xa1\x9e\x99\xfe\xa4\xe9\xb7\xc9\x05\x4b\x3a\x4d\x45\x69\x03\xd4\x40\x67\x76\x57\x5e\xf6\x18\xba\x5f\xb3\x9e\xf0\x86\x89\x86\x36\x85\x22\xc2\x50\x54\x6f\x2a\x59\x7b\x2e\x8b\xb1\x8b\xa7\xaa\x0e\x71\xc9\x5f\xd5\xf1\x5d\xb2\x56\xbf\x07\x2e\x66\xab\x3a\xa1\x9d\xad\xaa\x46\x34\xda\xe7\xd8\xa2\x5a\xaa\x98\xba\x38\xaa\xea\x24\x36\x29\xa9\x72\x66\xd7\x52\x55\x23\x75\xe9\xa3\xaa\x93\x99\x45\xae\x4a\x73\x97\x1a\xa9\x3a\xdc\xa5\xa2\xaa\x4e\x61\xb7\xd0\x3a\x22\x36\x1a\x36\x76\xf5\x00\x13\x0b\x93\xb1\x6f\xd3\x38\x1c\xb8\x88\xc5\xa1\x4b\x2c\x38\x72\x31\x03\xc7\x8e\x2e\xda\xfc\x6f\x62\x17\x21\x66\x2e\x4d\xc5\xa9\xd3\x1f\x66\x2e\x8b\xc2\xb9\x5d\xbf\x31\xb7\x29\x1d\x2e\xc6\xad\xab\x9d\xdc\x58\x6b\x60\xb7\x2f\xc0\x64\x5c\xe1\xb0\x3f\x66\x7d\x38\x70\x5a\x1f\x0e\xc7\x9d\x40\x2d\x6c\x67\x77\xe3\x71\xa7\x84\xe9\xb8\x73\xc3\xc9\xb8\x37\xa8\xd5\xc1\x65\x65\x52\x29\xac\xa5\xd9\x98\x5b\x93\x8a\xe1\xa0\x93\x8f\x79\x9c\x5a\x49\x00\x8b\x36\xb2\xcb\x8f\x7a\x5e\x83\xa7\x6c\xfd\x7e\x8d\xaa\x19\xab\xd0\x9a\xcf\x79\x56\x41\x3e\xa2\x97\xdf\x3c\x7b\x8d\xca\xc5\x59\xfd\x4c\x44\x93\xd1\xe0\xe9\x83\x97\xbd\x87\x8b\xdb\x8b\x89\x53\xd4\x1e\xfc\x87\x07\x14\xd5\x17\xf8\xac\xbe\x4c\xf5\x86\x9e\xfa\x55\x56\x90\x5f\xea\xcf\xe2\xcb\x54\xeb\x4f\x9f\x72\x2d\xab\xd2\xb7\x8f\x5e\xca\xc4\x58\x48\x26\x7e\x71\xbf\x51\x25\x6a\x37\x0f\x54\xc9\x2f\x5a\x96\x94\xab\x3e\x51\xe5\x4e\xad\xf7\x9e\x5f\x36\x29\xc0\xde\xf3\x4b\x43\xea\xbb\xf7\xfc\xb2\xce\xab\xf7\x9e\x5f\x9a\xd3\xea\x09\x1c\x52\x44\x61\x84\xd2\xb2\x5a\x23\x96\x65\xcb\x55\x5e\x2e\x4e\x50\xb5\x44\xcf\x1f\x62\x23\xdc\x6f\x4a\x48\x05\xf4\xa6\x9f\x03\xd9\xf4\x76\x48\x18\xd9\xdf\x0e\x69\xc1\x3d\x5f\x0a\x80\xcf\x1f\xe2\x37\xe5\x5b\x74\x07\x61\x43\x8e\x52\x85\x57\xa6\xe7\x9f\xd4\xbd\x7b\xd3\xb6\x57\xe9\xf8\xc4\x7f\x26\x3e\x46\x77\x34\xd0\x90\x87\x6f\x0f\xdd\x1c\x00\x36\x24\x2c\x7d\xb0\x5e\xf3\xd3\x74\xce\x11\x8e\xd0\xfa\x3c\x7d\xcf\x2f\x0d\xec\x5f\x9f\xa7\xdf\xf3\xcb\x75\x23\x82\xf6\xbb\x9d\x29\x8b\x97\x50\x49\xb2\xa6\xfe\x72\x1f\xe1\xa8\xf9\x66\x7f\x62\xe5\x21\x64\x9c\x52\xf4\x98\x19\xb9\xae\xa1\x2b\x5a\xde\x28\xa0\x6f\x15\x51\x46\xb8\xee\xa7\x5b\xd2\xb2\x7a\x09\x59\x51\x8e\xb4\x24\x28\x0d\x5c\x1b\x48\xa9\x50\x01\x35\x2a\x14\x19\xb6\x31\x69\x0d\x09\xec\x5a\xd3\xc5\x53\xac\x96\xa7\xe0\x60\xe6\xbc\xa8\x10\xa1\x60\x19\x02\xb3\xb9\xa1\x64\xce\x9b\x49\x89\x0e\xe5\xdb\x10\x1e\x24\x70\xac\x95\x6b\x32\x79\xfe\x90\x28\x1d\xdc\x43\xfb\x0d\x07\xf6\xd0\xdf\x10\xa1\x6f\x21\xc7\x23\xe8\x56\x89\xfe\x06\x6f\x5c\x6c\x4d\xde\xaa\x3c\x99\x6d\x4f\x5f\x00\xe9\x3b\x5b\x22\xf7\x3a\x54\x12\x0a\xc5\x92\x56\xb4\x8f\x48\x60\x21\x78\xcf\x40\xf1\x00\xad\x29\xb3\xbf\xe8\x40\xb9\xc8\x38\xe2\x2c\x9b\x29\xb5\x43\xe5\x1a\xb1\xb3\xb3\x79\xc9\x73\x21\x4b\xb6\x40\x7c\x73\xc6\x16\x39\xcf\xeb\xbc\x8c\xe0\xde\xa7\x46\x68\x82\x05\x0a\x4c\xc6\x16\x28\xe5\x28\x5d\x2d\xdf\xf3\x05\x2a\x17\xd5\x12\x51\x99\x14\x78\x8d\xd6\x19\x9b\x4b\xf0\x12\xe4\xda\x0c\xed\x62\x56\x66\x33\xc4\xe6\xf3\xe5\xc5\x1a\x40\x0b\xb8\xd5\x52\x80\x3d\x5f\xf3\x1c\x5d\x94\xd5\x6c\x79\x5e\x49\x02\xd7\xe5\x72\x31\x84\xa2\x18\x0d\xe9\x35\x27\xed\x97\xfb\xf7\xd5\xb3\x32\xed\x4f\xc2\xa1\xf8\xd8\xc4\xb9\x8e\xe6\x62\xa9\xb9\xb1\x5b\x71\x15\x58\x70\x62\xed\x67\xf0\x59\x93\x52\x0a\xf1\x36\x12\xd2\xf7\xcd\xa2\xb2\xf5\x23\xd6\xfb\x11\xbf\x55\x89\x3d\x7f\xd3\x7f\x82\x47\x01\x06\x4f\xed\x18\x3c\xe0\x43\x99\xf8\x12\x95\x8b\x0f\x7c\xb5\xe6\x76\x2f\x58\x2e\x3e\xbc\xec\x39\xc2\xce\x4f\x5b\x0d\x10\xd8\x31\x40\xb4\xd0\x74\x8e\xad\xdf\xe0\x50\x28\x74\x1f\xfa\xc7\xce\x82\x43\xfb\x85\x2f\xb2\xd5\xe5\x59\xb5\xc3\x53\x80\x2a\x63\xed\xf2\x61\xd3\xae\xad\x3c\xed\xba\x7c\x6b\x0a\xdd\x9c\x7f\x0e\xac\x2d\x47\x5c\xb9\x7b\x1f\xba\x31\x4f\x6b\x46\x9a\x82\x8e\xff\xe0\x95\x1e\xa7\x75\x89\x9b\x03\x50\xed\x69\xac\xbe\x0c\x64\xb5\x55\xbf\x1a\xbc\x9c\x65\x88\x3e\xbe\x5b\x94\x55\xc9\xe6\x7a\xea\xab\x6e\x1d\xbe\xc9\x66\x6c\x71\xc2\x9f\xbc\x68\xd3\xa2\xca\xcc\x63\xde\xc6\x2b\xe4\xff\xfa\x2a\x6d\x6e\x23\xdf\xa7\x86\x19\x6b\x51\x58\xdb\xbc\x78\xa2\xb7\x21\x80\xc7\x57\x7f\xdb\xb5\xa1\x92\x36\xaf\x28\xc4\xff\xb7\xa4\x0d\xda\x84\xea\xcf\x98\x99\xd6\xf5\x54\x9b\x4c\x1f\x06\x16\x25\x3f\x4a\xab\x82\xcf\xe3\xcf\xb6\x19\x46\x22\x63\x3c\x01\xe0\x6c\xcf\x5e\x34\x8a\xa1\xeb\x89\xa5\xee\xaa\x5b\x77\xa5\xea\x1a\x89\x7c\xcc\xcb\x75\xc5\xe7\x8d\x16\x9b\x21\x16\xd0\xf9\xed\x42\x0b\xea\x76\xd0\x85\x18\x68\x65\xaa\xb5\x37\xe5\xdb\x37\x93\x89\xa2\xf6\x5d\xeb\xae\x45\x20\xd9\x4c\x5d\xe0\x3b\xa4\xd5\x36\xb1\xc6\xe0\xb0\x7b\x86\xb4\xb2\x71\xaa\x67\x49\xf3\x9a\x8c\x62\xdc\x81\xff\x7d\x91\x2f\xd1\xfa\x82\x9d\xc9\xf0\x63\xce\xd6\x95\x54\x86\xa1\x0b\xaf\xdc\x22\xeb\x11\xdb\x15\x98\xcb\xf0\x2b\x83\x0e\x43\x46\xf1\x5d\x4d\x7d\x60\x1a\xd7\x66\x82\x57\x31\xf5\xab\xb8\x94\x11\xd7\x65\x98\x91\x55\x68\x79\x5e\x0d\x3c\x70\xe3\x72\xdd\x22\xeb\xb8\x5c\xbb\xcc\x3a\x43\xc6\x7b\x7e\x29\x53\x40\x47\xc1\xa1\x4f\xf4\x92\xf2\x83\xa5\x40\xcb\x1b\x1d\x19\xb3\x46\x1f\xa2\x97\x42\x03\xd5\x24\x60\xb5\x5c\xaf\xdb\x30\x1d\x72\x1e\x42\x40\x0c\xd3\x52\xd9\xa2\x19\xa8\x5a\xc6\x4d\xea\xf1\xea\x94\xad\xdf\x77\x4c\xb6\xd6\xdd\xc9\xa4\xa3\xa2\xc2\x10\xeb\xd1\xf5\x5d\xa7\xeb\xc2\x68\x05\x14\x8d\x05\x1d\x95\x7d\x07\x3a\xfb\x95\x51\xf1\x45\x99\x88\xa8\x24\x64\x55\xab\xb6\xbb\x01\xd9\x2f\x9e\x6c\x4f\xf6\xca\x4e\xf6\xdc\x4d\xf6\xdc\x41\xf6\x6a\x0b\xb2\x9d\x49\xa4\xd7\x75\x16\x69\xb9\xfc\xb1\x5d\x1e\xe9\xb1\x24\xcc\x12\x56\xc5\x37\x95\x9e\x8a\xf9\xdb\x47\x2f\x0f\x54\x80\xd6\xc9\xc5\x3c\x45\x59\x71\x62\x48\xae\x7d\x36\x67\x82\x88\x4d\x85\xfa\x50\x54\xc0\x35\x69\xf1\x98\x00\x35\x99\x9d\x87\x0b\x35\xdd\xa4\xdb\xdf\x3e\x7a\x69\xcc\xb8\xfd\x6a\x55\x9e\xcd\xf9\x9d\xdd\x96\x88\x64\xa3\xce\x42\x91\xfe\xd3\x9f\x67\xb9\x48\x2d\x44\x08\xb2\x4b\xc8\x50\x9a\xf5\x9f\x07\x52\x51\x2c\x5f\x63\x74\x24\xea\x1d\x48\xae\x3e\x92\x32\x5e\xae\x26\xed\x3b\xeb\xea\xe1\xf8\x1a\xf5\xc1\x7a\x5e\x66\x7c\xe2\x4d\x11\xd9\x1b\xbc\x85\xd1\x80\x25\x57\x04\x4b\xa6\x28\x70\x80\xf5\xaf\x08\x36\x98\xa2\x68\xcf\xfe\x90\xc6\x95\xe7\x1e\x7c\x8d\x0f\xf4\xc6\x5a\x0b\x2b\x67\x0e\xf4\x39\xc7\x16\x0d\xfc\x2d\x30\x5c\xcf\x9c\x46\xe0\xda\x91\x38\xb2\x6b\xf7\xf1\x16\x18\xcc\xa3\x1e\x4e\xc8\xb5\x0d\x7b\xff\x24\x6e\xb5\xf1\x2e\xd7\xe0\x5c\x5b\x58\x3b\xba\x58\x9b\x8b\xeb\x3a\xda\xa6\x96\x33\x7f\x7e\x53\xab\x97\x42\x5f\x4b\xcc\x7e\x37\x24\xd3\x5e\x56\x7d\x2d\xb9\xfb\xdd\x30\x98\xb6\x59\xdd\xef\x86\xd1\x54\x25\x7b\xbf\x1b\xe1\x8f\x6f\xa7\x34\xf8\xa4\x84\xfb\x7f\x64\xa6\xfd\xcf\x96\x0f\xff\xbf\x27\xb3\x3d\xbc\x54\x50\x2e\x78\x7e\xbd\x29\xee\xbf\x61\x6b\xde\x66\xad\x67\x6b\xae\x95\xbd\xf6\x89\x33\x03\xfe\xd0\x96\x37\x51\x80\x16\xec\x94\xaf\xcf\x74\x2b\x3d\xd4\xc9\x10\x55\x04\x19\xf2\xbf\xbf\x7e\x34\x81\x79\x80\xa2\xa0\x79\xc2\xc6\x04\xe6\x75\x14\x08\x3a\x80\xa8\x4d\x14\x1c\xa8\x2f\x82\x7e\x43\x64\xd0\x82\x96\xe0\xd5\x72\x4a\xf9\x0b\x5f\x23\x86\x16\xfc\x62\x7e\x89\xa4\xad\xe5\x26\xc4\xba\x43\x41\x9d\xd7\x3c\x16\xe7\xa7\x29\x5f\x7d\x44\xf0\xaa\x14\xbc\xaa\x22\x3e\xf8\x04\xc2\xf9\x03\x67\x93\xf9\xf2\x02\x5a\x88\xff\x9a\x1a\x74\x1b\x77\xbd\xdb\xb0\x42\xcd\x97\x4d\xcb\x97\xda\x23\xd4\xec\xa9\x07\x66\xb9\xfb\xe7\x11\xcf\x87\x59\x59\xe0\x85\x5e\xe4\x75\xd7\x3b\x6b\x4e\x83\x8b\x5f\x94\x9d\x88\x4a\xf4\x70\x2a\xa8\x36\x8f\x61\xea\x7d\x2d\xc3\xab\x9e\x50\x2c\x7a\x7b\x84\xba\xaf\x6f\xeb\x33\xf3\xbe\xa4\xbe\x29\xab\x8b\x72\xcd\xd1\x0f\xcf\x5e\xad\x01\xc2\x98\x60\xea\x87\x52\x94\x82\x7c\x44\x0f\x84\x7c\x05\x5f\xee\x00\x63\xd4\x48\xc2\x8a\x8a\xaf\xd0\x82\x9f\xb0\xaa\x5c\x9c\x5c\x03\xe3\x01\x14\x17\x8c\x57\x22\x38\x58\x2c\xab\x89\x95\xab\x87\x87\x68\xb1\x1c\x8d\x54\xe1\x4d\x16\xc9\xd0\xdf\x1b\xee\xde\x33\x56\x93\x8c\xfd\xbd\x66\xb2\x21\x24\x55\x9c\x51\x8c\xa9\xb5\xa1\x15\xe7\xbd\x0e\x75\x9d\x08\xc0\x26\x95\x07\x3f\x7c\xab\x49\x05\xb6\x13\x60\xdc\x3e\x63\x6b\xd8\x5e\xd8\xca\x86\x1a\x49\x01\x0c\x61\x12\x8d\xb0\xaa\xa5\x40\x51\xc3\xbd\x66\xe1\x3f\xf8\xe1\xdb\xeb\x11\xbd\xdc\xdb\x69\x05\xcf\x16\xf9\x84\x2d\x96\xd5\x8c\xaf\x14\x21\x2e\x35\x60\x8b\x5c\x57\x03\xd1\xc3\x11\x55\x68\xed\xec\xa6\x64\xc8\x98\x56\x34\x96\xa7\xea\xff\x61\xfa\xf1\xec\xc5\xe7\x56\x8f\x67\x2f\x3e\x93\x76\x3c\x7b\x71\x3d\xca\xb1\x5c\x75\x74\x63\xb9\xda\x41\x35\x96\xab\x2b\x6b\xc6\x6f\x3b\x6a\xc6\x6f\x7f\xb0\x66\xbc\xfe\xfc\xaa\xf1\xfa\xb3\xe9\xc6\xeb\xeb\x52\x8e\x4d\x4f\x3b\x36\x3b\xa9\xc7\xe6\x13\xf4\xe3\xdd\x8e\xfa\xf1\xee\x0f\xd2\x0f\xd8\x94\xd7\x35\x63\x21\x57\x46\xd5\x84\x70\xce\x8b\x6a\xfb\xa8\x6c\x01\x3a\x21\xbf\xa1\x65\xd1\x40\x82\x27\x6c\xae\x4b\x19\x00\xd8\xf5\xa8\x03\x80\xea\x28\x04\xfc\xf2\x64\x42\x42\x97\x1e\xc8\x4a\xba\x2a\x2c\x4c\x7a\x20\xa6\x40\x0b\x74\x1f\xf9\xc4\xb6\xd3\xa5\x69\xca\xa4\x55\x95\xfb\xf7\xd1\x02\xb6\xc8\x1b\x65\x90\x47\x87\x08\xba\x83\x16\xc6\xc7\xea\xcd\x2a\x24\xe0\x0c\x75\xed\x23\xaa\x27\x4f\x6e\x82\x74\x30\x93\x05\xba\x63\x78\x31\x74\x80\xba\xbf\xd5\x25\xd0\xfd\x77\x6a\x2f\x2c\xe5\xff\xdb\xa9\xef\x8b\x89\x7d\x72\x51\x6b\xef\x8b\x6b\xd2\x5e\x29\xf7\xae\xa6\x6a\xca\x5b\xeb\xf3\x16\xca\x3b\xf0\x98\x00\xea\x0a\xfa\xab\x59\x41\x03\x67\x5c\x81\x15\xfa\x3f\x5c\x83\x5f\x2c\x2b\x56\xf1\xcf\xed\x80\x57\x80\xe5\xba\x54\x18\xa0\x5d\x8f\x0a\x4b\xc2\x74\x15\x5e\x2d\x47\xfd\xaf\xa8\x32\xaa\xbf\xaa\x47\xa0\x07\xca\xab\x2f\xf6\x44\x38\xd8\xfe\xf2\x62\x12\x05\x03\xb5\xfc\x54\x81\x5d\x93\xcf\xf9\x73\x49\x6c\xc4\xe5\x88\x1a\xbb\x0b\xec\xc5\x40\x60\x4f\xae\x22\xb0\x07\x79\xfe\xb9\x23\x5f\x96\xe7\x9f\x29\xf2\x95\x4f\x7e\x5f\xc7\x9c\x39\xef\xcd\x99\xf3\x9d\xe6\xcc\xf9\xd6\x73\xe6\xfe\x88\xb0\xdf\x04\xb2\x70\x60\xd4\x1c\xfc\x66\x6c\xb5\xba\x14\xcd\xea\x31\x44\x3e\x0c\xdf\x19\x56\xda\xe7\xe1\xcd\x30\x86\x81\xd4\x7e\x1b\x73\xa3\x7d\x89\x43\xd1\xf0\xa9\x1e\x5d\x7e\x33\xef\xae\x3c\x58\xa8\x27\xc0\x97\x85\xbe\xb6\xb9\x36\xbd\x70\xbc\x5a\x9e\xf1\x55\x75\x89\x7e\x55\x4f\x0c\x43\x45\x50\xaf\x06\xc4\x60\x59\x51\x29\xc8\xfa\xc0\x04\xa7\x76\x2b\xcd\x9b\xe8\x5d\xef\xb2\x2e\x4f\x16\x65\x51\x66\x6c\x51\xa1\x14\xca\xcb\x85\x66\x1b\x80\xd4\xb1\xfa\xdb\xae\x4b\xd7\xc4\xd4\xbf\x5c\xc3\x3a\xf0\x90\x02\xbb\x39\x76\xd8\x35\x79\x76\x26\xd4\x92\xcd\xf7\x3a\xbc\x1f\x65\x1c\x32\x3a\xe4\x86\x73\x1a\xd8\xad\x98\xc8\xbb\x62\xfe\x04\x5b\xbd\xd0\x59\xdd\xef\x45\x67\xcf\xb7\x6b\xb3\x9f\x08\xec\xcd\xa0\xbd\xf8\xdb\x75\x59\x7b\xba\x2b\x14\x4c\x71\x82\x19\x4e\xe1\x4e\x4d\x86\x73\xcc\x71\xb1\x37\x00\xf2\xf6\xdf\xa8\xab\x53\x84\xbd\xad\xb7\x07\x40\xe9\xa6\x8d\xda\x0e\xdc\xf2\x85\x3a\x3c\x01\x6e\xb1\xfe\x22\xff\xfb\xdb\x6f\x86\x0b\x18\x22\xee\x6f\x6c\xe0\x2f\x47\x68\xb8\x0b\xa6\xff\xc9\xb1\xb9\xae\x7e\xd4\x90\xd1\x3f\x0b\x68\x0d\xda\xfb\x00\xa4\x0d\xcd\xf9\xe2\xa4\x9a\xa1\xdb\x88\x6e\x79\x94\xba\xef\x68\x1e\x2e\x17\x1f\xf8\xaa\x9e\x1a\x6a\x6e\x58\xf9\x07\x31\x68\xd7\xb7\x03\xb6\x72\x3c\xf5\xa8\xdd\x48\xb7\xb3\x33\xf7\x11\xbd\xea\x3a\xd1\x5b\x6b\x94\xb3\x8a\x21\xb6\xde\x11\xcf\xd6\x2b\x59\xdd\x9d\xc2\x8d\xe6\xa0\x0f\xaa\xe5\x6b\x9f\xd8\xb7\x42\xa0\xf8\x13\xce\xec\x28\x5c\x5d\xa5\x32\x9c\xdc\xa9\xeb\x3d\x91\xc2\x6c\x88\xac\xc5\x6b\x3a\xc5\x23\xc5\x66\x80\x25\xbb\xbb\xf5\xe1\xfd\x2e\x6e\xf7\x4d\xaf\x76\x0b\xaf\x6e\xf5\x66\x70\x84\x5f\xfc\xd5\x34\x1c\x9c\x9d\xaf\x67\x93\x3a\x90\x12\x31\x82\x69\x5e\x69\xae\xdd\x8b\x25\x90\xe1\x9c\x6c\x1d\x8a\x68\x02\xae\x3d\x48\x0d\x73\xda\x35\x1b\xeb\x41\x92\x81\x55\x00\x18\xa1\x92\xd9\xf2\x0c\x06\x49\xcb\xd8\x8f\x46\xc3\xd6\x46\xed\x39\xca\xe6\xcb\x85\x6b\xa6\xb2\xad\x4a\x03\x9c\xbe\x2e\xc3\x8f\x76\x5d\x86\x62\xa7\x2e\xeb\x90\x21\x4a\x91\xe4\x36\x27\x5f\x4d\x27\x5d\x1f\x42\xfd\xbf\x82\x62\xff\x55\x72\x66\x08\xb4\xf6\xa5\x12\xde\xd0\xcd\xd6\xa7\xc6\xec\x08\xe0\x0e\x53\xbd\xb1\x2e\x83\x13\x0b\x9a\xc6\x84\x2e\x3a\xf6\x33\x6a\x06\x17\xdb\xd8\xc0\x85\x52\xf9\x1a\xfc\x9b\xf2\xad\x89\xed\x76\x55\x85\xca\x9d\xfd\xe5\x26\x3c\xb6\x9e\x9b\xe9\x9d\x96\x51\x47\x63\x3e\xbe\x9d\xd2\x70\x9b\xf3\x2e\x87\xb7\xff\x82\x66\x55\x75\xb6\xbe\x7b\x78\x78\x5a\xcd\xd6\x07\x29\x3f\x3c\xaf\x0a\xfa\xf3\x1a\x7d\x20\x07\xf8\x80\xa0\xf4\x12\xfd\x8f\x53\x56\xcd\x4a\xb6\x16\x1a\xd3\x1e\x90\x81\x53\x21\xf2\xb0\xc7\xe1\x21\xfa\x96\x57\xf2\x3a\x1c\xe7\x82\xdd\x25\x4b\xe7\x7c\x8d\xfe\xa1\x30\xfd\xe3\xc6\x57\x70\x8c\x7f\xc5\xf9\xa3\xe6\xfc\xcb\xe0\x24\x0d\xba\x25\x85\x77\x0b\xdd\xbc\x59\xff\x7c\xcf\x0e\x1e\xfd\x43\x76\x47\x03\xfe\x14\x7e\x68\x61\x9f\xaa\xef\x5d\xd0\xea\xd7\x9b\x37\x0d\xe7\x73\x8e\x3a\x44\x36\x95\x9d\x64\x9c\xc0\xc9\x99\x7f\x4c\xe5\x69\xfc\x1f\x96\x39\x3f\xf8\x79\x8d\x96\x2b\xf4\x8d\x3c\x4a\x53\x16\x25\xcf\x51\xb6\xcc\xf9\x14\xa0\xb0\x45\x8e\xce\xd7\x1c\x95\x95\x18\xd7\xfe\x21\xf8\xa8\xf5\x41\x9d\xc3\x69\xfa\x70\xa2\xbe\x77\xfb\x20\x7f\xbd\x27\xcf\x24\xb5\xcd\x0e\x9a\xda\x47\x3a\xb0\xdf\x7e\xd3\xbe\x1d\x5c\x94\x8b\x5c\xcc\x2e\x3b\x75\xe4\xd1\x21\x41\x0b\xd2\x7f\x86\xc3\x3e\x37\xbe\x3a\xbc\x7d\xe7\xda\xfe\x6e\x1f\xde\x90\xbd\x5d\x57\xab\x72\x71\xf2\x78\xb5\x3c\x7d\x38\x63\xab\x87\xcb\x5c\x48\xee\x25\xfc\x78\x50\x68\xbf\x2a\xe6\xbf\x62\xef\xf9\x42\xf2\xb8\xaf\xb2\x67\xe7\x8b\x4b\xc1\xdf\x1b\x5f\x35\x1e\xec\x3c\x5b\x93\x9c\x8b\x1f\x27\x12\x8f\xec\x20\x6c\x6d\xc2\xe1\xfb\x7a\x08\x84\x9f\xb2\xe5\xf9\xa2\xe2\x2b\xb5\x72\x09\x3f\xcd\x6b\x5f\x21\x9b\xb7\xce\x02\x4a\xe1\x3e\x63\xfd\x85\x6f\xaa\x15\x13\x5f\x2e\x66\xe5\x9c\xa3\x49\x0d\xed\xbe\x02\x22\x51\x7f\x05\x6d\x5a\x80\x99\xea\xde\x83\xaa\x6e\xb0\xbf\x2f\x4c\xfd\x2b\x90\xa9\xac\xfc\xf5\x11\xf2\x36\xdf\x52\xcf\x13\x32\x97\x3f\xdd\x87\x9f\xbe\x79\xfc\x58\xfc\x64\xc1\x24\xd8\x05\xd3\xf5\xf5\xf9\x6a\xb5\x3c\x61\x15\x9f\x82\xd6\x55\x33\xbe\xe2\x70\xcf\x13\x2d\xf8\xa6\x42\x82\x04\x96\x55\x7c\x05\x8d\xa0\x1b\xdb\xd0\x07\x04\x4e\x64\xf5\x9b\xc8\xdb\x3c\x7e\xe8\x79\x7b\x42\x43\xbd\xcd\xb7\xf0\xf1\x57\xe1\x9c\xe7\xcb\x8b\x16\x3f\x34\xfb\x4a\x72\x5e\x0e\xe5\x13\xd5\x45\x01\xc0\x7f\xfc\x78\x0f\xae\x66\x7a\x7b\x68\x1f\x69\x90\xa1\x60\xbf\xce\x38\xa4\xb0\xb7\x51\xb0\xea\xea\xf9\xe2\x94\x55\xd9\x8c\xe7\x2d\xbe\x7b\x68\xb9\x98\x5f\x22\x76\x76\xc6\xa1\xdf\xe5\x1a\x0c\x10\x9d\x2f\xca\x6a\x2a\x26\x9a\x19\x5b\x73\x98\x6d\x0a\x46\x34\x90\x9a\x3a\x82\x49\x55\x7d\x2e\xaa\x81\x2a\x86\x7a\xa6\x7d\x3d\x63\xe5\x6a\xd8\x33\xe8\x97\xa2\xf5\x2b\xc5\xba\x3b\x77\x14\xed\x37\xfa\x1d\xb0\xb4\x14\x15\xc5\xff\x95\xbf\x97\xb5\x6a\x6b\xbc\x8a\x31\xf0\x05\x18\x03\x8c\xc2\xad\x2d\x34\x5a\x2e\xe3\x96\xae\x92\x97\x8b\x9c\x6f\xd0\x11\xba\x83\x8d\x6a\xdf\xd8\xd1\xad\x5b\x9a\xf2\xef\xef\xcb\x66\x16\xe5\x07\x3c\x6f\xa0\xca\xdb\xbe\xb2\x0b\x55\x7a\x2c\x24\x2e\x39\x23\x7f\xbd\x73\x54\x8b\xff\x9e\xc6\x2f\xb4\x7f\x64\xf0\x1f\x35\xa0\xaf\xbf\x46\xd8\xab\x15\x08\xfd\xa6\x6c\x48\x89\xa4\xa6\x44\x2a\x2b\xfa\x0d\x75\xf4\xb0\x61\xfe\x16\x88\x00\xa0\x4d\x48\x0d\xf3\xb3\x19\xcf\xde\xbf\xcc\xd8\x9c\xad\xfe\x97\x68\x35\x11\x72\x78\xbe\x2c\x17\xf2\x34\x35\x30\xa0\xf9\xa9\x6b\xf1\xed\xcf\xd2\xea\x5b\xe6\x54\xb3\xd5\xf2\x02\x3d\x5a\xad\x96\xab\x09\xf4\xea\xd6\x13\x11\x0a\xb5\xaa\xf9\xf7\xfd\x5b\x68\xbf\x05\x70\x50\x2d\xa5\x67\x9d\xe0\x68\xef\xa0\x5a\xfe\xfd\xec\x8c\xaf\x1e\xb2\x35\x9f\xec\xa1\x7d\x09\x40\xa8\xfc\x62\x59\x09\x05\x07\x62\x25\x5f\x6e\x89\xc2\xba\xa3\x1f\x3f\xc3\x48\xd0\xf2\x09\xa2\x6a\x11\x89\xb7\xec\x98\xca\x6d\x36\x35\x38\x49\x2e\x1b\xa4\x31\xd1\x19\xf8\x75\xdd\x46\x4a\x14\x96\x2a\x37\xd4\xdb\xeb\xcb\x45\x1a\xc4\xc3\xba\xa1\x49\x2c\x1a\xd8\x9b\x4a\x39\x1f\x3f\xa6\xca\xd7\x29\x37\x87\xef\xa4\x97\x15\x47\x6b\xfe\x5f\xe7\x7c\x91\x81\xa3\xb3\x13\xda\xe2\xa8\x55\x07\x06\xc2\xcb\xd3\x74\x39\x6f\x0c\xc9\x86\x99\x7a\x5d\xcc\x64\x88\xb9\x81\x34\xce\xa4\x48\x32\x08\x2b\x06\x3d\xf4\x1a\x92\x9a\x83\xc7\x06\x22\xc0\x0d\xeb\x44\xf8\x43\x22\x1c\x0a\x7f\x6f\x47\x22\x31\x91\x54\x7a\x8a\xca\x47\x5e\x07\xc4\xfe\x91\x45\x6b\xa2\x2d\x3a\xf3\xc8\x1b\x74\x26\xf8\x24\x8e\x62\xaa\x88\x8d\x25\xb1\x8f\xb7\x24\x16\x93\x5d\x3b\xd5\xd6\x34\x51\xd5\xed\x68\xd7\x02\x1a\xdd\x04\x08\x7d\x93\x10\xa1\xbf\x1a\x27\xfa\x41\x53\x03\x54\x84\xee\xc3\xe0\x6a\x10\x35\xb5\xf5\x47\x07\x95\xa6\x6a\xfd\x83\x10\x82\xf4\x56\x5b\x0e\x2e\x6d\x8f\x75\xc4\xfa\x28\xa3\x81\xdc\x3f\x72\x98\x7e\xcf\xa3\xb7\xcd\x3e\x57\x20\xdc\xf0\x7e\xc5\x59\xfe\x70\xb9\xa8\xca\xc5\x39\x5c\x9e\x05\xe9\xb7\xae\x48\x50\xf2\x1d\xf4\xfd\xeb\x23\x20\xeb\xa1\x08\x2c\x0c\xa3\xc1\xad\xef\x16\x1f\xd8\xbc\xcc\xa1\x92\xe4\xf6\x2d\xd5\xad\x86\xdf\x5d\x2c\x48\x02\x84\x85\x82\x37\x0d\x9e\xb7\xca\x4c\x44\xd3\xe6\xc7\xfd\x7d\x11\x8c\xd7\x1e\xaa\x07\xe6\xa6\x74\x23\x32\x10\x14\x5e\xf2\x57\xcd\x19\x1a\x6b\xfb\x8f\x1b\xc2\x0e\x0f\xd1\x77\x05\xba\xe0\x48\xc4\x6b\xe7\x67\x48\x44\xaa\x53\x54\x56\xff\xf7\x7f\xff\x9f\x7a\x58\xd2\x41\x00\xc5\x37\x2c\x3d\x1f\x54\xbc\x35\x70\xfe\x52\x7b\x5f\x82\x15\x4c\x5a\x2d\x17\x95\xb1\xae\x86\x44\xff\xe2\xeb\x5f\x02\x83\xfa\x0e\x65\xf5\x09\xa2\xea\x42\x3a\x1a\x4a\x5d\x71\xb6\x60\x73\xb8\xfc\xd0\xf0\xf1\x05\x67\x39\x2a\xca\xd5\xba\xaa\xb9\x04\xdd\xda\x5d\xcc\xc3\xd1\x0d\x4d\x16\xcb\x21\x7b\xd7\x7b\xb5\x4e\x48\x44\x37\x95\xfc\x95\x67\xd5\x68\x6d\xf8\x5b\xd3\x3a\x1c\xc3\x7a\x70\x1e\xd5\x0a\xf5\xb0\x06\x05\x62\x41\x47\x16\x83\xb9\xd7\xf7\x07\x3a\x30\x2c\xa7\x19\x90\x73\xa7\x91\xae\x29\x00\x6b\xb4\xb7\x55\x5f\xcd\x47\x75\x03\xf8\x1d\x54\xb0\x0e\xeb\x65\xdf\xfd\x3e\x6f\x4f\xd9\x25\x2a\x17\xd9\xfc\x1c\x26\x21\x62\x72\xa1\x4f\x69\x4c\x5c\x7e\x5c\x73\xe7\xd1\x0e\xdc\x01\x55\xbe\x1a\x03\x3d\x35\x4f\x23\x70\x36\x49\xe2\xd2\x19\xea\xdb\x18\xea\x41\xf0\x22\x19\x36\x16\x1f\x7c\x4e\x9e\x0f\x47\xf8\x3e\x47\xa9\xe2\xe8\xe3\xeb\xe5\x28\xb8\x8c\x2b\x32\x3d\x06\xa6\x7b\x9b\x3e\xdb\xbd\x8d\xf7\x70\x0f\xfd\x06\x1c\x99\x48\x1a\xe4\xaf\x8d\x3c\x02\xab\x3c\x60\x46\x65\x98\x63\x60\x4f\x9f\x82\x99\x25\x51\xf3\xd3\x28\x85\xbf\xbf\x7a\x7c\x87\xa2\x1c\x56\xca\x78\xde\x78\xde\xda\x6d\xaa\x1b\x58\xcd\x77\x70\x68\xda\x77\xf0\x3f\xf7\x7a\x31\x89\x8a\x35\xda\xd1\x58\xd2\xd7\xc0\xeb\x86\x24\x5a\xb5\xda\xab\x01\x16\xdd\x01\x6a\x41\x89\xe6\x63\xdb\xd5\x9f\x4e\xb8\xd3\xae\x13\x55\xa7\x67\x5a\x34\x32\xa9\x4e\xcf\xd0\x51\x6f\x2c\xd9\x43\x7f\x39\x3a\x92\x4e\xb9\x1f\x9d\xa8\x4d\x8c\xea\xf4\xac\x1f\x67\x68\x13\xf4\xb6\xf6\xde\xe7\x5c\x7c\x13\x6c\x45\x47\x40\xe0\xad\x0f\x7c\xb5\x2e\x97\x8b\x5b\x77\xd1\x2d\x58\xf4\xbd\x35\x15\xbf\x4a\x7a\x6e\xdd\xd5\xa2\x42\xf8\x5d\x76\x57\xfd\x2e\xbf\xdc\xf8\xea\xa3\x5a\xa4\x7b\xb9\x3c\xe5\xe8\xc1\xd3\x6f\x51\x7a\x5e\xce\x73\xb4\x3c\xab\xca\xd3\xf2\x17\xbe\x5a\x4f\xd1\xbc\x7c\xcf\xd1\xea\xe0\xe7\xf5\x54\x4e\x89\x61\xa5\x7d\x7d\xc6\xb3\xb2\x28\x33\x61\xbc\x79\x09\x02\x3f\x63\x55\xc5\x57\x8b\x35\xc0\x83\x46\xd5\x8c\xa3\x62\x39\x9f\x2f\x2f\xca\xc5\xc9\x5d\xb9\xe6\x29\xd4\xaf\x77\x2f\x12\xdd\xaa\x95\xe6\x96\x5c\xdc\xed\x54\x38\x60\xa7\x79\x6f\x15\xb5\xb9\x22\x29\xca\x6e\x7c\x25\xc5\xa5\x2e\x4d\x36\xcb\xdc\xdd\x01\x4c\xf4\x19\x64\x07\xc2\x69\x67\x17\xbd\x55\xe3\xbf\x68\xdf\x0f\x16\xcb\x9c\xbf\xba\x3c\xe3\x6d\x30\xd7\xae\x55\xab\x89\x47\xb9\xd0\xd7\x8d\x5f\x94\x8b\x93\xe5\xff\x7c\x89\x3e\x78\x07\xf4\xc0\x83\xe9\x79\xdb\x42\xbb\x4b\xda\x10\xa3\x5c\x63\x0d\x89\xad\x2e\x66\x6c\xde\x83\x14\x1f\x78\x77\xe4\x42\xcc\xaa\x3e\x1b\x25\x6f\x31\xaa\xdf\x66\x6c\xfd\xec\x62\xf1\xbc\x3e\x02\x73\xa4\x2a\x1d\x74\x7f\x87\xea\xcd\x16\x09\x64\x8d\x93\x4c\xa9\x3d\x46\xb7\xba\xdc\x1f\x12\xe5\x70\x91\x78\x4f\xf0\x46\xe7\xd5\x9b\xf7\x32\x81\xa1\xa8\x01\x9f\x3b\x8b\x5f\xbd\x7e\xbd\x98\x95\x8b\xa5\xe8\x15\x43\x17\x3c\x45\xea\xa2\xaa\x5a\xb5\x3e\x50\x0a\xad\x78\xf2\xf1\x86\xba\xa2\x0a\xdb\x26\x1f\xa7\xbf\x7e\x7c\x3b\xa5\xd1\x36\x5b\x22\x83\x1b\xbb\xaf\x9f\x3e\x39\xae\xaa\xb3\x17\x62\xc8\x58\x57\x0d\xb4\xbf\xa6\xe5\x89\x3c\xcc\x72\xf0\xf3\xfa\xaf\xdb\x40\xbe\x75\xbe\xe6\x30\x61\xcb\xaa\x5b\xf7\x6e\x0c\x11\x7d\x53\x9e\xfc\x00\x00\xef\x89\x0e\xff\xbc\x9e\x09\xa7\x5c\x9e\x2c\x96\x2b\x7e\x77\x5e\x2e\xf8\x8d\x06\xf5\x05\x4f\xfd\xad\x50\x0a\x21\xfd\xc8\x53\x39\x36\xc9\x6b\xc6\xb7\x0e\x0e\xe7\x65\x7a\x28\x40\x08\xe7\x7c\xe3\xf0\x10\xe5\xcb\x45\x85\x96\x1f\xf8\x6a\x55\xe6\xbc\xde\x70\xa8\xf7\x37\x6e\x68\x57\x90\xd5\xce\x81\x70\x70\xb7\x9a\x03\x0d\xb0\x1f\xd1\xa9\x70\x20\x51\x76\x6b\x09\x05\x81\x6d\x32\xbd\x0a\x10\x77\xef\xc6\x47\x03\x37\x64\x89\xda\xd8\xaa\x29\xfe\xeb\x5d\x42\x3e\xbe\x15\x5c\x98\xbe\x91\x5c\x78\xbb\x77\xe3\xf0\xf0\xff\x43\xeb\xe5\xf9\x2a\xe3\x4f\xd9\xd9\x59\xb9\x38\xf9\xfb\x8b\x27\x47\xa2\xf0\xce\x1c\x0e\x91\xfe\xbc\x3e\x38\x65\x67\x37\xfe\x5f\x00\x00\x00\xff\xff\x72\x02\x45\xd0\x9c\x29\x06\x00")
+var _web3Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xfd\x6b\x7b\x13\x39\xd2\x38\x0e\xbf\xcf\xa7\x50\xfc\xdc\x0f\xb6\x89\xb1\x9d\x84\x61\x18\x67\x32\x6c\x08\x30\x64\xef\x81\x70\x01\xd9\xd9\xbd\xb3\x59\xae\x8e\x5b\xb6\x7b\x68\x77\xfb\xd7\xdd\xce\x61\x48\xbe\xfb\xff\x52\xe9\x54\x3a\xf4\xc1\x49\x98\xd3\x26\x2f\xc0\x2d\x95\x4e\xa5\x52\xa9\x54\x2a\x55\x65\xf4\xff\x2d\xa3\x8c\xee\x76\x26\xcb\x64\x5c\x44\x69\x42\x68\xa7\xe8\x25\xbd\xac\xfb\x45\xa5\xe4\x9d\xb4\xb7\xec\x7e\x89\x26\x9d\xf5\xe4\x38\x3d\xe1\xbf\x0a\xf8\x75\x16\x64\x24\xd8\x2d\x2e\x17\x34\x9d\x10\x59\xd7\x6e\x4b\x16\x6d\x3d\x78\x20\x12\x77\x58\x99\xe5\x83\x07\x41\x37\xa3\xc5\x32\x4b\x48\xd0\x49\x7b\xeb\xc3\x2e\x4b\x8f\x64\x5a\x24\xd2\x58\xad\x93\xdd\x84\x9e\x93\x97\x59\x96\x66\x9d\xd6\x7e\x90\x24\x69\x41\x26\x51\x12\x92\x79\x1a\x2e\x63\x4a\xda\xad\x8d\x74\xa3\xd5\x6e\x75\x77\x8a\x59\x96\x9e\x93\x49\x7f\x9c\x86\x74\xb7\xf5\xe6\xf0\xc5\xd1\x4f\x2f\x3f\xbd\x3d\xfc\xf8\xe9\xd5\xe1\xd1\xdb\x17\xad\xde\xe4\x9a\xd5\x17\xef\xb2\xbe\xef\x7e\xa1\x17\x8b\x34\x2b\xf2\xd1\x97\xeb\xeb\x1d\x36\x86\xe3\xe1\x49\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\xbb\x00\xb8\x79\x72\x4c\x4f\x76\x44\x57\xf3\x4e\xf2\x2c\x19\xd1\xee\x75\x2f\xee\xe9\x92\xb4\xc7\x71\x77\x2d\xa0\x58\x93\x32\x13\x7a\x11\x35\xc2\xd5\x24\xcd\x3a\x0c\x3a\xdd\x1d\xee\xa4\xdf\x67\xfd\x98\x26\xd3\x62\xb6\x93\x6e\x6c\x74\xf3\x4e\xc6\x10\xaf\xba\x71\xdd\xed\x7c\xd9\x1c\x1d\xab\x2e\x8b\x2a\x7a\x1c\x4b\x3d\xd1\x76\xf7\xcb\x1a\x4f\x90\x9d\xd9\x3d\x5e\x23\xe4\xcb\x1a\x21\x84\xb4\xc6\x69\x92\x17\x41\x52\xb4\x46\xa4\xc8\x96\xb4\xc7\x53\xa3\x64\xb1\x2c\xf2\xd6\x88\x1c\xc3\xb7\x84\x86\xbc\x24\x98\xd3\xd6\x88\xb4\x3e\xa5\xe7\x09\xcd\x5a\x3d\x9d\xc3\x46\xc7\x72\x82\x30\xcc\x68\x9e\xb7\x44\xce\x35\xfc\x7f\x22\xaa\x96\xc5\xe1\x7f\x91\x96\x2e\x8b\xfa\xf6\xd2\x4f\xa8\x88\xd1\xde\xe9\x65\x41\xf3\xed\x2d\x7f\x7b\x12\x48\x61\x7a\x8d\x90\xeb\xde\x9d\x20\xe0\x46\xfd\x51\xc3\x41\xd8\x6b\x86\x80\x95\x51\xfd\x47\x1d\xfa\x38\x4d\x0a\x9a\x14\xb7\x1e\xfc\x9f\x72\xde\xd9\x8c\xfd\x61\xa6\x7d\x12\xc4\xf9\x6f\x37\xf4\x8c\xe6\x34\x3b\xf3\xad\xfa\x3f\xfa\xa4\xe5\xcb\xd3\xf7\x74\x1a\xe5\x45\x16\xfc\x17\x4c\x5e\xaf\xaa\x0e\x7a\x7e\x78\x2b\xbe\x5f\x64\x41\x92\x4f\xbc\xac\xef\xcf\x82\x83\xcc\x22\x85\xd5\x91\x90\xd3\xe2\x43\x35\x49\xdd\x19\x2e\xec\xa6\x7f\x93\x46\xbf\xf2\x04\x04\x4d\x10\x5f\x55\xc1\x22\x8b\xe6\x41\x76\xe9\xed\x47\x9a\xc6\xb5\x93\xb7\x27\xda\xfa\xf3\xa2\xd0\xdc\x83\x2b\xab\x29\x43\xc2\x7e\xe9\x36\xfe\x47\x42\x82\xb7\xf7\x61\x94\xa7\xe7\xc9\x2d\x7a\x1e\x24\x69\x72\x39\x4f\x97\xf9\x0a\x5d\x8f\x92\x90\x5e\xd0\xd0\xd8\xbb\xee\x6c\x62\x75\xe5\xa8\x3b\x66\xed\xe7\x51\x72\x1b\xc6\xbd\xb7\x04\x4c\xbc\x4c\x42\x1a\xb6\x2c\x34\xd1\x33\x46\x08\x7f\x01\x1c\x9d\x46\x61\xd8\x0c\x47\x37\xab\xff\x2c\x88\x97\xde\xee\x2f\xa3\xa4\xd8\xfa\xe6\x49\xf5\x14\xbc\xa5\xe7\xcf\xa3\xdf\x11\xf9\xb7\x5a\x73\xfb\xb3\x20\x99\xfe\x9e\xa4\x73\x27\x94\x53\x52\x37\x92\xea\x2b\xa9\xc6\x8b\x99\x77\x7c\x37\xaa\x45\xd0\xda\xc9\xda\xda\x75\xef\xcb\xf5\x49\x6f\xeb\x77\x3b\xf4\xff\x85\xce\xbc\xbf\x93\xec\x38\x59\x26\xe1\x8d\x49\xe5\xd6\x1b\xd7\xfd\xb1\xf7\xcf\x7d\xec\xbd\x3f\xf4\xfd\x91\xcf\x1c\xde\xc1\x8b\xf3\xc2\x1f\x4d\xda\xfc\xba\x9b\xb9\xde\xab\xb6\xef\x6c\xaf\x5a\x75\xde\x27\x59\x3a\xbf\xe5\xb4\x17\xe9\x2d\x8f\x9a\xb7\x13\xf8\x7e\xdf\x75\xf3\x47\xc0\x5f\x94\x84\x51\x46\xc7\xc5\x81\x77\xcf\x5c\xa1\x27\xb7\x9b\x88\x68\x1c\x2c\x3e\xfe\xae\x93\xe1\xc7\x64\xb3\xd3\x2e\x5d\xa4\x79\x54\x75\x50\x5f\x04\x97\xc1\x69\x4c\x4d\xa1\xe0\x77\xe1\x4a\x65\x34\x77\x27\xc7\xaf\xdb\xd1\xc0\x9e\x1c\xef\x0b\x13\x9f\xbf\xfd\x49\xe6\x4e\x90\x54\x52\x77\x33\x3a\xfb\x1d\xd0\xff\x87\xc5\xfa\x5d\x9c\x1f\x6f\xcc\x27\xbf\x36\xd6\x6d\xa6\x77\x8f\xf6\x86\x68\xbf\xf5\xc6\xf5\xb5\x67\xf6\xc0\xb3\xa5\x55\xc9\x71\x8f\x9b\xc8\x71\x60\xbc\x41\x76\xa5\x85\x43\xa7\xdd\x1f\x4c\xd2\x6c\x1e\x14\x05\xcd\xf2\x76\x77\x07\x00\x3e\xa4\x71\x14\x46\xc5\xe5\xc7\xcb\x05\x35\x61\x59\xfb\x0c\x6a\x6d\xf0\xf0\xe1\x1a\x79\x68\x40\x0a\x9d\x3b\x89\x72\x12\x90\x45\x96\xa6\x0c\x98\x14\xb3\xa0\x20\x19\x5d\xb0\x43\x56\x52\xe4\x44\xcc\x1d\x61\x99\xac\x86\x83\x82\xcc\x83\x62\x3c\xa3\xf9\x88\x7d\x8a\x6c\xf4\xf3\xf8\x04\x7f\x3c\x36\xbe\x4e\xcc\xcc\x6d\xeb\xfb\xe4\xf8\xc9\xc9\xf1\x49\x8f\xf4\xfb\xfd\x35\xf2\x70\xe0\x8c\x4d\xf6\x78\x97\x28\x6b\x9a\x4e\x57\x4c\x71\x31\x8b\xf2\xfe\x27\x58\x18\xaf\x24\x82\x18\x60\x9f\xa3\xeb\x80\x65\x1c\x24\xc5\x0e\x02\xe6\xfb\xb6\x0f\xfa\x10\x72\x44\x73\x3b\x6b\xd7\x3b\x6b\x6b\x9e\x7e\xf4\x17\x59\x5a\x70\xac\xed\x92\x84\x9e\x1b\x7d\xed\x7c\xb9\xee\xee\x54\x97\xea\x83\xf4\x92\x2d\xc7\x45\xca\x1a\xf7\xc0\xd6\xb5\xdb\x8f\x72\x31\xe7\x1a\x21\x8c\x1c\x25\x52\x84\x5d\xcb\xfa\x3a\x4b\xec\xc3\xbc\x75\x06\x02\xdb\x9d\x7f\x1f\x77\x8e\x87\x8f\xbe\x3b\x79\xd8\xfd\xf7\x49\xf7\xd9\xa0\xcb\xc7\x69\x1e\x1c\x4a\xbb\x75\xdd\xfb\xd2\xc2\xa4\xd8\x1a\x7d\xd7\x6b\x71\x7a\x6b\x8d\x36\x1f\x5f\x9f\xf4\xbe\xf9\x9d\xc9\xfb\x79\x9a\xc6\x35\xb4\x7d\xca\x40\x4a\x08\x9b\xe5\xc9\xff\x39\x95\xc2\xaf\xc7\xfa\xe7\x09\x4a\xde\xc6\x1f\x75\x64\x0c\x3d\xbb\x29\x0d\xb3\xc2\xab\x10\x31\x87\xb7\x29\x98\xa5\xae\x48\xbe\x66\x91\x0a\xda\xe5\x2d\x56\x95\xbd\x09\xd5\xfe\x87\xa1\xd6\xa4\xd9\x87\xff\xd3\x88\x68\x45\x7f\xea\x29\xf6\xc9\xef\x4d\xb1\x6c\x0f\x53\x24\x5b\xf8\x69\xb6\x98\x51\x02\x9b\x1d\x10\x6e\xdf\x47\xb9\x2c\x57\xfd\x10\x74\x09\x3f\x1f\xa3\xdf\x27\x38\x63\xdb\xf8\x32\xe9\x97\x88\xad\x55\xfd\x7c\x6a\xd4\x23\x8a\x7a\xa8\x1c\x3a\x79\x63\x32\x67\xa5\x57\xa2\x73\x5e\xc0\x21\x74\x96\xbc\x2a\xa5\x9b\x65\xaa\x48\x9d\x37\x5a\x59\xfa\x66\xc4\xce\x2a\xe1\xa4\xfe\x65\xb3\x77\xdd\xbd\x19\xe1\x8b\xde\xd5\x53\xfe\xb7\x4d\x28\x7f\xf0\x10\x3a\xfc\x71\x16\xe5\x64\x12\xc5\x94\x51\xea\x22\xc8\x0a\x92\x4e\xc8\x39\x3d\xdd\xee\xff\x92\xf7\xd7\x00\x44\x7c\x31\x80\x49\x46\x29\xc9\xd3\x49\x71\x1e\x64\x74\x44\x2e\xd3\x25\x19\x07\x09\xc9\x68\x18\xe5\x45\x16\x9d\x2e\x0b\x4a\xa2\x82\x04\x49\x38\x48\x33\x32\x4f\xc3\x68\x72\x09\x75\x44\x05\x59\x26\x21\xcd\x80\xe0\x0b\x9a\xcd\x73\xd6\x0e\xfb\xf8\xf1\xed\x11\xf9\x89\xe6\x39\xcd\xc8\x8f\x34\xa1\x59\x10\x93\x77\xcb\xd3\x38\x1a\x93\x9f\xa2\x31\x4d\x72\x4a\x82\x9c\x2c\x58\x4a\x3e\xa3\x21\x39\xbd\x14\x54\x44\xc9\x2b\xd6\x99\x0f\xa2\x33\xe4\x55\xba\x4c\xc2\x80\x8d\xb9\x47\x68\x54\xcc\x68\x46\xce\x68\x96\xb3\x19\xda\x96\x6d\x89\x1a\x7b\x24\xcd\xa0\x96\x4e\x50\xb0\x31\x64\x24\x5d\xb0\x82\x5d\x12\x24\x97\x24\x0e\x0a\x5d\xd6\x45\x81\x1e\x69\x48\xa2\x04\xaa\x9d\xa5\x72\x65\x47\x05\x39\x8f\xe2\x98\x9c\x52\xb2\xcc\xe9\x64\x19\x73\xc1\xf1\x74\x59\x90\x9f\x0f\x3e\xbe\x3e\x3c\xfa\x48\xf6\xde\xfe\x8b\xfc\xbc\xf7\xfe\xfd\xde\xdb\x8f\xff\xda\x21\xe7\x51\x31\x4b\x97\x05\x61\x12\x25\xd4\x15\xcd\x17\x71\x44\x43\x72\x1e\x64\x59\x90\x14\x97\x24\x9d\x40\x15\x6f\x5e\xbe\xdf\x7f\xbd\xf7\xf6\xe3\xde\xf3\x83\x9f\x0e\x3e\xfe\x8b\xa4\x19\x79\x75\xf0\xf1\xed\xcb\x0f\x1f\xc8\xab\xc3\xf7\x64\x8f\xbc\xdb\x7b\xff\xf1\x60\xff\xe8\xa7\xbd\xf7\xe4\xdd\xd1\xfb\x77\x87\x1f\x5e\xf6\x09\xf9\x40\x59\xc7\x28\xd4\x50\x8f\xe8\x09\xcc\x59\x46\x49\x48\x8b\x20\x8a\xe5\xfc\xff\x2b\x5d\x92\x7c\x96\x2e\xe3\x90\xcc\x82\x33\x4a\x32\x3a\xa6\xd1\x19\x0d\x49\x40\xc6\xe9\xe2\xb2\xf1\x44\x42\x65\x41\x9c\x26\x53\x18\xb6\xa2\x32\x42\x0e\x26\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1a\x0c\xce\xcf\xcf\xfb\xd3\x64\xd9\x4f\xb3\xe9\x20\xe6\x15\xe4\x83\x1f\xfa\x6b\x0f\x07\x92\xd9\xfe\x0d\xc8\x76\x9c\x86\x34\xeb\xff\x02\x2c\xf2\x6f\xc1\xb2\x98\xa5\x19\x79\x13\x64\xf4\x33\xf9\xdf\xb4\xa0\xe7\xd1\xf8\x57\xf2\xfd\x9c\x7d\xff\x8d\x16\xb3\x90\x9e\xf5\xc7\xe9\xfc\x07\x00\x0e\x83\x82\x92\xad\xe1\xe6\x37\xc0\xf0\xea\xb7\x82\x0a\x01\x16\x95\x11\xf2\x98\x6f\xef\x10\x92\x02\x02\x66\xbb\xa0\x0f\xf2\x20\x29\x4c\xc0\x28\x29\x7c\x70\x47\x0e\xe0\xb2\x04\xf2\xc5\x65\x12\xcc\xa3\xb1\x64\xe3\xa8\x44\xc8\x73\x80\x47\xf9\x4a\x7e\x28\xb2\x28\x99\x9a\x65\x72\x48\xf3\x41\xbf\xa7\x81\x35\xc6\x8c\x06\xde\x31\x1e\xb9\xa0\xcb\x32\x58\x4f\xb7\x55\x7f\x01\x38\xca\xc5\x00\x0d\xce\x9c\xa3\x2a\x7a\xb0\xc3\x0a\x3e\x2d\x2d\xc4\x51\x7e\x5f\x55\x01\xdb\x08\x07\xbe\xba\x52\xa7\x47\x52\x02\xbd\x97\x65\xc1\x25\x07\xe7\x4c\xdc\x12\x05\xf6\x19\x7d\x22\x09\x40\xac\x24\xce\x21\x42\x52\xa4\x84\x26\x8c\x86\x07\x21\x65\xff\xa9\x56\x18\x33\x0e\x38\x9b\x64\x5c\x49\xc8\xb5\xe6\xc6\xcc\xeb\xc6\x23\x66\x60\xb9\xb9\x33\x43\x12\xd9\x85\x1a\x72\xa3\x8b\xc0\xfb\xe7\xb4\x98\xa5\xa1\xa7\x5b\x5c\xb9\x9e\x66\x73\xc2\x25\x97\xd4\x98\x91\x35\xc2\xd7\xa0\x28\xfe\x49\xcc\x8c\xc8\x22\x7f\x83\xde\x93\x2f\x9c\x78\xae\x95\x58\xfe\x37\x8e\xf9\x9c\x7c\xc1\x95\x5d\x43\x16\xbc\x55\xc8\xc9\x17\x78\xd7\x70\x4d\xc4\x67\xc4\x78\x03\x97\x88\x18\x19\x42\x5f\xd8\x4e\xc4\xd8\x3d\x20\xc4\x40\x06\xda\xa9\x71\x97\x1c\x1c\x49\x14\x31\x6c\xe6\xa6\x78\x87\xb0\xd6\x9f\x44\x71\x41\xb3\x0e\x2a\xdb\x45\x3a\x08\x41\x45\x85\x10\x0a\x24\x11\x80\x4e\xa1\x7b\x3c\x3c\xd9\xe1\xfc\x33\x9a\x90\xce\x3a\x6e\x04\xd7\xc1\x1f\x68\xf0\xa7\x1c\xed\x28\x39\x0b\xe2\x28\xd4\x34\xc0\x6a\x5c\x1f\x91\x36\xd9\x20\xb8\xf2\x35\x2c\x6b\xe0\x9a\x4d\x0a\x2c\xa1\x34\xb2\x88\x83\x28\xe1\xf4\x65\x4d\x23\x07\x78\x27\x72\xca\x67\x51\xa4\x1f\x9e\xfe\x42\xc7\xc5\xb5\x55\xa1\x9c\x64\x5d\x8e\x57\x1b\x5a\x70\xe5\x53\x87\xba\xe1\xcc\x5c\x8f\x97\xb7\x04\x2e\x98\x34\x54\x2c\xef\x1c\x33\xe0\x93\x1e\x39\x06\xf0\x93\x6e\x33\xd4\xc4\x51\x0e\x12\x10\x5f\x7c\xe5\xd8\xc9\x31\x1a\x80\x05\x70\xec\xf8\xd2\x17\xba\x40\x19\x62\x9c\x66\x1b\xe1\x26\x77\x97\xbe\xc0\x4e\x5e\x46\xdf\xb9\x24\xf0\x29\x2d\xf0\x0a\xcc\x05\xe7\x10\x24\xcb\x8a\x89\xbe\xb1\x12\x46\x0d\xfd\x79\xb0\xe8\x94\xf1\x58\xd0\xca\x79\xd6\x88\xc1\x3b\x79\xcd\x1d\xde\xd3\x63\x28\x72\xc2\xd9\xb3\xfc\x52\xab\x08\xf5\x47\xec\x53\x87\x93\x49\x4e\x0b\xa7\x53\x19\x0d\x97\x63\x8a\xfa\x15\x8c\xc7\x3d\x52\xd3\x39\xc0\x4e\x11\x14\xd1\xf8\x5d\x90\x15\x3f\xc1\x4b\x22\xab\xe6\xbe\x9d\xdf\xf1\xf4\x53\xd6\x95\x31\xa6\x44\xc3\x0f\x6e\x95\x6f\x82\x62\xd6\x9f\xc4\x69\x9a\x75\x3a\x4e\x8b\x1b\x64\x7b\xb3\x4b\x06\x64\x7b\xab\x4b\x1e\x92\xed\x2d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xf6\x16\x19\x19\x09\x25\x9d\x95\xa8\xef\x91\x21\xc6\x7e\x46\xf3\x65\x5c\x48\xea\xe1\x33\xf8\x66\x19\x17\xd1\xcf\x51\x31\xe3\x73\x22\x29\xd0\xe8\x5b\x4f\xd1\x51\xcf\x9c\x41\x59\xb9\x18\x21\xaf\xdf\x3c\xf1\xf9\x49\xdf\x6a\xd5\xb7\x06\x1a\xf6\x00\xad\x11\x35\xbc\x56\x6b\x47\x2f\x1c\x1a\x4f\xc4\x88\x45\x67\xc5\xae\x90\x66\x2f\x83\xf1\xac\x63\x33\xa6\x08\xd3\x16\xe3\xfa\xa5\xf3\xa5\xe7\xea\xa4\x8b\x0b\x71\x84\x40\x57\x36\x5c\x6d\x67\xc7\xec\xbe\x5c\x47\x88\x08\xd5\xda\x65\x54\x4c\xe3\x89\x00\xb1\xe7\x08\x3a\xe0\x76\x49\xe2\x09\x3e\xec\xc9\xc2\x4d\x98\x4b\x71\x63\x97\x50\xf1\x0c\x8f\x0c\xc8\x96\x06\xbd\x26\x34\xce\xa9\x35\xbc\xc1\x80\x84\x69\xd2\x2e\x48\x10\x86\x44\x94\x2a\x52\xb3\xca\x3e\x89\x8a\x76\x4e\x82\x38\xa3\x41\x78\x49\xc6\xe9\x32\x29\x68\x58\x82\xa5\xaf\x34\xce\x6b\xbd\x08\x07\x03\xf2\xf1\xf0\xc5\xe1\x88\x4c\xa2\xe9\x32\xa3\x84\x1d\xd8\x12\x9a\xb3\x13\x20\x3b\xa5\x5d\xe6\x26\xb3\xfa\x2d\x88\xe4\x8f\x33\xc9\xe6\x64\x50\x8c\x40\x89\x95\x92\x65\xae\xd0\x9a\xd1\x49\x00\xea\x98\xf3\x59\x1a\x53\xde\xc3\x28\x99\xae\xd7\x30\x82\x0a\x1e\x60\x73\x7e\x31\xe8\x1e\x49\x9d\x95\x6f\x2c\x72\x39\x27\xb5\xa2\xbe\x67\x8b\xeb\xb8\xaa\x31\x44\x40\xbc\x61\x72\x1e\x68\xb2\xce\x69\xe1\xcc\x29\x27\xab\xb7\xc1\x9c\xda\xfb\x90\xce\xc1\x72\xa6\x5b\xd6\xb3\xf9\x54\xef\x67\xba\x62\x4f\x9d\x8a\x2f\x0a\x0c\x6a\xa9\x56\xfe\x55\x0c\x5b\x56\xb2\xc8\xe8\x59\x94\x2e\x73\xd5\xa1\xad\x1d\x86\x92\x28\x21\x51\x52\x38\x25\xea\xf0\x8f\xfa\xeb\x6b\x90\xfd\x4d\xd2\x8c\xc0\x23\xe1\x88\xec\x92\xcd\x1d\x12\x91\xef\xe5\x00\xe4\x7b\x61\x12\x6d\x6c\x94\x15\x67\x7f\x56\x9f\x37\x76\xc9\x46\x47\xe2\x20\x22\x8f\xc8\xe6\x09\x93\xf0\xc9\xd5\x15\x19\xee\x94\x56\x52\xc1\xca\x05\x3d\x6c\x90\x88\x3c\x2c\x9b\xb9\x0d\xbb\x17\x4c\x38\x28\x63\xfb\xf2\xef\xda\x49\x35\x53\xae\xbb\x9d\xae\x35\x85\x83\x01\x99\x44\x59\x5e\x10\x1a\xd3\x39\x4d\x0a\x76\xbe\xe2\x68\xea\x91\xfc\x73\xb4\x20\x51\xb1\xca\x94\x1b\xd8\x1f\xfa\xb0\xcf\xf0\x57\x39\x03\xf0\x74\x3e\x0c\x23\xd6\x48\x10\xab\x45\x2e\xf0\xe9\xf0\x1f\x17\xdf\x7e\xbe\xa8\x49\xa7\x84\x41\x1c\x47\x64\x83\x6c\x9e\x48\x3e\x41\x36\x88\xd3\x0d\x0f\xda\x6b\x11\x6c\x31\x3f\x0f\xa4\xd8\x2a\x3d\xb4\xcf\xa9\xe2\xc6\xac\xe7\x0f\xcd\x54\x98\xb0\x65\x62\xea\x96\x8b\xbf\x86\x32\x49\x19\x43\x1a\x56\x31\x24\xd2\x88\xa6\x6b\x39\xca\x60\x40\xc6\x41\x3c\x5e\xc6\x41\x41\xa5\xe0\xc3\x8e\x7c\xa2\x2f\x24\x2a\xe8\xfc\x16\xec\x88\xb1\xa2\xe3\x3f\x11\x53\xea\xda\xb0\xd7\x2b\xed\x2b\xb7\x9c\x90\xdf\x8f\xc1\x60\xe6\xf2\xd5\x79\x0b\x71\xb4\x45\xa2\x1f\x35\xda\x10\xa1\x8b\x14\x37\x93\x69\x85\xc6\x88\x43\x36\xd6\x18\xc9\x74\x75\xab\xa9\x54\x22\x7e\x5d\x52\xb9\x1e\x04\x35\xec\x11\xff\xa0\x7e\x9f\x8e\x08\x15\xd3\x3a\x22\x0e\x0d\xb2\x4d\x13\xb4\x54\x2a\x89\x4a\x10\x52\xa6\x23\x2a\x47\x88\x28\x01\x27\x0c\x68\x4d\x23\xa6\x5a\x43\x84\x87\xe8\x3b\x1d\x1b\xb8\x59\x5d\x41\x24\x4b\x71\x2a\xc6\xf0\x9c\x88\x73\xef\x29\xdc\x3a\xee\xdf\xb1\x46\x89\x0f\xb9\x03\x23\x93\xeb\x4b\xab\x45\x0c\xbd\x88\xac\x51\x6b\x98\xaa\x54\x0e\x7a\x54\xb5\x7a\x06\x8c\x51\xce\x81\x58\x99\xbb\x1e\x69\x13\x75\x94\x3a\x89\xfa\xe4\x60\xd1\xb5\x52\x26\x39\x18\x90\x7c\x39\xe7\x37\x74\x9e\x5d\x4a\x88\x88\x0a\x5e\x54\x77\x1c\x9d\x30\xae\xa8\xbe\x60\x4b\xf2\xf1\x1f\xd9\xbc\x89\x08\x29\x6d\x3a\x28\x18\x0c\x48\x46\xe7\xe9\x19\x5c\x63\x92\xf1\x32\xcb\x98\x7c\xaa\x84\xd3\x14\x92\x45\x37\xa3\x1c\x7a\xee\xe9\x6d\xbe\x8a\xc6\x4f\x22\xb3\xb1\xe6\xcf\x18\x19\x79\xe4\xd4\xdf\x98\xd2\x3e\x58\xeb\xb0\xe4\x5a\xc7\x7b\x6a\x95\x3c\xce\x43\x65\x85\x75\xe5\x20\xc9\x8a\xed\x60\xf8\x92\xc4\xbc\xbf\xe0\xbd\x65\x6d\x8d\xc5\x2d\x13\x36\xb5\x80\xde\x77\xb8\xbd\xaa\x6d\x82\x21\xae\x45\x3b\xdd\x9e\x37\xfb\x79\x9a\xc6\x65\x79\x4c\x08\x29\xc9\x3a\xaa\xc8\xc3\x97\x9b\xa5\xcd\x56\x65\x72\x2e\x5c\x96\xfb\x9e\x06\xa5\x3d\x3e\xe2\x99\x6b\x8c\x20\x5c\xfb\x0d\x40\x9d\xb2\xd9\x90\x86\xb3\xa3\xc7\xbd\x16\xbf\xfb\x6d\x8d\xbe\x81\x9f\xac\x6f\xad\xd1\x13\xf6\x1b\x5f\xc7\xb6\x46\x4f\x7b\x3e\x5b\x8f\x28\x29\x5a\xa3\xcd\x21\xfb\x99\xd1\x20\x6e\x8d\x36\xb7\xd8\x6f\x7e\x2b\xdb\x1a\x6d\x6e\xb3\xaf\x25\x87\x82\x06\x96\x02\xec\xc9\xf5\x49\xef\xe9\x6f\x69\x17\x55\x73\x0d\x7d\x33\x6b\x22\x5c\xc9\x2a\x46\x45\x66\x39\xdb\xb6\x08\xe7\xae\x68\x62\xe4\x2f\x5a\x61\x69\x64\xf6\xa4\x49\x5d\xb7\xb0\x3b\x2a\x31\x36\x6a\xd4\x28\xba\x12\xf7\x4e\x97\x64\x3b\xd9\x92\x36\x30\x61\xb2\x86\x5d\x6f\xc9\xf4\xdd\xbd\x25\xd3\xbd\x25\xd3\x7f\x8b\x25\x93\x5e\x08\x77\x65\xce\xf4\x3c\x9a\xbe\x5d\xce\x4f\x81\x15\x2a\xee\x7c\x1a\x4d\x13\x48\xec\xff\xa2\x38\xf9\xb2\x88\x62\xd3\xbe\xa6\x3f\x80\x34\xfe\xaf\x04\x1b\x7b\x41\xc6\x69\x32\x89\x1c\x63\x20\x79\x32\x43\xbb\x02\x9c\x5d\x60\x5b\x90\x03\xe7\xbc\x3a\x27\xc0\xef\x09\x3c\xd8\x60\xe7\x2c\xc6\xb7\xb4\x95\x2c\x2c\x05\x36\x37\xa0\x9c\x79\xc8\x70\xcc\x21\xa3\x9c\x24\x74\x1a\x14\xd1\x19\xed\x49\x4e\x04\x17\x47\xc5\x79\xda\xce\xc9\x38\x9d\x2f\xa4\xb4\x0a\xa5\xd8\xdc\xaa\x92\x93\x38\x0d\x8a\x28\x99\x92\x45\x1a\x25\x45\x8f\x5f\x87\x32\xb2\x0f\xd3\xf3\xc4\x3a\xd3\x99\x6a\x12\xf7\xf8\x76\xc5\xb1\x7c\xa5\xf0\x7d\x2d\xc7\xc2\x96\x52\x42\x69\x08\xa7\xe8\x53\x3d\xc7\xa1\xdf\x18\x06\x90\x76\xad\xec\x7c\xcc\x76\x0d\x06\x0c\xf5\x4b\x2e\xac\xda\xed\xf3\xb9\xe8\x8c\xfb\x2f\x3f\xbe\xfe\xf4\xfc\xe0\xc7\xb7\x47\x6f\x9e\xbf\x7c\xff\xe9\xfd\xe1\xd1\xdb\x17\x07\x6f\x7f\xfc\xf4\xe6\xf0\xc5\x4b\x74\x86\x53\x9a\x38\x98\xc9\xfe\x22\x08\x7f\xa2\x93\xa2\xc3\xbf\x8a\xf4\xe3\x79\x9a\xef\x2b\x2c\x8a\x36\xfb\x45\x2a\xc4\xa5\xcd\x27\xdd\x1e\x79\xf2\xd8\xbc\xe1\xc1\xbb\x25\x0c\xa7\xc3\x1b\x31\x0d\x30\xcc\x89\x97\x87\xdf\x12\x9c\x3f\x57\x67\x63\xf3\xd0\xbc\x2a\x0e\x5d\xa9\xc3\xc0\xa2\x07\x21\x45\xfa\x9a\x5e\xc8\x71\xe7\xcb\xd3\xbc\xc8\x3a\x5b\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xdd\x25\x03\x8c\x22\x1b\xdd\xef\xa3\xe9\xac\x10\xc5\x7a\x24\x26\x0f\xbf\x32\x3e\xc5\x0e\x7c\xa7\x68\x2d\x95\xe9\x6e\x8d\x5d\x79\x3c\x33\xd1\xaa\xb4\x73\xbf\xdb\x0c\x58\x6a\x53\xde\x58\xb7\xcf\xd7\xfc\x06\xa9\x9f\xa0\x3a\x4e\xc7\x25\xf9\xf2\x15\xf1\x41\xe6\xdf\x76\xee\x94\x71\x67\xf3\x59\x9b\x64\xe9\xfc\xa8\x98\x3c\xbd\x9f\x38\xcf\xc4\x89\x77\x46\x65\x8c\x4c\xbc\x42\x92\x93\xc6\xbe\x69\x90\xac\xce\xc8\xec\x27\x47\xe5\x73\xd6\x1e\xde\xee\xaf\x4d\x36\x44\xf5\xe4\x19\x21\xed\xcd\x36\x19\x91\xf6\xb0\x7d\x7b\x1e\x55\x87\x49\x76\x62\x65\xa5\xfe\xc1\xe0\x72\xc2\x04\xe3\xf9\x32\x2e\x22\x2e\x54\x9e\x5e\x92\xad\xff\xcc\x99\x78\xae\x6c\xe8\x02\x56\x73\x41\xa7\x34\xab\xd8\x4a\xde\x8b\x5a\xeb\xf6\xef\x55\x67\x44\xd8\x32\x97\xcc\x88\x40\x93\x45\x7d\x0c\x6b\xaa\x45\xb5\xb9\x46\x73\x9a\x5b\x59\x5b\xdd\xfe\x22\x3d\xef\x6c\x6e\x3d\xed\x76\x4d\x94\xee\xcf\xe8\xf8\x33\x89\x26\x06\x4e\x91\x58\x64\x21\x22\x8f\xa6\x09\x0d\x0f\xf2\xb7\x3a\xdb\x51\x44\xab\x3a\x66\xf4\x42\xf4\xd8\x44\x86\x24\x5a\x38\xf4\x41\xdb\x85\x29\x89\xa5\xec\xc8\x72\x1e\x31\x31\x3c\x88\x73\x6d\xb5\x6c\xb7\x5e\x8b\x2f\x1f\x86\x24\xbb\x19\xf6\xc8\x66\xb7\x47\x36\x9f\x20\x79\x64\xab\x6b\xe4\x76\xc9\xee\xee\x2e\x23\x59\x2f\x15\x66\x8c\x7d\x3c\x0a\x62\xe8\x14\xe1\xaa\x03\x7d\xe1\xc1\x45\x4d\x97\x88\xb8\x22\xc1\x16\x02\x0d\xf2\x70\xec\x60\x19\xce\xb4\x60\x58\xd1\xae\x12\x0e\x61\x59\x44\x53\xc2\xe5\x74\x8b\xde\x54\x17\x0c\xfc\x19\x46\xb1\x0c\x98\xcf\xe3\x2e\xef\x0d\xd2\x65\x76\xba\xe4\xea\x8a\xb4\x86\x2d\xa1\x23\x1e\x0c\xc8\x58\x51\x11\x13\x9e\xe5\x44\xaa\xd6\x39\x50\x54\xf0\x89\x56\x92\xb6\x2b\x64\xcb\xfb\x5b\x6b\x9e\xc5\xdc\x7a\x54\x90\x9e\xf9\xe5\x53\x3a\x8f\x92\xa5\xbd\x0a\xda\x93\x5b\xfe\xb5\xa1\x6e\x59\xf9\xa6\xba\x1e\x6b\xd0\xa1\x1b\x50\xd0\xb2\x9a\x84\x8e\x2a\x69\xc8\x47\x3d\x74\x25\xf2\x11\xcd\xbb\x84\x73\x74\x17\x94\xf3\x75\x50\x26\x58\x7e\x19\xca\x1c\xde\x5d\x8b\x32\xc0\x18\x12\x89\x4d\x14\x89\xe6\x5c\x14\x39\xcc\xdc\x67\x71\x6e\x2d\x46\x01\xd3\x0f\xa3\xb3\x28\xa4\xe1\xf3\xcb\x0a\x1e\x7e\x13\x6a\xaa\xc1\xcd\xd1\x5d\x23\x67\x59\x8a\x9d\xa3\x95\xd1\x73\x74\x1b\xfc\xb8\xb7\xb0\xbc\x6a\x85\xa2\x32\x89\x4b\x3f\x98\x6e\x8c\x17\xb9\xb3\x99\x73\x51\x8a\x23\xd1\xb4\x8b\x22\x47\x3e\xf3\x61\xc8\xb3\xbc\x60\xbf\xba\xa5\xc0\xb6\xd9\x26\xcf\xf8\xd6\x2c\x3c\x63\xac\x86\xcd\xd2\x93\x23\x7a\x97\x5b\xb1\xf7\xc5\x74\xa2\x11\xc7\x24\x88\x8a\xb3\x8d\x23\x7a\x24\xc1\x9c\xf2\x07\x3e\xec\x97\x25\x82\x09\x18\x56\xa7\xaa\xc1\x83\x79\xe7\x10\x0a\x6d\xf4\x08\x56\x96\xb3\x42\xe2\x89\x35\xd9\x25\x65\x2f\x75\x1f\x76\x07\xe8\x48\x93\x47\xbf\x0a\x9e\x98\xc3\x2d\x95\x28\x7f\xbc\x79\x62\x8a\xc2\xed\xe1\x05\x13\x99\xdd\xc9\xed\xe7\x71\x34\xa6\x4c\x32\xd9\x22\x0f\xa1\xba\x15\xe9\xbc\x66\x66\xf0\x29\xfc\xce\x26\x68\x55\xf4\x97\xaa\x02\x9c\x4d\x46\x1d\x11\x2d\x3e\xc0\x11\x27\x2e\xc1\x6c\xcc\x3d\x79\xdc\x15\x7b\x78\x91\x0a\xf8\x2e\x79\x28\x4f\x95\xbe\x19\xb0\x2a\xe2\xd2\xe1\x93\xc7\x3d\xd1\xfe\x6a\x53\x50\x71\x2a\xe7\xc3\xf7\x1c\xcb\xef\x14\xfb\x41\x3e\x8e\xa2\x2a\xfc\x7b\x8e\xf3\xbf\x21\xe6\xa5\x56\x07\xb4\x03\xcd\xf0\xbf\xda\x04\x68\xf7\x34\x65\x33\xb0\xa7\x1d\xd8\x94\x4c\x41\x29\x6f\x2f\x41\xb9\xaa\xd0\xc5\xb6\xcf\x81\xcd\x0a\xd2\x94\x81\xbb\xd6\xf0\xa2\x45\x36\x88\x38\xe3\x00\xda\xf9\x6f\x65\x56\xf0\x78\xd8\x23\x38\xa9\xcc\x67\xc0\x17\x69\xfa\x81\xce\x9a\x23\xeb\xbb\x67\xc3\xc0\x8a\x1d\x39\x29\x0e\x1c\x5e\xe0\xa3\xb2\x0c\xa7\x14\x47\xe6\xc8\x4d\x72\xfb\x91\xa6\xf1\xc8\x4e\x70\xa0\x98\x04\x32\xb2\x13\x30\x94\x12\xcb\x46\x76\x82\x0b\x75\xe4\x80\x1d\x79\xe1\x70\xa3\x3a\xc5\x53\x9f\x0b\x78\xe4\x87\xc4\x83\xd5\x29\x1e\x38\x8c\x6d\x94\xe4\x42\xfa\xa6\xc7\xcd\x71\xcb\x99\x13\x84\xd3\x5c\x58\x41\xf5\x23\xef\xba\xbb\x96\xd7\xba\xe6\xe5\x50\x6b\xb4\xf9\xb4\xd7\x32\x2f\x95\x5a\xa3\x2d\xb0\x60\x80\x85\xd1\x1a\x6d\x6e\xf6\x5a\xf8\x6a\xaa\x35\x32\x3f\xaf\x4f\x7a\x9b\xc3\xdf\xd9\xa5\xcb\x01\xb7\x8d\xaf\xf0\x41\x14\x25\x45\x99\x0b\x22\x71\x7b\x15\x25\x05\xf7\xce\xc2\x7e\x3c\x56\xbf\x4e\x74\xe2\x36\xfa\x6d\x39\x6f\x89\x92\x82\xbb\x6e\x89\x92\xe2\xc9\x63\x05\xf6\x54\x57\xb4\xf5\xcd\x93\x92\xba\x18\x7c\x8d\x2b\x23\xfb\x68\xf8\x15\xbd\x71\x01\xb8\x6d\x86\x70\x90\x14\x2b\x5a\x5e\x18\x25\x2a\x0c\x2e\xa0\xb9\x8a\x92\x37\x32\xaf\x88\x92\x42\x8a\x8a\xcf\x6e\xe4\xd2\x85\xf7\xaa\xde\x0c\x62\xb3\x51\x14\xbb\x7b\x3b\x88\x7b\x3b\x88\x3f\xaf\x1d\x04\xd1\x86\x10\x5c\x54\xba\x23\x1b\x88\x06\xa6\x0d\x36\xab\xe7\xa6\x0b\x29\x18\xa4\x6b\xcf\x1d\x7d\x8f\x84\x7a\x3e\xa3\x89\x7a\xaf\xd8\xe3\xb6\xdf\x4c\x00\x57\x0e\x1c\xa4\x64\x39\xf0\xda\x46\x58\xea\x6f\xfb\x79\x22\x70\x52\x29\x3f\xf2\xff\xaf\xae\x48\xbb\x8d\xf8\x6c\x2a\x5f\x2e\xf0\x1f\x3b\xe8\xa9\x61\x94\x88\xd6\x1b\x7b\xfc\x98\xd2\x02\x9b\xfc\x82\x01\x79\x3b\x97\x0f\x41\x81\x97\xb0\x4a\x0c\x6b\x77\x2d\xdf\x73\x63\x57\x53\x8a\x96\x6a\x26\x5d\x2b\xae\x8c\x74\x64\x1f\xbb\x86\x41\x3b\xa0\x07\x1b\xb4\xdb\x8d\x54\x9a\xa2\x81\x95\xbf\x71\xec\xc0\xd7\x8f\x8d\x91\x31\xce\x28\x23\x26\xb9\x1e\x4c\xb7\x2c\x9c\xdc\xc3\x68\x32\xa1\x60\x90\xcc\x51\x6e\x9d\x4b\xce\xd5\xbb\x10\x7c\x1c\x91\x28\x11\xb3\x24\x6d\x97\x13\xef\x21\xc4\x3c\xba\xb0\xed\xd0\xd7\x8f\x60\xc1\x39\x8c\xea\x45\x39\x2a\xcf\xfd\x6f\x66\x4d\xba\x2b\xbd\xd5\xd3\x04\xa9\x48\x75\x15\x8c\xa6\xf3\xd3\x28\x71\x3d\xdc\x14\xe9\x94\x32\xee\xce\x6a\xa0\xd3\x3e\x5f\x54\xc1\x62\x41\x13\x58\x4b\x41\xc2\xdf\x40\x58\xd8\x15\xb5\xd5\xdd\xc3\x08\xc6\x34\x8b\xc6\x8c\x3d\xc9\x5e\xd5\x17\x16\x17\xa8\xe9\x44\xc0\xc2\x3e\x54\x89\x5a\x39\xbc\x3a\xbd\x5f\x15\x5a\x95\xde\x82\x5f\x99\xec\x90\x7a\xec\x8e\x83\x38\x16\xf8\x95\xd7\x38\x7c\x44\xb3\x40\x2f\xdd\x3c\xfa\x55\x38\x17\x84\xeb\xba\x59\x90\xf7\xd8\xff\x92\xd0\xc0\xfd\xaf\xe7\xde\x0e\xe3\x5b\xd9\x82\xfa\x75\xa6\x95\xa8\xf1\x7b\x67\xf2\x2d\x5c\xb1\x2a\xd6\x77\x77\x41\xba\x98\x44\x89\xf5\x56\xa9\x0e\x09\xda\x6b\x91\xa8\x4a\xdc\x30\xdb\x4a\x03\x9e\xbb\x97\x3f\x2f\x3f\xfa\x73\x8d\xaf\xab\xa1\x69\xb0\xcc\x8c\xda\xab\x06\xbd\x0e\xa3\xd6\x2e\x00\xba\xe4\x19\x69\xb7\xc9\xa8\x99\x41\x16\x42\x99\xd7\x2c\x6b\x05\xbc\x31\xde\xcf\x95\x13\x4a\x66\xf4\x3d\xf7\xd2\xfa\x0b\x3f\xce\xe4\xde\x23\x6f\x85\x03\xcc\xf0\x83\x39\x26\x32\x20\xf1\x4a\x2c\xea\xc6\xbc\x28\x04\xbf\x4a\x36\xfe\x7c\xfe\x99\xd4\xf2\xda\x21\xfc\xca\x8f\x94\xd0\x9d\x98\xb0\xce\xea\xa8\x33\xb6\xb5\x12\xdc\xa1\x4d\xc9\x8f\x3c\x99\x10\xc8\x4b\xf8\x06\x58\xa4\xf3\x45\x71\x89\x55\x82\x0d\x36\xd1\xda\x55\x68\xd2\x23\x62\x4f\x23\x90\x3e\x56\xc0\x8d\xf4\x38\x55\xea\x6b\xca\x8b\x89\xca\x81\x88\x2a\xeb\xc6\x60\x5c\xac\x6c\x78\xc4\x82\x9b\x8c\x43\x3f\xc6\x2b\xf7\x0f\xf5\x53\x94\x17\xce\xcb\xbf\x63\x63\x34\x27\x1e\xa7\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x17\x24\x6f\xea\x97\x8b\x90\x5b\xb6\x8a\x77\x70\x4a\x15\x59\xa4\x05\x7a\xeb\xca\x0b\x4b\xe1\x88\xfb\x1d\x22\xc6\xdb\x3e\xf5\x84\x50\x80\x9a\xcf\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\x76\x16\x62\x36\x4f\x76\x71\x8f\x35\x2c\x1e\xc6\xc6\xae\xab\xe8\x17\xaf\xb5\xdc\x17\x5a\x1c\x52\x8b\x40\x9d\x14\xbf\xba\x55\xaf\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x7c\x91\xa0\x7a\x30\x76\x5c\xc7\x53\xd2\x22\xcd\xc1\x8f\xf1\x52\xd7\x7f\x43\xa1\x7c\x2f\xdd\x69\x13\xae\xd2\xf9\xba\x47\xda\x6d\xa9\x7c\xaf\x50\x52\xbc\xe3\xb3\x64\xe9\xf4\x94\xfa\xee\xfa\xa4\xb7\xd9\x28\xd6\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x66\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x41\xc9\xdb\xf8\xc3\xd2\xcd\xb1\x34\xae\x9c\x63\xbf\xa4\x76\x8e\xfd\x7e\x8a\xaa\x43\xfa\x39\xa7\xc6\x06\x1a\x3a\xe7\xee\x7d\x15\x15\x1d\x2b\xbc\x8a\x8e\x8e\xc3\xdb\x4a\x3a\x96\xba\xa2\x96\xce\x2c\x52\xa1\xa6\xe3\x2d\x56\x95\xbd\x89\xa2\x8e\xe1\xb6\x44\x51\xd7\xcc\x51\xbe\xe8\x56\x03\x45\x5d\xa3\x68\x5e\x5f\xeb\x71\x9d\xe7\xf6\x6f\x15\xf2\xe0\xc5\x57\x21\x10\x59\xc2\x26\x11\x9e\xbe\x22\x91\xd8\x85\x2a\xc8\x44\xb6\x5b\x5d\xfe\x46\x3a\x5d\x2e\x49\x35\x79\x33\xe7\x69\xef\x6e\x5f\xcb\xa9\x51\x36\xa0\xbb\xbb\x8f\x3e\x52\xf9\x7e\xc7\xc3\x87\x91\x8b\xdb\x28\x6f\xee\xdb\x76\x4c\xb3\x22\x88\x12\xbf\x7f\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\xf5\x06\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\xaf\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x77\xb9\x96\xfd\x0b\x3f\x66\x6b\x8b\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7f\x8b\x21\x9a\x2f\x11\xee\x9c\xb6\x55\x78\x85\xe3\xf4\x8c\x66\xd9\x79\x16\x15\x05\x05\x73\x2f\xde\xab\x16\xd9\x80\xde\x37\xc6\xdd\x39\x68\xd9\x73\xfc\x90\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xef\xb0\xfd\xd6\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x42\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\xf0\x7a\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x3f\x5c\x00\xd9\x37\x4c\xa2\x81\x82\xb9\x9b\x19\xee\x96\x83\xb7\x76\x75\x45\x8e\xdb\xc7\x9b\x27\xed\x93\xae\x12\x06\x6b\xdc\x00\x88\xee\x99\x78\x67\x5f\xd8\xb5\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xb3\x47\x1e\x81\x3d\xf6\x10\xfa\xb2\x89\x1d\xd1\xe8\x0e\x39\x82\xac\x74\xd4\xd0\x93\xae\x1d\xca\x4e\x0b\xd2\xa1\xc3\x43\x09\xa8\x1b\x18\x0c\x48\x10\xc7\xe4\x34\xc8\xa3\x31\xf7\x7f\x00\x8f\x05\xb6\xb7\x84\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\xb6\xb7\xea\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x28\xd4\xf1\x49\x8b\xec\xfe\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x3d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\xdd\x7e\x19\x0e\x61\x1c\xa2\xdb\xb1\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x8f\xd1\x6f\xe4\x02\xee\x44\xee\xba\x2a\x32\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x71\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xd7\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x38\xd2\xf9\x21\xa9\x25\x8c\x15\x36\xc5\x54\x6c\x3e\x56\x80\x5b\x27\xc7\xdb\x02\x54\xa6\x71\x10\x05\xb1\x79\x62\x25\xe8\x6f\x77\x77\x00\xac\xde\x60\x7b\xc0\x63\x11\x43\xac\xdf\x13\x50\x63\x77\x34\x91\xd1\x84\x74\x50\x16\xe2\x90\x36\x3f\xbe\xe1\xc4\x02\xc3\xf6\xbd\x86\xd8\xac\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xf1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x8e\xcd\xca\xf1\x74\x6b\xef\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\xbc\x28\x96\xee\xf6\xc2\x5b\x51\x1c\xe4\x05\x39\x3e\x61\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc7\x12\x1c\x68\xf4\x4b\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x9c\x5e\x92\x90\x4e\x82\x65\x0c\x8a\xd0\x7c\xc9\xe4\x54\xb5\x31\xb7\x84\x9b\x9a\x9e\x08\xf3\x68\xcf\xa2\x71\x8c\xba\x01\x03\xd6\x3b\xe2\x8a\xa2\x70\xc3\xd3\x5b\xa9\x51\xbd\xf4\xd5\x2e\x75\xc4\x68\x89\xe4\xf6\x1a\x01\x8a\x17\xa4\x7c\xdc\x62\x14\xdf\x23\x2d\xb6\x08\xd8\x7f\x27\xad\x13\x4d\xed\x02\x02\xa5\x41\xa1\x64\x19\xdb\xcf\x1e\xd0\x6c\x36\x42\x9b\xed\x60\xce\xea\x6f\xcd\x42\x70\x9d\x54\x39\x2b\x81\xef\x0d\xc2\x59\x1e\x9f\xf5\x1c\x6e\x78\xd9\x70\x8c\xf1\xb2\x7f\x61\xd5\x5b\x44\x2c\xb8\x55\xe7\xdf\xc7\xfc\x34\xfe\xef\x93\x6e\xbd\x88\x20\x94\xb7\xca\xdb\x43\xf9\xbd\x83\x15\xc6\x42\x42\x37\x67\x1d\xf2\xed\xa9\x7b\x97\x65\xe1\xcc\x73\x69\x21\xee\xd1\xed\x8d\xc1\xeb\x8f\xda\xbc\x95\x11\xae\x50\xa5\x13\x54\x9b\x2d\xd4\x78\x83\x55\xf6\xdf\xd8\x98\x78\x87\x94\xfe\xf9\x1d\xa3\xba\x7e\x65\x69\x3c\xc1\xfe\x64\x05\x2b\x73\x0a\xa9\x97\xc9\xc7\x27\x3e\x27\xe2\xfd\xc5\x32\x9f\x75\x1c\xcf\xa4\xf2\xa5\xb6\x74\x33\xea\xd6\xcc\xc6\xe2\xfa\x5c\x3f\xf3\x39\x00\xc5\x2d\x21\x3f\x9e\x9d\xb3\x1e\xc1\xfe\x65\x2d\xf7\xa4\xb7\x72\xea\x2b\x26\x10\x3b\xf3\xbd\xf5\xfc\x41\xd7\x1d\xa9\x43\x20\xfe\xb7\x9f\x3f\x9f\x47\xd6\x1a\x4f\xac\xa5\x13\xc1\x66\x13\x5c\xa5\x56\xcc\xc7\xca\xb3\xb1\xe6\xdc\x11\x5a\xba\x23\x63\x49\x22\x8f\xb6\x4d\x7c\x82\xf2\xfb\xd1\x49\x96\xce\xbd\xe6\x06\x1c\xca\xc7\x5b\x4e\xed\x07\x3b\x96\x81\x90\x61\x19\xb4\xc2\x83\x29\xc9\xd4\x78\xcb\x0d\x58\x94\x18\x08\x66\x51\x86\x3f\xcd\x1a\x56\xf5\x55\x78\x15\xec\x4d\xf8\xc6\x92\x0b\xba\xe2\x89\x0f\x74\x4f\x0a\x3a\x02\x5d\x0f\xc9\x16\x18\x3f\x74\xa5\x47\x67\x81\xbc\xb2\x45\x54\x59\x27\x6e\xde\xa9\xd8\xb7\xa2\xa0\xc0\x87\x82\xdf\xb1\xe3\xd2\x1b\x64\x9b\x3b\xbd\xe7\xbb\x6d\xce\x40\x72\x12\x4c\x0a\x9a\xa9\x45\x82\xfb\x7b\xa3\xb5\xea\x2f\xe3\xf3\xdd\xad\x39\x47\x89\xcf\x6e\x52\x89\x3d\x11\x3a\xe6\x6d\x59\xfd\xd8\xaf\x47\xa9\x1b\x69\x3b\xe6\x4d\x25\xa3\x69\xc8\x69\xc8\xc3\xea\xbe\x31\xd8\x8d\xdd\x6a\x98\x46\x8c\xca\x74\x38\x8b\xa6\x7d\x83\x44\x77\xcb\xb5\xfe\x10\x7b\x08\xfe\x6b\x48\xfd\xd2\x20\xb5\xe1\xdf\x1f\x8a\xf8\xef\x69\x1f\xfd\xfd\x2e\xb4\x4f\xbc\xa4\x8f\x03\x34\xde\x94\xf4\xed\x30\x62\x2b\x6e\x2a\x0e\xb1\xda\xf5\x37\xdb\x59\xcc\x5e\xac\x52\xbf\x98\x3f\x2f\xbd\xc5\x0e\x7d\xf9\xd7\x5f\xf9\x12\x5e\x88\x5b\x3f\xd7\x48\xb5\xae\xfb\x1d\xb2\x49\x36\xcc\xde\x75\xb9\x4f\x26\x1e\x49\xcc\x33\xf5\xdc\x03\xb1\x75\xe9\x66\x3c\xd8\xae\xf0\x67\x6f\xe0\xda\xb2\xf8\x32\xb8\xd8\xda\x8a\x63\xc3\x73\xae\x56\xd6\x56\xd7\x54\xab\x7a\x2f\x12\xad\xae\xd7\x5e\xf0\x96\x5f\xed\xaa\x37\x71\xd7\x27\xbd\xcd\xdf\x3b\xf4\xfe\x51\xfd\xb3\xb7\x65\xc5\xbb\x37\xe1\x89\x04\xfe\xe7\xb6\x2e\x4b\xfd\xf4\x6d\x89\xde\xbe\x2d\xf1\x83\xb5\xa5\xe7\xf5\xdb\x52\x3d\x7f\x5b\xa2\xf7\x6f\x4b\xf4\x00\x6e\x69\xbe\x80\x73\x6a\x6c\x60\x61\xe3\xf8\x47\xf9\x8a\x8f\xe0\x8e\xbc\xaf\xe0\x8e\x56\x7f\x06\x77\xd4\xf4\x1d\xdc\x91\xfb\x10\xee\xe8\x0e\x5e\xc2\x2d\x6f\xfd\x14\xee\xa8\xf1\x5b\xb8\xdf\x3b\xae\xff\x51\x03\x8b\xb3\x65\x95\xc9\x99\x74\xad\xc2\x7f\x08\xe2\x44\x56\x67\x4b\x6c\x76\xb6\x34\xac\xc4\x96\x3e\xc3\xb3\xa5\xb6\x3c\x5b\x62\xd3\xb3\x25\xb6\x3d\x5b\x5a\xc6\x67\x9e\x7a\x9b\x2c\x8e\xdf\xd4\xfe\xec\xc8\x6f\x80\x76\x74\x03\x0b\xb4\xa3\xc6\x26\x68\x47\x1e\x1b\x34\xbb\xf4\xcd\xd6\x48\x85\x19\x5a\xd3\x45\xd2\xdc\x10\xed\xdb\x26\xab\xa4\xbd\xcc\x29\x28\x66\xc7\x45\x9b\x07\xe4\x9b\xa6\x84\x26\x67\x24\x4c\x29\x58\x2b\xc0\xeb\xc0\x20\x09\xc1\x87\x2d\xf9\xe7\x9b\x9f\x5e\x17\xc5\xe2\x3d\xfd\x7f\x4b\x9a\x17\x6b\x20\x98\x5d\x2e\x68\x3a\xb1\x72\xb8\x1f\x1b\xf5\x7e\xa3\x2d\xf1\x22\x1a\xee\xdb\xd0\xe4\xcb\xf5\xce\x9a\x11\x2c\xb2\x14\xd2\x4c\x00\x49\xfd\x97\x7c\xc6\x76\x9f\x68\x9a\xa4\x19\x1d\xc5\x51\x42\xd7\xae\xb9\xc5\x2a\xc3\x43\x23\x6f\xf7\xf7\x2f\x67\xef\x5f\xce\xfe\x89\x5f\xce\xf2\x57\xb3\xc2\x86\xcd\x78\x36\xcb\x37\x1c\x72\xb3\xd7\xb3\x62\xef\x3b\x2a\xa2\x18\xea\xe4\xfa\x4c\x58\x3b\xfc\x79\x92\x03\x16\x15\x97\x8a\x25\xea\x22\xe3\x38\xc8\x73\x72\x0c\x45\x4e\x44\x37\x79\x86\x66\xc2\xbc\xaa\xb5\x01\xdc\x1b\xc1\x2a\x15\xca\x55\xc6\x41\x48\x85\x33\xeb\xe6\x7e\xce\x01\x92\xd5\x74\xf4\xf6\xe0\xe3\x07\x76\xb6\x86\x49\x68\x9f\xd3\xa8\xcd\x49\xb3\xfd\x19\xfd\x7e\x83\x7e\xff\x88\x7e\xe7\xbf\x06\xa7\xa9\xfc\x98\x44\x49\x42\x2f\xd5\x17\x9d\x17\x29\x3c\x65\x94\x29\x8b\x68\x6c\x26\x24\x41\x62\x26\xcc\xa3\x71\x66\xa7\xc4\x71\xe4\x14\x32\xe0\x0d\x50\xf9\x61\x14\x99\x66\x41\x12\xaa\xa1\x18\x59\x3f\x1a\x5f\x1f\x8d\xaf\x77\xc6\xd7\x4b\xe3\xeb\xff\x8c\xaf\x7f\x19\x5f\x6f\x8d\xaf\x17\xc6\xd7\x3f\x8c\xaf\x23\xfe\xb5\x76\x52\xee\xba\x86\xcd\xd1\xbb\xbd\x17\x6c\x8a\x47\x64\x7b\xab\xa7\x12\x3f\x1c\xfc\xf8\x76\xef\xe3\xd1\xfb\x97\x9f\x7e\x7a\xf9\xf6\xc7\x8f\xaf\x47\xe4\xb1\xce\x84\x59\x1d\xe9\x9f\x3a\xa7\x84\x72\x46\xe4\x0b\xb1\x12\xb4\x1f\x75\xc8\xf8\xf4\xe2\xf0\xe7\xb7\xe4\x5a\xd7\xf4\xee\xf0\xa7\x9f\x18\xf4\xc7\x83\x37\x2f\x0f\x8f\x3e\x8e\xc8\xe6\x70\x38\x1c\x88\x1e\x8a\x1b\xef\xe7\x71\x3a\xfe\x3c\x22\x6d\xc6\x3a\xf3\xa2\x6d\xe4\xed\x8d\x21\x94\xf1\x48\xbf\x6d\xe4\x0f\x30\xd8\x7e\x5e\xe7\xfb\xe4\x3e\x14\xc6\xfd\x46\xf6\x57\xdf\xc8\xd6\x94\x0b\x88\x7c\x16\x6c\xdf\x95\x07\x88\xfd\xec\x72\x51\xa4\x7f\xff\x80\x37\x87\x31\xa4\x3d\xd2\x11\x30\x58\x83\x5e\x80\x01\xcb\x69\x7b\xa3\x3b\xb9\xee\x1b\x80\xe2\x72\xfc\x40\x55\x24\x91\x07\x0f\x64\x6e\x5f\xfa\x8b\xe0\x62\xf2\x8c\x5e\xb4\xed\x57\x74\x86\xe7\xaf\x1f\xc8\x16\x2b\x6d\x7b\x3f\xde\x92\xee\x22\xcd\xe2\x44\x5e\x86\xab\x0b\x7e\xcb\x3f\x3b\xb1\x5e\xdb\x71\x50\x89\x23\xd6\xb9\xfe\x6b\x7a\xd1\x07\xed\xa5\xf0\xdc\xeb\xb3\x31\x62\x58\x91\xc3\xd6\xad\xf3\x13\x1d\x57\xbf\x8d\xc8\xd6\x37\x4f\x78\x49\xf4\x38\x59\xbe\x39\x63\x2c\x4f\xe1\xb8\x35\xfa\xe6\xbb\x5e\xcb\x44\x79\x6b\xf4\x74\x78\x7d\xd2\xdb\x6a\xe4\xf3\xe9\x9e\xef\xdd\xf3\xbd\x3f\x2f\xdf\xd3\x6c\x8f\xbf\xf3\xbf\x03\xbe\x67\xc9\xee\xab\x8b\xee\x1e\xc9\x5d\x16\xf4\x09\xee\x2b\x45\x1b\xb2\x79\x6d\x7f\x20\xd8\xbd\x0e\x47\x34\x79\x8a\x01\xd8\xb7\x12\xe1\x97\x49\x54\xbc\x09\x16\x4a\x5c\x6c\x4b\x89\x7a\xc4\x79\x50\x7b\x28\x65\x4d\x26\xb5\x8f\x34\x5b\x6c\x6f\x1a\x72\xfe\x08\x65\x0c\x87\xaa\xd0\xff\x56\xe4\x9d\x06\xa7\xa7\xc1\x94\xaa\x96\x70\x1e\x12\xfe\x47\x76\xde\xdc\x53\x27\xca\x7e\x53\x9d\x1d\xa7\x67\x34\x0e\xc6\xb2\x59\x3b\x5b\x9f\x31\x46\xbe\xec\xa9\xbf\x72\x04\xf1\x63\x2d\x44\x3e\x0b\x92\x24\x4d\x8c\x71\x9b\x10\xfa\x5c\x33\xaa\x80\xa8\x69\x05\x4e\x56\x23\x0f\x04\x46\xa5\x3e\x2f\x8d\xaa\x81\xea\x6a\x12\x67\xb7\x91\x17\xc8\xa8\x4c\x9d\xc7\xec\xb1\x79\x00\xfd\x43\x34\x01\x0d\x72\xf5\xc0\x21\xd0\xcf\x26\xac\x0f\x14\xcf\x35\x9c\xfa\x2a\x2b\xc6\xfd\x6d\x54\x37\xae\xbe\x69\x01\x54\xa6\x58\xa1\x0c\x2b\xe6\x37\xb6\xd2\x8e\x18\x16\x41\x28\x4c\x49\xc1\xd4\xf3\x62\x41\xc7\x6c\xf3\x52\xe6\xf9\xd8\xe8\x4a\x78\x4f\xf1\x59\x4e\xe9\x2a\x4e\x29\x83\x0b\x45\x44\x2e\xcb\x06\x6b\x3c\x0b\xb2\x60\x5c\xd0\x2c\x97\x2a\x7e\xb8\x97\x17\xa5\xd1\x3e\xe2\x6d\x23\x9a\x26\x3d\x64\x0b\x4d\x86\x6b\x7e\xb7\x1f\xd1\x74\x56\x10\xe9\x91\xd6\xf2\xee\x2b\xc6\x60\x48\x9b\x1c\xa4\x07\xbd\xcb\x7b\xd0\x8e\xc7\xc7\x10\xb7\x10\x01\x18\x08\x4a\x0b\xaf\x55\xd5\x0d\xf1\x66\xb7\xff\x4b\x1a\x25\x10\xac\x81\x3c\x83\x3a\xc8\x88\xb4\x86\xad\x2e\xd9\x10\xc0\x25\x86\x6f\x37\x9e\x0b\x08\xd8\xf3\x67\x9f\x0c\x18\xc4\x8a\xb3\x21\x7a\xb8\xc1\x3d\x2e\xdf\x74\x5e\xca\x0c\x11\x4d\x47\x34\xb0\x75\x82\x19\x22\x04\xf3\x70\x7d\x4c\x5b\xf3\xc2\xbd\x35\x57\xcc\x4a\x94\xb0\x4a\xfc\xc8\xc2\xfe\xa8\x3d\x8e\x92\x58\xe3\xda\xec\x90\x7b\x20\x39\xe2\x5b\xbb\x12\xe9\x67\x3c\xde\xf3\x60\x40\x5e\x45\x49\x48\xf8\xe3\x2e\xd1\x51\x15\xaf\x99\x49\x14\xad\x96\xbe\xc9\x07\xdb\x97\x1e\x84\x90\x9a\xd1\x0b\x69\xc2\xac\xce\x5c\x2c\x8d\x9f\x7a\xd8\x89\xa3\xfc\xac\xc4\xaa\xd9\xc2\xef\x5e\xc0\xb8\x46\xd8\xd4\xec\x90\x68\x63\x77\x0b\x83\xcb\x58\xc8\xd8\xb6\x43\x37\xd5\x89\x58\x3b\x22\xf4\x85\x6a\x61\x42\x3a\xbc\xc8\xee\x2e\x19\x76\x8d\x53\xda\x69\x46\x83\xcf\x1a\x94\x8d\x72\x63\x97\x88\x57\xe5\x6c\x06\xf7\x67\x41\xb6\x9f\x86\x14\x6a\xf0\x1e\xc2\xd8\x64\x4b\x73\x9c\xbc\xc8\x9a\x51\x08\x9f\xb4\x95\x48\x64\x8f\x15\xf9\xed\x68\x04\x9a\xfb\xef\x21\x92\x9b\xcc\x7c\x5e\x94\xbd\x4e\x37\x27\xdb\xe3\x63\xbe\xb3\xc8\xe8\x24\xba\xe0\x41\xb4\x86\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xb4\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x45\xf9\x34\x7c\xed\x0b\xb7\xeb\xa2\x1b\x98\x2a\x18\xc5\x0b\xcc\xf3\xd9\x87\xe5\x20\xcc\xb6\xf9\x72\x90\x33\xc2\x5a\xd2\xd4\x31\x49\x33\xdb\x84\x2e\x2f\xb2\xb2\x88\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x5e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x04\x07\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x5d\x92\xc8\x7d\xe1\x7b\xb2\x45\x9e\xb1\x93\x0d\xd9\x20\x6c\x3f\x48\x7c\x34\x21\x5c\xc8\xcf\xe8\xc5\x5d\x92\x86\x15\x73\xc0\xa6\x8d\x1a\xd6\xf0\x9b\x11\x87\xc3\x33\x10\x75\xfc\x36\x14\xf0\xbb\x4d\xab\xe5\xb1\x74\xb2\x8c\x63\x85\x86\x01\x3d\xa3\x49\xc1\x1f\x0a\x00\xcb\xff\x25\x4f\x13\x12\x9c\x46\x36\x8f\x97\x6e\x13\x3f\xa6\xaf\x96\x71\x6c\xbf\xa1\x94\x8f\x09\x58\xe9\x47\xbc\xb4\xfb\x18\x8a\x37\xec\xb4\xab\x19\xbb\xdb\x86\x21\x48\xb1\xca\xb1\xea\x94\x7d\xf7\xc1\x84\x22\x4a\x42\x7a\x71\x38\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe9\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x84\x70\x9f\xc1\xef\x90\x6b\x21\x8a\x99\x96\x7f\xaa\x15\xb2\x41\xda\x1d\x36\x73\xaa\xf6\x0d\xd2\xee\xb6\x1b\xad\xbd\x30\xca\x17\x71\x70\xc9\xe7\x05\x7c\x8c\x26\x05\x93\x6d\x15\x36\xec\x37\x6b\x17\x90\xfd\x82\x17\xab\x7a\xe1\xca\x6a\x33\x27\xdf\xbf\xbc\x8c\x1e\xb0\x2d\xcd\xa2\x18\x3a\xed\xcb\x78\x8b\x97\x1d\x61\x56\xd7\x25\x8f\x7e\x50\x89\x6a\x5a\xdd\xbe\x55\x3e\x7c\x56\x36\x9b\xce\xcc\x1a\x68\x16\x60\x7c\xb2\xc9\x33\xfb\x4d\xab\x78\x0f\xc6\xd6\x8c\x76\x36\x32\x18\xe8\x81\xa6\x67\x34\x8b\xd3\x20\xa4\xa1\x52\x04\x7b\xd6\x04\x1e\xc0\x47\x4d\x24\x65\x6f\x1a\x07\xe4\xe3\xe1\x8b\xc3\x11\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x70\x1a\xd3\xbb\x1c\xa0\x3e\x0d\xd8\xaf\x77\x37\xc9\x23\x82\xb2\xbb\xdd\x7e\x46\x17\x71\x30\xa6\x9d\x36\x69\x83\x53\x37\x76\x5a\x68\x99\x41\x22\xd3\xe4\x8c\x66\x45\xae\x43\x6e\x82\xdc\x17\xd2\x71\x34\x0f\x62\x9b\xc9\x46\x89\x9f\xd9\x17\xe9\x0b\x5e\xc0\xa5\xbc\xca\xf0\x99\xa6\x5b\x43\x2e\xe0\x89\x9a\x6a\x03\x40\x16\xa9\x1b\x1f\x53\x85\x9f\x69\x32\xc6\x5a\xd9\x96\xf1\xc4\xbb\x1a\x17\xaa\xab\x3a\x38\x6b\x22\xb5\xa4\xee\xf8\x3c\xa1\xb9\x85\xfa\xd4\xdc\x51\x8c\xc3\x3e\x07\x88\x69\x9e\x7f\x9c\x05\x49\x67\x08\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xb3\x0b\xe1\x5b\x51\x8e\x81\xc5\xbd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4b\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\xa7\x74\x92\x66\xb4\xef\xd0\xd5\x6b\x71\x74\xa8\xc6\xfd\x95\xd8\x83\x6a\x48\xeb\x35\xec\xf3\x06\xf2\xd5\xfa\x7d\x28\x4c\xc5\xe6\xc1\x05\x0f\x5b\x79\x11\x15\x97\x23\xf2\x14\x54\xd8\x72\xd7\x89\x72\xe1\xd2\x18\x8a\x76\xed\x4d\x06\x4d\x72\x67\x83\x41\xec\x18\x45\xf1\x74\x56\x17\xb6\xca\x0a\x43\xba\x33\x46\x3b\xec\x14\xc2\x91\xd6\xf6\x56\x01\xf1\x95\xfe\xfe\xe1\xf0\x6d\x5f\x61\x99\xb7\xa7\x1d\x58\x82\xeb\xd8\x9c\x04\x76\x34\xcf\x1e\x59\x04\x79\xce\x78\x57\x31\xcb\xd2\xe5\x74\x66\xae\x00\x35\x10\x41\x6b\x50\xab\x7b\x39\xa9\xb9\xda\x23\x38\x2d\x79\x64\xde\xd2\x11\x4b\x00\xf1\xb6\xc3\xac\xae\xa6\xb6\x33\x69\x3f\x8a\x2a\x20\x9d\xf5\x28\x7f\x15\x25\x51\x41\x2d\xa4\x5b\xdd\x00\x09\x11\x75\xc2\x94\xb2\xdc\x8e\xa2\x75\xf1\x5e\x6c\x2a\x7c\x1d\xb0\xf3\x52\x02\xdc\x9f\xfc\x4c\x6d\x41\x6a\x4a\x0b\x88\x58\x7c\x38\x39\x4a\x22\xaf\xb6\x0b\xca\x16\x33\x2a\x7e\xa8\x05\x47\x8a\xb4\xa7\xb4\x53\xca\x21\xba\x37\x6a\xa3\xea\x87\xaa\xa6\xc3\x3b\xd3\x85\x22\xe0\xb6\x2b\x27\x34\xcb\xd2\x4c\xba\xa4\xe1\x3d\xce\x49\x92\x16\x64\x9c\x66\x19\x1d\x17\xa3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\x3f\xa5\xe7\x34\xdb\x0f\x72\xda\x01\xe6\xc2\xf5\xbd\x9a\x8f\x31\xa8\x7f\x88\x5b\x66\x71\x75\x73\xcc\xfe\x3f\xd1\x47\x71\x04\x82\xfd\x7e\x63\xc2\xe3\x9e\xc8\x12\x7a\x4e\x5e\xb2\x51\x75\xda\x70\xd5\x0b\x1d\x01\x5b\xd5\x7f\xb7\x0b\x42\x2f\xa2\xbc\xc8\x7b\x64\x11\xd3\x20\x07\xb1\x18\x46\x9e\x26\x0a\x55\x93\x34\x8e\xd3\xf3\x28\x99\x42\xc9\x9c\x71\x41\x6b\x19\x89\x1e\xf6\xc0\xbf\x42\x4f\x3f\xfb\xa8\x88\x12\xab\x7a\x0f\xde\xaf\x4c\xaf\xc2\xc1\x67\x0a\x8b\x90\x33\x7c\xb8\x8c\x8e\xc0\x9e\x56\x31\x59\x4e\x02\x8c\xd5\x82\xaf\x0a\x3e\xf1\x1c\xb5\x82\xb2\xde\xa5\x79\x1e\x9d\xc6\x7c\x0a\xc1\x85\x86\x30\xea\xfb\x70\xc0\xe4\xcb\xac\xe0\x3f\x99\x48\x2d\xb1\xf5\x72\x32\x89\xa6\x97\xe2\xe3\x50\x92\xd2\x23\xf2\x99\x35\xcf\xff\xf4\x75\x15\x7c\x8a\x9b\x2d\x0e\x36\xd7\x60\xea\x72\x89\x7f\xca\xab\x28\x0e\x37\xd5\x70\xea\xfe\x87\x7f\x8a\x0b\x23\x9d\xc7\x0b\x3c\x7a\xa4\x16\xa6\xbe\xc7\xe1\x05\x7e\x0d\x4e\x53\x23\xcf\x53\x42\xde\xc3\xf0\x01\xc0\xf5\x0d\xce\xe3\x25\x50\x2f\x50\x61\xfe\x29\xb0\x80\x40\x88\x05\x81\x3e\xe0\x32\x45\x20\x84\x6a\x1c\x4e\xd1\xef\x42\xfe\xb6\x45\x0a\xce\x17\xac\x93\xef\x95\x92\xd3\x39\x39\x8c\x83\x84\x9d\x0c\x02\xc5\x9a\x45\xba\xd0\x95\xa5\x19\x09\xc8\xeb\x97\xff\x84\x43\xb8\x94\xd6\xee\x8c\xa1\xa8\x7d\x56\x1e\xed\x7e\x9e\x51\xe9\x67\x2f\x40\x57\xb9\x22\x0a\x0a\x0a\x16\xc0\xd6\x53\x90\x93\x73\xca\x16\x88\x76\xb0\x22\x87\xb1\x86\xa4\xa1\x9f\xa9\x71\x24\x97\xe3\xc4\x2c\x85\x8b\x3a\xac\x66\xc9\x24\xb0\x50\xc4\x4b\xe0\xa8\xb1\x26\xa7\xe2\xdc\xc9\x92\x87\xf0\x36\x2c\x2a\x20\xcf\x8c\x46\x46\xf8\x0b\x49\x56\xb5\xcb\x37\xe0\x38\xf6\xac\xe0\x73\x1a\xdd\x2f\xd8\xff\x96\x25\x5e\xa4\x55\x0b\x1c\x9d\x17\x7e\xb3\xa5\xce\x56\xdb\xef\xb8\xd8\x01\x21\x77\xb3\xd4\x8b\x68\x4e\xf3\xdf\x63\x99\x27\x42\xb9\xc8\x16\xb7\x52\x55\xe5\xfc\x98\x0f\x5b\x34\x51\xb6\x2c\x0e\x39\xa8\x9e\x34\x22\x0a\x4d\x06\xf2\xee\x90\xcd\xbd\xa6\x05\xb3\x36\xe5\xe5\x4a\x57\xa0\x01\x14\xfe\xb1\xf1\x8d\x35\x0b\x35\xe7\x9f\x6f\x98\x10\x08\xcb\x5e\x96\x17\x3f\xae\xae\xc8\x70\xc7\x7b\xb8\x11\xf5\x3a\x87\x13\x9e\x6e\x9c\x88\x04\xce\x65\x4f\x1e\x3c\x20\xe2\xb7\x4f\xe8\x67\x4d\xda\xb9\xf8\x84\xe1\xf3\x81\x66\xc8\x62\xa2\xb0\xd2\x89\x0c\x2f\xda\xbd\x76\x1b\x5f\xb8\x58\x9e\xd2\x7c\xa5\x31\xa1\x94\xca\x74\x89\x8c\x1d\xeb\x21\x15\x45\x27\x1c\x4c\x46\xf1\x50\x47\x31\x61\x36\x09\xb0\xc5\x79\xda\xce\xc9\x58\xc5\x74\x71\x48\xcb\x0c\xf9\xd2\x84\xbe\x4a\xa8\x06\x1d\x92\xcd\x3a\x4d\x85\x97\x41\x32\x0c\xfc\x14\x51\x96\x6f\xc1\xc2\x93\xef\x0e\xf2\x5a\xa7\x0a\x60\x95\x44\xed\xd4\xb5\x26\xb7\xfc\x6b\xc1\x2c\xf7\x17\xf1\x32\xd7\x5d\x10\xdf\x5e\xf7\x86\x0a\xc8\xd4\x24\xcd\xe8\xf8\x73\x2e\x8f\x4d\x9c\x47\xca\x6b\xce\x5c\x3c\x96\x8b\x2f\xc1\x8f\xaf\x37\x1a\x31\x27\xf9\xb1\x37\x12\xb1\x19\x53\x18\x35\xc0\xd6\x7f\xa0\xe1\xb1\x63\x3b\x08\xae\x24\x66\xce\xaa\xdb\x98\x38\x51\xa9\xa5\x41\x1b\xfc\x67\x78\x71\x3c\x7c\xf4\x5d\xf0\x68\x72\xf2\xe5\xf1\xf0\xfa\x7f\x06\x51\xbf\xa0\x79\xa1\xc0\x57\x18\x7b\xc5\x90\xbf\xce\x60\x1b\x0c\x13\xce\xff\x83\xff\x74\x86\x17\xdd\x67\x95\xe3\xc4\xf4\x37\x18\xe8\x58\x59\x3c\x1a\x16\xf4\x8e\x7b\x10\x16\x46\x87\x73\x78\xc7\xcb\xf6\x63\x34\x6a\x93\x7e\x85\x23\x40\x62\xba\xaa\xf0\x76\xc6\xec\x0b\x63\x73\x08\x6c\xef\xd1\x2b\x2f\x98\xd5\x65\x08\xdd\xd5\xce\xc1\xd9\x71\x3e\x67\xff\x8e\x83\x45\x0e\xb2\x43\x1c\x13\xf9\xdd\xc3\x1e\x1a\xed\x1e\x73\xc7\xf3\xa8\xc3\x46\x03\x87\x6a\x7b\xe7\xd8\xa1\xc1\x78\x46\xc6\x41\xee\x54\x13\xe5\x9c\x50\x96\x73\x31\x43\x88\x9a\xf8\x2a\x6b\x4e\x53\xbc\xad\x7c\x39\x9f\xd3\xb0\x94\xbc\xac\xe6\xee\x98\xcc\xac\xda\xab\xc8\x6d\x30\xe0\xe3\xb1\x70\x13\xa8\x92\xe2\x97\xb3\x03\x69\x7d\x88\x80\x78\x1d\xe4\xe0\x8c\x66\x16\x6c\xcb\x46\x4c\x5d\x8a\x94\x76\x7c\x0e\x5f\x1e\x0f\xe1\x8e\x92\x58\x14\x02\xce\xbb\x8b\x19\x89\x29\x3c\xa7\x46\x11\xf8\x16\x0b\x9a\xb1\xde\xca\x69\x48\x20\x7a\xe1\x34\xe2\x01\xee\x82\x9c\xce\x83\x05\x9b\x8e\x4d\x43\xd3\xd7\x51\x16\x0c\xa8\xd3\xe0\x96\x6d\xf3\x49\x97\xfc\x40\xbe\x65\xdb\xb9\xc8\x3a\x8e\x4e\xfa\x45\x7a\xc4\x1a\x12\xba\xa0\xf5\xdd\x5d\x94\x09\x44\x5f\x5d\xe1\xf7\xbb\x9e\x1a\xb1\x76\xc9\xaa\xb1\xc4\x57\x38\x5a\x96\x9a\xe5\x1b\x8c\x5f\xc7\x5f\x50\x54\xfa\x46\x1c\xf5\x24\x35\x96\x90\x62\x91\xde\x25\x29\x4a\xed\xb5\xda\x97\x57\xa0\x44\xa4\x33\x56\xd4\x67\xbf\xba\x16\xed\xb4\xdb\x82\x94\x5c\x32\x35\xf0\x7b\x23\xa2\x45\x40\x63\xa7\xf7\xac\xa2\x0a\x32\x96\xbd\x40\xd7\xee\x36\x49\x03\xd3\x9b\x69\xd3\x3f\x46\xa4\xdf\xb1\x83\xcf\x84\x3b\xd0\x97\x37\x71\x8a\xc2\x0d\x02\xae\xa3\x5f\x93\x82\xec\xfe\x6f\xec\x96\x12\x37\x22\x2f\x9b\x91\xd6\xd6\x54\x49\x9a\x56\x49\x53\xf2\xd4\x92\xa6\xc1\x46\x8b\x94\x49\x94\x51\x48\xb6\x86\xdc\x67\xd0\x23\x71\x41\xc8\xdb\xe4\xef\x13\x86\x17\x84\x1b\x77\xb8\xc6\x5d\xb5\x94\xec\xbf\xed\x17\xde\x07\x30\xd7\x56\x06\x5c\xcd\xe8\xd7\x12\x67\xbc\x1b\x9f\x74\xaa\x2b\xf1\x81\x64\x78\xbe\xdb\x56\x6d\xb4\x9e\x8a\xc4\xe5\x97\xaf\x3e\x13\x42\x86\x5e\x84\x2b\x25\x55\xa3\x7e\x4d\xd5\x23\x8f\x87\xfe\x5b\x02\xe9\x88\x58\x9e\xa6\x73\x2d\xe5\xd6\x07\xd9\xf4\x9e\x24\x7d\x57\x5f\x46\xe0\x4d\xbe\x91\xf9\xce\x80\xa4\xc3\xbb\x61\xc9\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xd7\x6b\xf0\xcb\x70\x9c\xe1\x4d\xe5\xb6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x8c\x8b\x1a\xa2\xc8\x13\x22\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x95\x36\x22\x59\x15\x3f\xb2\xe8\x8d\x30\x24\x4b\xb4\x1b\x8e\x88\xf5\xa9\xa8\x1f\x0e\xef\xfa\x0d\x06\x73\x28\xfa\xd6\x70\x31\x30\xf1\x22\x59\xc6\x31\x44\x49\xe8\xb8\x2b\x04\x0c\xb7\x41\x85\xe1\x19\xbb\xb8\xaf\x6d\x38\xf2\x53\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x0b\xc0\xfa\xb1\x38\x11\x35\x1a\x8e\xc4\x8d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\xc7\x11\xbe\x8f\x82\x8d\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x0b\x84\xca\x31\xb0\x62\xf6\xab\x01\xe5\x3a\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x99\xe4\xcf\x70\xb5\x31\xeb\x35\x19\x43\xd8\xa7\x0e\xf5\xec\x6d\xf8\x40\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x4e\xe3\x74\xfc\x99\xcc\x68\x10\xd2\x8c\x7d\xa4\x73\xdb\x6a\x23\xca\x9f\xb3\x64\x9f\xd0\x30\xa3\x17\xca\x2f\x3a\x94\x25\x93\x28\x2e\x6c\x65\xa6\x87\x60\x01\xd6\x70\x3f\xcc\x52\x2a\x4f\xfa\xdf\x6c\x6e\xe9\xa3\x3e\x07\xaf\xc1\x4b\xf9\x41\x9d\xd7\x85\xab\xf2\x9d\xd3\x5d\x28\x5f\xc4\x61\x7d\xce\x5e\x73\xfb\x71\x83\x99\x89\x53\x26\xe6\x2d\xa2\xb1\x3b\x0f\x1f\x59\x72\xdd\x3c\x14\x0a\xa8\x62\x02\xa0\x26\x63\x02\xa0\x58\xe5\x04\x3c\x79\xac\xf1\xcf\xa1\x6f\x8c\x7f\xa8\x0a\xd7\xe4\x43\xbf\x03\x74\x23\xec\x97\x38\x1e\x11\x22\xdf\x48\xfe\xe8\xc9\x54\x78\xf4\x33\x52\xbf\x78\x3a\x08\x86\x23\xfe\x9f\x4c\x11\x16\x24\x23\xfd\x93\xe7\x20\xeb\x92\x11\xfe\x90\xe5\x8e\x8a\xc9\xd3\x91\xf8\x5f\xa6\x81\xbd\xca\x48\xfe\xd0\xf5\x70\x58\xf9\x4b\xa7\x0b\x78\xf5\x53\xd4\xe3\x1a\xdd\x8e\x7c\x89\x1c\xda\xb5\xe5\x1c\x79\xd2\x0c\x58\x69\x36\x39\xb2\x13\xe4\x38\x7e\xa6\x30\x8a\x9f\x29\x1a\x03\xa4\x89\x1f\x12\x4e\x49\x8b\x23\xfc\x21\x73\x4d\x95\xf5\xc8\x49\x51\x58\xe3\x82\xfa\x48\xff\xe4\x39\x48\x3a\x1e\xe1\x0f\x99\x6b\x9c\x44\x46\x76\x82\x84\x42\xf9\x56\x8e\x75\x74\x1f\xb9\x49\xb2\x87\x0e\xa4\x93\x24\xeb\x94\xc2\xd8\x08\xfd\xc6\xfd\x4d\xa6\x23\xf5\x4b\xa6\xf3\x3d\x75\xa4\x7e\xa9\xd1\xf3\xf5\x3e\xd2\x3f\xd5\x98\xd8\x2e\x39\x92\x3f\x64\x2a\xdb\xb0\x46\xe2\x7f\x55\x07\xe3\x77\x23\xf9\x43\xa6\x02\xdb\x18\xc9\x1f\x3d\x58\x60\xdc\x41\x9d\x78\xd5\xdd\x1a\x6d\x7e\xd7\xab\xf4\x6f\xd3\x6b\x2d\x8b\xc9\xd3\xd6\xe8\xe9\x37\xd7\x27\xbd\xad\xcd\x26\x1e\x1f\xcc\x25\xbc\xcb\x17\x70\x4b\x38\x3a\x68\x8d\x48\x6b\xd8\xdf\x1a\xf6\x37\x5b\x6b\xd7\xd2\x15\xdc\x56\xa3\x48\xc5\xf7\x9e\x24\xee\x3d\x49\xfc\x15\x3c\x49\x88\x5a\xd6\x5c\x5f\x70\x7f\xa7\x93\x49\x46\x2f\xc9\xcf\x51\x3c\xfe\x4c\xc9\xf7\xbf\xd0\xc9\xc4\x76\x27\xd1\xd0\x63\x1c\x80\x45\x41\x42\x0e\x99\xc4\x1d\x00\x54\x14\x24\x2e\xd8\xab\xe0\x94\x81\xfd\x23\x9d\xd2\x38\x2f\x68\x1c\xd3\x8c\x7c\x3f\x81\x44\x17\xf8\xc7\xe0\x8c\xfc\x9c\xa6\x21\xf9\x7e\x5a\xea\xe6\xe2\xb1\x76\xef\x23\x7c\x41\xbe\x09\x92\x60\x6a\xfa\x9e\xe8\x0f\x18\x16\x06\x19\x07\x98\x73\x00\xe9\x63\xe2\xe0\x14\x0e\x47\x36\x70\x74\x1a\x24\x12\xe4\x25\x98\xf1\xdb\x10\x5c\xf2\xca\x07\xb4\x98\x49\xc0\x17\xcf\x2b\xe0\xc2\x53\xe5\x6f\x76\x56\x55\x5f\x3e\x53\xf5\xbd\x05\xcf\xe4\x65\x80\x09\x2d\x24\xe0\x3b\x9a\xe5\xf0\x94\xaa\x1c\x7a\x21\x40\x54\x27\xce\x83\x6c\x5e\xd5\x0d\x96\xaf\x80\x69\x51\x40\xd4\x26\x17\x3e\x17\x59\x12\x54\x72\x15\x03\x52\xb2\x0b\x76\xa2\xd2\xce\x3d\xa2\xd8\xaa\x10\x85\x95\x2f\xf7\x11\xc2\x81\xa4\x37\x26\xf1\x70\x83\x26\xa1\xa7\x6f\x3c\x43\x82\x3d\x87\x13\x93\x0b\x75\xca\xd2\x15\x26\xb3\x74\x41\xb3\xe2\xd2\x03\xb7\x10\x59\x12\xf4\x75\x51\x2c\xde\x65\xe9\x59\x14\x7a\xc9\x8d\x2d\xd4\x85\xc8\x56\xc4\xb6\x18\x57\x94\x88\x16\x63\xbb\x40\x33\x8f\x86\x6b\x6b\x4a\x56\xff\x99\x9e\x6e\x93\x8e\xac\xc6\xf4\xca\x9b\xd9\x2b\x24\xa1\xe7\xd6\xb2\xd1\x25\x91\x83\x5e\x11\x6a\x15\xf5\x5c\x42\x21\x20\xca\xdf\xba\xd0\x73\xb6\x5c\xc0\x51\x3f\xae\x22\x3c\x15\x99\x2f\x9e\x3b\x79\xf9\x4c\x96\xfc\x30\x73\x4b\x26\xb0\x06\x58\xee\x5b\x5a\x38\xb9\x0b\x4d\xf8\x0c\x44\xae\x03\x07\xee\xf4\xd7\x5f\x65\x1b\x8c\xae\xdd\x3e\x68\x02\x07\x20\xf1\xd9\xc1\x30\x9a\xb2\xf5\x51\x23\x58\x44\x23\xb5\x19\x8a\xff\xf9\x91\x03\x77\x52\x60\x2b\x37\x8a\x62\xf2\x19\x19\x5f\x3d\x05\x83\xe8\x65\x84\x3f\x9c\x26\x3e\xa9\x35\xc0\x7f\x38\x03\x14\x00\x1d\xdd\xbe\x20\xe7\x88\xe6\x23\xf4\xbb\xc3\x8d\x79\xae\xbb\x3b\x4c\x62\x1a\x0c\xc0\x05\x6f\x4e\x89\x1e\x43\xca\x77\x62\xf0\x09\xb4\xc6\xc8\xcd\x33\xbe\xba\xb1\x95\x8e\x8b\x09\x8d\xb2\x4e\x19\x4f\x93\x62\xca\xc3\x31\x83\xeb\x69\x1c\x17\x5e\x99\xb4\x3d\x7d\xc9\x28\x0f\x16\xa1\x7b\xf1\x99\xd2\xc5\x41\xfe\xe1\x32\x19\x47\xc9\xb4\xb2\x2b\x50\xd6\x82\x6f\x46\x81\x9e\x8e\x60\xbe\xf0\x5c\xdb\xaf\x58\x50\xf2\x19\x0c\x77\x27\x05\x5f\x1e\x18\xf9\x64\x56\x42\xc1\xb7\x07\x4e\xbc\xbb\x96\x60\xec\xd3\x81\xc2\x4f\x70\x39\xa0\x4a\xf1\xc2\x1a\x75\xca\x04\x4f\xdb\xfa\x3d\x95\x6c\x5e\xa4\x78\x6b\xb5\xa1\x51\x9a\xa7\x6e\x8c\x4b\x59\x7b\x15\x4e\xb9\x89\xa3\x84\xfc\x99\xfa\x47\x86\xa1\xc4\xb7\x03\x87\x4d\x5b\x38\xa4\x4a\xf1\xc0\xba\xb7\xc2\xb2\xcc\xbe\x7d\x5b\xe8\xf4\xb9\xac\xac\x93\xe3\x69\xf7\xe0\xf9\xde\x5b\xd4\x18\xfb\x74\xa0\xb4\x7b\x1a\x0e\x26\xbe\x7d\x70\xd2\x73\x8a\x02\x84\x04\xb6\x8b\xd9\x0b\x9f\x6f\xfd\xf8\x25\x37\xbf\x14\x32\xbd\x2b\x9a\xd7\x75\x70\x27\x6d\x43\x96\x5d\x9f\x86\x51\x06\xaa\xe2\x71\xb0\x80\xd7\x17\xe8\x02\xd3\x33\xa3\x07\xfb\x7b\xef\x8c\xb5\xcf\xca\x61\x0b\xb9\x88\x8b\x92\x6c\xf9\x32\xa9\x92\xe7\x1b\x8f\x3d\x19\x44\x5f\x34\x23\x57\x36\x38\x94\x51\xfc\xb7\x2a\xe2\xe8\xb1\xe2\xdd\xb0\xd7\x09\x71\xa4\x63\xde\x39\x27\xa0\x83\x69\xcb\x3d\x29\x49\x43\xda\xee\x19\x10\x53\x30\x0b\x19\x91\x36\x13\x3a\x3e\x8d\xe3\x88\x26\xc5\x3f\x38\x78\x5b\xdf\x49\x77\x7b\x37\x69\x8d\x16\xe7\x69\xf6\xb9\xac\xc1\x84\x16\x9f\x04\xa8\x05\x62\x06\x0c\x18\xd9\xab\xfc\x96\xdd\xa2\x42\xa1\x5d\xd6\x2f\x5a\xcc\x3e\xc1\x5c\x8f\xd3\xf8\x1f\xbf\x43\xff\xce\x67\x51\xbe\x50\xbe\x91\x9d\xee\xe5\xb3\xd9\xad\xd1\x06\x3f\x4f\xbc\x7b\x49\x94\xef\xa7\x49\xc2\x7d\x36\xa1\xe5\xd6\x35\x68\xaf\xe3\xdd\x2e\x1f\x3c\xf0\x6e\xa3\xb8\xca\x4e\xd7\xbf\x83\x71\x2f\x05\x52\x26\x2f\xa5\x79\x30\x0e\x85\xc8\x09\x42\xa2\xf1\xea\x6d\x59\xdd\xd2\x9b\x28\x3e\x21\x70\x95\x93\x71\xb0\x68\x8d\xb6\x86\x2c\x09\x1f\x49\x5a\xa3\xad\x4d\x96\xa6\x8f\x03\xad\xd1\xd6\x63\x95\xc2\x45\xa7\xd6\x68\xeb\xa9\x4a\xc2\xc2\x7d\x6b\xb4\xbd\xa5\x32\xd8\x0a\x6f\x8d\xb6\xb7\x75\x82\x16\xea\x5b\xa3\x6d\x5d\xa9\x3e\x16\xb6\x46\xdb\xdf\x3a\xc9\xb4\x98\xb5\x46\xdb\x4f\x9d\xf4\x84\x16\xad\xd1\xf6\x77\x4e\xba\x14\x84\x5b\xa3\xc7\x43\x27\x33\x9f\xcd\x5a\xa3\xc7\x9b\x6e\x3a\x93\x85\x5b\xa3\xc7\xba\xfb\xf2\x8c\xd3\x1a\x3d\xfe\x46\x25\x9a\x07\xe7\xd6\xe8\xf1\x13\x95\x25\xa5\x96\xd6\xe8\xf1\xb7\xd5\xba\xbd\xeb\x93\xde\xd6\xf6\xbd\xe6\xed\x5e\xf3\xf6\xdf\xa2\x79\x0b\xe2\x18\x1c\x4c\xdc\xce\x8f\x2b\x52\x70\x39\xaa\x10\x9f\x2e\x44\x86\x89\x79\x79\xc6\x2d\xfa\x91\x8e\x01\x7a\x23\xe1\x74\xd0\x98\xba\xe8\x48\xae\x9e\xc6\xab\xa8\x79\x05\x97\xbb\x56\x65\x90\x26\x21\xce\x79\xec\x23\x13\x44\xb2\x22\x91\xa9\xbc\xbb\xee\xc5\xb1\x31\x14\x53\x30\x32\x8f\x56\x3d\xb8\xa9\xef\x11\xcb\xb4\xac\x44\xe9\x61\x26\xe0\x23\xf2\x2f\xfc\x72\x9e\xfd\x87\x93\x1d\x73\x49\xbe\x09\x39\x3d\xac\x0e\xf3\x6d\x49\xad\xd2\x1f\xf8\xae\xfa\x75\x75\x05\xf1\x6f\x88\xed\xf7\x81\x25\x42\xea\x71\x9b\x49\xa1\x10\x57\xa0\xdd\x23\xed\x22\xe5\x3f\x4f\xfa\x1c\xcd\x28\xde\xe1\xc4\x73\x1b\x2a\x9a\x39\x9e\x9c\x80\x81\x8b\xb2\x0f\x15\x37\xa4\x5d\x4f\xd0\x6c\xab\x1a\xd6\x1f\x56\x7c\x17\x11\x0f\x77\xa1\x03\x1d\xe1\xe7\x25\x1d\x04\x4f\x37\x28\x6d\x16\xf4\xc3\x2d\xf0\x45\xa1\xf1\x6a\xe0\xd9\x7c\xdd\x85\xbd\x53\x54\x61\xdc\x13\xb5\x38\x0c\x8a\x40\x8e\x80\xfd\xee\xb3\x7f\xc8\x2e\xfa\x7d\x75\x05\x46\xb1\x0a\x00\xae\x92\x73\x09\x22\xbe\xae\xae\x74\xf4\x4d\xd0\x36\xb2\xa6\xe5\x1d\x39\x02\x3c\x1e\x9e\xf4\x73\xc6\x10\x94\x8b\x75\x06\x3d\x17\x02\x8e\xa6\x30\x77\xba\x7e\xf1\x4c\x17\x6e\x65\x57\x98\xda\x0a\xe9\xce\xbd\xb4\xed\xfc\xa2\xde\xa7\x77\x8f\x87\x27\xe8\xe1\xd5\x3a\xb4\xdf\x25\x5f\xe0\xb1\x43\x90\x24\x69\x41\x26\x51\x12\xf2\x7e\x45\xc9\x94\x37\xf4\x4c\x35\x3f\x4e\x93\x3c\x8d\x69\xff\x3c\xc8\x92\x4e\x1b\x97\xe0\xde\x72\x18\x2b\x8e\xd3\x69\x1b\x99\xbe\x8a\x1e\x33\x54\x38\x1e\x97\xa8\x60\x43\x38\x32\x17\xcc\x5d\xc7\xb7\x3a\x7b\xbc\x5b\x3d\x93\x20\xcc\x23\x14\xd4\x28\x9d\x1d\xc2\x14\x37\x58\x8e\x17\x74\xcc\x24\x00\xcf\x7a\xec\x81\x47\xa6\xd3\x60\xfc\x59\xc5\x10\x05\x57\x04\xe2\xb0\x2b\xaf\x5b\x3b\x41\x36\x5d\xc2\x5b\x90\x63\xf5\x0b\x79\xe3\x31\x8d\xd0\x65\x8d\x10\xfb\xb9\xb2\x18\xf6\x1b\xd7\x71\x20\xd8\xc4\x6f\x9a\x7e\x2c\x34\xdb\x48\x96\x71\xec\xa0\x3b\x95\x94\x26\xbc\xdf\xe9\x03\xb0\x84\x98\xa0\x28\x6b\x5c\x33\x0b\x98\xec\x9f\x46\xa6\xd2\x10\x89\xdf\x9c\xb3\x77\xd2\x1e\x1c\x94\xda\x3d\x2f\x63\xed\x49\xf6\xce\x0e\x5b\x9d\x6e\x4f\x37\x84\x30\x5c\x3f\x53\x41\x51\x04\xe3\xd9\xc7\x74\x5f\x3a\xc2\xc2\x53\x26\xbd\x63\xe1\x33\xb7\x9e\x5a\x3e\x6e\xfe\xe9\x0c\x47\x16\xed\x07\x71\xac\xf6\x13\x01\x5c\x72\xa6\x70\xba\xa9\x0e\x18\x9e\x13\x86\xf7\x88\x01\xa4\xda\x1a\x6d\x81\x74\xcf\x57\x7d\x6b\xb4\x05\xb2\x3b\x8e\xd9\xb6\x0d\xc0\xd6\x46\xd8\x1a\x3d\xde\x66\x22\xf3\xe3\x7b\x91\xf9\x5e\x64\xfe\x6b\x8b\xcc\x28\xdc\x0b\x9c\xbd\xef\x2a\xde\xcb\xdf\xf3\x34\xc9\x16\x63\x53\xde\xfc\x85\x27\xaa\xab\xc3\x2c\x4b\x6d\x11\x98\xa7\x29\x49\xd4\x55\x51\xb0\xc1\x1a\x42\xa6\x23\x63\x02\x3a\x3e\x95\x4a\x9a\x22\x23\x17\x81\xbd\x6b\x1c\x05\x06\x61\x28\x7d\x3a\x32\x76\x2c\x0a\x83\x9b\x6c\xe8\x9a\x48\xb0\x2c\x02\x83\x30\xf4\xd8\xd8\x12\x31\x7e\x5e\xa8\xd0\xd6\xad\x83\x35\x18\x27\x66\xc5\x61\xe8\x93\xb9\x7d\x03\xcf\x79\x54\x70\x09\x51\x3b\x22\xc9\xb4\xab\xfa\x2f\x60\xbc\x5d\xf3\xed\xe7\xa6\x77\x01\x85\x5f\xa3\x9b\xee\x14\xe8\x7b\xa2\x24\xe4\x6a\x26\x09\xdb\x43\x75\xd3\x2c\xeb\x09\x49\x34\x77\x65\x62\x4e\x3e\xfc\x97\x10\x16\x35\x80\xc0\x0f\x76\x31\xa9\x50\xd9\x23\xf0\xba\xbd\xe4\xfd\x9a\xa8\xf2\x18\x60\x4e\xf0\xf1\xa0\x54\x60\xe7\x45\x4a\xaa\x65\x62\x8d\xec\x8f\xa8\xb4\xef\xc8\x3e\x76\x81\x75\xb1\x88\xfa\x51\xfe\x8f\x20\x8e\xc2\xf7\x34\x5f\xa4\x49\x4e\x45\x53\xce\xdb\x3b\x67\x0c\xfe\xf6\x3a\x7c\x8d\xf5\x0f\x92\x33\x6f\xad\x3b\x4e\xa5\xd7\x6e\xff\x4a\x2b\xe7\x3e\x9b\x9c\xc1\xf2\x3d\x17\x7c\x43\xf8\x32\x44\xe3\x7d\xd1\x07\xf0\x1a\x81\x13\x9c\x28\xf6\x7a\x2a\xd4\xf9\x86\xf8\x45\x09\xa0\x2c\xad\x9f\xe4\x83\x6f\x8d\xb6\x40\x8f\x26\x56\x64\x6b\xb4\x0d\x56\x6f\x8d\xa2\x7c\xdf\x6f\xf8\xf7\x1b\xfe\x9f\x77\xc3\xd7\xfb\xbd\x12\xcb\xef\x48\x45\xd6\x50\x57\xc5\x4e\x3c\x99\x05\x96\x0b\x59\x7f\x00\x99\xab\xaa\xd3\x24\x1c\x7a\x37\x85\xf5\x60\xf2\x41\x94\x80\xde\x43\x87\x10\x04\xa6\x34\x86\x46\xc8\x71\xdf\xfe\xc9\xd5\x4b\xf8\x91\x19\x6c\xf3\xf6\x33\x65\x0e\xb7\xaf\xc1\xde\x49\x28\x25\x17\x80\xb1\xef\x35\x91\xbe\x9c\xcd\x54\x6f\x03\xc2\xdb\xaf\xbf\x6a\xf3\xa9\xe7\x69\xd4\x13\xe5\xac\x5b\x9d\xe0\x34\xf2\xa8\x41\x90\xdf\x67\x62\x39\x5a\xe6\x01\xbe\x77\x77\x49\x1b\xf5\xa9\x4d\x1e\x3c\x30\x1c\x39\xa3\x73\x33\x6f\xd6\xf0\xf6\x7f\xdd\xb5\xb6\xe1\xaa\x06\x3d\xae\xa1\x49\x07\x12\x4b\xb6\x6b\xc8\xe3\x1e\xa3\x3d\x3b\x83\x55\x11\x03\xcb\x3d\x4d\x03\xed\x89\xc3\x3b\x47\x28\x07\x55\x68\x44\x5a\x1e\xa9\xbd\x6a\x20\x3d\xaa\x80\x5e\xc2\x55\x14\x3f\x5a\x7b\x5f\x36\x05\x61\x28\x69\x38\xd7\xc7\x70\x4c\x1b\x32\xed\x5a\xd5\x54\x4a\x4f\x9c\x54\xfc\x55\x56\x9e\xec\xf5\x71\xfd\xe6\x84\x82\x5e\x21\xae\x32\xfb\x58\x53\xa5\xb4\x3f\xaa\x3f\x9f\x68\x31\x93\xea\x66\xdd\x49\xd3\xeb\x45\xad\x2a\x75\xe2\xa8\x39\x34\x02\xb4\xaa\xb4\xc1\xbc\x72\x6e\xd1\x68\x52\x39\xbf\xb9\xbb\x19\xb5\xeb\xab\x57\xd4\x48\x86\x77\x17\x73\xcb\x79\xaf\xa5\x56\x16\x9c\x55\x68\x1b\x15\x8f\x35\x27\xcf\xd5\x5b\xf1\x8e\x95\x4e\xe7\x5e\x1c\x57\x4e\x17\x00\x89\x8b\x9e\x95\x09\x8c\xab\x42\x6b\x3a\xb8\x3a\xb5\x19\x8f\x02\x5d\xa5\x5a\x19\xb5\x55\x91\x9b\x72\x94\x03\xb6\x7f\x72\xd2\xa7\xb4\xc8\x85\xf1\x4a\x7c\x49\x42\xba\x88\xd3\x4b\x1a\x4a\x13\x41\x78\x3e\x38\x9e\x05\x51\x62\x3f\x57\x83\xda\x5e\xa5\x99\xec\x91\xc7\xf7\x80\x3c\xb0\xfa\x48\x52\xae\xcb\x6b\xa5\x5a\x5c\x33\x5c\xe4\x1e\xc9\xcb\x0d\xfd\xac\xad\xa4\x45\x6c\xf0\x20\x5b\x42\x0a\x4b\x4d\xbe\x10\xb0\x19\x22\xc9\x38\x6a\xde\x57\x10\xa5\x7c\x57\x3e\x2c\x83\xfc\xc1\x80\x9c\x07\x11\x57\x97\x83\xc8\xb5\x28\xb4\x0a\x56\xde\x94\x99\xf3\x2e\x96\x82\x0a\x18\xad\x3b\x46\xbb\xa6\xe7\xe5\x75\x0a\x4f\x93\x8d\xf6\xed\x5d\x09\xfa\xbb\xb1\xb1\x63\x1e\x9b\x06\x03\x92\x17\xe9\x82\xeb\x6a\xa3\x64\x4a\x82\x09\xeb\xca\x37\x43\x3e\x57\x39\xe9\x14\xd1\x9c\xa6\xcb\xa2\xeb\x1c\x1d\x39\x02\x7e\x20\xdf\x0c\xbd\x87\x45\xde\xfb\x3e\xab\xfd\x67\x51\xb9\x8e\xa9\xd0\x25\x5f\xae\x3d\x67\x3a\x1b\x81\xfc\xc1\x9e\xf7\x1c\xaa\x66\xc4\x7b\xda\xd4\x27\x3f\xed\x18\x58\x31\x26\xb8\x2f\x09\xf8\xca\x18\x33\xc2\x06\x27\xc1\xa7\x4c\x62\x5e\x26\xa1\x8d\x81\xb6\xef\xf0\x49\x63\xe4\x50\x04\xff\x39\xee\x88\x6f\xdc\x2a\x5b\x7e\xb8\x66\xe5\x4f\xc4\xc5\x9a\x41\x35\x53\x5a\x7c\xd4\x4d\xbd\xe7\xa4\xa6\x39\x0a\xea\xc6\xeb\x20\x9f\x61\xa2\xea\x49\xc2\xec\xfa\x8f\xf0\xd1\xa4\x23\x00\xfc\xd4\xe6\x2d\xe4\xed\x20\x84\x30\x12\x75\xf5\xc7\xe6\x02\x34\x7b\x04\x71\x8e\xfc\xdd\x91\x7f\x65\xde\xdb\x9f\x28\xef\xed\x65\x7f\xd1\xa4\x63\x52\xdc\xd5\x15\x59\x87\x16\x2b\x8b\x11\xc5\xba\x3d\xb4\x89\xff\x6e\xb2\x04\xf0\x5f\xc3\xe5\x60\x0f\x29\x0d\x51\x88\xe8\xed\xca\x99\x91\x7f\x83\x81\xba\xe7\x8b\xd3\x29\xa2\x5a\x38\x56\x48\x36\xbe\xde\xee\xd6\x34\x4f\x0c\x51\x4d\x71\xd4\x92\xa9\x6e\x50\xd9\x60\x40\xf8\x66\x25\xc5\x85\x20\x09\x89\xb8\x19\x21\xc1\x34\x88\x12\xb1\x72\xce\xa9\x88\xf0\x57\xf3\xe7\x97\x3d\xed\x0d\xb0\xa6\x06\x5b\xd6\x71\xb6\xff\x9a\x21\x8d\xb9\x53\x36\x71\x29\xc8\xb6\x04\xb6\x3b\xe6\x74\x9c\x26\x21\x61\x0c\xb7\xb6\x12\x44\xba\xf5\xc4\x4a\x0c\x8e\x08\xba\xb0\xa6\x1d\xf6\x7a\x31\xba\xe3\x0e\x61\xdf\xed\x48\x94\x10\x27\x5a\xc4\x29\xf3\x22\xcd\x68\xa8\xfc\xb8\x73\x09\x04\x34\x3e\xd3\x20\x27\xc1\x9c\x6d\x48\x7d\x2f\xbf\xb6\xff\x4a\xf9\xb7\xfd\xe7\x71\x2f\x7f\x17\x5d\xac\xee\xe1\x75\x69\x6e\x19\xc7\x70\x4b\xd8\x90\x48\x3b\xd9\xf4\x40\x81\xae\x18\x24\xa1\xbf\x0a\xd8\x31\xfb\x52\xf9\xd2\xb0\xa4\x38\x0b\xac\xe6\xd0\x60\x57\x8a\x0f\x0c\x70\xaa\x0a\x4e\x23\xe3\x72\x81\xbf\x28\xa2\xf2\xf8\x0e\x69\xc1\x69\x44\x76\x19\xa4\x94\xb3\x1e\x72\x4d\x68\xfd\x98\xf4\x09\x29\x21\x01\x12\x4d\x45\x71\x59\x8b\x1c\x5b\x42\xcf\x55\x92\x1c\x53\x72\x79\x8d\x89\xc1\xd2\x8d\x6c\x4a\x9b\x82\x20\xee\xae\x58\x74\xab\xa2\xa8\x2d\x07\x1b\x92\x85\xf0\x75\x22\x15\xc5\xa1\x53\xda\x27\x29\x0b\x08\x25\x2d\xeb\xe3\x9f\x4c\x52\x6d\xe9\x89\x87\x42\x03\x3d\x11\x0c\xa5\xbe\xeb\x17\x52\xb1\x45\x7f\x2b\x6b\x60\x7f\xea\x07\x97\xae\xd5\x29\x12\xd3\x5f\x47\xd2\x41\x4f\xcd\x3e\xe6\x60\x83\x01\x8f\xad\xa8\xad\x2c\x8c\x4a\xb5\xad\xc4\x97\xeb\x1d\x06\x2c\xb1\xb4\x6e\xb6\x2d\x10\x83\x2a\x86\x33\x6e\x06\x6f\x71\x80\x90\xf1\xa3\x84\x38\x1a\x53\xb8\x6a\xd0\xf6\x1a\x56\xf8\x3f\x9f\xed\x08\xd8\x7f\x94\x5b\x8c\x10\xc7\x6a\x24\xef\x2f\xd2\x85\xe1\x60\xce\xec\x5e\x1c\xe4\x85\x80\x74\xaa\xf6\x77\x87\x13\x52\x87\x15\x04\xe7\x45\xeb\xea\xc5\x09\x04\xa2\x85\x74\xbb\x4f\x1a\x85\x35\x5d\x62\x0d\x09\xe0\x3e\x8f\x4a\xf2\x03\x19\xda\xb5\x89\x99\x96\xb4\xbf\x27\xd7\x72\xbd\x16\x40\xfe\xdd\x4a\x25\x88\xd0\x64\x31\x4b\xa9\x4e\x53\xa6\x76\x78\x58\xeb\x66\x97\xfb\x8b\xe0\x32\x38\x8d\xa9\xaf\x7b\xee\x71\x80\xdb\x4f\xe5\x34\x09\x75\x44\xaa\x24\x4d\x1e\x89\x4a\x30\x3a\xec\x6d\xe2\xba\x6c\xea\xc1\xb7\x1f\xe3\x8c\x7e\x15\x6c\x47\x2e\x95\x1e\x8c\x18\xd5\x2a\x27\x08\x6c\xdf\x36\x76\x79\x45\x3b\xe6\x24\x96\xde\x08\xe2\x13\xad\xa1\x03\x90\x72\x5f\x10\x26\xb6\x96\x20\xa4\xe4\x3c\xc8\x95\x40\xb9\x66\xe2\x8a\x2f\x6d\xb8\x7a\x45\x47\x18\x6d\x98\x65\xdd\xbf\xce\x82\x7c\xe6\x43\x3a\xeb\x35\xcd\xb2\xb2\x9b\x48\x7c\xe5\xe8\xbb\x57\xac\x92\x78\x98\x38\x1a\x86\xfc\xda\x0b\x71\x5d\xd6\x13\x7f\x5b\x25\xc7\x2e\xb2\x0b\x65\x4a\x84\xaf\x52\x09\x71\x12\x65\x79\x51\x2e\x20\xae\x28\xe3\x95\x68\x40\x7c\x6a\x0f\xdf\xf5\xab\xf1\x55\xe7\xf8\x12\x22\x6d\xf2\x81\xd7\xcd\xb3\xd5\x58\x53\x94\xd7\xa2\x7a\x95\xa1\xfb\x79\x9a\xd2\xc9\x73\x20\xa1\x2b\x13\xd8\x95\x9b\x20\x3b\xdf\xbe\xe0\x76\xa5\x90\x24\x3e\x0d\x03\xb4\x1b\x0b\x5e\xb6\xd6\xac\x4e\x3b\xeb\xd9\xd4\x45\x4d\xd7\xa6\x0c\x34\x51\xf5\x0f\xd6\x06\x03\x6b\x07\x36\x2e\x70\xb4\xc7\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x5c\xe9\x96\xc6\x9d\x1e\x8f\xc1\x2b\x6e\xca\x03\x35\x45\xc9\xb4\x42\x36\x33\xd5\xd8\xe6\xc8\xf9\x24\x5e\xbb\x9c\x08\x8b\x43\x55\xa2\x10\xf9\x82\xa4\xae\xa6\x12\xd1\x84\x24\xa9\xae\x81\xb1\xb7\x45\x90\xe7\x34\xec\xb1\x2a\xb4\xeb\x3b\x06\x91\xa3\x25\x6d\xf2\x32\x45\x78\x30\x03\x16\x3a\x0d\x73\x48\x9f\xef\x54\xd3\x66\x95\xac\x2c\x43\x69\x4b\x79\xad\xad\x2c\x66\xc8\xb5\x24\x04\xab\x81\x10\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x9c\xd2\x71\xb0\xcc\x29\x3b\x89\x87\x69\x52\x90\xf3\x20\x01\x9b\xa4\x7c\x91\x46\x31\xbf\x0d\x4f\x0a\x9a\x4d\x82\xb1\xf2\x8d\xdd\xe0\x24\xde\xe4\xb4\x6d\x6f\x53\xf5\xfc\x90\x38\xee\x75\xd5\x9a\x46\x6b\xf3\x47\x5a\x70\x67\xcd\x6c\x7f\xec\x91\xf3\x59\x34\x9e\x81\xd1\x00\x5b\xde\x45\x2a\xb6\x31\xb2\x88\x97\x79\xfd\xd5\xab\xe0\x03\x35\xf3\xab\x99\x87\xdf\x90\xa9\x46\x84\x5d\x5d\x4e\x55\xc5\xea\xe5\xc7\xdb\xc8\x8e\xe5\x72\x23\x32\x56\xbe\x91\x1c\x53\x25\xc3\x98\x2f\x1d\xfa\xdc\x20\xbd\x39\xf3\xf5\x9c\x7a\xbc\xc7\xdd\x06\xd7\xe7\x65\xac\xc9\x39\x0c\x7b\x4f\xc1\x25\x2f\x59\x7c\xe7\x61\x77\xf7\xd3\x76\xe1\x1c\x7f\xee\xe3\x15\xe2\x39\x4c\x7b\xcd\x96\x2c\xba\xdd\x51\xe6\xcf\xa6\xad\x44\x6b\xf4\x6d\x99\x05\xb4\xb2\x68\x68\x8d\xb6\xb6\x5d\x93\x68\x31\xf2\xd6\x68\x7b\xf3\xfa\xa4\xb7\xf5\xe4\xde\xf4\xe9\xde\xf4\xe9\xaf\x6d\xfa\x84\x6c\x9d\x85\x09\xe4\x1d\x18\x3b\x97\xb8\xb1\x14\xc6\x95\xfc\x5d\xd6\xe1\x44\xde\x39\xef\x65\xd3\x7c\x54\xa2\xb9\x41\x32\x9e\x38\xc1\x8a\x4a\x70\xec\x3b\xb9\x9d\x30\xf6\x29\x2b\x25\xd8\xc4\x09\xf8\x7c\xcf\xd7\x87\xf7\xef\xf6\x39\x73\xbf\x4d\x07\x78\xbc\x25\x60\xb5\x14\x1e\x30\x16\x29\x79\xff\x6e\x5f\xdc\x13\xf8\x3b\x20\x9e\xa3\x83\x13\x45\xdd\xf2\x2c\xcd\xf1\xed\x97\xdb\xf8\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\x2f\xdf\xbf\x3f\x7c\x3f\x22\xfb\x4a\xfd\x3b\xe6\x55\xf2\x13\x7d\x48\x49\x7b\x83\xb0\xfa\xc8\x46\xbb\xef\xef\x83\xf6\x78\xd3\x74\xec\xea\x9d\x3d\x57\x22\x14\x6c\xf5\x44\xbc\x32\x7f\x13\xd2\x90\x76\x44\x6c\xa3\x60\x34\x4c\x78\x96\x46\xf3\x3c\x98\x52\xb2\x4b\xd6\xd7\xc5\x4b\x43\xb6\xad\x8b\xdf\x7d\x1e\x32\xd6\x49\xe9\xcb\x62\xcf\x88\x37\x79\x44\xd4\x74\xfd\xfd\xc3\xe1\x5b\x98\x95\x4c\x75\xc9\x13\x66\x55\xf4\xcd\x79\x4b\xa6\x71\x20\xaa\x36\x47\xab\x67\xf3\x23\xbf\xae\xc6\xe3\x9d\xe7\x4d\xa7\xf4\xe3\xc1\x9b\x97\x87\x47\x1f\x47\x44\x5c\x7a\x33\xe2\x62\x9d\x9c\xe7\x64\x83\xb4\xd9\x7f\xc1\x78\xc6\x38\x46\xdb\x08\x68\x23\xdc\x48\x7e\x7b\xbf\x5b\xdd\xef\x56\x7f\xed\xdd\x0a\x6d\x56\xf0\xea\xf2\x8f\x6a\xa5\xdb\xfc\x31\x7b\xa3\x37\xf4\x77\xf8\x94\x5d\xfa\x1c\x62\xeb\x5f\x1d\xce\x70\x44\xa6\xdc\x38\x86\x88\x37\xb6\xd0\x96\x3e\x2c\xd8\x46\xc8\x5f\xfb\x1d\xfc\x42\x9a\xf2\x22\x45\x3a\xce\xe7\xa1\x2b\x48\xc5\x73\xe4\x3c\x4d\xba\x35\x4f\xe8\x51\x66\x92\x26\x97\xf3\x74\xa9\x5a\x54\x09\x25\xa7\x37\x89\xb4\x29\x95\xb8\xa2\x21\x97\x07\x20\x88\x81\x13\xac\x49\xa4\xa9\xe3\xd9\xf3\x34\x8d\xaf\x21\xbc\x6a\x08\x2e\xc8\xf9\x26\x41\x39\x64\x88\x66\x07\xde\x87\xd0\xd0\x70\x98\x2e\x4f\x7c\x10\x8c\x80\x2d\x4a\x51\xfb\x60\xcd\x98\x26\xec\x7d\x8b\x41\x98\x8e\xa3\x78\xbd\x76\x00\x06\x84\x7c\xf7\x4a\x24\xf2\x88\x0a\x51\x5f\xd4\x04\xf7\x1b\xe2\x77\x89\xb9\xab\xbf\xbc\xb6\x57\x2e\xbd\x21\xc6\xd8\xe6\xf4\x19\x72\x17\xe0\xe0\xc5\xc8\xc2\x75\xa8\xbd\x83\x7b\xa3\x05\x79\x2b\x28\x47\x1d\xaa\xae\xca\x49\x10\xa7\x44\xd7\x41\x79\x47\xd3\x6b\xf3\xd1\xc1\x0a\xf5\x0c\xad\x10\xfe\xcc\x2b\xc6\x85\x8b\x56\xd3\xc3\x4a\x23\x92\x9e\xd4\x6f\x34\x9c\x3c\x9a\x26\x41\xb1\xcc\xec\xe1\xe0\xf4\xb2\xf1\x60\x98\xf2\xf1\x28\xa8\xaa\x01\x81\x03\x83\xe6\xfd\x17\x2f\x1c\x24\x79\x0b\x8e\x14\x24\xa1\x52\x2d\x15\x29\x04\x25\x9e\x44\x49\x10\xfb\xad\x9e\x79\x1d\x3e\x9b\x52\xbc\xae\xad\x2c\x51\xbd\x81\x14\x99\x47\xcf\x68\x76\x59\xcc\xb8\xc6\x7a\x7e\x1a\x01\xcb\x48\x79\x94\x68\xe8\x9b\x08\xb3\x50\x89\x2d\x8f\x6b\x10\xd1\x1d\xc7\xb3\x9d\x5a\xdc\xea\x17\x7a\x04\x78\xef\x40\x44\xbb\xeb\x50\xfe\x39\xea\x3c\x8b\x48\xbd\xe6\xba\xb5\xf3\xb8\xfd\x14\x95\xf3\x87\xad\xc2\xb7\x20\xf7\xd3\x29\xa9\xbd\xd3\x75\x55\x9a\x62\x9e\x3e\xca\x8e\xdd\x96\xa5\xa3\x10\x16\x95\xfc\x1c\x1c\x2f\x8b\x60\xda\xa2\xfc\x71\x04\x21\xa6\x2c\x63\x00\x01\x84\xe7\x8f\xd1\x8d\x4e\x4e\x96\x71\x5c\xf2\xc2\x45\x6b\x16\x89\x7b\xf9\x6f\x2a\x84\xa1\xbe\xb2\xc0\x8c\x90\x69\x8d\xe6\xac\xe2\xb6\x5f\x60\xdf\x79\x1b\xd3\xe1\xdb\x57\x8f\x9c\xd9\x37\xe7\x5d\x3b\xb6\xde\x4a\xb5\x41\xdf\x6b\x28\xce\x24\x92\x71\x9a\x8c\x83\xa2\x63\xcc\x7e\xb7\xdc\x8f\x4d\x29\xd7\x13\x4e\x6c\xca\xb9\x9e\xbd\xdb\xd2\x32\x0e\x17\xf2\xbb\x07\x97\x87\x09\xae\x20\x0c\x87\xe0\x84\xc0\x6b\x09\x55\xb3\x0f\x1e\x80\xbe\xc1\xec\x45\xf5\x36\x5d\xee\x7c\x07\x70\x70\x87\xde\x77\x82\x6c\x6a\xad\x2e\x2d\x3e\x3e\x33\x4a\x8e\xf0\x97\xf0\xcc\xb3\x89\x3c\xa1\x88\xf1\x89\xfb\x17\x55\xaf\xfd\x52\x8b\x4f\x26\xf9\xa2\xa4\x34\x5c\xdf\x56\x77\x87\xad\xcc\x5f\xd2\x28\xe9\xb4\x5a\x6e\xe5\xea\x51\x1c\x27\x37\x8e\x27\x7c\xbd\x01\xb2\x61\x87\x2d\xf3\x6e\x0f\xf7\x08\x5f\xd5\x24\x69\x71\x60\xf4\x55\xa1\xd0\xe3\x6f\x48\x03\x37\x6c\x1b\x5e\x2d\x74\x7b\x56\x2b\xb8\x7d\xb5\x91\x20\xae\x9d\x2e\x8b\xc5\xb2\xf8\x29\x9d\x6a\x76\x2d\x7c\xf1\xa0\xd5\x22\x9d\xff\x70\x3f\x33\x48\x2c\x33\xc1\x34\xb7\x86\x31\xd9\x6e\xa0\x38\x0c\xbf\xe5\x32\xf8\x69\x46\xc3\xe5\x98\xa2\xb9\x0a\xc6\xe3\x1e\x11\xae\x28\x31\x3f\x09\xc6\xe3\x63\x91\xcc\x79\x22\x43\x8a\xf8\x96\x54\xfe\xcc\x9c\xb2\x7e\x3e\x8b\x26\x45\xa7\x4b\x46\x0e\x46\x65\x96\xa3\xb4\x0a\xc6\x63\xa9\xa5\xe2\xc6\xde\x9c\xb4\x69\x4c\x0b\x2a\xc7\xa1\x9d\x24\x99\xe9\x9c\xaa\x6e\xc0\x32\xd0\xfd\x95\x78\x57\x22\x96\x36\xdb\xea\xb9\x18\x57\xea\x58\xe1\xae\xe4\x22\xa3\xe1\x6a\xe1\xc7\xe3\xb8\xc1\x96\x7e\xfe\xe8\x1e\x99\xb6\xea\x3d\x32\x55\x15\xdf\x2c\xb7\xb1\x33\x2b\x20\x86\x04\x68\xf8\x7e\xb0\xc5\x0e\xdb\xed\x93\x23\x50\xfe\xa1\xfc\x3f\x95\xd2\x32\x36\xfd\x6f\xf0\xa8\xd1\x7a\xd5\xe6\x7d\xd1\x58\x49\x8d\x5f\xcb\xd9\x14\x03\x35\x4f\xae\x65\x1c\x50\xda\x17\x42\x4b\xc7\x08\xe0\xc4\xa0\x5e\x1f\x00\xf6\x5f\xa5\x89\xc2\x0b\x7a\xac\xd8\x3d\x6f\xfb\xa4\x74\x00\x86\xd5\x84\xf7\x4e\xd8\xc0\x25\xf2\x88\x55\x75\x25\x5c\xe7\x27\xeb\x86\xae\xb1\x9e\x36\x51\xc0\xdf\xd6\xd7\xe5\xc0\xaf\x9b\x7c\xc3\x69\xd0\xa3\xff\xab\x0e\x24\x82\x63\x88\xac\x0d\x06\xe4\xe3\xe1\x8b\xc3\x11\xc9\x28\x37\xc8\xea\x91\x3c\x15\xa6\x33\xea\x8a\x4b\xdb\xe2\x04\x5c\xd3\xd5\x67\xe5\xa2\xa2\x9d\x93\x84\x8e\x69\x9e\x07\xd9\x25\x5b\x2c\x10\x01\x3b\x67\xe4\xd6\x06\x7f\xc5\xe0\x2d\x9a\x9c\xa7\xd9\x67\x2e\xe5\xcd\x97\x71\x11\x2d\x62\x14\xc9\xc1\x8c\x9d\xe2\x77\x6f\x34\x78\x48\xbc\xb6\xdc\xdf\x48\x53\x6e\x5e\x87\x69\xc6\x20\x9b\x37\x6c\x48\x75\x63\x34\xe4\x1b\x87\x79\x32\x51\xa5\xfa\x12\x47\x3e\x07\x36\xeb\xac\x73\xc7\x2e\xec\x89\xef\xfc\x50\x06\x6b\xb1\x53\xe2\xd8\x37\x9a\xfd\x14\xfe\x9c\x7c\x35\xd5\x98\x41\x7a\xeb\x29\x3d\x42\xe9\xfa\x05\xc1\xdb\x63\x72\x00\x3c\x47\x6e\x9e\xe3\xc3\x06\xcf\x51\x4c\x4f\x98\xf4\x98\x5d\xf4\x58\x7e\x8a\x62\x39\x2d\xac\x48\x31\x3e\x1f\x57\x95\x07\xb1\xea\xe9\x8e\x68\xc5\x78\x35\x8c\x67\xc8\x65\xf4\x42\x74\x90\x93\xcb\x95\x87\xad\x0a\xde\xc1\xc0\x09\xb2\x1b\xa5\x17\x7d\x83\x1d\xe9\x8f\x1d\x22\x01\x24\x17\x82\xff\x77\x64\xaa\x62\x39\xfc\x87\x4a\x47\x8c\x46\xfe\x34\xe5\x48\x7a\x21\x9e\x77\xbb\xdc\x9c\xa3\x41\x7b\x26\x2a\xe1\xcf\x25\x1c\xb9\x35\xda\x06\x0f\x46\xd8\x69\x38\x63\xcc\xdf\xdd\xdf\x8c\xde\xdf\x8c\xfe\xb5\x6f\x46\xc5\xb5\xa8\x78\xf2\xfb\x5f\x11\x5f\xef\x4e\x3d\x86\xc3\x21\xe0\x21\xd9\x4f\x93\x33\xca\x58\x51\x20\x42\x1e\xc3\x39\x18\xce\x02\x10\xb7\x58\x06\x72\x61\x04\x1c\xc4\x79\x4a\x82\x38\x4e\xcf\x73\x1e\x9e\x1d\x14\x75\x79\x7f\x8d\x55\x24\x05\xff\x37\xd1\x05\x0d\xaf\x79\xd6\x9a\x7b\xaf\xb1\x26\x6e\x54\x8b\xd4\x0e\x72\x2c\x54\x96\xea\xc0\xd9\x31\x55\xa2\xe4\xea\x4a\x06\x48\xd7\x19\x6d\xa5\x43\x6d\x77\x6d\x65\x00\x3f\xcb\x09\x11\x89\x2b\x66\x79\x1f\x3a\x52\xbf\x68\x34\xc4\xf5\x10\x87\x13\x50\x35\x77\xa1\xf6\xa1\x53\x27\x40\x0a\xbe\x8f\x5f\xb4\x1a\x77\x46\x32\x88\x92\x6a\x07\x8e\x5c\x4c\xd4\x64\x9c\x56\x5e\xfe\xd8\x96\xb0\xa9\xd2\xef\x8b\xc3\x56\x8f\x4d\xc2\x19\xcd\xa2\x09\xf8\xf5\xc8\xe8\x38\x60\x1c\x07\x05\xaa\x79\xf0\x80\xc4\xc1\xaf\x97\x24\x4e\x83\x90\x84\x97\x49\x30\x8f\xc6\x24\x4d\x68\x0e\xad\x89\x09\xd1\x0d\x89\x60\xd6\xa9\xd2\x13\x00\x94\xb4\xaf\x97\x8d\x3b\x50\x6c\xb6\xa6\xb4\x38\x54\x87\x64\x8f\x07\x67\x36\x31\x5a\x60\xad\x73\x0f\x80\x95\x09\x62\x4a\xe4\x31\xb9\xfc\xd6\xc3\xd0\xf4\x97\x5e\xbd\xf0\xec\xfc\x3c\x82\x78\x25\xa8\x57\x04\x74\x10\x39\xe5\x27\xe8\x91\xf3\xb2\x8a\x0b\xef\xcb\x8c\x0a\xf5\x62\x0f\x2e\xf0\xc6\x7c\x75\xf0\xc3\xf1\x8c\x5e\xf8\xd4\x06\x5a\x6b\x6a\x25\x58\x9e\x28\x1b\x14\x31\x34\x9f\x22\xac\x76\xa9\x52\xde\x52\xf8\xcb\x20\xdc\xcf\x44\x78\x72\x56\x95\x58\x64\x5d\x32\x92\xeb\x4d\x80\xb9\xb2\x92\xef\x9a\xc0\xf3\xbc\x0e\xba\x39\xb2\xba\xdd\x73\xe0\xd8\x12\xd0\x50\xec\xcb\x85\x29\x52\x5c\x8f\x9b\x1f\xc8\xa8\xcc\x12\x28\xc0\x31\x99\xed\xd6\xe0\xfe\x6a\xb4\xd2\xb5\x56\x5f\x95\xeb\xfa\x7a\x77\x93\x1a\x45\x29\x53\x3f\x85\x0e\x3a\x9c\x02\xf3\x19\xa3\x40\x0f\xc2\x2d\x52\x97\xaa\x9a\xbd\x30\xe4\xcf\x22\x94\x12\x2d\x48\x42\x92\xd3\x22\x27\xcb\x05\x64\x88\xd3\x08\xb0\x8c\xa8\xa0\x19\xdb\x3b\xd2\x33\x21\x6c\x09\x37\xa6\xfd\xb5\x35\xf4\x34\xe2\xa7\x74\x9a\xef\x15\x1f\x8a\x20\x2b\xd6\x6c\x4d\x63\x4e\xe3\x89\x4a\x9c\xb8\xef\x97\x05\x0b\x37\x6b\x31\xe2\x84\xd1\x78\xe2\xf8\xf0\x91\x8f\xec\xa6\xb4\xe0\xfa\x2c\x56\xd8\x7a\x69\x07\xfa\x05\x3d\xcc\x1c\xba\x47\xe4\xc9\xd3\xe2\x19\xac\x95\xbe\x8f\x71\x40\xc6\x94\x16\x1d\xeb\xcd\x8f\xb0\x64\x74\x4e\x39\x83\x01\x09\xd3\xa4\x2d\x5e\x89\xb2\x3e\x0a\xb4\x81\xd9\x24\x5c\x74\xcb\x44\x69\x76\x04\x9e\x30\xfa\xfd\x3e\xf9\x65\xc9\x1d\x01\xb3\x36\x19\xef\x75\xce\xcb\x25\x0f\x23\x2b\x1e\x45\x5e\xdb\x2f\x60\xad\x95\xae\x86\xe1\x3f\x63\xf2\x4c\xef\xc1\x94\x1b\x72\xd6\x3d\xd3\xe4\x8f\x77\x4c\xb3\x4f\xa3\x7f\xf5\x7e\x58\xbf\x1e\xe9\x2e\xd2\x38\xe6\xe4\xe3\x27\x5b\x41\x9b\x1a\xcc\xa6\x4b\xa5\x12\x01\xb5\x6d\xf2\x46\x99\xe1\x1a\xc4\x92\x96\x90\x8b\x98\xd1\xd4\x99\x53\x69\x64\xc1\x48\x4f\x8e\xd5\x37\x09\xbe\x67\x53\x3e\x9a\x48\x1b\x9f\xe4\x9b\x52\xc7\xcd\x28\x43\x9b\x29\xc3\xd0\xb4\xf2\xfa\x99\x95\xa0\x2b\x19\xc9\x42\x2e\xe9\xdc\x0a\x3d\xb7\x23\xd2\x52\x7d\x00\xf4\xc9\x76\x46\xcd\x18\xcf\xbb\x34\x8e\x19\x9f\xd1\x3d\xe1\x34\x38\xe2\x45\xd8\x39\x8d\xce\x69\x52\xc0\x91\xb3\xcf\x28\x0e\x86\xa6\xf7\x92\x85\x30\xb4\x3f\xe6\x98\x02\x72\x3c\x08\x4f\x7a\xf2\x8a\xca\x48\xee\x69\x62\x14\x39\xd8\x8d\x11\x57\x10\x03\xfd\xb2\xcd\x5a\x46\x2d\x74\x48\xdc\x92\xc9\x7a\xc4\x09\xef\x21\x97\x9b\xe7\x76\xa0\x27\x4e\x53\xfb\x19\x85\x31\x81\xbd\xf6\xbe\xe7\xa1\x23\x30\x3b\xae\xc1\x46\x17\xae\x06\x3e\x90\x86\x6f\x15\x55\x59\xa9\xae\xab\x54\xd9\xe3\x57\xaa\x99\x9d\x41\xb6\x04\xa4\xd4\x63\x7c\xa9\x35\xa6\x16\x36\xb5\x18\x6c\x89\xbe\x08\xda\x41\x83\x99\x80\x20\xe5\xcc\xbb\x4f\xc6\xd4\x0a\x11\x96\x35\x2a\x43\x6c\xb9\xfb\x65\xf9\x9a\xed\x39\x59\xf8\xda\x49\xfd\x2e\xed\x77\x3f\xa1\xe7\xe2\xd6\x09\xe3\x00\xfb\x0a\xe3\x4c\x32\x0a\x0d\xd7\x78\x7e\xe6\x58\xb3\xec\x3b\xe3\x53\x8f\x98\x3b\x3e\xad\xe5\x83\x44\x70\x64\x71\x2e\xac\xa0\x5e\xcb\x21\xa9\xcb\x5e\x2a\xca\xfa\xbb\x51\xad\x77\x36\x96\x36\x23\x82\xd0\xf5\x03\x88\x5d\x35\x64\x14\x2e\x19\xd8\x99\x63\x41\x93\x10\x0c\xdc\xd4\x24\x07\x39\x28\x5a\x92\x9c\x51\xa8\xf2\x05\xa3\x2b\x4a\x27\x00\xcc\x0a\x31\xa9\xa7\xcb\x95\x2b\xaa\xf5\x65\x12\xe4\x79\x34\x4d\x68\xd8\x77\xfb\x68\x53\x94\x8f\x27\xfb\x66\x47\xc9\x58\xe3\xd3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x5e\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x3e\xe0\x86\xae\x47\x2e\xbd\x32\x39\x17\xf7\xb9\x85\x64\x52\x74\x91\x8a\xb1\x45\xf3\x39\x0d\xa3\xa0\xa0\xf1\xa5\xdd\xac\x20\xf7\x71\x53\xda\x36\xa5\x13\xa8\xbe\x53\xe2\x69\xc2\xe7\xb5\x0a\x6b\xb2\x39\xcb\x67\xdb\x0f\x1f\x0c\xba\xcb\x3d\x77\xa2\x74\xd8\x9b\xb9\xc9\xdb\xb8\x61\x1f\xea\x87\x54\xc7\x18\xcc\x11\x8f\xc6\x9a\x27\x71\x5d\xea\x0e\x04\xe1\x1a\xdd\x09\x5f\x37\x1d\x08\xde\x77\xeb\xc7\xe3\x48\x0e\xe9\x42\x0a\x0e\xe6\x40\x6a\xf8\x3b\x3c\x2d\x9f\xa7\x67\x52\xa5\x49\x82\xfc\x32\x19\xab\xc3\x8f\x4f\x30\xf2\xf1\xed\x65\x02\x6f\xa7\x0d\x04\x20\x19\xc3\xc2\x96\xc3\xbb\xb0\x21\xfc\x2a\x35\x1b\x82\xbf\x83\xd1\xa9\x15\xb2\xdd\xe7\x3c\xc1\x91\x29\xbc\x26\x27\xaa\xa4\x2d\x94\x5b\x3b\x6a\x89\x1d\xe5\x60\x40\x0e\x26\x9a\x33\x46\xb9\x7a\xd7\x77\x49\x85\xfb\x15\x12\x15\x44\x7b\xe9\xd2\xe5\xce\x67\x14\x8c\x31\xc4\xe8\xbb\x84\x33\xd5\x9c\x44\x85\xc9\x56\xbd\x1b\xb5\x43\xec\x6a\x99\xf9\x76\x0f\x1f\xfa\x45\x8d\xf6\x84\xe2\xfd\x18\x22\xa4\x78\xf8\xdb\x57\xf4\xcf\x63\xc9\xe3\x19\xb5\xad\xf7\xe2\x74\x5a\xd6\x2e\xb1\x18\x53\xc5\xd9\x02\x6a\x19\xb1\x3d\xa1\xc4\x1d\x9f\x3f\x60\x89\x09\xe2\x1c\x00\xec\x81\x35\xa7\x23\xc7\xcd\x94\x10\xc4\x0f\x5e\xf0\x84\x91\xa0\xb1\x4e\xb7\xcf\x77\xe4\x71\x20\x1d\x16\x82\x5b\x15\x1a\x12\xb6\xba\x67\x59\x9a\xa4\xcb\x5c\x79\x2f\x14\x86\x01\x6c\xb7\xb7\x3d\x11\xf1\x6a\x84\xb0\xdb\xf6\x9a\xd7\x82\x53\x89\x54\x5b\xe9\x35\x21\x20\xd7\x86\x8e\xd5\x50\x3f\x87\xb7\x98\xb7\xeb\x1a\x7e\xec\x5c\x91\x72\xdc\x3a\xb1\xdf\x2a\x2e\x48\xaf\x4f\x7a\xdb\xc3\x26\x57\xa0\xed\x65\xce\xf5\xe2\xe3\xa2\xbd\x76\x7f\x21\x7a\x7f\x21\xfa\x27\xbe\x10\xd5\x4f\x45\x91\xca\xfa\x26\xef\x45\x05\xf0\x0a\x37\x99\xbe\xd8\x6f\x8d\x9f\x98\x26\x93\x68\xea\x85\xe3\x59\x12\xf0\xe0\x34\xb0\x62\xba\x44\xa7\x41\xe2\x89\xd3\x02\xda\x64\x1e\x68\x8a\xdb\x48\xf3\xcb\xcc\xd3\x68\x2a\x3c\x18\x58\x56\x8c\x1c\xe8\x79\x34\xb5\x94\xfa\xd8\x9a\x91\x6b\x9c\xaf\x38\xc4\x95\x82\xbd\x36\x9d\x56\xe9\x74\x6c\x89\x0b\x7a\xc6\x92\x36\x0c\xa9\x88\xf7\xce\xfb\x0c\xad\x48\x55\x59\x09\xb6\xa3\x94\x40\x51\xfe\x2e\xa3\xe2\x1a\x14\xdd\x4e\x18\x75\x9f\xea\x74\xab\x81\x63\xe5\xee\xbe\x2d\x4e\x9e\xed\x5e\x9b\x06\x59\x1c\xf1\xc4\x71\x3a\x9f\x47\x45\x41\xc3\xf6\x89\xba\x22\x35\x6a\xfb\x61\x97\x0c\x51\x67\x92\xc5\xb2\x78\x41\x27\xc1\x32\xf6\x5e\x95\xd4\xf5\x8a\xed\xc1\xa7\x78\x10\xf8\xa1\x8c\x37\x5e\x0b\x23\x92\x7e\x88\x5a\xf4\x78\x9b\x2a\xbf\xb9\xc1\x5d\xb0\x46\xf1\x5b\x74\xdf\x7e\xc3\xc5\x45\x12\x56\x4b\xc9\xac\x1a\x8d\x7a\x2a\x44\xd9\x1e\x3c\x48\x6a\x7a\x4d\x2f\xaa\x46\xfe\x72\x91\x8e\x67\x55\x23\xa7\x1a\x80\xf7\x01\x44\x4c\x9d\xe8\xbe\x6f\xb2\x33\x5b\x9c\xea\x5a\x16\x35\xca\x64\xd6\x77\xd6\x73\x4f\xbf\x71\xdb\x86\x39\x33\xef\x6a\x8e\xcc\x37\xd3\x09\x09\x0c\x27\x86\x41\x12\xca\x3b\xdd\x1c\xee\x74\xb8\x05\x03\xe3\x10\xaf\x5f\xfe\xd3\x62\x0c\x50\x07\x93\xe0\xbd\x2c\x41\xde\x3a\x18\xce\x80\x1d\x13\x7d\x79\x99\x2f\xef\x25\xdc\x3a\xbd\x21\xca\xbf\x18\xd7\xdc\x70\x51\x89\x2e\x8b\xe1\xf3\xea\xca\xa2\xfd\xbd\x31\x04\x88\x40\x2e\xda\x30\xbc\xc7\x37\x98\xac\x16\xfa\x24\x1c\x66\xf9\x2f\x49\x4d\x89\x0d\x57\x5d\xa4\x22\xb2\x75\x54\x90\x79\x34\x9d\x71\x11\x57\xb9\x59\x16\xea\x34\xa7\xe5\x22\xad\x6d\xb7\x48\xcd\x56\x8f\xdb\xf3\xe0\xe2\x15\xa5\xef\x68\xf6\x63\x90\xb7\x7b\x84\x7d\xbf\xcb\xa2\x34\x8b\x8a\x4b\x23\x7d\x1a\xe4\xef\xb2\x68\x4c\xc5\x6f\xf6\x1f\x4c\x33\xfb\x91\xa4\xc9\x98\xfa\xde\x5b\x7e\xa6\x97\x15\x2f\x2e\x3f\xd3\xcb\xa6\x6f\x2e\xa1\x26\x07\xd7\xbc\x86\x5d\x64\x21\xf2\x82\x8e\xa3\x79\x10\x77\x30\x80\xfb\xe6\xcd\xbc\x16\xfe\xda\xc4\x8e\x9c\x83\xde\x35\xcd\xfb\xaa\xbe\x7b\xd2\xbf\x29\x75\xdf\xd3\xf5\x1f\x91\xae\x85\xf8\xe6\x10\x36\xdc\x14\xcb\xa8\x47\x82\xaa\xbd\x42\x5d\x63\x7a\xbe\x30\x05\x39\x91\xbe\x66\x48\x6f\xb5\x14\x5c\x5c\x74\xbf\x28\x1d\xe6\x45\x1f\x8b\x01\xeb\x52\x8f\xa0\x75\x77\x26\x80\xf2\xe5\x91\x4a\xfc\x99\x00\xea\xb5\x0a\x4b\x47\xb8\x80\x77\x71\xfe\xea\x1d\x28\x6f\x1b\x36\x94\x54\x53\x5e\xf4\x81\xa4\xfc\x85\x20\x4b\x43\x4e\x83\xdc\x0f\x37\x0d\x72\x03\x0a\xc8\x17\x81\x6a\xa1\x16\xe5\x1b\x43\xc5\x6b\xc3\x24\x54\x43\x11\x6a\x01\x96\xb4\x80\x61\x0c\x9f\xa4\xaa\x2d\x67\xdd\xd5\xb5\xe9\x16\x28\x6f\xdb\x81\x35\xfa\x50\x5c\xf4\xa5\x99\xa2\xb7\x02\xfc\x28\x5a\xea\x4c\x2e\x56\x5e\x3a\x32\x9e\xd0\x4d\x96\x90\x08\x6d\x54\xb9\x92\x54\xa4\xad\x55\x96\x93\x5d\xb1\xe5\x60\x07\x87\x48\xd2\x21\x91\x6a\xd6\x97\x0f\xca\xa5\x51\x0f\x94\x26\x3f\x99\xd9\x60\xb9\x95\x82\x96\x37\x59\xb2\xf0\x54\xe4\x9e\xe5\x7c\x19\x07\x45\x74\x46\x7f\x0c\xf2\xa3\x1c\x5e\x20\x96\x55\xe5\xc0\x5a\x75\x4d\x6b\x6b\x98\x1a\xe5\xd0\xd8\xe9\x64\x42\xc7\xa2\x66\xbe\x7a\x4b\x17\x44\x79\x11\x1f\x45\x97\x42\xdb\x0b\xd3\xb4\x6e\x91\xc5\xe2\x74\x6a\x1b\x8b\xea\x0c\x14\x00\xc9\xd1\x66\x82\x4a\xd2\xab\xcb\xf4\x3c\xa8\x66\xb0\x75\x8a\x4b\xd1\x52\xa3\x95\x08\x64\xd6\x7c\xed\xc1\xb1\xaf\x72\xb9\x41\x85\x0d\x16\x9b\x59\x13\x36\x89\x82\x1a\x94\x4d\xd4\x60\x40\x94\x37\x29\x70\xab\x28\x14\x26\xf8\x64\xdb\x3f\x0d\x72\xda\x80\x41\xfa\x80\x7d\x94\xe0\x81\x33\x68\x80\xe7\x4f\x83\xfc\xa7\x68\x1e\x15\x1e\x22\x36\x01\x44\x59\x95\x58\x42\xf9\x46\xbe\x51\x26\x8f\x7e\xf5\x6d\x7b\x3a\xd3\x80\x2e\xa2\x39\xcd\x8b\x60\xbe\x28\x2d\xa2\x20\xf4\xea\xe2\x19\x49\x19\xef\x32\xb2\xcb\xaa\x55\x6a\x20\xd4\x99\x30\x9a\x4c\xa2\xf1\x32\x86\xa7\x48\x65\x98\xd6\x40\xe6\x40\xd2\x22\x88\x5f\x34\xa9\xc0\x82\xc4\xe2\xb3\xb9\x58\x05\xb8\x66\x74\xe6\x92\x75\xb3\x5d\xa1\x33\x2a\xe8\xbc\x6b\x3f\x42\x74\x2c\x41\x01\xca\xbd\x73\x37\x16\xb6\x4f\x7c\xe3\x05\xeb\x56\xf8\x29\x57\x2a\x5d\xef\x34\x5a\xde\x1f\xa2\x69\x42\x33\x12\x47\xb9\xfd\x5c\x7a\xa5\x45\xcd\xab\xc9\xfd\x6b\x9b\xb8\x8b\x5b\xc0\x97\xaf\x71\x01\xa0\xd5\x35\x9e\xb9\x92\x30\x72\x96\x70\x62\xcd\xdc\xd4\xcf\x8a\xc0\x26\x57\xcd\x1e\x42\xcf\x65\x14\x08\x34\x0d\x7c\x0a\x90\xea\x07\xf7\xa1\x11\x93\x8d\xd3\xa9\x17\xf1\x98\xb3\xfb\xd0\x1e\xa7\x53\xad\xb6\x75\x91\x0e\xf5\x1a\x78\xc7\x15\x62\x74\xa3\xeb\xb2\x68\xc2\xbe\x0c\xf1\x42\xe1\xc3\xca\xf0\x2c\x74\xbb\xe8\x0e\xae\xd3\x91\x1f\x8c\x8a\x1b\x08\x22\xde\x4a\x8c\x26\xe2\x74\xea\xa9\x5a\xa6\x96\x54\xa9\x0a\x99\x67\x3d\xb8\x02\xac\xd7\x5f\x9c\xcf\xa2\x9c\xed\x8a\x8b\x34\x2f\x6e\xa0\xc0\x78\x97\xe6\xd5\xf2\xa9\x1b\xba\xab\x72\xf7\x74\x2b\xc5\x13\xcd\x3a\x89\xb7\x4e\xf6\xdd\x5f\x04\x97\xf0\x1e\x67\xd7\x50\x5a\xe2\x2c\x81\x64\x48\x2a\x8a\xd8\x7b\x7a\x96\x99\x18\xf6\x3c\xcd\x3e\x7f\x4c\xdf\x65\xe9\x19\x2d\x2f\x83\x80\x70\xd9\x85\x38\x7b\x94\x17\x94\x10\x28\x22\xc5\x04\x07\x2c\x33\x2c\xf0\x39\xcf\xe0\x9d\xe4\xee\x79\x30\x63\x47\xe9\x64\xd7\xf8\x7a\x46\x8e\xd1\xe7\x09\x19\x29\xf3\x97\x6b\xdd\x2a\xbf\xbb\xe1\xd7\x38\x71\x9c\x9e\xc3\x73\x24\xa9\x65\xaa\xaa\xbe\xfa\xf9\x0c\x0f\xb9\xc9\x88\x89\xa4\x49\x7c\xc9\xe3\x88\x14\xc6\xab\x1e\xf9\xb2\x86\xbf\xa0\xf1\x3d\x08\x93\xcf\x6b\xc8\xc8\x7e\xec\x85\x1f\xd6\xd8\x8a\x0e\xd6\xc7\x46\xbc\x4b\xdd\x23\x02\xfd\x0b\xeb\x66\x2f\x37\xab\xa3\xf4\x26\x1b\x47\x35\x61\x0b\xba\x06\xfc\xd2\x8b\x45\x94\x5d\x7a\x56\x3c\xca\xc5\xe4\x96\x73\xb7\x43\x5e\x68\x96\x57\xb6\x04\x2c\x50\xcf\x02\x00\xca\xf6\x09\x74\x16\x44\x77\xc7\xb7\x2a\xdf\x07\xe7\x92\x64\x44\x8a\x17\x0c\x55\xbf\x97\x8f\xa3\xc8\x5e\xbe\xb2\x0c\xde\x46\xff\x9e\x0b\xc4\x29\x38\x1d\xb8\x47\xaf\x0a\xdd\x00\xf8\xe1\x86\xe0\x79\x3e\xe6\x30\x18\xac\xb2\x22\x60\x6d\xe2\xd5\x58\xba\x18\xf5\x72\xbb\xc5\x4a\xb2\x2e\x65\x38\x8a\x9a\xd1\xbf\x62\xaa\xb6\x7e\xd4\x17\x65\x07\x5f\xaa\x89\xa4\x7e\xbe\x3c\xe5\x2f\x14\x3b\xc3\xde\x36\x5f\x95\xad\x8b\x70\xdc\x32\xbc\x4d\x29\x77\x56\xad\xe1\x45\x8b\x6c\x10\xb7\xf0\xb6\x71\xc4\x80\x5e\xf1\xfb\xe5\x84\x9e\xc3\x55\x73\xc7\x0c\xf2\x0e\x37\x72\xa7\x41\xd2\x8f\xf2\x7f\x04\x71\x14\x76\x20\x08\x8b\x48\x79\x11\x65\x74\x5c\x74\x7c\xd7\x71\xc2\xd7\x1d\x00\x8a\x1a\x3b\x5d\xe7\xae\x0f\x0b\x4e\x3a\x36\x96\xec\x81\xa7\x5a\xc3\x9d\xa2\xa7\xa2\x06\x55\x88\x9e\x99\x35\x71\x4d\x94\x6d\xdc\x24\x1c\xde\x4b\xd8\xb6\x0c\x56\xaf\x39\xc9\x87\xcb\x64\x1c\x25\x7e\x71\x48\x78\x98\x47\x73\xb9\x6e\x26\x11\xd7\xe1\x96\x21\x84\x83\x7b\x2e\xb0\x8e\x8d\x92\x29\x08\xbb\x5e\x4d\x86\x0b\x66\x3a\x39\x13\xfe\xc6\x6a\x2a\xc0\x50\x66\xf9\x59\x34\x9d\xd1\xbc\xae\x3c\x86\x42\xb4\x23\x72\x3f\x27\xe9\x79\xf2\xa1\x08\x0a\xea\x73\x78\x89\x72\xcb\x1b\xc0\x55\xec\xd8\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\x89\x4e\x43\xfb\x3d\x2b\x09\x6d\x51\x77\xcf\x3f\xaa\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\xe9\xbb\xa9\x1e\x95\x67\xa1\x92\xc6\x25\xec\xc8\x93\x86\x60\x7d\x67\xc7\x51\x79\x16\x2a\x69\xb3\xb9\x91\x3f\x19\x95\x30\x36\xe5\x91\x27\x8d\xc3\x96\x59\x94\x8c\x4a\x73\x70\x39\xff\x80\xca\xf3\x4a\xca\xda\x8a\x5b\x4f\x15\x36\x88\xd1\x7b\xe3\x28\x3c\xf2\xa6\x3a\xf0\xf6\x41\x77\x54\x95\x89\x4b\xe3\xe3\xda\xc8\x93\x86\x61\xad\x49\xf0\x24\x62\x68\x9b\xfb\x8d\x4a\xd2\x39\xd7\x34\x8c\x18\xf9\x3d\x66\x6b\xb4\xf9\xb4\xcc\x35\x17\xdb\x3a\x5a\xa3\xed\xed\xeb\x93\xde\xf6\xe6\xbd\x5b\x97\x7b\x2b\xc6\xff\x1a\x2b\x46\x41\xe9\x77\x11\x9f\x69\xb5\x60\x16\x0d\x4d\x17\x79\xf8\x28\xd3\x26\x91\xa7\x7d\x85\xa8\x18\xcd\xe3\x58\x04\x71\x3c\xb0\x22\xbd\xc2\x03\x75\x3b\x4e\x94\x1b\xdd\x42\xbe\xb2\x70\x43\xe2\x55\x44\xb5\xf0\xc5\xc4\xfb\xc4\xb7\x46\x11\x75\x01\x07\x83\x5e\x3d\x22\x82\xae\x54\xec\x2d\xb8\x56\x9e\x74\xbb\x6a\x21\x90\x64\x00\xe7\x55\xa8\x53\x7e\x63\x18\x19\x2c\x5a\x80\x88\x4f\x0c\x71\x27\x11\x39\xd8\xfe\x60\x4f\x86\xe1\xbb\x15\xcc\x4f\xf4\x9b\x46\x7c\x64\xca\xa6\xc6\x79\xe9\x06\x21\xd0\xe5\xd9\x42\xc7\x8b\x04\xc7\x28\xc0\xeb\xf9\x2b\xbc\x6c\x9a\xf3\xb0\x1b\xeb\x42\x68\x6c\xd6\x61\x2c\x04\x56\x76\x1a\x77\xef\x07\x87\x94\x64\x0e\x8e\x7e\x29\x9e\xfb\xba\x83\xf3\x8f\xcd\x76\xe6\x51\x21\x9e\x76\x34\x1e\x1a\x22\xa2\x2a\xc6\x25\x8e\xca\xed\x0b\xe4\x16\xe5\x64\x9c\x66\x99\xeb\x63\x15\x4e\x5e\x41\x41\xf7\xb2\x69\xee\x0b\x7b\xa9\xe3\xee\x3f\x24\x7f\x83\x93\x5b\x4e\xbe\xc0\xb9\xed\x9a\xb5\x17\x15\xe2\x91\x93\xe1\x86\xd5\x33\x55\xb8\x9d\xd2\x39\xd2\xa7\x77\x0e\x05\x28\x72\x4c\x8e\x02\x8d\xf8\xc1\x40\xbe\x66\x03\x4d\x97\xe1\xdf\x08\x36\x4f\xf0\xaa\xa9\xc3\xd9\xb1\xad\x36\x80\xb7\xb0\x59\x70\x29\x5f\x76\x8a\xb9\x5b\xef\x38\xd1\x50\x83\xae\xf2\xd1\xcf\xce\xe3\xce\x05\x90\x75\xc7\x21\xc0\xb9\xbf\xed\x4a\x78\x7d\xe5\x65\x94\xb1\x0a\x58\xcf\xca\x41\x47\x20\xb1\x23\x09\x71\x7d\x77\xb7\x8c\x90\xcd\xa7\x7c\xec\xcc\x2d\x02\x12\x56\x84\x0d\xec\x38\x1e\x36\xaa\x7c\x52\x4b\x6d\x13\x98\xc2\x61\x52\x31\xa2\xaa\xa4\xef\x38\x98\x87\xbc\x9c\x4d\x43\xfb\xa2\x2f\xf1\x4f\x1d\xc4\xaa\x55\x6d\x19\x58\x49\x79\xaa\xfd\x4a\xb2\x33\xe2\xf0\xae\xce\x30\x56\xe5\x17\x66\xf8\xdc\x92\xf8\xbc\xd7\x9a\x9b\xe3\xe5\xd3\xf1\x04\xcb\x2d\x52\x7f\x28\x0c\x23\x98\xee\x2e\x29\x09\x73\xe1\x8b\x96\x20\x1e\x72\xa1\xe1\x1a\x51\x7a\x2b\x4c\xec\x4a\xc2\x38\x49\xd4\xdf\x2c\x5c\x8d\xb7\x78\xe5\xbc\xdf\x28\x68\x8d\xf0\xb7\x3f\xec\x91\xa7\x52\x0b\x55\xd1\xc4\x32\x59\x04\xe3\xcf\xfc\xae\xd1\x34\x36\x85\x24\x43\x27\x65\x26\xe9\x2e\x18\xfa\x91\x54\x56\xc5\x7f\x28\xd2\xdb\x25\x5b\xe4\x99\x4c\x94\x21\x01\x88\x3c\x07\x6a\x1f\x19\xca\x91\x7f\x59\x44\x00\x2c\xe4\xf4\x44\x71\x73\x46\x85\x0e\x07\xfb\x33\x57\xc1\x20\x8f\x87\x27\x64\xe4\xf3\x5a\xbf\x0f\xb1\xd0\x03\x14\x7e\x5e\x22\xcb\x0e\x70\x1f\xc4\x31\x5e\xdc\xfd\x7e\x5f\xae\xef\x7d\xbb\xac\xb5\xf9\x38\xfe\xa2\x0e\xf8\x76\x07\x71\xae\x25\x28\xdb\x8d\x02\x55\x43\x8f\xbb\x06\xb2\x2b\xe6\xce\x11\xe1\x31\xae\x3c\x74\x05\xc6\x73\xc9\x20\x09\x4d\xa7\x42\x12\x8c\x07\x82\xe7\x27\x23\x56\x07\x8f\xa2\xc9\xc0\x05\xda\xbc\xb4\x2b\x66\x15\x02\x59\xd7\x51\x2d\xf4\xaa\x2c\x58\xf8\x2a\x91\xc0\xfd\xfb\xa6\x94\xc1\x2c\xa3\x5c\xb5\xc7\xc0\x41\x46\xcb\x7f\xc2\x87\xb8\x21\x16\x62\xf6\x03\x7e\xd0\x4d\xe9\x0b\x17\xc1\xe2\x8f\x5d\x4c\xdf\x54\x70\xdf\xe5\x92\x4b\x4b\x38\x6d\xf4\xb1\xee\x7b\x9b\xae\x75\xc3\x8a\xf1\xd1\x62\xc6\x91\x20\xaa\xee\x19\x5d\x73\x5f\xa2\x42\x29\xbc\x84\x3b\xc6\x7a\x40\xde\xf7\x9d\xb7\xe3\x4d\x1a\xec\xb9\x1e\x9f\x5c\x1e\x80\xfc\x3d\xc9\x07\x47\x86\x57\x90\x1e\x37\xdd\xd9\x31\xbd\x6c\xf3\x4e\xd3\xd0\x89\x28\x50\x64\x97\xd6\x43\x5a\x04\x0a\x6f\x67\xcb\xc7\x4b\x8c\xc7\xbe\x63\xf0\xb6\xd0\x71\x7c\x36\x71\x8a\xdf\x25\x14\x17\x42\xa5\xcc\xce\xcb\xd6\x91\x24\x53\xb9\x51\x34\x39\x57\xda\xdb\x86\x59\xa4\x76\x57\xb0\x5a\xf8\x53\x2d\xb5\xda\x35\x23\x49\x4a\x00\x0a\x8b\xdf\x1f\xc8\x10\x0e\x35\xc6\x59\xd3\x95\x0e\x71\x08\xdd\x20\xe1\x8e\x0b\x92\x50\x38\x37\x85\x18\xc8\xc9\x23\x79\x50\x75\x82\x41\xd7\x2c\x57\x23\x00\x22\x5b\x37\xd6\x3c\x74\xcc\xeb\x49\x51\x5d\x2d\x78\xf3\xc8\x13\x34\x2f\xa2\x79\x50\xd0\x1f\x03\x50\x20\xd6\x51\x15\x02\xaf\xa3\x28\x5c\xf3\x5d\x50\xd3\xd7\xa7\x8e\x66\x33\x84\xc6\x55\x37\x3b\x1e\xd0\xb2\x99\x79\x2f\x9b\xa1\x32\x96\x1e\xc4\x04\x92\xba\x40\x21\x1f\xe0\xa9\x98\xd2\xe2\x85\x1d\xeb\x4a\xee\xac\x76\x35\x75\x73\x25\xea\xba\xe3\x79\x6a\x84\x78\x79\x55\x2d\x56\x26\x0f\x13\xd4\x5c\x6a\xbe\x45\x44\x4e\x5c\x54\xe2\x19\x91\x7d\x25\xc2\x7e\xdb\xf0\x9c\xaa\xfe\x1b\x45\xe8\x54\x85\x56\x1d\xe4\xd7\x0c\xd7\xa9\x75\x34\x6c\x80\xd9\x62\x2c\xfd\xc0\xe5\xfc\xd4\x5c\xc7\x88\x04\x74\xb9\xb9\x4d\xc5\xb8\x44\xd9\x3f\x36\x57\x22\x46\xd8\x22\x09\x86\xc5\x14\x23\xfa\x0e\x9e\x13\xd7\x71\xa2\xa5\x71\x7d\x06\xde\x98\x3f\xb1\x1e\xb7\xc9\x88\x7f\x58\x3b\x49\xbb\xe7\x08\x2f\x23\xed\xb0\x50\xe5\x29\x57\x8b\x62\x38\x27\x3a\x8b\x77\x5c\x3a\xf2\xe5\x0c\xb2\x96\x18\x64\x9c\xa1\xb2\xed\x47\x45\xf0\xaa\xde\x7a\x3c\xc1\xb6\xf0\x04\x17\x86\xa0\xb3\x6e\x62\x47\x9b\x19\xc1\x36\x5f\x60\x19\x4a\x3a\xab\xd1\x69\x65\x5b\x85\x85\xce\x7e\xb0\x58\xc4\x97\xc2\x75\x56\x23\xc2\xea\xda\xf6\x79\x7c\x0b\xb0\x9a\x61\x89\x37\xaa\xbb\x66\x1e\x44\x40\x2a\xcd\x78\x74\x4c\xaa\x5b\x07\xa3\xf2\x4c\xd8\xd7\x8a\x47\x25\xd3\xf5\x8a\xc7\xbe\xc3\x4a\xc1\xc5\x61\x53\x63\xb8\x0c\xd0\x95\x9a\xbd\x93\x5f\x56\xdc\x14\x91\xf8\x48\x74\x52\x69\x31\xbd\x5b\x4b\x9f\x57\xec\xf3\x4f\x19\x8c\x4b\x96\x05\x02\x8f\xb2\xf1\x32\x0e\xb2\xf5\xf5\xf5\xf5\xea\x10\x5c\x92\x82\x76\xee\x24\x08\x17\xd7\xfe\xb6\x46\x5b\x4f\xfc\x1e\x8d\xb6\xee\x6f\xff\xef\x6f\xff\xff\xda\xb7\xff\xe2\xea\x9f\xc1\xca\x20\x69\xfe\xd0\x2e\xbf\x5b\xd0\x16\x9f\x65\x41\xb5\x21\xc0\xda\x60\x00\x41\xe0\x82\x8c\x91\x32\xdb\xc1\x96\xb9\x39\x44\x46\x70\x61\x34\x99\xd0\x8c\x26\x05\xa1\xc9\x59\x0e\x85\x4e\xb3\xf4\x3c\xa7\xd9\x1a\xf2\x70\x7b\x1e\x25\x61\x7a\x0e\x1a\x0b\x14\xfa\x84\x3c\x78\x20\x72\xfa\xff\x7c\xf3\xd3\xeb\xa2\x58\x08\xe7\xc9\x9c\x6b\x9a\x69\x64\xd7\x0f\x0b\xac\x4f\x44\xee\x88\xa6\x49\xca\x18\x41\x1c\x25\x94\xf5\x24\x49\x43\xba\x86\xdc\xe5\x39\x35\xaa\x81\x5f\xcc\x63\x36\x32\xb1\xb1\xb5\xbb\x4d\x1b\xb9\xe6\x98\xfc\xe7\xeb\xf7\x5b\x46\x75\xb3\x6c\xab\xdd\x2d\x2d\x25\x25\x07\xd6\xc2\x3b\x89\x4c\xd7\x24\x02\xe4\x27\x26\xda\x83\xbf\x58\xee\x5d\x9e\xf5\x52\x19\x40\x18\xe5\xf1\x96\x3f\x4b\xf3\xa2\x47\x8a\x68\x4e\xd3\x65\xd1\x63\x15\x66\x3d\x50\x32\x9f\xa7\x99\x78\xec\x08\x9b\x09\x83\x23\xbb\x04\xfe\xbb\xba\x22\x6d\x41\xec\x71\x3a\x0e\x62\x96\x38\x7a\xfa\xcd\xe3\x6f\x20\xd2\x32\xdf\x7b\x78\x85\x6c\x27\x14\xbf\xae\xae\xc8\x50\x65\xb3\x66\xc8\x2e\xb4\xa6\xd2\x64\xa3\x64\x57\xb5\x5f\x2b\x3c\x2d\x32\xba\x80\xd0\x85\xf4\xdc\x9a\x32\x4b\x76\x12\x80\xef\xd1\x59\x46\x48\x4e\xcf\xd3\x34\xa6\x41\x72\x0d\x77\xac\x6c\x7f\x96\x12\x8c\xc6\xb2\xf0\x53\x8a\x0e\x7c\x66\x5b\x86\x33\x2c\x8c\x69\x24\x77\x99\x1d\x30\x2f\x02\x59\xf5\x1c\xd5\xfc\x06\x85\x13\xd2\x9a\x78\xc5\x86\xb2\x09\xd1\xe2\x15\x0c\xf9\xf5\xfb\x2d\x1d\xe8\x98\x4b\x5a\x08\xf3\x68\x22\xe0\xc9\x19\xf6\x06\x69\x55\x64\x8c\xa7\x23\x5e\xa8\xad\xe9\x5a\xd3\x05\x4d\x3a\xed\x77\x87\x1f\x3e\xca\xd8\xac\x9c\x70\x78\xe7\x76\xd6\x90\x6b\x49\x98\xdb\x07\x0f\xcc\x49\x35\x0e\x7d\x4b\x30\xa8\x69\x3f\x0f\xf2\x68\x4c\xda\x64\x03\xba\xf0\x7c\xc9\xd8\x03\xaa\x62\x83\xb4\x47\xea\xaa\x50\xd5\xd3\x2f\x52\xf1\xf8\xae\x7d\x1a\xe4\xf4\xc9\xe3\xb6\x35\x7e\xed\x58\xfd\x35\x0d\x42\x9a\x75\xda\x7b\xc0\x57\xa3\x5f\x03\x7e\xda\x82\xf6\xf9\x08\x2b\x0a\x31\xf9\x98\x26\xc5\x23\x76\xd0\x6e\xf7\x48\x9b\x49\xfe\xd1\x18\xaa\x18\xfc\x92\x4b\xb5\xa3\xba\xb1\x12\x53\x56\x43\xae\x3c\x04\xcf\x65\x32\x46\x87\x6a\x5b\x93\xec\xbb\x78\x5e\xa0\xeb\x6b\x7f\xb0\xf5\x2a\xd2\xcb\xed\xe0\x9b\x52\x97\x66\x93\x9c\xa4\x19\x93\x56\x45\xf4\x6e\xa0\x47\xad\xdd\xd7\x98\x4b\xc2\x0e\xbc\xf4\xe0\xef\x0e\xa2\xc9\xa5\xaa\x5f\x20\x59\x2a\xf2\xb1\xdf\x74\x9f\x35\xc0\x7e\x9a\x24\x54\xbc\xc7\x90\x14\xa6\x29\xd1\xb8\x5c\x94\xad\xcb\x08\x26\x1f\xe9\x45\xe1\x74\x50\xc0\xa2\x67\x28\xc2\x2a\xdf\xec\x56\x55\x97\xde\x8b\xfa\x3b\xbe\x06\xf1\x2a\x69\x1e\x4c\x1b\x68\x20\xa8\x21\x82\x3d\xc5\x71\x2a\x28\x41\x64\xbd\x72\xc2\xd7\x90\x22\x8b\xa6\x53\x9a\xf1\x98\x5b\x6c\xf6\x41\x6c\x51\x0e\x74\x19\x0e\xea\x08\x06\x7a\xe0\xa3\x1a\x33\x74\x76\x13\xfa\x01\xe3\x95\x1d\x83\x9b\x24\xe0\xec\x3c\x2f\x82\x82\x8e\x67\x41\x32\xf5\x2b\x10\xf8\xb3\x02\x89\xf8\x20\xbc\x04\x83\x7e\xb8\x11\x7e\xcc\x38\x8c\xcd\xf2\xd6\xcd\xd0\xd7\x0d\x28\x46\x03\xca\x5b\x25\x14\x53\xcd\xbe\xcc\xaa\xa1\x28\x38\x93\x79\x6f\xad\xd4\x8d\xd5\x8a\xb4\x45\xf0\xd5\x96\x7d\xb1\x65\xb4\xcc\xce\x82\xd7\x16\x8a\xf5\x46\xe0\x62\xd6\xac\x2c\xef\xeb\xa5\xf7\x91\x97\xea\xe0\xcd\x43\x2c\xe4\xbb\xe5\x00\x76\x17\xaa\x98\x80\x58\x69\x78\x5d\xe9\xcb\xf2\xf8\x92\xd1\x3b\x7f\x34\x0b\x8b\x8b\x51\x75\xc9\xda\x8a\x72\x51\x3f\x35\x99\xa9\x12\x02\xa4\x82\xd3\x16\x06\xd8\xf9\x21\x69\x17\x64\x12\x44\x31\x0d\xfb\xe4\x90\x9d\xd3\xce\x23\x76\xf6\x08\x20\x4c\x5e\xf9\x6a\x42\x6d\x7a\xe6\x42\xe3\x53\xe9\x33\x54\x38\x96\x28\x1c\x91\xef\xd4\x9f\xd4\xf7\xb1\xdd\x27\x5b\x8c\x47\xa4\xbd\xd5\x1f\x2a\xe5\xa1\xd4\x3f\xb6\x13\x5a\x7c\x8a\xa3\xbc\xa0\x09\x38\xba\x5c\xb3\xb4\x87\x27\x86\x41\x97\x54\x70\x65\x3c\xe6\x9f\x4b\xbe\xd2\xaa\x90\x0d\x52\x4f\x82\xa3\x2e\xc0\x43\x97\xaa\x02\xe3\xb4\xcf\xc4\xdc\xd6\xe8\x29\xfb\x65\xc8\xcf\xad\xd1\xe6\xb7\xec\xe4\xbf\x7d\x7f\xf2\xbf\x3f\xf9\xff\xc5\x4f\xfe\xda\xf0\x1f\x1e\x4b\xde\x91\xd1\xbf\x32\xe4\xc4\xa7\xca\xd3\x68\xca\x6d\x70\xfb\xbf\xf0\x13\x3a\xbf\x07\x09\x7f\xa2\x13\x73\x43\x50\xc1\x4f\x2f\xd1\x83\x3d\x63\xe3\xe4\x10\x9c\x5d\x9c\xcf\x58\xef\x3b\xa6\x81\xd6\xf7\xbc\x30\x79\x48\xb6\xdc\x17\x7f\x60\xf1\xc7\xa4\x78\xf3\xdd\x23\xf1\xbf\xc4\x13\xcc\xfd\x9d\x38\xd5\x05\x09\x39\x78\xbe\xf7\x56\x4c\x72\x48\xbe\xfb\x96\x8c\xd3\xf9\x62\x29\x02\x0f\x9d\x5e\x92\x79\x7a\x16\x25\x53\x14\x5e\xef\x31\x19\xcf\x82\x0c\xf6\x02\x7e\x33\x1b\x72\x53\x2a\x69\xae\x2e\xa1\x63\xca\x1f\x2d\x14\x29\x6b\x90\xe3\x2a\x27\x9d\x3d\xb2\x4b\x36\x87\x3d\xf2\x9c\xfd\xbf\xd9\x23\xfd\x7e\xbf\x47\xfe\x8f\xec\x92\xed\x6f\xba\xec\xb0\x43\xf2\x05\x1d\x47\x93\x88\x2f\xa4\x83\x0f\x87\x9b\xdb\x4f\x36\x9f\xd8\x26\x66\x51\x9e\x42\xba\x18\x87\xeb\x66\xf9\x9a\xbf\xc5\x65\x1d\x61\x03\x34\xaf\xd6\xf0\xcd\xb2\x90\xa4\x42\x09\x26\x7c\x36\x98\xf5\x1b\x13\xca\x2a\xc6\xf3\xc8\x46\xd4\xde\x6b\xf7\x19\x5a\xf6\xd3\x90\xee\x15\x9d\x21\xd2\x5a\xb3\xb1\xb5\xff\xcf\xc9\xe6\x0c\x90\xbf\x17\x06\x62\x2d\xd2\xa3\xc5\x82\x66\xfb\x41\xae\x55\xd9\x28\x9b\x3f\x3b\xee\x3c\xee\xca\x97\xc0\x22\x61\xd8\x7b\x6c\xdd\x98\xf1\xdc\x45\x1c\x15\x9d\x76\xbb\x6b\xbe\xc2\x4e\xba\xa6\x75\xd5\x38\x0d\xd9\xe0\x12\x5f\xe7\xa5\x7c\x08\x30\x3f\xec\x92\x3d\x26\x10\xc2\xc7\xf7\xbb\xe4\xff\xba\x4e\x50\x0c\xcf\xcc\x8a\x89\x35\x20\x95\xcf\xe5\x90\x92\x47\x64\x8f\x6c\x90\xcd\x21\xb2\x33\xf2\x05\x8a\x90\xc1\x78\x6d\x1b\xa6\xeb\x6e\xff\x97\x34\x4a\xd8\x30\x6d\x4b\xc5\xf1\x12\x9c\x00\xc3\x14\xbf\x39\x7c\xc1\x08\x7b\x73\x28\x99\x92\xb0\xf0\x03\xca\xf7\x50\xdc\xb7\xc3\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x0e\xcb\x08\xcd\xa4\x2f\xed\xd8\x9b\x53\x93\x28\x5c\x49\x45\x19\x9d\x07\x51\xc2\x75\x47\x2c\x4f\xdf\x3d\x0a\xd7\x41\x26\x7b\x10\xc0\xda\x6e\x79\xab\x6b\x39\x45\x02\x66\x25\xc1\x94\xc5\xeb\x77\x86\x89\x9c\x6e\x12\x64\xed\x83\xa4\xe0\x3e\x7c\x7a\x64\x73\xd8\x25\xff\x7f\x86\xb5\x0d\xa7\x16\xee\x72\x49\x98\x9f\xfb\x5e\xfe\xaa\xba\x54\x49\x5d\x9f\x31\x4f\xf5\xef\x90\xb8\x09\x3a\xac\x03\x61\xf0\x0f\x17\xea\x90\x20\xde\x3a\x08\xf6\x29\xe7\xcb\x3f\x39\x03\xec\xfe\xdd\x3f\x09\xc2\x12\x5a\x2f\x39\xb7\xab\x4e\xd8\xe5\xba\x7e\x52\x88\xca\xb5\x9c\xcb\xd7\x39\x16\x51\x31\x98\x3d\x95\xe3\xf4\x3d\x40\x59\x52\x8c\x66\x43\xb8\x56\x6c\x0d\x6b\xc5\x58\x4e\x1f\xd5\x58\xe5\x0c\x01\x74\x44\xf9\x73\xe9\xab\x00\xbd\x54\x10\xe1\x71\xc9\xe6\x13\xc4\xc2\x4e\x83\x9c\x6e\x3f\x21\xbb\x50\x46\xab\x87\xb6\x9f\x18\x26\x00\x61\x48\xb9\x66\x11\xf6\xc0\x0e\x2f\xd4\x23\x9b\xdf\x98\x92\xb0\xea\xe7\xf3\xd3\x20\xe9\xf0\x62\x26\xf3\xb3\x16\xb3\xf0\xb7\x82\x16\xee\x73\x36\xf4\x22\x35\x76\x2f\x36\x7d\x04\x3c\xf8\x66\x97\x72\x45\x73\x65\x12\xd8\xeb\xbe\xe3\xc1\x51\x92\xb4\x10\x42\xd9\xf7\xd1\x0f\xad\x29\x48\x24\xdc\x8f\xcf\x44\x23\x35\x9f\x05\x5c\x5a\x83\xfd\xed\x62\x1c\x2f\xf3\xe8\x4c\xc5\x72\x8d\x4e\xa3\x38\x2a\x94\x80\x73\x1a\x24\x9f\x07\xa7\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\x1d\x0a\xb7\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\x71\x55\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x4e\x39\x5b\x12\xf2\x0f\x4d\xfa\xe7\xd1\xe7\x68\x41\xc3\x28\x00\x21\x88\x7d\x0d\x0e\x92\x82\x66\x49\xc0\x9f\x4e\x7c\x7a\x1e\x24\x9f\x3f\x09\x6f\xc6\x9f\xf8\xbc\xfe\xff\x7e\x14\x23\x4d\xa6\x9f\xd8\x10\x3f\xc1\x5b\xa2\x4f\x61\x34\x8d\x9c\xa7\x1c\x72\x6a\x7c\x14\x79\x2a\xf7\x54\x39\x03\xd2\x19\x4e\x91\x7a\xb6\xd9\x06\xb4\xfa\xdc\x5e\x91\xa7\x16\x5b\x14\x33\xba\xcf\xf7\xa9\xf6\x3f\x5f\xb6\x77\xd6\xbc\x3c\x53\xf0\xd8\x8e\xb5\x73\x77\x70\x05\x1b\xa4\x3d\x04\x51\x09\x5a\xc1\xe6\x2e\x0c\x1d\x2f\x18\x36\xc8\x2e\xe9\x70\x71\xaa\xf3\xdd\x53\xf2\x48\x37\xd1\x95\xcf\x06\x1e\x6d\x59\xfb\xad\xf2\xf6\x61\x36\x85\xea\x14\x0d\xd6\xa8\xad\x04\x13\x41\xb8\x02\xc2\xe6\x11\xf5\xa3\x24\x2f\xa2\x62\x59\x48\x9f\xdc\x51\x48\x93\x82\x6d\x5a\x76\x24\x0a\x5e\xcb\x41\x12\x46\x19\x35\x0d\x18\xcc\x37\x36\x79\x4f\xca\xb2\xea\x91\x0d\xbc\x9a\x6a\xa1\x96\x5a\xd0\x54\x4b\xb7\xd5\x5a\x85\x17\x99\x3d\xf1\xfa\xe9\x36\x8f\xc0\x26\x67\x68\xbf\xfc\xf8\x9a\xcd\x83\x7c\xdd\x82\x31\x80\x52\x55\xdf\xba\x16\xbf\x4e\xab\xf8\xb5\x7c\x4a\xc7\x91\x2b\xc2\xd5\x47\x39\x7f\x29\x87\xf9\xb8\x23\x77\x82\xe7\x96\x52\x79\x53\xed\x45\x1e\xc5\x87\x54\x78\xf0\xe7\x74\xbc\x25\x25\x74\x1e\x20\xbf\x30\x95\x72\x42\x84\xfd\xcb\x44\x9c\xac\xb0\xf0\xa7\x9d\xcb\xd4\xea\xca\x15\x16\xa0\xeb\xa5\xaf\x07\xf1\x98\x75\x98\x12\xef\xa8\x7a\x24\xf5\x68\x6d\x60\x6c\x58\x5b\xe3\x8e\xd2\xa2\x84\xc1\x7f\xfe\xf9\xf2\x78\xf8\xe8\xbb\x93\x2f\x5b\xd7\x9d\x97\x1f\x5f\xb3\xdf\x7b\x8f\xfe\xef\xe4\xcb\xe6\xf6\xf5\x95\xfa\xd8\x1e\xf6\xb6\x37\xaf\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x73\x45\x84\x89\x27\x98\x70\xfa\xf7\xa2\xed\x85\x5e\x82\x77\x83\xb7\x17\xee\x4a\xb2\x10\xa7\x07\x85\x1f\xf7\x6c\x3f\x26\x57\x57\x65\x79\xdf\xdc\x70\xd8\x13\x12\x25\x25\x03\x37\xb8\xcf\xdd\x0c\xdd\xcb\x46\x1a\x0d\x7e\x6b\xd8\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x72\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\x39\x54\xd9\x70\xc6\x85\xde\x8d\xd3\x98\x74\x0e\x3e\x1c\x0e\x0e\x5e\xee\x13\x76\x6e\x18\x6d\x0d\x87\xdb\x5d\x9b\x27\xa3\x6a\xdd\x53\x28\xca\x75\x06\x2e\xf3\x1a\x0e\x5b\x9c\x09\xb7\x7a\x64\xab\x99\xad\x2a\x66\xaa\xc6\x96\x42\xe8\xb4\x4f\xfe\xf9\xfe\xe5\x8f\x8e\x87\x44\x55\xc0\x3f\x9a\xd2\x1a\xdd\x49\x45\x90\x75\xc3\xd3\x04\xd0\x01\xf7\x79\xce\x90\xbf\xed\x91\xc7\x5d\x32\x22\xed\x76\xa3\x71\x8f\xe3\x08\x1e\x92\xa9\x0e\x82\xf2\x29\x4a\xec\xf1\x31\x2c\xfc\xb8\xf7\x8f\xc3\x57\xff\x3a\x7c\xff\xbf\xf6\xac\x42\x1d\x25\x73\x6a\xd7\xef\x9d\x5c\x0e\x74\xeb\xb1\x6f\x6e\xae\x3e\x72\xb1\x9a\xfc\xe7\x12\xf7\xe0\xe1\x0e\xcd\xa9\xc0\x19\x5e\xe0\x39\x87\xe0\x7b\x27\x31\x38\x9f\xe3\x33\xe3\xd0\xe1\x0e\xf8\x31\x3a\xc4\x96\x1e\x65\xe4\xf9\x43\x9d\x52\x8c\x13\x2a\x3f\xa3\x98\xe7\x99\xcd\x27\xdd\x1e\xd9\x1a\x2a\xd7\x6a\x86\x94\x27\xd1\x6b\x0d\x52\x16\x6e\xb6\x40\x4b\xbc\x61\x1d\x40\x16\x57\xea\x63\xbd\x62\x6b\x64\x7e\x5e\x9f\xf4\xb6\x1f\xdf\xab\xf1\xef\xd5\xf8\x7f\x71\x35\xbe\x50\xe1\x2f\xc6\xd5\xf6\x7b\xb7\xb0\xb8\x6b\xe9\x98\x9d\xad\x9d\x95\x62\x0d\xd6\xd8\xe9\x71\x3d\xd3\x62\xec\xb5\x04\x5b\x04\xc5\xac\x47\x12\x6a\x58\x7f\x7f\x02\xcd\x85\xf3\xf0\x54\x5e\x55\xe3\x68\xe7\xd2\x6b\x81\xb0\xd7\x01\x1b\x1f\xf6\x1f\x4f\xd5\x59\x63\x75\xc3\x0b\x5c\xb1\x90\x09\x9d\x2f\x0c\x7a\xa4\xcb\x2b\x1f\x9b\x56\xb1\x7e\x9a\x74\xda\x30\xaa\x36\x8e\x0e\xdc\x35\xec\xa7\xf3\x94\x31\x31\xfe\x96\xf0\xe0\xdd\x3e\xd1\xf7\xca\xfc\x85\x61\xbb\x47\x28\x62\xbd\x9f\x38\x1b\x14\x17\xde\x1d\xdb\xcb\xa7\xb7\x07\x49\x88\xdb\x47\xcd\x97\x56\x46\xd6\xd4\x1b\x83\x9f\x0e\x3e\x7c\x7c\xf9\x16\x56\xd0\xfe\xe1\xdb\xb7\x2f\xf7\x3f\x1e\x1c\xbe\x25\xef\x5f\x7e\x78\x77\xf8\xf6\xc3\xcb\x0f\xa5\xad\x86\x41\x11\xe0\x66\xd9\x37\xde\x9c\x06\x0f\x85\x19\xe1\x3c\xb8\x18\xa7\xf3\x45\x4c\x2f\xa2\xe2\x72\x44\x9e\x00\x65\x59\x3d\x04\x5d\xa8\xb2\x43\x60\x55\xe9\xfd\xa6\xeb\x09\x90\x24\x6c\x0e\xbe\x98\x91\xdd\xe1\xe0\x17\xda\xb6\x13\xa2\x3b\x3c\xe2\x3d\xf0\x97\x90\x9c\xcf\xa2\xf1\x8c\xcc\x83\x62\x3c\x13\xe2\x2b\xdf\x84\x18\x43\x0b\x8d\x72\x9e\xb0\x18\xd0\xb4\x3f\xf4\x3b\x5c\x47\x39\xbd\x05\x0b\x04\x17\x5c\x54\xff\xc9\x4f\xc8\xc7\xf0\x36\x2e\x0a\x4f\x5c\x67\xfb\xaa\x30\x1b\xab\x00\xdb\x71\xa0\xec\x20\xfa\xa5\xc1\xa5\xa1\x1a\xd1\x77\xbb\xa2\x6b\x07\x8b\x93\x28\xa3\x86\x47\x00\x1b\x5d\x65\xe3\x61\x43\xf1\xb4\x5e\x01\xae\x23\x5d\x63\xd3\x16\xfd\x17\xd2\x98\x16\xb4\xaa\x06\x7b\x30\x36\x6e\xf0\x2b\xec\x9f\xd9\xae\x05\x84\x28\x08\x82\xd7\x07\xca\x1d\x6e\x2b\x95\x70\x67\x39\x24\xe5\x3e\xa4\xa3\xa2\xbf\xb6\x26\x85\x41\x93\x84\xd7\x6c\xb5\x07\xbc\xc8\x64\xc2\x9f\xe6\x79\x48\x3c\x32\x0b\x63\x8f\xae\x78\x55\xd9\x6c\xb0\x67\xc9\x6b\xff\xe0\x2e\xdb\xb5\xe7\x61\xb9\xc4\x5f\xbc\x7c\xb4\xff\xfa\xe8\xed\xff\xbe\x7c\xaf\xea\x09\xe9\x78\xb6\x4c\x3e\xd3\x50\xbc\x2a\xe1\x2f\x46\xc5\x5f\x3f\xa3\x8b\x38\x18\xd3\xce\xe0\xdf\xd7\xc7\xff\x4e\xfe\x9d\x9d\x3c\xfb\xf7\x97\xc1\xb4\xd7\xbe\xbe\x7a\xf4\xe8\xea\x4b\xbb\x0b\x3e\x93\xbf\x78\xe1\xff\x7d\x22\x4b\x1c\x8b\x32\x27\xac\xd0\xb1\x2c\x75\x72\xec\x2f\x67\x97\x32\x0a\x95\x94\xd1\x6d\xa1\x96\x54\x43\xa8\x8c\xb8\xe6\x63\xd9\x6d\xc9\x49\x0d\x0c\xb8\x6b\x16\x10\x8f\xf8\xcb\x60\x00\x77\xa0\x54\xb8\xc3\x00\x4f\x1b\x50\xc1\x9a\x43\xfa\x2c\x6f\x9f\x65\x99\x2b\x57\xf8\x9d\xb1\x60\xc8\x06\xe1\xef\x5f\x0d\x51\x5d\xdd\x59\x5b\x9c\xcc\x75\x6a\xe0\xb3\x05\x83\xbe\xa3\x52\xc2\x9a\x86\x1b\xd3\xac\xb9\x8b\x4f\x77\x66\xd7\xee\x8c\x18\x3a\xf8\xfa\x55\x16\xd4\xe0\xfa\x2e\x19\xd3\x18\x22\x05\xc8\x47\x9c\x46\x99\x71\x4c\x83\x4c\x9a\x70\x59\xad\x88\x64\x6b\x41\xfb\x81\xc0\x57\x43\x21\x2b\xf2\xed\x71\x66\x79\x7b\xaf\xc3\x7f\x95\x76\x95\x02\x67\x18\xfe\xba\x47\x36\x87\xc3\x21\x79\xc8\x2f\x67\x3c\x77\xad\x5e\xc7\x0f\xf0\x6e\x0f\xb0\x23\xf1\xc5\x38\x48\x4e\x05\xbd\xf0\x58\x3f\xe2\x5d\xdf\xea\xa8\x72\x67\xcc\x22\x11\x88\x28\x25\x2c\x2b\x9d\x0e\x73\x16\xd1\x5f\x2c\xf3\x99\x69\x31\x68\xbb\x11\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x8a\x2d\x34\x08\xc3\x1c\x87\xce\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x0f\xbf\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x10\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x0d\xbf\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x04\x31\x76\xc5\xd7\xf1\xf0\x44\x6d\x99\x70\x85\xcd\xfb\xa6\xa1\x45\x82\x59\x82\x27\x62\x89\xce\xbb\x79\x91\x5d\xd5\x9b\x2a\x89\x97\x81\xf6\x55\xc3\xb2\x6e\xb9\xab\xc9\x75\x84\x57\x2a\x39\x9f\x51\xe9\x77\x20\xe4\x62\x39\x9c\xbe\x40\xe3\xce\xf6\xf7\x10\xa1\x59\x10\x71\x05\x6a\x5d\xfb\x4e\x75\xb4\x9f\xa4\x59\x87\xe1\xe5\x33\xbd\xe4\x27\x45\xdf\x00\x4c\x27\x30\x1d\x3f\x50\x7f\x16\xe4\x87\xe7\xc9\x3b\x88\xe4\x55\x5c\x42\x88\x4c\x8b\x0b\x94\xa0\xe7\x33\xbd\x3c\x29\xb7\xed\x6c\xa7\x09\x39\x78\xb7\xdf\xee\x5a\x8b\x5f\xc8\x16\x15\x75\x3a\x66\x16\x7a\x99\xec\x63\x1f\x84\xc2\xcd\x39\x41\xc7\x8d\x28\x27\x79\x11\xf1\x28\x2b\x51\x88\x88\x1a\x9b\x85\x96\x22\xdc\x6f\xc7\xd9\x29\x3f\x2d\x49\x39\x80\xed\x1e\x19\x15\xfd\xe8\x71\x2a\x30\x7b\x35\x4d\x13\x2a\x34\x4f\x9d\xf5\x4f\xb6\xd8\x7f\x9e\x45\x05\xf8\x4b\xb1\xb8\x11\x02\xb1\x8e\x50\x9f\xdc\x33\x94\x74\x31\xb8\x5e\x56\xbb\x50\x20\x79\x87\x5e\xf5\x82\x60\x0d\xd3\x8f\x55\x2f\xfd\x80\x9e\xae\x10\x63\x93\xdd\x31\x38\xf7\x0a\x28\x92\x68\xaa\xc7\x12\xf1\x1c\xa1\x6a\xcf\x9a\xb2\x97\x21\x7a\xf6\xeb\x1b\x55\x85\xc5\xf3\xcd\xc4\x06\x45\xd5\x58\x6a\x30\x87\x52\xbb\x8f\x12\xeb\xcf\xb7\x4f\x5a\x66\x77\x42\x9b\x68\x9d\x51\x1c\x77\x3c\xff\x4a\x97\x60\x65\xad\x5f\x9b\xb5\xda\x1b\x36\xbb\xdd\x68\xb7\x48\x8e\x0d\xb3\xfb\xd8\x4e\x5b\xf3\x41\x78\xb1\x95\x16\x24\x5f\x2e\x16\x69\x56\x80\x6e\x8d\xdf\xd4\xbe\xdb\x27\x4a\xab\xd2\x36\x1c\x41\x96\x13\x66\xe3\x97\x0a\x37\x59\x8c\xf5\x54\xb6\x12\x85\x79\x8f\xf5\x40\x53\x95\x16\xf4\xc8\xa1\xae\xbd\x9b\x96\x7a\xbb\x71\xf5\xb8\x1a\x83\x8e\x93\xf6\x92\x57\xda\xd7\x27\xbd\xed\x6f\xee\x55\xba\xf7\x2a\xdd\xff\x0a\x95\xae\x78\x58\x71\xab\xe7\xd8\x7b\x41\x96\x26\xe4\x7f\x97\xf3\xe0\x2c\xca\xc9\xf7\x01\xfb\xfc\xdb\x67\xfe\xd9\x9f\x53\xaf\xba\x77\x30\x20\x07\x49\x54\x44\x41\x1c\xfd\x4a\xc9\xdf\x79\x2f\x18\xa1\x06\x24\x07\x4b\x2c\x69\x70\x03\x03\x65\x4b\xd5\x70\x72\xde\x07\xad\xae\x2c\x26\xa3\x97\x88\xc8\x5a\x07\xe1\x88\x0c\xeb\x6e\xde\xb8\xb5\x07\x1b\xbe\xed\x56\xd7\x6b\x66\xe2\x75\xa7\xab\x5f\xa1\xc9\x20\x5e\x13\x89\x50\x68\x49\x1b\xf4\x78\x9c\xf0\xf2\xd7\x29\x3d\xa4\xea\x99\xc8\x6a\x64\x96\xf4\xbd\xeb\x75\x43\x84\x46\xc0\xda\x73\x7a\x3f\x58\x13\xe8\x29\x71\xc5\xcb\xdb\xea\x89\xc6\x0c\xa7\xa9\x3c\xab\x5b\xa6\x5a\x96\x4d\x3a\xc6\x3c\xca\x6c\x77\xbd\x8d\xc2\x69\x05\xe1\x19\x3b\xa3\xca\xd9\x21\x07\x2f\x20\x47\xf6\x4e\x4d\xda\xc6\x46\x99\x9f\x21\xff\xeb\x1f\xfe\x56\xc8\xa9\x46\x67\xcb\xe7\x41\x62\xa4\x2a\x5d\xbe\x0b\xe2\xff\xb3\x03\x93\x7c\x21\xd4\xdc\xf0\x42\xe2\x40\x1d\x1e\xa5\x01\x91\xdf\x54\x47\x29\xeb\xea\x42\xba\x79\x5e\x66\x5b\x0d\xf8\xcd\x33\x24\x1a\xac\xf6\xac\x98\xdb\x3c\xd1\xba\x0c\xe5\x3e\x7d\x90\xce\x59\x00\x3d\x53\x6d\xf7\xe9\x19\xcd\x2e\x3b\xd2\x1b\xf2\x87\x28\x99\xc6\xf4\x0d\x47\x78\x97\x8c\x88\x37\x43\xd7\x24\xa6\x55\x75\xc4\x0f\x2e\x26\x50\x1d\xb4\x94\xf0\x2e\xe9\x06\x59\x10\xc9\x34\x4e\x91\x86\x6d\x91\xc8\x90\xf3\xb3\xbb\xbb\xcb\xa9\x06\x03\x09\xb7\x0b\x12\x96\x9d\xb9\x19\x18\xbf\xd6\x6d\xfb\xaa\x13\x32\xac\xe5\x53\x72\x30\xe0\x31\x07\x55\x92\xf0\xca\x8e\x99\x8b\x5c\x8f\x8d\xfc\xc9\x73\x46\x74\x0a\xef\xd1\x6a\xd8\xd1\x73\x06\x54\xee\xe2\x5b\x74\xdc\xe2\x2f\xbc\xae\x9c\x33\x55\x51\x95\x14\x70\xc2\x2e\x28\x8f\xc4\xa2\xe8\x48\xde\xd3\x25\x93\x88\xc6\xa1\x65\x7a\x20\x5a\x31\x7a\x6a\xf1\x1c\xdc\x41\x8b\xf1\xf0\xae\x59\x64\x28\x93\xad\xa8\x0f\x92\x2c\x5c\x47\x58\x0e\x7b\x93\xb0\x7d\xc9\xda\xe4\xb7\x60\x71\xa6\x1e\xde\x91\x15\x45\x7d\x42\x4e\x64\x62\xe0\x93\x7b\x31\xf0\x5e\x0c\xfc\x6b\x8b\x81\xfa\x7d\x1e\x5f\x34\x77\xf5\x42\xef\x6e\xee\xee\x19\xc8\x1b\xa9\x6e\x2c\x35\x56\x86\x73\xa2\x88\xd4\x22\xad\x90\xd9\x27\x3a\x45\x0a\x97\x6b\x32\x97\x7d\x1a\x17\xf7\xc0\xf3\x74\xbe\x96\x0c\x86\x08\x0c\x7c\xf2\xe3\x60\x88\xda\x10\x1a\x67\xa0\x12\xdc\xd3\xb3\xaf\x88\x95\x63\x28\x5d\x41\x63\xf0\x26\x48\x82\x29\xd5\xaf\xf3\x19\xcb\xe2\xa8\x30\x54\x01\xd2\x85\x87\x06\x47\xfb\xfd\xdc\xc0\x90\x53\x71\x36\xaf\xb1\x7f\x0f\x29\xe3\x30\x51\x62\xfa\xf7\xb4\xc4\xbf\xd3\x20\xe7\x3e\x17\xca\x22\x51\x4c\x29\x78\xa9\xf4\x6c\x52\xa6\xa7\x79\xdb\xb1\xa8\x6c\xd3\x6c\x0f\x48\xcc\x41\x84\x68\xa3\x34\xd6\x84\xe1\x4e\x14\x85\xcf\x51\xc4\xa1\xec\xf8\xa4\x2f\xc3\x9c\x09\x36\x2a\xa5\xce\xcd\x31\x77\xc6\xa9\x2f\x29\x44\x68\x0e\xb1\xed\xaa\x71\xf6\xc9\x1b\xc6\xca\x23\x9a\x8b\xe8\xd8\x80\x0f\xc7\x0b\xa5\xe1\xd9\xb3\x31\xde\xe4\xa0\xae\xde\x2e\xe3\x58\x3b\xc6\xe8\x31\x29\x92\x5e\x44\x70\x6d\xe6\xc3\xdd\x1f\x33\xfe\xd0\x9d\x85\xdd\x21\x6b\x5f\x2b\xee\x8e\x83\xc9\x46\xd1\x76\xec\x00\x27\x2a\x94\x8c\x79\x10\x23\x35\xe1\x63\xde\xbf\xdb\x17\x11\x26\xaa\x63\xc7\x68\xb4\x09\x57\xaf\x9c\xf0\x00\xe9\xea\xc4\x69\xa3\x89\x83\x1e\x30\x48\x17\x4b\x06\xd1\xa9\x24\x0f\x3a\x50\x2d\x95\xd8\x58\xf7\x70\xd7\x12\x0a\xf2\x3d\x6e\xf4\x94\xb6\x64\x48\xe5\x74\xb1\x47\x20\xfa\x77\x55\x08\x29\xf2\x4c\xff\xe6\xd4\x0d\x45\x4e\x18\x3b\x40\x9f\x35\x9e\xf5\x1d\xac\x73\x7e\xaf\xa2\xe6\x62\xcc\xbb\x88\xe7\x0e\x78\xab\xcf\x8a\xa6\x3b\xe2\x12\xdc\x7b\x62\xa4\x98\x41\x7a\x31\x0a\xed\xcd\x0a\x9c\xcd\xc0\xb1\xe7\x99\x17\x40\x55\xe5\x8d\x4d\x22\x70\xe1\x0b\x59\x24\xdf\x4f\x49\x3a\x5c\x21\x72\x51\x20\xd7\x6d\x23\x24\x34\x8b\x41\x84\xdd\xb1\x8a\x7d\xc4\xf6\x92\xbc\xb2\xf3\x65\x21\x4f\x00\x30\x5a\x06\x18\x10\xf2\x8c\x00\x43\xea\x98\xe2\xd7\x82\x48\x75\x06\x68\x96\x4a\x94\x19\x55\x6e\x95\xb1\x8a\xc3\x41\x95\x74\x91\xcb\xf1\x69\x4a\x5b\xa7\xbf\x60\x74\xb1\x0c\x39\xb4\xd3\x65\x14\x87\x80\x30\x31\x28\x96\xe9\xf8\xb7\x05\x86\xff\xf1\xf0\xc5\xe1\xfa\xfa\x3a\x88\xf7\xed\x9c\x2c\xa7\xf1\x65\x5f\x44\x11\x63\x07\x82\x65\xce\xf6\xc4\x42\xb5\x92\x20\x97\xb2\xec\xb7\xb4\xab\x51\x37\x24\x8c\x71\x40\x86\x7a\x6f\xbd\x69\x44\x7a\x3a\xfd\xe5\x98\x65\x1f\x0f\x4f\x4e\x98\xd8\x85\x3f\xaf\xae\x94\xdd\xa6\x0d\xca\x7f\x6c\x42\x19\x36\x96\x1d\xff\x55\x91\x55\x3b\x40\x12\xc4\x85\x1d\xf4\x2a\x44\x95\xdd\xa2\xaa\x4b\x75\x6d\x74\xca\x43\xa0\x24\xfe\x67\x59\xc4\xf1\xf3\x2d\xe4\x77\x7d\x1a\x5e\xc5\x0f\x34\xb1\x22\x58\xf8\x42\x15\x18\x67\x75\x68\xcb\x94\x28\xf5\xc5\x94\xbe\x9f\x31\x62\xb1\x28\xf3\x3a\x8f\x69\x9e\xdd\x30\x87\x17\xed\x60\x66\xa6\x8c\x22\x2d\x03\x1a\x6f\x38\x15\xb3\xbb\x46\x35\xe5\x43\xb0\xaf\xa1\x04\xa9\xb0\xac\xa6\x9e\x9e\x65\x98\x2b\x9a\xd4\xbb\x73\x94\x1c\x72\x99\x51\xb8\x21\x7d\xff\x6e\x5f\x79\x60\xe2\xa6\x2c\xe3\x20\x51\xc2\x66\x94\x08\xa5\x8b\xdf\xd7\x53\xe6\xfa\x7a\xec\xf7\xfb\xd7\x38\xbe\x9b\xed\x4b\x4f\x6b\x32\x65\x51\x0f\x27\xad\xf3\x69\x5f\xea\x6e\x7e\x15\x22\x94\x34\x60\xfa\xa4\xc7\xb3\x56\x86\x68\x51\xb2\x44\xb1\xf3\x46\xda\xc0\x34\xbd\xfe\xfb\xf6\x5e\xef\x73\xaf\xf7\xf9\x6b\xeb\x7d\x84\xd2\x27\x3c\xbd\xc5\xcd\x9f\x4f\xef\xa3\xb4\x35\x58\xf1\xc3\x99\x93\xd2\xe8\xbc\x78\x6e\xf0\x11\x36\x0c\xd3\xe5\x87\xa3\xa9\x80\x91\x5a\xc9\x3b\x15\x81\xc2\xd6\xb4\xbc\x94\x77\x3c\x36\xfd\xe2\x82\x8b\x7c\x21\x96\x74\x65\xc9\x41\x1d\x56\x33\xda\x59\x04\x90\xa3\x76\xe9\xf8\x3a\x68\xe9\x9b\xf5\x2e\x5f\x1e\xb0\x68\xb1\x2c\xd4\xe3\xb5\x84\x9e\x0b\x6c\x76\xf4\x76\xc9\x84\x8e\x11\x69\x2b\x38\x2b\x8e\xc6\x88\xb4\xc3\xd3\x4f\xbe\x5c\x29\x26\x6e\xab\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xe5\x36\xba\x58\x16\xaf\xe9\x45\xfd\x30\x5f\xd3\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x1d\x6b\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4a\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xda\x86\x2d\xea\xe9\xfd\x16\x75\xbf\x45\xfd\xb5\xb7\x28\x7d\x35\x41\x8b\xd9\x8d\xee\x25\x04\xf0\xdd\xbe\x4a\x2c\x89\xfe\xef\x0b\xff\xef\xbb\x04\xf1\xdf\x83\xd4\x6c\x9b\x0c\x44\x9a\x23\x5b\x40\x0b\x91\x2c\xc1\xc6\x65\xed\x8d\xd3\x64\x12\x4d\x25\x18\x0a\x85\x83\xa1\x65\x64\x15\x09\x76\x2e\x9e\xad\x19\x17\x34\x22\x51\xc2\xbc\xe2\xa1\xc0\x2d\x64\x40\xa2\x04\x39\xc8\x3f\x5c\x26\x63\xbe\xc5\x60\xa8\x9c\xa7\x4a\x30\xc6\x8a\x33\x6a\x03\x89\x54\x55\x17\x77\x50\x84\x21\xa2\xd3\x20\x91\xd9\xdc\xeb\xa1\xd3\x1f\x99\xac\x84\x10\xf0\x99\xd6\xe4\xce\x40\xe9\xbc\xc5\x1b\x41\x50\x02\x0e\x4f\xba\xe4\xc1\x03\x22\x7e\xf7\x41\x27\x78\x38\xe9\xb4\x87\x17\x6d\xee\xba\x64\xd8\x25\xcf\x48\x8b\x16\x33\xb6\x7b\x40\x60\xd2\xe7\x97\xaf\x83\x7c\xd6\x22\x23\x3b\x99\x6b\x74\x5b\x5a\x4a\x80\xae\x7d\x88\xa6\x09\xcd\xf2\x8a\x1e\xde\x71\xff\x44\x83\x25\xdd\x54\xb9\x4e\x6f\xf3\x22\xf8\x4c\xb3\xf7\x87\x07\xf5\x5d\xbd\x59\x4f\x51\x47\x3f\xc8\xb6\xde\x04\x79\x41\xb3\x24\x0d\x29\xee\xa9\xca\xb6\x91\xf9\x2a\x4a\x82\x38\x2a\x2e\x7f\x3b\x6c\xca\x16\x4b\xd0\xa9\xb3\x1d\x7c\xa2\xe8\x5f\xaf\xb2\x74\xfe\xfc\x37\xa0\xd3\xb6\xe8\x1a\x0a\x2a\xf5\xfc\x12\x1a\x66\x9d\xdf\x4b\xc2\x03\x56\x4e\xc5\x72\xf3\x42\xf2\x71\x28\x58\x3d\x9e\x65\x32\x8e\xe9\x6f\x34\x80\x23\xd6\x56\x4d\xd7\x31\x4c\x69\xa7\xe5\x3c\xa1\x71\xee\xa7\xcb\xa4\xd1\x25\xe3\x1d\x8c\xc3\xdb\x36\x27\x25\x3c\x94\x12\x30\x3e\x2a\x67\x0a\x7e\xc3\xfe\x1f\xa9\x06\xd1\x64\x38\x93\x80\x01\x8c\x3e\xab\xee\xbd\x2c\x66\x77\x7d\x3c\x6c\x7c\x34\xbc\xa3\x93\x21\x84\x7f\x2e\x3f\x19\x72\xc5\x17\xdf\xc3\x23\xea\xed\xd1\x02\x77\x66\x51\xd3\x8f\xc5\x0d\xba\x80\x2c\x1c\xf8\xde\xca\xbd\x9f\x10\xec\x9f\xfd\xe0\xf9\xde\x5b\x2b\x14\x9d\xd8\x51\xb9\x4e\x8e\x3f\x9f\x16\x9a\xb9\xeb\xb5\x35\xde\xbb\x3e\xb7\x8b\x53\x2f\xa9\x5e\x16\x33\xad\x0b\xec\x91\x36\x0e\xdc\xdd\xee\x89\x61\x4e\x69\x31\x2a\xd1\x78\x4b\x4f\xb5\x7d\x5c\x50\x8c\xa4\x27\xb4\xb4\x46\xe1\xb3\x20\x36\x62\xcc\xf5\xad\xb0\xe9\x67\x41\xec\xb8\xa2\x51\x69\xd7\x6b\x80\x9e\x95\x86\x22\xbc\x3c\xde\x64\x30\xa2\xe8\x4d\x86\x23\x8a\x36\x1c\x50\x13\x4d\x04\xe3\x2e\x41\x0c\x76\xbb\xb5\xe7\x66\x01\xe8\x9e\x9d\x25\x9b\x72\xf2\xd5\x01\x1a\xd9\xf2\x1a\x17\xb8\x23\x72\xac\xc5\x69\x7e\xb9\x2b\x9c\xa8\xbe\xd2\x77\xb9\x36\x04\x8e\x7b\xcf\xf9\x89\x02\x46\x81\x43\xad\x5b\xcc\x11\xae\x86\xe7\x29\x8f\x45\x0a\xa8\x44\x69\x92\x66\xc1\x94\xee\x15\x4d\xf4\x26\x02\xb4\x14\x47\x3e\x08\xa5\xd2\xa8\xc0\x12\x5f\x77\x9c\x63\x17\x29\xe8\x15\x56\x41\x8b\x77\x60\xc2\xb5\x67\xcd\x98\x18\x54\xe9\x70\xac\xcc\xdf\x7e\xbe\xbd\x03\x13\xcb\xe4\x20\x99\xa4\xf5\xe3\x43\xc0\xa5\xc3\xf4\xc3\xfc\x41\x46\x2b\x79\x5c\xdd\xea\xe5\xcc\xd7\x1a\xa1\x3a\x1e\xdd\x6e\x58\xbe\xde\xf6\x1c\x86\xa6\x6d\xbd\x19\xab\x22\xd7\xab\xad\x56\x90\xa7\x2b\x57\x2a\x3e\xc1\xf8\x11\x62\xa1\x43\xc0\x2a\xac\x20\x9c\xa0\x73\x99\x1d\x67\x64\x53\x26\xdc\x08\x2d\x6a\xd0\x0d\x87\x2c\x3a\x52\xc7\xa3\xc4\x89\xa8\x09\x8f\x12\xa0\x0e\x2d\x18\x27\x3c\x97\x1e\x36\xab\xe8\x41\x0a\x31\xd6\xce\x45\x8c\xdd\x09\xdb\xdc\x94\xac\x07\x5e\xc1\xc8\x74\x67\xd0\x94\x50\xf0\x15\xe2\x7b\x1a\xc4\xe5\x44\x22\xcf\x65\x8d\xa8\x44\x02\xfb\xc8\x04\x9f\x38\x7f\x07\x3a\xc1\x23\x3e\x48\x0a\xef\x80\x41\x06\xaf\xa7\x0b\x00\x73\x68\x42\x9d\xea\xbe\x06\x7f\x40\xdb\xd9\x2d\x58\x41\x6f\xad\x64\x77\x9b\x2f\xa2\xb8\x94\x13\x98\x5b\x9c\x00\xad\xd8\xe7\x5c\x08\x89\x87\x61\x39\x99\xd9\x67\xb6\x86\x5c\xda\x2e\xe6\x74\xab\xea\xd8\xba\xe2\xc2\x5d\x85\x12\x3d\x73\x23\xa7\xf0\x05\x1d\x47\xf3\xaa\x15\xa7\x4f\x82\x0d\x91\xa0\x0b\x94\x10\xe5\x1f\x77\xc0\xe6\x01\xaa\x66\xb0\xe5\xd1\xf2\x4b\xd4\x30\x70\xc6\xae\x1c\x74\xfd\x0a\x42\x15\x56\x6f\x2c\x1f\x3d\x5a\xaa\x95\xc6\xa4\x4a\x39\x83\x2b\x53\x80\xfd\x91\x38\xcd\x4d\xf0\xf4\x9e\x8e\x69\xb4\x68\x40\xe6\x6e\x99\x26\x04\xe0\x82\xde\x96\x02\x44\x8d\x8d\x07\xd8\x70\x15\xd7\x72\x31\xcf\xe0\x6c\xc0\x26\x14\xc0\x8f\x46\x77\x74\x48\x2c\x5b\xde\x44\x5a\x1b\x48\x6b\xbd\xf7\xc1\x79\xf3\x65\xee\x16\xf0\x23\xa3\x12\xae\x09\x77\x63\xb8\xf0\x9c\x12\x58\xbd\xab\xf5\xb6\x51\x57\x6f\xde\x4f\x7b\xb6\x7c\xeb\xcc\x37\x8e\x68\x9a\xac\x30\x0e\x13\xba\x64\x1c\xa5\x40\x5f\x79\x1c\x0d\x3a\x5f\xde\xe3\x3b\x3f\x85\x96\x10\x8e\x30\xf1\xad\xea\x28\x03\xf1\x77\xd4\xca\xb9\x49\x47\xd9\x7e\x70\x67\x67\x65\x9a\x17\xd1\x3c\x28\xe8\x8f\x41\x9d\x4c\x88\x20\xfd\x43\xf3\x03\xdc\x84\x62\x8c\x11\xde\x4a\xf0\x18\x73\x19\xf5\x43\x1a\x47\x61\xe9\xd1\x46\x4f\x1b\x87\xee\xe7\x02\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\x4f\x3f\xfd\xd4\xb0\x0f\x71\x29\x05\xa9\x9a\x56\x6a\xf9\x03\xcd\x16\xb4\x76\x8b\x52\x18\xe0\xd0\xd5\x08\x70\x60\x2a\x7a\x91\x2f\x4f\xe7\x51\xf1\x73\x9a\xd5\x49\x4a\x1a\xb0\x64\xa5\xfb\xf2\xab\x0d\xa0\x1a\xb4\x2a\xa0\x4a\xb7\xe3\x92\xf6\xfc\xc7\x9c\xfd\x20\x09\xf9\x23\xff\x22\x28\x96\xb5\x5a\x97\xff\x8f\xbd\x77\x5d\x6f\xe3\x56\x12\x45\x7f\xdb\x4f\x81\x78\x9f\x15\x91\x31\x4d\xf1\x2e\x99\xb6\x32\x23\x53\x92\xa5\xb1\x65\x69\x4b\x72\x92\xb5\xf5\x29\xfe\x9a\x24\x28\xb6\x4d\x76\x73\xba\x9b\xba\x24\xd6\x7e\x9f\xf3\x1c\xe7\xc5\xce\x87\xc2\xfd\xd6\x6c\xea\xe2\x38\x19\x69\xcd\xc4\xec\x6e\xa0\x50\x00\x0a\x85\x42\xa1\x2e\x46\x71\xe3\x44\x2d\x4e\x5b\x9e\x52\x16\x07\xb9\x07\x75\xdb\xf6\x2c\x1e\x8c\xbd\xbc\xc3\xd5\xd3\x22\x07\x4a\x51\xd6\x7f\xa2\x74\x15\xb9\x0d\x03\xf1\x77\xc0\x4a\x85\x2b\x2d\xd6\xa4\xb6\xbe\xa2\xbe\x13\xda\x69\xed\x6d\x2f\x1e\xea\xc5\x14\x75\xa8\xf6\x1e\xf8\xb0\xfd\x86\x69\xb0\x8c\x96\x98\xae\xc9\x2e\xce\x55\x2a\x3a\x0e\x62\xb8\xdc\xaf\x29\xa5\x68\xdf\xe0\x00\x69\x74\x84\x9d\xe2\xed\x46\x4d\x19\xd4\x2e\x21\xcf\xa3\xda\x37\xa5\xa2\xef\xbd\x38\xdd\xf8\x0a\x30\x01\xdc\xf7\xd9\x68\x54\xf7\x0b\x52\x76\x22\xf9\xd2\x96\x23\x95\x6f\xba\xc0\xa3\x57\xf2\xd6\x50\x9a\xd7\xb7\x04\xeb\xc3\xfb\xf7\xef\xed\xc2\x94\x7d\x2a\x20\x05\x67\xd3\x3a\x4d\x5e\xc0\x33\x33\x94\xa4\xa9\x5d\xc5\xad\x69\x5e\xaa\x07\x49\xdb\x64\x6d\x8a\xeb\x3b\x5d\x15\x29\x38\x7f\x18\xf5\x83\x54\xd5\x76\x31\x04\x60\x89\x31\xc6\xcf\xca\x88\x22\x37\xe5\xca\x12\x6d\x4c\xc3\x48\x37\x92\xb5\x5a\x60\x25\x6e\x09\x7f\x1c\xa4\xe3\x24\xc8\x72\xfb\xe0\x29\x53\x48\xb4\x58\x1e\x23\x6e\xe4\x95\x83\x90\xbb\xc8\xe2\xc3\x2a\xb3\x2a\xd3\x4f\xa8\xcb\x63\x78\x1e\xa4\x87\x49\x38\xc8\x1d\x33\x4f\x99\x5b\xdf\x26\x2e\x8f\x25\xcb\x5e\x98\xe6\x61\x29\xca\xdc\xb2\x8d\xbe\x62\x8b\x91\xd3\x8c\xbf\xd8\x03\xd1\x10\x4f\xed\xf4\x0b\x35\xd9\xcd\xc3\xcd\x2c\xaa\xb4\xa8\xb2\x10\xed\xfe\xbe\x3a\x90\xe6\x90\x8a\x6d\x4c\x3f\xd4\x1c\x1f\x83\x41\x16\x27\x5c\x7e\xe6\x06\x94\xe0\x8d\x54\x41\xa4\xac\xb6\xa7\xb2\xd2\xae\xc6\x46\xdc\x60\xd2\x8a\x68\x51\x51\xbc\xf6\x69\xa9\x5e\x82\xc1\xe0\x19\x7c\xd0\x7b\x86\x57\x9e\x92\xee\x90\x1a\x61\x4a\x38\x64\x28\x56\x2a\x4e\x83\x99\x0a\xb7\xea\xac\xe2\x6c\x5c\x2a\x57\x6c\x92\x7d\x1f\x9f\x2b\x92\x51\x31\x94\x5c\x1d\x95\xf6\x9c\xf9\x99\x78\xf8\xe8\x97\x58\x85\xea\xf9\x24\xee\x07\x93\x2a\x19\xd4\x6a\x60\xbf\x66\xa9\x53\x5d\x4d\x86\x83\x60\xf6\xe1\xb6\xcd\x92\xca\x56\xa3\xf4\x65\x5e\x93\x8a\x71\xab\x6c\xd0\xf4\xa0\x54\x53\x53\xf2\x0a\x25\xf7\xf4\x2c\x0a\x6a\xb9\x9d\x8d\xa5\x5b\x80\x61\xdf\xfb\xac\x5b\x5f\xaf\x3c\xb3\xec\x8c\x99\x9f\x9b\x34\xf0\x7d\xd6\x6d\xb4\xe1\x05\x9d\xd3\x67\xdd\xc6\x4b\xfa\x28\x68\xe1\x59\xb7\x49\xab\x84\xfd\x20\x7a\xd6\x6d\x36\x2b\xba\x17\x02\x3c\xb2\x41\x7a\xd6\x6d\xb5\xe0\x99\x5b\x23\x3f\xeb\xb6\x28\x78\xc6\xd9\x9f\x75\x5b\x14\x2d\x6e\x35\xf4\xac\xdb\x22\x0d\x72\x5b\xe2\x67\xdd\x56\xf3\xe6\xac\xd2\x7c\xf9\xe8\xd6\xf0\xe8\xd6\xf0\xcf\x76\x6b\xf0\xf9\x34\xdc\xd9\xf5\xae\xb8\xb7\x41\x01\x57\x02\x28\xf7\x01\x67\x0f\xe9\xa9\x07\x6f\x17\xdb\x3e\x4a\x1f\xbd\xdb\x18\x3f\x16\xf0\xcc\x5b\x5d\x5d\x95\xa1\xed\x5c\xe1\xf2\x58\xde\x67\xc2\xe2\x01\x1c\xce\xc6\x28\x98\x85\x0a\xee\x0f\x74\x20\x99\x84\x69\x86\xf3\xce\x0b\x11\xce\x3e\xc9\x42\xb7\x15\xae\x30\x4e\xcc\x0b\x16\xab\x15\x5f\xa1\x25\x04\x3e\x55\xfc\xb2\x36\xb5\x0f\x38\x73\x6c\x6a\xfa\xe6\xa5\xee\x2e\x37\x67\x95\x56\xed\x71\xb7\x78\xdc\x2d\xfe\xd9\xbb\xc5\x77\xea\x04\x77\x7f\xfe\x6a\x05\xdd\xe9\xa4\x4f\xc0\x21\x4e\xd2\x38\x0a\x26\x8f\x8e\x01\x0f\xed\x18\x70\x53\xcc\x54\x3c\xc2\x97\xd2\xfe\x3c\x4f\x01\x2e\x0b\xda\xda\xef\x19\x9b\xd5\x4f\xce\x42\x77\xb8\xe2\x0e\xa7\x64\x23\x38\x0a\x2e\xdf\xe1\x45\x57\x5f\x6a\xd1\x95\xca\xd3\x27\x4f\x4c\xdc\xac\x02\x39\x0e\xee\xc5\xaf\x72\xed\x76\xc4\x07\xc5\x02\xfc\xc9\x93\x82\x06\x0e\x85\xef\x70\xf1\xe0\x08\x0f\xe2\x0b\x1a\x63\x32\xef\xd2\x93\x97\x73\xe2\xaa\x7f\xcd\x19\x90\x79\x34\x89\x07\x5f\x8a\x51\x8a\x56\x36\x87\x58\x7c\xe5\x8a\x58\xce\x17\x1b\x37\xef\xe8\xdd\xb3\xe9\x84\x9c\xfb\x85\xf6\x13\xcb\xdc\x93\xbb\xec\x0e\xbc\x5d\x2a\x3e\x3f\xc5\x66\x27\x7f\x6e\x96\xb9\xcb\x32\xe7\xc6\x40\xde\x25\x59\xb3\x86\x95\x46\x94\xc5\x2b\xdf\x6a\x14\xa4\xdc\x9e\x70\xaa\xf6\xdd\x76\x78\x2f\x45\x14\x70\xaa\xbc\xfb\x70\xe7\x83\xcd\x05\x6a\x61\x39\x1d\x6a\x61\x8f\x58\x6e\xcb\xe5\x7c\xbb\x95\xc2\xb9\x43\x45\x64\x68\x85\x4c\x39\xbd\xfe\x28\xa7\x3f\xca\xe9\xff\x6c\x39\x9d\x09\xe9\xe9\xd8\xa3\xd5\x59\x20\x7e\xe3\x04\xcf\xa7\x04\xf4\xcf\x0b\x94\x40\x83\x38\xc1\xd5\x30\xd6\xe5\xf4\xb5\xc2\xf1\x97\x0a\xc6\x6b\x58\x14\xf6\x01\x0a\x1d\x8f\xc7\x0f\xae\x1d\xfa\x7e\xe4\x71\xc2\x1d\x8f\xc7\xda\xed\x06\xbe\x64\xb9\x2b\x76\xbe\xc5\x85\x4e\x3a\x5e\x7c\xa1\x93\x8e\xe1\x42\x87\x0a\x2e\xcb\xdc\xdb\xe4\xc9\xf9\xfe\xcd\xc9\x12\x0f\x94\xad\xe9\xc2\x79\x53\xc7\x44\x84\x74\x3c\xfe\xe4\x2e\xa0\x5b\x15\x21\x87\x2e\x2b\xaf\xd1\x50\x77\xc4\x33\x5a\x74\x7c\xbd\x5b\x73\x29\xce\xf6\x83\x2b\x46\x04\xc7\xe1\x1f\xe6\xe5\xb0\xd2\xf6\xa2\xa2\xba\xd9\xd8\x6d\x10\x09\xa3\xc3\xf8\xd7\x7c\x04\x5c\x45\xee\xd6\xf0\x34\x48\xbe\x9c\x24\xf3\x34\xc3\xc3\x43\x6c\x5d\x06\x2b\xcd\xe7\x17\xbc\x1b\x12\x11\x26\x32\xdd\x61\x10\xe6\xb4\xef\x2d\x73\x37\x0a\x08\x86\xc3\xc3\x24\xbc\x08\x32\x4c\x8f\x84\x9e\xd6\xf3\x8a\xdd\xad\xef\x34\x77\xe8\xc2\xee\xe7\x15\xbb\x1b\x02\xe3\x20\x5d\xd8\xba\xb7\xcc\xdd\x9a\x3e\xc7\x19\xdd\xd0\x73\xc7\x3e\xa7\xd4\xdd\x9b\x2f\x30\xf7\x79\xc5\xee\x4c\xf7\xc7\xd7\xd3\xdc\xc6\x7d\x45\xee\x4c\xf5\x8b\x1a\xf6\x15\xb9\xeb\x90\x13\x39\x2e\xc3\x14\xf4\x4e\x12\x4f\x0f\x83\x34\xbd\x8c\x93\x61\xde\xf8\x17\xac\x73\xe7\x75\xb0\x68\x4c\x7c\x45\xee\x4c\x86\x8b\x1a\xf6\x15\xb9\x0f\xd6\xb3\xa8\xed\x9c\x52\xee\xe6\xc5\xc3\xea\x2a\x4a\xe7\x7d\xb8\x79\xc3\x90\x9a\x6a\x1e\xc9\xe7\x69\x98\xa6\x61\x74\xfe\xb4\x30\xb6\xb3\x38\x35\xaf\xae\x14\x2c\x1d\x5f\x1d\x7a\x0a\x94\xaf\x77\x44\x8b\x6f\xb9\x8e\xc7\x63\x25\x0f\xa9\x61\x7b\xa1\x9d\xa2\x0d\xcb\x88\x56\xe3\xf1\x0c\xfd\x78\x86\xfe\x67\x9f\xa1\xe5\x5d\x57\xff\x8f\x3f\x8c\xbb\xae\xcd\x09\xbe\x42\x6f\x70\x82\xcf\xd3\x3f\x82\xf4\x8f\x10\xbd\x0e\x26\xf8\xea\x3f\x93\x6c\x94\x56\xc7\x73\xfd\x38\xdc\x61\x41\xd1\x8f\xf0\x08\x27\x38\x1a\xe0\x2e\x22\xed\xa7\xdd\xd5\xd5\xf3\x30\x1b\xcf\xfb\xd5\x41\x3c\x5d\xfd\x2d\x8c\x76\xc2\xe8\x20\x39\x5f\xfd\x6d\xeb\x30\x3e\xee\x8d\x83\x30\x5a\xed\x4f\xe2\xfe\x6a\x7a\x19\x24\xd3\xd5\x30\xca\x70\x12\x05\x93\x55\xd2\x25\x7c\x95\xf1\x7f\xab\xe7\xf1\xff\x7a\xdf\x6c\x3e\xf0\xd5\x98\xbc\xef\x3a\x26\xd8\xfc\xc3\x0f\xd7\xf0\xe3\x6f\x71\xd9\x45\x2d\x5f\x71\x76\x19\x27\x5f\x8e\x30\x44\xbc\xcf\x53\x94\x9b\xc5\x6d\x6d\x79\xff\x8f\x3f\x3e\xe5\x94\xba\x8b\x73\xe7\x75\x34\xd8\x8e\x82\xfe\x04\x2f\xc2\x52\x29\xe9\x46\xd0\x5d\xe0\x2e\xb8\x5d\x06\xb3\x82\xb8\xc9\x92\x1e\xdc\x9c\x05\xee\x80\xdb\x30\xbe\x8c\x58\x32\x83\x3c\xc4\x78\x31\x37\x56\x8e\xaf\xc5\x7d\x96\x3d\x88\xcd\x67\x05\xd0\xa2\x85\xdc\x48\x59\xdf\xee\x8c\x52\x82\xb3\x24\xc4\x17\x8b\xc2\x88\xf0\x62\x6e\xb4\x1c\x5f\xef\x42\x5a\x19\xd9\xed\x16\x10\x15\x29\xe3\x21\x27\xe3\xd3\x9d\x87\xe8\x1c\x17\xf0\x89\x77\xe3\xa2\x7f\xb8\xc3\x98\xd0\x24\x50\x0b\x42\xad\xbb\x71\xd0\x3f\xdc\x79\x34\x58\xde\xb7\x7c\x64\x68\x21\x37\x3e\xd6\x37\x8e\x52\xab\x10\x4a\x39\xb7\xba\x96\x8a\xd3\x64\xcb\xca\xed\x9f\xe4\x87\xca\x4b\xc9\x88\xe4\x4b\xce\x07\x94\x1b\xc7\x99\xfe\xcc\xa9\x5f\x01\x44\x48\x50\x3e\x9e\x63\xe5\x62\x72\x36\x57\x1e\x14\x59\xfc\x41\xaf\x19\xc7\xe1\x85\xd7\x37\x86\xcc\x09\x7c\xf7\x9e\x21\xf3\x61\x3b\x94\xb2\x1a\x6c\xf8\xee\x39\x5e\x39\xce\x57\x44\x58\x72\xc5\xcc\x77\xde\x4b\x36\x1f\xcf\x54\x8f\x67\xaa\x7f\xf6\x99\x8a\x1d\xa8\xf8\x05\xd1\xb7\x4d\xf6\x72\x1b\xc3\x6a\xee\x1d\x15\xcc\x42\x2e\x8c\xd3\x4c\xc1\xd9\x38\xcf\x02\x8d\x5e\x97\xe5\x86\x37\xe6\xa5\xb3\xeb\x19\x91\x0f\x58\x28\xe3\x57\x4f\x15\x06\x1e\x66\x83\x71\x89\x7c\x37\x63\xd5\x0d\x82\x14\xa3\x15\x42\xf1\x69\xb6\xd2\xd5\x3e\xc1\x64\x25\xe7\x69\x35\x1d\x87\xa3\xac\x64\xe4\x25\x43\x56\x8e\xe1\x9a\x5d\x80\xb1\x64\x70\x5f\x8b\xf0\x25\xf3\x76\x86\x0b\xd9\x57\x0e\x34\x66\x38\x1a\x86\xd1\xf9\x83\xe3\x71\x48\xdb\x51\x6d\x88\x5c\x48\xb1\x18\xb4\x36\x36\x06\x38\xab\x32\xcd\xd3\x76\xa3\x48\x07\xa2\xd4\x62\x4b\x42\x06\xcd\x94\x11\x34\x52\x70\xc8\x4e\x0e\xa9\x3a\x0a\xa3\x34\x0b\x26\x93\x42\x2d\x1b\xa5\xdd\x6e\xfc\xfe\x42\x39\x78\x9c\xe3\xec\x7d\x7c\x5e\x20\x88\x00\x29\xe5\x0d\x1f\x40\x5b\x34\x8a\xe4\xb4\x3a\x8b\x17\x06\x72\x21\x45\x16\xb4\xd7\x1b\x07\xd1\xb9\x3b\x62\xc1\x02\x19\x4b\xcc\x97\x6a\x92\xa5\x8d\x9e\x26\x08\x91\x8e\x29\x8d\xc4\x2c\x18\xe4\xd9\x2d\x1d\x39\xd2\xf1\xb8\x0a\xac\xd1\x62\x37\xe9\xd8\x66\x37\x7e\xf1\x69\xc1\x2d\x8d\x45\x06\xc8\xba\xa5\xd1\x2c\x09\xee\x55\x4d\xef\x27\x46\xe4\xd2\xd4\x3f\x1c\x22\x36\xe9\x22\xeb\x9a\x82\x36\xcb\x70\x30\x8b\xde\xad\x79\x83\x8c\xef\xa1\x6d\x95\xf4\x2c\x49\x94\xe2\x80\xb3\x71\x97\xfc\x87\x02\x4b\xc7\xe3\x2e\xf9\x4f\x85\x4a\xaf\xae\xc4\x4e\xad\xd6\xa3\x4c\xfa\x28\x93\xfe\xc3\x65\x52\xa9\xe8\xe7\x4e\xd6\xb7\x71\x6c\x71\x08\xa4\xd4\x41\xfc\x08\x9f\x93\x79\x0e\x92\xcd\x7e\xe8\xc9\x6f\x94\xae\xbe\xd5\x8b\x56\x3f\xa7\xb1\xc8\x21\x14\x0e\x82\x99\x0a\xc4\x07\x63\xaf\xb7\x79\x68\x43\x50\x30\x61\x9e\xe8\xcc\x7c\x19\x6d\xa0\x95\xda\xd5\xa0\x33\x7c\x39\x6c\x0c\x86\xad\xd6\xcb\x60\xad\xdd\x1a\xb4\x5e\xb6\x1a\x9d\x16\xae\xaf\xd7\x5e\x0e\xda\x35\xdc\x6c\x0d\x3b\xad\x76\xa7\xd1\x5f\x91\xb8\xb8\xc0\x04\xf5\xa0\x5e\xaf\xf7\x07\xb5\xb5\xd6\xe0\xe5\x60\x14\xac\xad\xd7\x47\xb5\x41\x73\x1d\x77\x9a\xfd\x61\xbb\x3e\x78\x59\xef\xaf\x07\xa3\x5a\x6d\xc5\xcf\x9c\x28\x8e\x5d\x45\xd4\x0d\xfa\x61\xd7\x31\x88\x92\x15\x32\x3f\xf8\xae\xb3\x7f\x74\xab\xa7\x85\x09\xda\x16\x64\x73\x5c\x1d\x70\xed\xee\x52\xa8\x1a\xc7\xcc\x9f\xc5\x67\xdd\x7a\xe5\xd9\x82\x79\x7a\xd6\x6d\x10\x66\xdb\x7e\x64\xb6\x8f\xcc\xf6\x9f\xcd\x6c\x25\xaf\xe5\xda\x2f\x83\xd9\xe6\x59\x26\x8f\x92\xf8\x0f\x3c\x0d\xa2\xea\x10\xff\x7c\x2f\x0c\xda\xe5\xa3\x6e\x38\xa8\x9b\x37\xa4\x96\x4d\xad\x76\x0d\xca\xf2\xc4\xb3\x4f\xf0\xa8\x64\xae\xa1\x9a\x44\xe5\x3b\x7d\xa1\xe5\xb6\x31\x4a\xa4\x66\x09\xc3\xc1\x59\x29\x6a\x7c\x51\xea\xe8\x0a\x68\xa5\x8a\xfe\x41\xa9\x61\xdd\xe6\x46\xf3\xc9\x84\x8a\x96\x7c\x2c\xd4\x2c\xda\xe6\xed\xa6\x36\x4e\xc9\x54\x1b\x22\x0b\x74\x32\x5d\x98\x94\x9c\xa5\xe0\x06\x74\x41\xab\x40\x68\x97\x0a\xaa\x46\xc6\x71\x5a\x72\x8f\x14\x54\xb3\x8e\x43\xde\xef\x1b\x2d\xe1\xb8\x78\xb5\xea\xea\x92\x02\xc7\xd4\xe0\xb8\x62\xb7\x18\x23\xfc\x1f\xae\xb7\xb4\x6e\x97\xe0\x5f\xb4\xc3\xb1\x96\x63\x7e\x41\xa7\x69\x78\x7d\xb5\xd7\x2c\xa7\xba\x2b\xcf\x7a\x7e\xbf\x29\x28\x7d\x16\xb5\x5c\xf9\x6a\xdf\x4d\x8a\xfc\xf1\x47\x96\x58\x1f\xfd\xb0\x41\x09\xc7\x78\x45\x36\x94\x51\x18\xe1\x21\x1f\x27\x03\x82\x68\xab\xcb\x6a\x79\x86\x0b\x32\xd0\x67\x31\xc2\x57\x34\x58\x12\xb7\x30\x47\xa3\x24\x9e\xca\xd3\xb6\x48\xec\x5e\x45\xfb\x64\x63\x0b\x71\xca\x28\x09\x86\xc9\x18\x4b\x06\x8c\x1b\xa4\xdb\x44\x24\xe1\x69\xe3\xba\xc3\x46\xea\xeb\x87\xf9\x64\x72\xa3\x58\xbb\x87\x23\x84\xaf\xc2\x14\x8a\x3b\x87\xdc\x68\xd1\xab\x30\x0c\x47\x32\x17\x1a\x6f\x8d\x66\x43\x03\x3d\xdb\x04\x47\xe7\xd9\x18\xbd\x40\xf5\xb3\xb2\x23\xaf\x13\x94\x99\xc5\xb3\x52\xf9\x15\x5a\x5d\xe5\x17\x5f\x84\xff\xc3\x7a\x82\xd1\xfa\x41\x15\x6e\xf4\xe1\xa6\x06\x0e\x12\xb3\x2c\x76\x93\xa2\x6e\x08\xe1\x23\x46\xf6\x8a\xf7\xc2\x4b\x8d\x3a\x34\x9d\xfb\xf6\x3f\x6b\xa9\xaa\x49\x1d\x21\x4a\x22\x9e\xea\x0a\xc8\xab\x3f\x0f\x27\xc3\xb7\x38\x2b\x29\xc7\x73\x1c\xcd\xa7\x38\x09\xfa\x13\xdc\x45\x59\x32\xc7\xb6\xee\x2f\x98\xc2\x8d\x95\x60\xeb\xd5\x74\x36\x09\xb3\xd2\x4a\x75\x45\x09\xb8\xc9\xf8\x3d\x14\x06\xed\x2d\x9f\x28\x78\xc3\xe7\xe4\x67\x54\x57\x67\x24\xee\x7f\x3e\xe5\x35\xce\x08\x37\xd6\x9e\xbf\x7e\x45\x7f\xde\xbc\x52\x0b\x9b\x45\x5e\x69\x2a\x31\xd1\x7c\xfd\x8c\xa7\xd5\x82\x7f\xdc\x79\xc2\xe2\xfe\xe7\x0a\x94\xaf\xd0\x21\x63\x7d\x21\xf0\x83\xf4\x3a\x1a\xbc\x85\xfd\x86\x88\xbc\xd0\x85\xf2\x19\x1f\x02\x18\xc4\x4d\x56\xa4\xa4\xf8\x69\x18\xd5\xb4\x49\x02\x10\x3a\xcb\x80\xeb\x65\xf4\x1c\x70\xa8\x0e\xc6\x41\xb2\x99\x95\x6a\xe5\x6a\x16\x7f\x9c\xcd\x70\xd2\x0b\x52\x5c\x2a\xf3\xcf\x29\x91\x1f\x4a\xf5\xb2\x77\xe3\xe1\x33\xeb\xcf\x60\x2e\x37\x6e\x99\x8e\x9d\x87\x44\xe3\x35\xce\x49\x87\xec\x15\x23\x04\x14\x95\x27\x96\xc4\x5b\x7d\x1f\x83\xac\x74\x86\xa6\x87\x2e\x89\xae\x04\x44\xb7\x7b\x45\x65\xc3\x0d\x7e\xf2\x3b\xc8\x47\x7d\xb9\x5e\xca\xcb\x7e\x7f\x14\x30\x24\xed\x9c\x9c\x1d\x82\x96\x97\xed\x95\x9a\x50\x09\x27\x49\x05\xe9\x5b\x07\xff\xe3\xb8\xd0\x32\xee\xc1\x66\x35\x95\xcb\x83\x1b\x39\x64\x6c\x91\x73\xbc\x39\xa1\xb2\x47\x9a\x07\x90\x65\x00\x54\x66\xf5\x1c\xfb\xb6\x13\xb9\xfb\x0e\x12\x4c\x64\xc5\xd9\x3c\xc1\xe8\xbf\x8e\x0f\x3e\x1c\x1d\xf6\x10\x6f\xe5\x72\x1c\x0e\xc6\x70\x78\xe2\x3b\x50\x18\xa1\x3e\x28\x6d\x59\x11\x83\x23\xca\xb7\x82\xef\x55\xab\xd5\x1b\xa6\xc2\x73\xed\xcd\x88\x1c\x09\x93\xd9\x40\xa9\xea\xe4\x8e\xb2\xe3\x1e\xb2\x08\xae\x99\x85\x8e\x69\x37\xd7\x55\xe5\x51\x5b\x4b\x7e\x7a\xa6\xeb\xd7\xc9\x34\xb1\x2a\xc6\x66\x55\x82\x3d\x51\x15\x05\xc9\x92\xad\x92\x4a\x25\xb1\x4f\x96\xcb\xea\x94\x31\xac\xd8\x44\xf3\x59\x53\xa7\xdd\x37\x75\xac\xa6\x47\xc3\xc9\x07\x48\x39\x98\x1b\x41\x7b\xc8\x11\xbb\xf3\x78\xc4\x7e\x3c\x62\xff\xb3\x8f\xd8\x8a\x3e\x93\x71\x88\x29\x63\xe9\xfa\x49\xfb\xbf\xf0\x68\x94\xe0\x6b\xf4\x6b\x38\x19\x7c\xc1\xe8\xf5\x67\x3c\x1a\xf9\xc2\xf5\x2c\x15\xdb\x67\x3f\x48\xc8\x11\xfe\x20\x88\x06\x38\x80\xb2\xae\xa8\x3e\xb7\x08\x04\xc4\xaa\xbc\x0d\x2e\xd0\xaf\x71\x3c\x44\xaf\xcf\xbd\x87\xfc\x96\x3c\xe4\xff\x17\xe3\xa6\x9a\xf7\x30\x63\xb1\x79\xa9\xf1\x1d\x91\xea\xcc\x6c\xf6\xae\x54\xf6\x38\x49\x62\x23\x7a\xd0\x2a\x7d\x47\x8d\x10\xe8\xb6\xb3\x97\xad\xa4\x64\x63\x9c\xc5\x51\x1a\xf6\x27\x94\xc0\x66\x01\x78\x91\xa0\x29\xbb\xf4\x21\x7b\xd1\x2c\x89\x2f\xc2\x21\x4e\x52\x51\x2b\x98\xa4\xb1\x5d\x35\x9e\x4c\x48\x55\x42\x6d\xdc\x81\x1b\x45\xf1\x90\x7e\x0d\xa3\x41\x3c\x55\x21\x13\x60\x2c\x2b\x05\xbd\x73\xcd\xc2\x29\x26\x8b\x2d\x4c\x51\x1d\xa5\x78\x10\x47\x43\xd8\x1d\xc3\xe8\x7c\x82\xb3\x38\x82\xe1\x24\xdd\xcb\x39\xe8\x73\x54\xb5\xe3\x3e\x7f\x89\x36\x44\x57\x14\x3d\x03\x69\x1b\x34\xc0\x37\xca\x4b\x8e\x8b\xaa\x75\xf0\x1e\xfe\x88\x84\x32\x4e\xe2\x28\x9e\xa7\x93\x6b\x88\x83\xe1\xd9\x87\xc9\x27\xc7\x79\x04\x0d\x83\x2c\xf0\x9e\x90\xf5\xde\x6a\x2a\x8f\x68\xa8\x75\x9e\x80\x51\x4f\x6a\x3f\x68\xbd\xd7\xd2\xe4\xc6\x51\x1a\x93\xad\x8b\x10\x45\x89\x92\x46\x75\x2f\xba\x08\x26\xe1\xf0\x90\x95\x2f\xa9\x32\x0f\x77\xc3\x86\xc1\x50\x24\x7c\x7d\x8f\x67\x64\x5e\xcd\xe2\x43\xfa\x0e\x50\xaa\xd2\xde\x57\xa0\x9b\xcc\xda\x42\x39\xbf\xb0\x53\xf9\x86\x3e\x57\x54\x98\x65\xa0\xf9\x5d\x39\x74\x8a\x37\x12\xa6\xbf\x10\x74\x8f\x28\x15\x62\x21\xa8\x29\xdd\xcc\xc6\x49\x7c\x89\xf4\xee\x99\xe5\xb5\xee\xb0\x6e\xd2\x4f\xd5\x42\x27\xff\x60\xa9\xd9\x07\x69\x36\x97\x04\xcc\x73\xa9\x90\x7e\x16\x13\x03\x00\xb7\x28\x42\x89\x9e\x5b\x88\x36\x78\x12\x66\x45\x36\xce\xa3\x8e\xfb\x21\x04\x7b\xee\xa9\xdc\xcf\x40\x16\x90\xe7\x49\xa7\x70\x92\x78\x52\x6a\xaa\xbd\x29\x9b\xf6\x36\x88\x67\xac\xba\x0d\x8d\x2d\x1e\x32\xab\xb6\xda\xbe\x25\xe4\xb2\xbc\xe1\x1a\x09\x9a\xd1\x39\xfd\xc7\x06\x17\x35\xe6\x9d\x0c\x48\x81\x37\xe4\xbb\x43\xc9\x44\xeb\xdd\x07\x61\x42\x0b\xdf\x19\x61\x02\x4e\x2a\x75\x72\x26\x73\x3b\x52\x4c\xef\x81\x16\x75\x1a\xe4\x7a\x36\x98\x8d\x12\x6f\xe5\x4e\xa4\x97\x2e\xa2\x3d\xad\x43\x82\xe8\xd0\x82\xed\x0f\x67\x62\x5f\x25\xd2\x26\x3f\x13\x32\x91\xcf\xa2\xb8\x8c\x4f\x95\x5b\x35\x97\x4b\x4b\xa2\xae\xbe\xeb\x7b\xb7\xfb\x45\x3b\x77\x46\x8e\x54\x4c\x70\x31\x11\x25\xdf\x0e\xc5\xa7\x85\x1c\x9b\x06\xff\xbf\x01\x68\x7b\xc3\x85\x4b\xc6\xf1\x55\xd8\x25\x71\x4c\xb2\x78\x18\xa3\xc1\x04\x07\xd1\x7c\x86\x22\x80\x4f\x06\x58\x1c\xdb\xf3\x86\x4a\xc1\xde\xb1\xf2\x28\x92\x6a\x44\x14\xd1\xb8\x3e\x96\x44\x38\x3a\xa5\xa5\xcf\x88\x90\x44\xaa\x77\x11\x05\x12\x0e\xbb\x16\xa0\xae\x0b\x64\x57\xfe\x04\xbd\x2e\x62\xbe\xcc\xfa\xe8\x6b\x0c\x80\x09\x60\xfa\x6e\xce\x10\x2a\x89\x15\xbe\x60\x72\xe3\x99\x10\x4a\x89\x08\xca\xec\x68\xe1\x74\x73\x1e\x92\x23\x5d\x68\xea\x8e\x49\x1d\xc7\x9c\x5b\x73\x9b\x3b\xf2\x02\x84\x4e\xa4\x50\x97\x77\x88\x9a\x96\x39\x06\xf9\x95\x32\x3c\x12\x7f\x36\x3a\x25\xa6\x51\xfd\x82\xaf\xd3\x92\xac\x5b\xe6\x5a\xde\x8d\x8d\x0d\x54\x43\x3f\xfe\x88\x7c\x63\x48\x88\x29\x39\xa1\xef\x4b\x5a\xa1\x57\xfa\x38\x9b\x02\x70\xce\x78\xcb\xdd\x27\xc1\x84\x17\x10\xf9\x9f\x0f\xfb\x14\x0f\xc6\x41\x14\xa6\x53\x7e\x0c\xcd\x67\x0e\x00\x20\x7f\x78\x69\x1b\xea\xc0\x7e\xc1\x78\x26\x12\x08\xf0\xce\xae\xfe\xf4\x39\x1d\x87\x11\x69\xe8\x6a\x10\x4f\x67\x13\x7c\x15\x66\xd7\xdd\x36\x1c\xc9\x48\x01\x42\x10\x25\xb2\x39\x7c\xc1\xd7\x54\x53\x20\x46\x53\x19\xaf\xd5\x55\x94\xe0\x69\x7c\x81\x51\x30\x99\x40\xaf\xd2\x0a\xc2\x57\x03\x3c\xcb\x40\xec\x67\xaf\xd4\xf2\xd9\x18\x5f\xa3\x08\xd3\x11\xe9\x63\x56\x7f\x48\x7a\x3c\x0f\x26\x93\x6b\xd4\xbf\x86\x21\x23\xc3\xc3\x72\x01\x00\xcd\xfc\x4a\x36\xa4\x30\x3a\x2f\x95\x95\x7d\xa0\xf4\x83\xd6\x3b\xf4\xf5\x2b\xc1\xb7\x1a\x46\x43\x7c\x75\x30\x2a\x81\x9f\x22\x21\xb6\x4f\x2b\x65\x98\xfc\x17\x75\x73\x83\x50\x28\xec\x0b\xbe\x3e\xab\x8a\x95\x68\xda\x43\xdb\x14\x49\xca\x5b\xb6\xc9\x7f\x63\xf2\x84\x53\x26\x99\xf7\x01\x35\xce\x45\x71\x54\x84\x27\x50\x9b\xda\x3c\x9a\x64\x26\xc3\xb6\x0a\xd4\x43\x85\xa8\x43\xc0\x39\x3a\x93\xe2\x4c\xeb\x3d\x01\xac\xa8\x22\x2b\x68\x50\xdd\x3e\xd9\xfd\x74\x78\xf0\xfe\xfd\xde\x87\xb7\x9f\x4e\xf6\xf6\xb7\x0f\x3e\x9e\xa8\xc7\xa3\x22\x33\x60\x0b\x55\x9a\xc4\xf4\x20\x47\x47\x5b\x26\x23\x78\x6d\x05\x59\x80\x36\xd0\xe9\xd9\x2b\xfd\xfd\x1e\xf8\x1b\xf3\xd7\xc5\x96\xaa\x00\x58\x9d\xcd\xd3\x71\xc9\xa4\x7b\x26\xe2\x69\xa5\xf7\x86\x29\x2d\xfc\x05\x5f\x97\xad\x31\x90\x00\x97\x18\xbc\x42\xe2\xa6\x80\xac\xa6\xcb\x5d\x5d\x45\xd3\x60\xa6\x31\xc9\x10\xc8\x16\x18\x0a\x90\x18\x21\x4d\x7d\x98\xf6\x83\x99\xa2\xba\x50\xf4\xda\xba\xab\x38\x15\x5c\x81\x6b\x94\xff\x34\xc7\x60\x3f\x98\x9d\x42\xb5\x10\xb6\x78\x3e\x32\xa7\x50\xfc\x4c\x71\x49\x17\x8d\x6b\x8e\xf3\x68\x69\x99\x39\xd6\xa5\x66\x2d\xbe\xc9\xc9\xc1\xd6\x41\x97\x13\x19\x9a\xc4\xe7\xff\x61\x4a\xd5\xb1\x47\xae\xbe\xab\x24\x5d\x40\x59\x90\x3a\x8f\x8e\xec\x5b\x75\x1a\xcc\x4a\x3e\x63\x05\xfe\x07\xf6\x8b\x43\x39\xca\x64\xec\xd9\x51\x2f\x1c\xaa\x9e\x37\x82\x22\xbe\x60\x94\xce\x13\xd0\x13\x73\x66\x15\xa6\x28\xcd\x42\x42\x0f\x94\x93\xe3\x21\x0a\x46\xe0\x21\x94\x24\xe1\x45\x30\x31\xf6\x5a\x0d\x26\x19\x10\xf0\xfb\xa7\x4b\x23\x1c\x9e\x99\x28\xca\x2e\x55\x07\xd2\x1e\x40\xaf\x23\xbe\x78\x3d\x66\xb8\xee\x44\xfd\x74\x83\xf0\x84\xe9\x99\x1d\x35\x46\xc1\x24\xc5\xea\x2d\x1b\xf3\x7b\x5a\x38\xa6\xac\xfe\x0f\x3f\xb0\x36\xd1\x2d\x60\x90\x79\x81\x19\x57\x16\xad\xe7\xf0\xff\xca\x1a\xcf\x1f\xa0\x66\x81\x71\x2c\xae\x18\x40\x1a\x85\x29\xbd\x84\x8a\xfa\x28\x19\x8b\xdd\x3f\x4c\x3a\x2e\x7e\x3d\x03\x52\x2f\x39\x7d\x29\x97\x8e\xcc\xa8\x1a\xfa\x8d\x97\x91\x7b\xc9\xce\x5d\xc1\x14\xd2\xcf\xba\x0d\x88\xed\xc3\x94\xe1\xcf\xba\x4d\xf0\x43\x5d\x2b\x72\x47\xc6\x82\x6e\xe2\x2c\x0b\xa3\x73\xb7\x6b\x2f\x30\xa6\xa1\x92\xfb\x18\x6d\x08\xa7\xb5\x57\x56\x09\x19\xea\x59\xd8\x07\xf9\xa2\x16\xb1\x46\x59\xbf\x09\xca\xeb\x8f\xd7\x7a\x8f\xd7\x7a\xff\xf0\x6b\x3d\x16\xd2\x97\x9d\x5a\x6e\x13\xd6\x77\x91\x39\xac\x27\xf9\x85\x91\xfb\x62\x19\xc3\x59\xbe\xa4\xeb\xec\x70\xb0\x39\x1c\xa6\x30\x74\x62\x77\x0b\x22\x50\x4b\xa5\x68\x4e\xc5\x2f\xe6\xf5\x56\x21\xc2\x57\x98\x41\xa8\x3c\x04\x59\x01\xe8\xa6\x4a\x77\xfb\xa7\x4f\xd5\xf3\x01\x3b\x9f\x3d\x35\x95\x44\x64\xdb\x7c\xca\xae\xad\x94\x72\x0a\xaf\xa2\x81\x7a\xb8\x2f\x1d\x29\x17\x47\xcc\xe3\x4a\xe3\x68\x4c\x6e\x22\x63\xef\x50\x35\xfa\x84\x22\xba\x6f\xf3\x9e\xa6\x8e\xcd\xc2\x65\x8f\xc3\xff\xf4\x7d\xcb\xdc\x9e\x7c\xba\x4b\x61\x21\xc8\x23\x11\x01\xca\x3f\xfe\x08\xb8\x53\xc5\x54\x18\x9d\x03\x37\x2e\x6b\x10\xf9\xf5\xc5\xa2\x9c\xa6\x14\xa2\xea\xa6\x7c\xdb\x4e\x0a\x69\x68\x12\xa4\xd0\xcc\x71\x46\x26\xfb\x87\x8d\x0d\x6b\xa0\xf9\x9f\xf5\x62\x75\x95\xe6\xfe\xd7\x48\x0a\x96\x5a\x96\xcc\x89\xcc\x96\xa4\x19\x4a\x63\x6a\xe7\x38\x9b\x01\xeb\x86\xb3\x73\x10\x5d\x67\xe4\xc0\x5f\x41\x7d\x3c\x22\x0c\x80\x2e\x71\x7e\x85\x0a\xa3\x41\x95\x8c\xc6\x5f\x38\x2a\xfd\xe0\xc0\xfa\xc7\x1f\x91\x6b\xe4\xcb\x56\x7d\x64\x5f\x37\x10\x54\x1d\xfe\xd1\xde\xce\xc6\x94\x6f\x46\xf8\x2a\x43\xbd\xc3\x8f\x68\x70\x3d\x98\xe0\x8a\xe8\x26\x0c\xbb\xd8\x6c\xa0\x27\xd0\x65\x66\xb3\x34\x4b\xe2\x01\xe1\x59\x29\x1d\x1d\xab\x15\xe5\x18\x2c\x96\x89\x6b\x2e\x1c\x1d\x61\xa4\x61\x96\xba\xa9\xa0\x5a\x91\xfe\x39\x86\x95\x92\x82\x4f\x34\x53\x8c\xc1\x9e\x0a\x00\xa6\x19\x9b\xa2\x8b\x2d\xd9\x76\x50\x9e\x7c\xbf\xa6\x25\xd4\x4d\x45\x0a\xe1\x7b\xc3\x8a\x64\x13\xec\xbd\xaa\x43\xa2\x3a\x03\xe0\x2c\x64\x9d\x70\x3b\xc9\x3d\x67\x5e\x4e\x6f\xb2\xcd\x7c\x93\x79\x43\xfe\x43\xaa\xae\x69\x8f\xc8\xd1\x8a\x72\xea\x39\xe5\xc2\xcf\x9f\x2b\xe5\xc4\x7a\x55\x4e\xfa\xf0\x21\x18\x0e\x85\x6d\x97\x92\xf8\x53\x7c\x37\xa7\x47\x39\x38\x28\x2c\x96\x1b\x6f\xc1\x7b\xc5\x56\x9c\x0a\x74\x62\x24\x54\x4b\x5f\xd9\x6e\xae\xc5\x62\x38\x92\xaf\x74\xad\x94\x64\x41\xa0\x55\x30\x90\x2f\x84\x84\x3a\x8b\x7e\x89\xd6\x22\x30\xa1\x72\x2e\x29\x73\x50\xce\x19\x6d\xa7\x54\x2b\x10\xf2\x1b\xb0\x11\x59\x5d\x4f\x77\x41\x64\xdf\xc7\x24\xa5\x8f\xb2\xef\x3f\x5d\xf6\x95\x26\x6d\x3c\x63\xef\x7d\xf9\xe8\xee\xf5\x83\x48\x97\x76\xc3\x7e\x20\x5c\x6f\xf1\x15\x55\x57\xe7\xb9\xee\x1e\x4f\x83\x24\xdb\x66\x05\xa5\xdb\xad\xf7\x6a\x0c\xd4\x4a\xd0\x2c\xef\x8b\xa1\xf3\x56\x5e\x8b\x4b\xb0\xe3\x2c\x09\xa3\xf3\x1b\x70\x6d\x71\xbd\x27\xd2\x72\x3f\x88\xd4\x4f\xbf\x04\x93\x39\xbe\x41\x17\xe4\x1f\x76\x1d\x42\x20\x8f\x70\x82\x17\xdc\x90\x56\x74\xf3\x02\x88\x52\xc3\x70\xd2\xc5\xe2\x6c\x5c\x01\x8c\x88\xb4\x5e\xa1\x2d\xd9\x5b\x18\xa8\xdd\xe8\x28\x43\xba\xe9\x7e\x10\x95\xb2\xb8\xcc\x54\x45\xa0\xc3\x21\x9f\xb9\xca\xa7\xe4\xb0\x22\x22\xf5\x20\x4f\x44\x69\x25\xa4\xea\x1b\x0a\x91\xf9\xe9\xae\xd8\xfa\x63\x06\x71\x2b\x4c\x88\x2c\xe6\x72\x88\xe1\x3d\x3a\x89\x99\x67\xaf\xda\x1d\xa8\xce\xa0\x97\xca\x76\xd7\x78\x7b\x42\x8e\x81\x6e\xb8\x24\x5d\x70\x91\x10\x9e\xd2\x38\x1b\xab\x39\xc1\x4b\x65\x68\x84\x61\x1b\xa5\x59\x98\xcd\xa9\xc0\x65\x9b\x7f\x0d\xf1\x2c\x4e\xc3\x4c\xc5\x92\xc1\x15\xe8\x01\x98\xc1\x24\xc4\x51\x66\x5a\x62\x14\x6e\xd8\x32\xb1\xe0\xb9\xc6\xed\x11\x5c\x16\x23\x7b\xfc\xb8\x0a\x3e\xf7\x2a\x59\x90\xde\x68\x1e\x0d\xc1\x26\x72\x80\x93\x2c\x08\xc5\xf4\x7b\x96\x8f\x98\xd8\xe5\xd6\xd1\x83\x2f\x21\x81\xd7\x2d\xd6\x12\x1b\x79\x32\x9b\x46\xca\x2f\x45\xb6\x15\xde\xeb\x59\x2c\x25\x5a\x02\xba\x4b\x1b\x50\x68\x73\x32\xc7\x5d\xfa\x0f\x17\x73\x8d\x6c\xef\xde\x59\x61\x93\x2f\x27\x05\x02\xdb\x87\x03\xc4\x39\x21\xe2\x1c\x12\x95\xa6\xf3\x34\x83\xad\x0e\x4f\x71\x94\x09\xba\xe9\x5f\x67\x38\x6d\x36\xca\x4c\x18\xff\xa1\x6c\x4c\x24\x2b\x77\xef\xd3\x97\x5a\xf3\xc7\xab\x53\x4a\x45\xf3\x28\xfc\xef\x39\x46\xe1\x10\x47\x59\x38\x0a\x75\x4e\x5c\x68\xae\xf9\xe8\x14\x98\x61\x68\xd2\xcd\x35\x03\xd8\x75\x94\x3d\xe8\x95\x49\x04\x7c\x8c\x4b\x41\x3f\x2c\x57\x83\x8c\x30\xd6\x2a\x1f\x5f\x0e\xfa\xcf\xbb\x12\x81\x25\xab\xf2\x51\x74\x06\x41\xb0\xf7\xc3\x67\xdd\x26\x11\x5d\x79\xe6\xfe\x9b\xb3\x4a\xbb\x50\xae\x64\xa6\xdd\x6d\x17\x4a\xd8\xf6\x4a\x55\xc2\xc7\x44\xbe\x18\x05\x83\x2c\x4e\xae\x2b\x54\xa1\x4c\x06\xf6\x09\x61\xd3\x44\xd4\x8f\x47\x48\xf4\x66\x63\x03\x3d\xa3\x11\x99\x9e\x41\x99\x27\xab\xab\xa8\x17\x4f\xa7\x71\xf4\x5f\xc7\x4f\x9f\x3c\xb1\x3a\x2f\x7f\xb1\x06\x38\x4e\xa5\x67\x64\x18\x12\xfc\xac\x5c\x41\xca\x2b\x1c\x0d\x5e\xf4\x83\x14\x77\x5a\xc6\x87\xe9\xb0\x6d\x16\xbd\x98\x7d\x19\x8e\x8c\x97\x83\x70\x36\xc6\xc9\x0b\x0a\xb9\xfc\xea\xe9\x93\x9b\xa7\x4f\xf0\x24\xc5\x48\xe9\x0c\x55\x98\xd3\xbe\xf0\x61\x78\x86\x7e\xfc\x91\x7d\xa8\x06\xd3\xa1\xe8\xdb\xe6\xfe\xd6\xd3\x27\x4f\xe8\x87\xd2\x29\xc7\xb9\x82\x74\x54\xe1\x99\x60\x48\x3f\x50\xc4\xe0\xb7\x8a\xcf\x99\x18\x65\x15\x31\xd6\x10\x8d\x86\x81\x4a\xfd\x24\xbe\x4c\x71\x52\x7e\xfa\xe4\x89\x18\xb1\x38\xce\xaa\xbd\xe4\x7a\x96\xc5\xff\x75\x4c\xab\xde\xc0\xe9\x49\xdd\x7e\xc4\x77\xf4\xe7\xd3\xa7\x4f\x4a\xfa\x71\xec\x09\xa2\x1a\x91\xe3\x71\x9c\x64\x83\x79\x96\xd2\x37\x64\xd9\xf4\xd0\x06\xe2\x75\x5f\x29\xaf\x3f\x4d\xc2\x3e\xf9\x54\x9d\x84\x7d\xe5\x3d\x28\xc3\x7a\xd0\x29\xf2\x95\x94\xaa\x2a\xef\x34\x08\xc1\xe4\x3c\x06\x10\xe4\xc7\xab\xa7\x02\x8b\xf7\x71\xfc\x65\x3e\x43\x59\xd0\x9f\x60\x05\x93\xe3\x37\x07\xbf\xb1\x33\x9f\x78\xb7\xf7\xe1\x97\x4f\xae\xf7\xc7\x1f\xdf\x7c\xda\xdf\xfb\xed\x53\xcd\xf7\xa1\xee\xfb\xd0\xf0\x7d\x68\x3a\xdb\xf6\xb5\xa3\x7e\xb4\xda\x52\x3f\x5a\xed\xa9\x1f\x79\x9b\x62\x68\x7a\xf1\x74\x46\x0e\x8a\x13\x7b\x88\x5c\x53\x6a\xd4\x1a\xc6\xf3\x3e\x91\xfa\x49\x2d\x59\x00\x58\xac\x8a\x05\x52\x2d\x15\x42\x08\x27\x88\x42\xf4\x1a\x35\xda\x9d\x57\x28\x7c\xfe\x5c\x03\x2f\x64\x44\xf4\x1a\xd5\x1b\xeb\xd6\x37\xf2\x37\x3c\x0d\xcf\xd0\x06\x81\xf1\x1a\xd5\x5f\xe9\xdf\xe9\x55\x6a\x4e\xad\x12\xad\x56\x46\xbf\xa3\xda\x55\xbd\xde\x37\xeb\xcb\xc7\x9b\xa7\x5a\xaf\x7f\x0d\x26\x5f\xd0\xdb\x9d\x52\xe3\xf7\xf5\xb2\xde\xdb\x2b\x1a\x22\x51\x7f\x17\x1a\x2f\x97\x1a\x01\x65\x90\xd3\x7e\x7c\xa5\x7f\x04\x43\x03\xd2\xe6\x55\x88\x7e\x47\xa5\x2b\xd9\x21\xf6\xbb\xa1\xfc\x6e\x2a\xbf\x5b\x65\xa3\xb3\x00\xa5\x94\x5e\xa1\x9f\x7f\xfe\x19\xad\x43\xc9\xf4\x0a\xfd\x88\x6a\x57\xa3\x11\x1d\xa0\x4e\xd3\xa8\x42\x56\xc7\xe9\x15\x19\xc8\xf4\xca\xf8\xc4\x17\xcf\x69\x0a\xdf\xaf\x5e\x3d\xf5\x76\x6a\x3a\x9f\x64\xe1\x6c\x12\x0e\x40\x4b\x60\x77\xef\x8a\x90\xf1\xf0\xf4\xea\xec\x95\xe3\x5b\x8b\x7e\x6b\x38\x3f\xae\xd3\x8f\xad\xb3\x9c\xd6\xd3\x79\x1f\x81\x7c\x53\x41\xd3\xf0\x0a\x0d\xe2\xc9\x7c\x1a\xa5\x1a\xf5\xab\x30\x89\xa4\x50\x1a\x42\xaf\x7e\x22\x34\x53\xab\xf3\x91\x62\x8f\xb5\x7a\xad\x66\x0e\xad\x58\xc9\x74\xb0\x4a\x19\x4c\x4c\xab\x8c\xbe\x92\xdf\x74\xbc\x3d\x55\xea\x6a\x95\x7a\x47\xa9\x52\xef\xf8\xea\x34\xd4\x3a\xeb\x65\x24\xeb\x34\xac\x59\x17\xdc\x80\xd6\xc9\x72\x46\x2a\x8c\x2e\xd4\xd1\x22\x8f\x85\x47\xec\x6a\x5d\x19\x1f\x46\x9e\x2d\xf6\xaa\xc6\x5f\x34\xb4\x21\xcd\x1d\x51\x8d\x3f\x32\x1a\x2b\x32\xac\x1a\xeb\xd4\xea\x2d\x18\x5b\x8d\xad\x6a\x15\x17\x0c\xb0\xc6\x72\x59\xc5\xbc\x51\x86\xcb\x02\xd0\x03\xe3\xc4\xe6\x84\x3f\x5c\x39\x99\x20\x63\x00\x1b\x4b\x70\x40\xa8\xd2\x40\xbf\xa3\xe1\x29\xf9\xdf\xd5\x3a\xfa\x1d\x5d\x35\xce\xce\xcc\x85\x04\x65\x43\xf4\xfb\x06\x14\xbc\x0a\xad\x02\x1a\x93\x84\x9f\x37\x70\xa6\x15\xfb\xca\x61\x82\x07\xb4\x73\x43\x74\x34\x88\x23\xb6\xc1\xc8\x5d\xe9\xa8\x77\xf0\x81\xec\x11\xb5\xab\x5a\xad\x82\x6a\x57\xb5\x3a\xfc\xb7\x01\xff\x6d\xc1\x7f\xd7\x2b\x40\x0b\xe4\xbf\x0d\xf8\x6f\x0b\xfe\xbb\x0e\xff\xad\xf7\xc9\x7f\x9b\x1d\xb9\x99\xfd\xf4\x13\x43\xea\x27\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\x24\x61\x36\x9e\x56\x79\x99\x55\x89\x0a\x29\xbd\xc1\xc4\x87\x2a\x7d\x50\x24\x8c\x2a\xbe\xca\x68\xf4\x00\xd1\xe5\x4f\xc3\xf8\x08\xa7\x38\xeb\x22\xcf\x16\xc9\x06\xe1\xf8\x4b\x38\x63\x96\xbf\xf1\x08\x45\x47\x31\x9c\xc6\xc6\x41\x8a\xfa\x18\x47\xe0\x1d\xc0\xee\xb7\x82\x68\x08\x26\x7c\xc3\x70\x88\xa2\x38\x63\x66\x98\x36\x29\xd0\x6c\x2e\x1c\x12\x37\x17\xfd\xf4\x05\x5f\x1f\x26\x61\x9c\x1c\x51\x0b\xe0\x8d\x0d\xf9\xde\x49\x3a\xdc\x2c\xcc\x98\x53\xbb\x03\xba\xf8\xc6\xff\xb8\xc1\xe1\x86\xbb\x79\xf9\xd6\xc1\x9f\xbf\xe0\xeb\x5f\xe3\x04\x8c\x18\xbf\xe0\xeb\xea\x25\xf9\xed\x2e\x76\x1c\xfe\x81\x59\xa9\x34\x3c\x7f\x43\x18\x10\x5a\x45\xad\xbc\x65\x24\xfc\x00\x12\x18\x20\x1b\x2c\x1f\x39\x8e\xa3\x7c\xe6\x0d\x3e\x47\x9d\x42\x2d\x90\xfe\xa7\x83\x31\x26\xc7\x0f\x44\x44\x68\x47\x1f\xd2\xa3\xf8\x92\xc0\x2e\xf1\x66\x9e\x93\x5d\xfa\xa7\xdc\x3e\xa8\x70\xdd\xc3\xc2\x1b\x55\xc6\x59\x79\x77\x6a\x2e\x55\x69\x22\x4a\xd0\xa1\xa2\x07\xfd\xf9\x9a\x61\xc8\x9e\x1d\x52\x08\x62\x64\x27\xca\xd3\x41\x72\x96\x23\x7f\x0a\x2a\xa7\x50\xe7\x8c\x8e\x2c\xcc\x38\x7b\xe3\x60\x35\x7e\x86\x85\x94\xfd\xc4\x02\x0e\xd1\x74\xcc\xa1\x54\xd1\xfe\x81\x21\xfe\x2f\x81\xb8\x17\x73\x36\x0b\x47\x71\x86\x08\x49\xfa\x0b\x65\xea\x1e\xa0\x6f\x01\xb9\x90\x8f\xe7\xfd\x22\x90\x41\x7c\xe2\x30\xcf\x94\xbd\x0d\x3e\xc8\x9d\x8a\xc9\x68\x67\xca\x2e\xa6\x96\x58\xd7\x0a\x00\xa6\x0c\x32\x7b\xbd\x00\xdb\xfd\xf0\x0a\xd8\x76\x1e\xb6\xbf\x6f\x00\x13\x3f\x65\x83\xbc\x2a\xa9\xe3\x2b\xaa\x31\xd4\x1d\x93\x8d\xe4\x84\x03\x69\xb1\x75\xf7\x33\xea\x10\x7e\x66\x4c\x18\xda\xd8\x40\xad\x45\x93\xf6\xdd\x0d\xad\xbb\xcf\x9e\x11\xf7\xad\x19\x8b\xd6\xd9\x90\x9c\xa1\xdf\x89\x2c\x61\x2f\xa2\x85\xdc\x5c\x95\xe9\xf2\xd9\x4c\x18\x5d\xbc\x73\x70\x1a\xeb\xb5\x9f\xd9\x90\xa2\x92\xdf\x88\x27\xc9\x72\xf8\x2b\x0f\xd7\x51\x19\x16\xe3\xa3\x2f\x44\x1d\x17\xf1\xc2\x91\x91\x37\xf3\xaf\x1c\xa2\xf1\xb2\x93\xfb\xe5\x4c\x2d\x27\xb8\x45\x88\xbf\x46\x2d\x70\x64\xa1\x0f\x79\xb4\xaf\xcf\xc5\x29\x87\xc0\x24\xcd\x25\x3b\x92\x03\x4c\x17\xba\xf5\x35\x44\x48\x51\x17\xae\x3d\x4b\xe9\x0c\xfd\xee\x5f\x9c\x9e\x3f\x5d\xf8\x76\xaf\x40\x13\x81\xe6\xa9\xbe\x14\xdd\x73\xe0\x95\x64\x2b\xca\xf4\xe0\x68\x90\x5c\xcf\xa8\x65\xac\x2a\xe7\xed\x57\x50\x3c\x1a\xa5\x38\xb3\x66\x86\xae\x91\x61\xdc\x13\xf5\x64\xe1\x8a\xbd\x57\x57\xe4\x09\x51\xfe\xac\xcb\x9f\x0d\xf9\xb3\x59\x01\x16\xa3\x9e\x32\x34\x5c\x87\x78\x59\x5c\x09\xd7\xbc\x0c\x66\xa8\x11\x0d\x41\xf6\x6c\x65\x63\x8f\x10\x43\xe8\x7b\xff\x94\x82\x21\xf2\x8b\x39\xa4\xda\x37\xbd\x6c\x33\xa7\x6c\xd3\x79\x24\x2a\x32\x84\x3a\xad\x56\x74\x02\xd5\x1f\xeb\xfa\x63\x43\x7f\x6c\x56\x84\xc2\xc2\xda\xbc\x57\x57\xd1\x1e\x39\xf9\x7e\x17\x63\xe4\x9e\x74\x6d\x98\x9c\xb3\x5e\x41\x77\x23\x37\x17\xd1\xb0\x03\x41\x61\xc9\xda\x31\xb0\x6f\x31\x8b\x15\x0a\x17\x92\x54\x54\x27\x98\x3a\x74\x5c\x35\x65\xb0\xce\xe0\xf5\xef\x1a\xb3\xad\xb9\x34\x40\x69\xdd\x9c\x0e\xa3\x96\x35\x3f\x50\xab\xa1\xd7\x6a\x98\xb5\x9c\xda\xa6\xb4\x69\x4e\xa7\x51\xab\xe9\x52\x43\xbd\x33\xce\x0e\xee\xa3\xbf\xba\x05\xba\x4e\x0c\x47\x8e\x33\x8e\xd8\x7f\xe9\xa8\x6e\xa0\xfa\x2b\xf6\xf3\x35\x9f\x21\xf6\xc2\xb3\xef\xc2\x1c\x87\xa3\x0c\x28\xbd\xe2\x51\x94\xe5\x4e\x1c\x47\x3d\x23\x93\xa7\xa8\x6b\x6a\x42\xf2\xfa\x5d\x51\x74\x95\xd2\xba\x25\x77\xfd\xae\x28\xb5\x4a\x69\xc3\x94\xba\x7e\x57\xf4\x57\x69\x53\x79\x6d\x6d\xc3\xcf\x9f\xbb\x36\x00\x40\xae\xae\x23\x57\xf7\x20\xd7\x58\x80\x5c\x33\x17\xb9\xda\x2d\x91\x6b\xe8\xc8\x35\x3c\xc8\x35\x17\x20\x57\xcb\x45\xae\x7e\x4b\xe4\x9a\x3a\x72\x4d\x0f\x72\xb5\x05\xc8\xd5\x73\x91\x6b\x2c\x44\xce\x49\xba\x1f\x67\x60\x43\x94\x66\x41\x86\xed\x02\xc0\x4e\xb2\x9a\xa3\x63\xc0\x32\x32\x53\x8f\x06\x5f\xc8\x5c\x64\x0d\xd7\x17\x32\x10\x99\xa9\x1d\x77\x2a\x51\x9c\xeb\x69\x01\xef\x83\xe5\x53\xa2\x27\x0f\x65\xed\x98\xa7\x16\xc7\xf2\x31\x8f\x2d\xf6\x0a\xd2\xce\x2d\x72\x09\x95\x8b\x51\x82\x58\x3f\x1c\xbb\xba\x1f\x3b\x7b\xfd\x58\xd8\x59\x4b\x48\xc7\xae\x76\x1b\xec\x1a\x0a\x76\x0d\x3f\x76\xf6\x02\xb2\xb0\xb3\xd6\x90\x8e\x5d\xfd\x36\xd8\x35\x15\xec\x9a\x7e\xec\xec\x15\x64\x61\x67\x2d\x22\x1d\xbb\xc6\x62\xec\x6c\x6a\xc5\x3c\xb0\xb5\x5b\x2e\xa1\xdb\xb0\x63\x1d\x99\x42\x8e\xb5\x9c\xf4\xcd\xd5\xb1\xaa\x2c\xd1\xa7\xe9\x93\x7d\xd8\x51\xb8\x8b\x1a\xed\xce\x6a\xb3\xc1\x34\xd0\x65\x97\x2a\x98\x4b\x2c\x42\x40\x4a\x99\xe3\x30\x53\x0d\xaf\xa4\x2c\xe1\x13\x82\x1c\xde\xa3\x60\x80\x85\x8e\x58\x00\xf9\x4f\x7c\x15\x4c\x67\xe2\xa4\x2c\x3f\xf0\x39\xa5\xb0\x32\x7c\x95\x29\xb7\xdb\xd5\xcd\xed\xe3\x2a\x3b\x47\x94\xa6\xdc\x22\xfd\x0b\xbe\xae\xa0\xc1\xe8\x5c\x48\xf3\x12\xca\x6c\x12\x10\x24\xae\x32\x64\x42\x61\x12\x7e\x49\xb6\xe3\x02\xc4\x74\xda\x3d\x87\x12\xfb\x13\x8d\x9a\xba\x8b\x27\x33\x9c\x94\x36\xb7\xe9\xb5\x3e\xd5\xd9\x3f\x7d\xc2\x6c\x56\xd4\x26\x5f\x3d\x7d\x0a\x11\x70\xc1\x80\x44\xb3\x2a\xe8\xb6\x1b\x15\x6e\x97\xd0\x6d\x83\xed\x88\x62\x99\xd0\x6d\xb7\x2a\xd2\x24\xa1\xdb\x06\x17\xc6\xe9\xb0\xfd\xac\xdb\xa9\xdf\x9c\x55\xda\x8d\x3b\x59\x8b\x7c\x4b\x33\x91\x07\x33\xe6\xf8\x86\x66\x19\x74\x25\xfc\x84\x98\x01\x05\x69\x1e\x0d\xe2\xe9\x2c\x8e\x20\xe4\x3a\xf9\xb6\xfa\xf4\x89\x98\xf7\x49\xd8\xaf\xb2\xa2\x5f\xbf\xaa\x06\x00\xc2\xe9\xf3\x9e\x8d\x3b\x82\x14\x4b\xab\x8e\x20\xc5\xca\xb7\x5f\xe3\x64\x08\x6e\xe9\xa2\x80\x78\xa3\x42\x98\x8f\xc0\x5e\x0c\x68\x7d\x93\xdf\xf2\x48\x98\xce\xcf\x1a\x66\x18\x3c\xab\x7a\x64\xa1\x2a\xef\x3f\x66\xa3\x75\x80\x82\xa3\x41\x95\x3c\x18\x58\x77\x5a\xe2\x2b\x7d\xcc\x33\x44\x11\x5f\xb6\x2f\x66\xef\xb6\x76\xe4\x65\x13\x7d\x76\xde\x60\xf5\x53\x6a\x9e\x47\x96\x15\xbf\xc5\xca\xf0\x74\x36\x09\x32\x17\x83\x12\x41\xa6\xff\x8c\x58\x40\x1e\xae\x41\x05\xa7\x02\xc1\xeb\x40\xef\x17\xfe\x81\xab\x3c\xc0\x64\x17\xb5\x50\xa9\xde\x58\x47\xfd\x30\x4b\xcb\x79\x00\xc3\x0b\x07\xbc\xbd\x5f\x6e\x0b\xee\xd3\xf6\x87\xde\xa7\xdf\x76\x0e\x8e\xf6\x3f\xed\x1f\x6c\x6d\xa3\x4d\x08\x6d\x90\x05\x51\x86\x12\x3c\x4b\x70\x8a\xa3\x2c\x8c\xce\xb9\x22\x86\x90\xe1\x34\x1e\xca\xbe\x3b\x61\x6e\x6d\x17\x82\xc9\xd8\xa9\x05\x53\xb9\x14\x34\x4c\x8e\xc4\xa3\x9b\xa2\x1c\x97\x84\x72\x36\x29\xba\x3d\x70\xfb\x9e\x27\x60\xf0\x20\x72\x7c\xa8\x45\xb4\xe2\x4a\xef\x04\xdd\x93\x39\x40\x27\x63\x4c\x46\x3d\x8b\xd1\x9c\xb9\x09\x10\x16\x80\x48\x61\x00\xad\x81\x5c\x95\x0f\x83\xd1\x79\x17\x48\x97\xe3\x5a\x56\x77\x54\x0b\x5b\xd8\x2e\x52\x0a\x9b\x91\x5f\x18\xf9\x26\xc3\x85\x3e\xb5\xc7\x54\x70\x27\xa4\x47\x90\xff\x82\xaf\xab\xce\xb2\xdc\x33\x74\x30\x3a\x47\xa5\x03\x68\x25\x98\x94\xa1\xce\xc0\x35\x78\x05\xc7\x40\x6f\x8b\xc7\x11\xa5\x13\x7a\x43\x48\x84\xf7\x8e\x10\xca\x20\xaf\x4f\xe4\x5c\x11\x0e\xfc\xdf\x75\x29\xc1\x2e\x80\x34\x69\x41\xdd\xe3\xf9\xd5\x73\x95\x6e\xd3\xdb\x74\x98\xe3\xa4\xc4\x2e\xcf\x60\x08\x2b\xe8\x4f\x14\x5e\x74\x51\x78\x21\x79\xe3\x8d\x66\x7a\xa0\xcd\xb7\x0e\xa9\xab\x85\x85\x62\x92\x83\xa9\x01\x50\x13\x87\xd0\xfa\xec\xc6\x59\x5f\xab\x0e\xd9\xc3\x94\xd0\x0a\xd2\x93\x67\x21\x3e\xd2\xd3\xfd\xd2\xd3\x16\xbe\x2f\x7a\x12\x90\xee\x46\x4f\x3a\x9f\xbe\x05\x3d\xed\x45\x61\x16\x06\x93\xf0\x0f\x9c\xa2\x00\x45\xf8\x72\x72\xcd\x30\x1c\xb2\xe1\x58\x4c\x4b\x7c\xd7\xb8\x1a\xc5\xc9\x74\x3f\x1e\x62\xb4\x4d\x7d\xd5\x20\x4c\xb3\xe4\x74\x71\xa2\xd2\x29\x58\x57\x83\x9b\x1f\xa7\x5a\xb1\xc9\xb8\xc9\xf0\xbb\x23\xd9\x7b\x23\xab\x92\xfd\xc1\xc5\x29\x6e\x49\x70\x61\x14\x6a\x16\x36\x62\x9a\x14\x72\x71\xa8\xa8\x37\x67\x33\x42\x0b\x30\x5a\x3c\xdd\x74\xea\xb8\x66\x20\x43\xbc\x21\x7e\xf2\x4d\x91\xd2\xa0\x7d\x2a\xce\x88\xe4\x4c\x0d\xeb\xe3\x64\x4a\xa7\x3d\x70\xe9\x6e\x28\x7d\x4b\x92\xda\x90\xe4\xf5\xca\x55\x92\xda\xd1\x80\xad\x8c\xf3\x2c\x1e\x52\x42\xa7\x1e\x00\xae\x7e\x80\x7d\x51\xa9\xf0\xc2\x01\x1b\x1d\x9d\x0f\x43\x2c\x87\x54\xb4\x04\xda\xb3\x3b\x92\x0f\x5b\x82\x36\x6e\xda\x0c\x27\x45\x8c\xa8\xa8\x51\xd1\x30\xc8\x02\xd4\x07\xd9\x4b\x2f\xe1\x91\xc7\x00\x34\xcd\x74\xc1\xbd\x9d\x4d\xc0\x87\x38\x81\xb9\x1c\xc4\xd1\x20\xc1\x19\x7e\xc1\x86\x63\x12\x9f\x6b\x4c\x59\xb9\x97\x3a\x5a\x6e\xac\x21\x9e\x06\x60\x4e\xdd\x5b\x18\x4f\xc1\x43\x85\xa5\xe0\xe1\x12\x9b\xde\xd7\x94\xb9\xc2\x10\xa0\x4c\xd9\x49\x78\x03\x6f\x83\x35\xa0\x80\x2f\xb0\x73\x29\xfc\x49\xc0\xa2\x41\xb3\x58\x30\x82\x30\x3a\xbf\x07\x6e\x22\x3b\xbf\xc1\xc9\x83\xc1\x2f\xad\x90\x36\x57\x74\x32\x29\x52\xef\x92\x63\xee\xa5\x30\x56\xb2\x6b\x44\x79\xa5\x43\xe7\xe1\x1e\x38\x1a\xba\x66\x3f\x80\x2f\x6a\x75\x17\x4d\xd1\xf6\x50\x70\x11\x84\x93\xa0\x3f\xc1\xd4\x0c\x31\xf5\x6f\x8b\x9f\x78\x67\x0a\x53\xd5\x4e\x18\xb1\x8d\x2f\x77\x9f\x62\x70\xf5\x7d\xe6\x43\x9c\x31\xef\x68\x1a\x34\x8d\x42\x92\xbb\x06\x0a\x53\x84\x47\x23\x3c\xc8\xc2\x0b\x3c\xb9\x46\x01\x1a\xe2\x34\x4b\xe6\xf0\x5c\x41\x09\x0e\x86\x2f\xe2\x68\x80\x0b\xed\x33\x45\xa9\x17\xd0\x78\x28\x1a\xa6\xc0\x1f\x9a\x92\xf9\x48\x96\x8a\x13\xb1\xa8\xb2\x2c\xf5\x8b\x8a\x8b\xc9\x9f\x17\x2d\x4e\xff\x3b\x72\x2e\xe6\x50\x48\x2f\x11\x8e\x72\x01\xa0\xdc\xd5\xa2\x15\x75\x5c\x94\x2c\xc1\x90\x21\x1e\x12\x41\x95\x2d\x38\x3c\x64\xf1\x32\x39\xa7\xde\x51\x26\xc4\xb9\xf8\xec\xda\x0b\x95\xcd\xf5\xc6\xfa\x6a\xb3\xa1\x7e\xa2\x2a\x11\xd7\x17\x43\x0e\xea\xa2\xba\xf6\x55\x97\x7f\xbb\xa8\x51\xe4\xec\x94\x3a\x55\xd9\xc1\x62\x45\x36\xf2\xae\x4d\x7e\x6a\x61\x23\x7d\x32\xc6\x8a\x50\xc0\x12\x6d\x05\x68\x0c\x5a\x63\x22\x64\x16\x58\x8a\x5c\x84\xdd\x8c\x38\x3e\x10\x60\x80\x2f\x6b\x22\x34\xb1\x75\xed\xe8\xd0\x37\x38\x2c\x31\x6b\x6f\x5b\xe5\x69\xe8\xc8\x2d\xd9\xd6\xbb\xca\xb4\x7a\x5d\xaf\xdf\x14\xf9\x13\x9f\x52\x3c\xc1\x83\x8c\x36\x7c\x9c\x25\x41\x86\xcf\xaf\x4b\x3e\x73\x6d\x45\xfb\x0c\xe2\xe2\x06\x5a\xa1\xac\x74\xc5\x6b\x1e\xc6\x66\xe3\x30\x48\x53\xc2\x26\xde\x04\x29\x1e\x6a\x1e\x73\xea\x5f\xbe\x71\x18\x03\x75\x8c\x13\x38\x70\x91\x5d\xcd\x0f\x29\x7f\x91\x9b\xb9\xfd\xd8\x7d\x46\x8e\x8d\xba\x0f\x29\x46\x4e\x2a\x63\xb3\x6f\x58\xf2\xec\x46\x65\x10\x30\xf7\x3c\x88\x8b\x1b\x8a\x62\x05\xf9\x2f\x70\xcc\x31\xa8\x78\x2c\x3d\x19\xd9\x77\xad\xfe\x1b\xf7\x39\x77\x42\x5b\xbf\x29\xaa\xa0\xdc\x1b\x23\x13\x73\xc7\x84\x9a\x6c\x5b\xe5\x92\xa5\x32\xd3\xf0\xba\xaf\xde\x74\x1d\x76\x9a\x25\x38\x98\xde\x4a\x95\x0d\x32\x14\x53\x3e\xab\x36\xf8\xcd\xc6\x8b\x7e\x48\x0d\xb6\xf5\x13\x0d\x95\x4e\x20\x8c\xb5\xa2\x99\xae\xa3\x52\xb3\xa1\x2b\xa6\x15\x85\xef\x31\xe0\x67\xa8\x7d\xcd\x97\x39\x1e\x21\x3b\x8e\xbd\xd6\xb5\xc3\x72\x11\x71\x16\x24\x70\xdc\x72\x09\x88\xf6\xf6\x06\xc7\x1b\x69\x5d\xc5\x85\xc6\x1f\x7e\x58\x19\x4d\xe6\xe9\x78\xa5\xd8\x36\x47\xa1\xf8\x36\x3a\x31\xcc\x5d\x54\xcf\x9b\x57\x38\xd7\x42\x56\xd3\x99\x7a\x5b\xaa\x2a\xcf\x3f\x4d\xe9\xd9\xb7\x57\x65\x3f\xfe\xbc\x59\x4c\x21\x9a\xc7\x0e\xd4\xb3\xa8\x44\x69\x43\xb9\xdd\x64\x07\x6d\xcb\x39\x98\xbd\x57\x95\xde\x79\x0a\x7a\x55\x45\x39\xe5\xc9\xb9\xa4\x7c\xbd\xf4\x6e\xba\xa9\xf7\xc8\xa9\x10\x34\x33\xcb\x48\x05\x3f\x50\xf5\x37\xd8\x0f\xf9\x4c\xf1\xed\x0e\xf4\xb0\xbd\x37\x3d\x4b\x15\xcd\x39\x4a\x78\x41\xbd\x76\x6e\xa3\x79\x96\x30\x72\x75\x85\xa2\x2e\x57\x34\x29\xf5\x6e\xa5\x71\x16\xd3\x29\x0f\x48\xff\x33\xa7\x53\x6a\x82\x97\x9c\x4e\xa7\xe2\xb7\xe0\x74\x8a\xba\x77\x98\xce\x3c\x85\x6f\xb1\xab\x83\x6f\x3a\x9d\x77\x9e\xae\x9c\x25\xb0\x60\xbe\x4c\xbd\x69\xce\x24\xd1\xcd\x44\xe8\x79\x07\x2e\xb1\x8e\x59\x5d\x5f\xa0\x0d\x14\x5e\xa8\xb3\x95\xb7\x45\xb0\x1d\x93\xc6\x95\xee\x8d\x83\x30\x82\x94\x27\xbe\xbb\xd6\x37\x60\x37\xf0\x89\x77\x1e\x6d\xf8\x83\x0f\x98\x2a\x36\x6d\x07\x21\x75\x2d\x62\x50\x86\x46\x36\x66\xec\x12\xe2\x4e\xf4\x55\x1e\x47\x79\xd3\xe3\xdb\x81\x71\x12\x52\x9a\xd0\xe6\x8e\xf4\xea\x4d\xcf\xb1\xf7\xd8\xe0\x69\x13\x87\x22\xfc\x67\xc6\xd5\x18\x94\x4a\x83\x8c\x19\x75\x57\xcd\x3a\x16\x0c\x83\x66\xa9\x74\x24\xb4\x22\x4c\x58\x8a\xb9\x8c\x84\x74\x4e\x88\x9c\x37\x24\xcc\x2e\x8b\x00\x61\x3f\x2f\xc7\x98\x45\xde\xa7\xf8\x41\x20\xcf\xb4\x00\x72\xf6\xc2\x70\x17\x24\x7f\x30\x95\x4c\xd4\xa1\xde\x00\x90\x1e\x0f\xba\x20\x5c\x1b\x4c\x59\x56\x9d\x0c\x24\x55\x80\x96\x99\xbc\x0e\xc5\x6b\x0b\xed\x74\x80\x45\xe6\x0d\x89\xba\x90\x3c\x86\xb3\x52\x88\x15\x9a\x1c\xf1\xca\x63\xce\xfa\xdb\xc1\x11\x9c\x97\x19\xd1\xd9\x65\xae\xe2\x04\xfa\x25\x15\xdd\x15\xa4\xf5\xab\x22\x9b\x75\x09\xfd\x0c\x0f\xd5\xd7\xa5\x64\x8e\xae\x13\xb3\x23\x3c\xc5\x20\x85\xc3\xee\x4a\x49\x80\x5d\x45\xc1\x69\x1f\x1c\xda\xe1\xb5\x5d\x9d\x4b\xb0\xf8\x82\x87\x9d\xa7\xcc\x94\xe6\x93\xe7\x78\x0b\x53\x40\x6f\x07\x54\xcf\x9d\x85\xeb\x76\x88\x0b\xac\x5b\xb1\x4f\x3d\xae\xdb\xc7\x75\x8b\x6e\xbf\x6e\xef\xb2\x3a\xc0\x42\x78\x1c\xa6\x4b\xaf\x0d\x27\x26\x8c\xa2\x81\x8b\xfc\x76\x70\xe4\xe5\x00\xaa\x07\x99\xc5\x01\xee\xca\x76\x9c\x98\x9d\xc8\xa1\xe9\xe3\x41\x3c\x65\x4b\x87\xb0\x85\x30\x9e\xa7\xc5\x99\x87\x18\xac\xa2\xec\x41\x90\x12\xef\x46\xc9\x8b\xfb\x52\x1e\x50\x20\x22\x71\x69\xc9\xe5\xe1\x3f\x8e\xe3\x14\xa3\x69\x78\x45\x64\x21\x47\xff\xc0\x13\xd4\x16\xd2\x90\x4a\x88\x4c\x0a\xf3\x91\x5d\x7c\x01\xd2\x29\x39\xe9\xa4\xf3\x7e\x8a\xff\x7b\x8e\xa3\xcc\xa9\x62\x40\xba\x68\xa7\x64\xf5\xd0\x47\xd1\xab\x1a\x54\x51\x32\x66\x65\xb1\xaa\x9f\xec\x6c\x2e\xac\x5c\x31\x92\xe4\x6a\x73\x46\x4a\x22\x7f\x30\x81\xd2\x7a\x3c\x3c\x43\xbf\x6f\xd0\x7a\xa7\x61\x6e\xe8\x12\xf9\x9b\x9b\x40\xbf\xe9\xb1\xf2\x5a\x40\x13\x45\xb4\x3d\x0c\x86\x43\x32\x81\x0b\x14\x20\x33\xc8\x72\xd5\xab\xd2\x7f\xdd\xea\x8f\xc3\x77\xbd\x63\xf4\xbf\xda\xab\x6b\x68\xc6\x80\xa6\x4c\x97\xe7\x82\x79\xf8\x65\x90\xae\x81\x9c\x3c\x0b\x86\x55\xfe\x94\x23\x1b\x1f\x06\xfc\xfa\x79\x9e\xf2\xd0\xf9\x22\x10\x0a\x33\x57\x86\xb8\xc9\x02\x8f\xa5\xec\xaf\x00\xb2\x7a\xfb\x4c\xd0\x72\x56\x72\xeb\xf1\x58\x08\x28\xe5\x3e\x12\x00\xa5\x22\x98\x25\x19\x14\x08\x67\xf9\xc0\xc7\x66\x71\xf8\x12\xe3\x4a\x7e\xc9\xeb\xb5\x8a\x11\x37\x4b\xbb\x60\x0e\x86\xe6\xe5\xda\xad\x19\x88\xa8\x46\x63\x9d\x6c\x28\xe3\xe5\x8b\x19\x32\x8f\x32\x41\x3b\xe0\x57\x64\x43\x8d\x18\xc1\x5a\x40\xe9\x8b\x17\x34\xe5\xb4\x88\xb0\xf2\x2f\xa3\x80\xab\x59\x7a\x2f\xc4\xdb\xb5\x43\x2f\xd0\x4c\x6f\xf0\x95\xd0\x0b\x44\x40\xd1\xb0\x90\xbe\x2e\xd6\x7b\xe6\xe0\x62\xbd\x07\xb7\x16\xed\xed\x42\xcc\x72\x91\x4a\xf3\xc3\x17\x48\xf6\xa3\xb7\x89\x42\xf4\xdc\xe7\x96\xaf\x42\xa7\x61\xee\x95\x37\x39\xd2\xab\x81\x1d\xda\x90\xb6\xef\xfc\xf0\xaf\x82\xae\xe8\x28\xb9\xcc\x10\x36\x87\x43\xf7\x20\xc0\x5c\x0f\xe2\x68\x10\x64\x1c\x66\x61\x0d\xcc\xc7\x68\x26\x18\x0a\x2c\xd9\x71\x30\xa4\x81\x8c\xd8\x42\xfd\x36\x5c\x66\x1e\x99\x7c\xe6\x9b\x70\x04\x68\xb6\xc0\x95\x3b\x94\x33\x59\x82\x8b\x0f\xbc\xc5\x99\x96\xb8\x58\x59\xc4\x10\x03\x16\x4d\x82\x34\x83\xe7\xc5\x6b\x5a\x8a\xd7\xa7\x25\x7d\x39\xbf\x40\xf5\x32\x75\x31\x3b\x63\xce\x60\x2e\x4f\x62\x2a\x38\xf8\x29\x46\x82\xdb\x30\xd7\xa0\xb2\x99\xd2\x6d\x73\x49\x3d\xff\x5f\x71\x11\xe4\x72\x51\x70\xdf\x2c\xb8\x6e\x15\xf2\xee\x81\xee\xcf\xe8\x7f\x3f\x1e\xe2\x1b\xaa\x1e\x3c\x11\xa7\x35\x7a\x29\x02\x27\x09\xa5\x3b\xbd\x37\x3d\x1f\x14\x36\x57\x37\x82\xbe\x08\x2c\x53\xd8\xb0\x21\x02\xc9\x7b\x08\x1c\xfc\x08\xd8\x00\x28\x86\x93\x06\x81\x13\x4c\x01\xb3\x8a\x71\xaa\xa3\x6d\x5b\x4d\xdc\x68\xde\x08\x4b\x18\x06\xd2\x89\xd6\x3f\xf6\x14\xeb\xc3\x7c\x1b\xc0\x9c\x00\x67\xba\x7d\xa8\xc3\x8f\x13\xe4\x66\x32\x02\x9a\x5a\x14\xe9\x8a\x5d\xf2\x7d\x0a\xb6\x9f\x1e\xfc\xe5\xc4\xda\x87\x01\xcb\x96\x94\x4b\xda\xba\x71\x89\xf7\xc4\x40\xa0\xc2\x96\x08\x1a\x0d\x38\x95\x1b\x77\x33\x6e\x69\x7f\xf5\xa7\xfc\xe6\x75\xeb\x95\x32\xfa\x69\x75\x69\x0c\x84\xaa\xc5\x73\x96\x79\x87\xf1\x0c\x05\x19\x9a\x60\xc2\x05\xe3\x88\xaf\x00\x96\xe5\x83\x5a\x82\xc2\x7e\x0d\x0c\xd7\xe6\x5b\x48\x9c\x6f\xa6\x61\x44\x8d\x44\xd9\x21\xde\x0a\x97\xa8\x3f\xb2\x4a\x74\xfa\x14\xfc\x29\x21\x4d\xc1\xfe\x98\x1e\x79\xc3\x0b\xf4\xe3\x8f\x4e\x7d\xbc\x19\xa8\xe3\xf0\x56\xba\x0c\x89\x89\xae\x4c\xf1\x9e\xcf\xcd\x66\x8b\x5e\x49\xfb\x45\x52\x29\x92\x08\x43\x69\xf6\xca\x41\xd0\xbc\xb9\xfb\x25\xe4\xd5\x55\x72\x90\xa1\xe9\xbe\x7c\x22\x17\xc8\xeb\xcc\xf4\x0b\x24\x70\xf8\xbd\x50\x07\xc1\xaf\xe2\xa9\x8d\xa0\xef\x94\x7c\xab\xcb\xf8\x87\x5b\x56\x0f\x8b\xb7\xb3\x3d\x90\xfc\x16\xcc\x00\x95\x8f\x5c\xed\x2d\xb2\xfc\xbb\xa3\xa5\x02\x98\xde\x31\xd9\xc3\x6d\x86\x82\x06\xf1\x64\x82\x29\xfd\xc7\x23\x2e\x1a\x80\xa8\x89\x21\x97\x5e\x9e\xe8\xa1\x88\xa2\x8a\x93\x37\xd9\x46\x93\xe0\x52\x79\xe5\xf4\x4b\x74\xbb\x7e\x50\x07\x74\x21\xa4\x14\xa9\x2d\x2f\x1e\x21\xc3\x03\xe3\x82\xb4\x3e\x59\x9f\x96\x39\xae\x0f\x50\x1a\x4c\x28\xf6\xf0\x03\x80\x81\x4a\x32\xa0\xe1\x47\x71\x12\x5e\x50\x59\x85\x73\x0c\x27\x40\x7e\x95\x2a\xe5\x7c\xc5\x72\xd0\x8e\xb5\x5a\x4c\xae\xb9\x4d\xcf\xf2\xe5\x9b\xc1\x18\x4f\x6f\x07\xd7\x2d\x70\x32\x95\x39\x58\x4c\x8f\x14\x78\x4e\x10\x34\x27\xe3\x8d\xcc\xd9\x48\x4f\x31\x54\xc4\xe2\x6f\x4d\x31\x6c\x10\x47\x17\x38\xc9\x34\x19\x96\x66\xbb\xe3\xc6\x94\x60\xf1\x49\xad\xff\xfc\x6e\xab\x87\xb4\x8a\xee\xbc\x2a\x5e\x16\xb4\x87\x59\xec\x62\xa5\xa3\xb6\xf8\x58\x27\xbc\x9b\x54\x7c\x0c\x3b\xd1\x20\x12\x49\xac\x66\x71\x9a\x86\xfd\x09\xf6\xaf\x58\x47\x53\xcb\x39\x37\xc9\x81\xb2\xed\x41\xe9\x37\x7e\x02\xff\xd3\x82\x82\x84\xfa\x9c\xac\xe0\xae\xf2\x5b\x3a\x3c\x39\x2b\x7d\xc1\xd7\x5d\xdd\x2f\xca\x59\xcc\xf0\x94\x72\x17\x22\xcb\xb8\x0b\xff\x5d\x50\x50\xac\xca\xae\xed\xce\xe5\xae\xc1\x44\x78\xd3\x32\xc1\x5d\x58\xc8\xf5\xfa\xd1\xf9\x5d\xef\x78\xcd\x5d\x41\x61\xe1\x2d\x77\x09\xb1\x70\x14\xa0\xf4\x5d\xf5\x60\x86\xa3\xe3\xe3\xf7\x56\xb5\xe2\xce\x64\xea\xf4\xbb\x05\xaf\x69\x78\xb5\x17\xe9\xe5\x0a\x9b\x1e\xd1\x55\x9c\x2e\xb7\x8c\x91\x77\xdd\xd8\xac\xc4\xf0\x0d\xf4\x70\x13\x72\xa8\xf3\x03\xe7\x06\xb6\xdc\x2b\x03\x76\x05\xf8\x1d\x8e\x42\x73\x8d\xe7\xc0\x81\x24\x60\x29\xcd\x00\x06\xd9\xe3\xb0\xf4\xa2\x94\x18\x47\x31\x7d\x63\x30\x40\x96\xb3\x1f\xe7\x71\x8f\xa2\x4b\x9a\x22\x2f\xae\xe9\xd8\xda\x7e\x8e\x56\x56\xdc\xbe\x15\xce\xf2\xd5\x2c\xa6\xf9\x86\x7c\xae\x1c\x0b\x6a\x79\x48\xd5\x4b\x98\xbc\xa2\x4a\x9c\x62\x6c\x7c\x56\x55\xb2\x04\xfa\xfa\x95\x92\xab\xac\x53\xe5\x93\x78\xcd\x8f\xbd\x96\x8e\xc6\x29\x27\x51\x2a\x5b\x74\xaf\x41\xdb\x81\xab\x0d\xf1\xd3\x7d\xbb\xc1\x7a\xee\x22\x4e\x17\x68\x56\x5c\xa4\x32\x86\xdd\x4b\x1f\xc4\xfc\xeb\x0e\xb1\xea\x02\xff\x92\x8b\x78\x33\x2f\x06\xf1\x74\x16\x64\xb0\xbd\x14\x5d\x86\xea\xb6\x60\x6c\x62\x8a\xf8\x53\x74\x4f\x74\x2d\xbf\xdb\x20\x77\x5f\x86\x83\x31\x6d\xfb\x98\x93\xb7\x87\x90\x15\xea\xf2\xf1\x46\x8d\xbe\x45\xf1\xc2\xdc\x77\x81\x5a\x46\x8d\xb4\xa4\x2d\x41\xf9\xc5\x15\xa8\x91\x88\xbb\x46\x05\xf2\xce\x75\x8c\x85\xfe\xda\x87\x58\x52\xdc\xab\x6a\xb9\x54\xa2\xd5\x58\xda\xfb\xd3\xda\x55\xbb\xd9\xa9\x77\x06\x6b\x90\xd8\xa0\xd3\xee\xb4\xda\xa3\xf6\xe8\xac\xcc\x55\xf1\x00\x9a\x3f\xc8\x7e\x78\xce\x91\x05\x50\xf0\x8e\x85\xe7\xf0\x25\xea\x4a\x46\x46\xc3\xda\x2c\xbf\xe7\xe5\xad\x31\xd5\x5f\x69\x59\xe1\x91\xaf\x13\x49\xa7\xb7\x5e\x32\x7a\xcc\x06\xbe\xa0\x6f\xb1\x86\xef\x37\x80\x83\x2d\x8c\x1a\x4b\x6f\x16\x24\x29\x2e\x69\x0b\x35\xe7\x62\x32\x49\x35\xc5\x8f\xac\xe6\xf4\x4a\x20\xc5\x11\x8d\xe1\xb5\x60\xd1\x51\xc2\xb0\x90\xc9\x53\xaf\xe6\x41\xe4\x97\x71\xca\x61\x98\x25\x85\xb0\xc0\x9d\xe0\x34\xa3\xb6\x0d\xc1\xc4\xb1\x40\x0d\x98\xa7\xb5\x33\xb4\xb1\x81\xe4\xda\x43\x3f\xfe\x68\xb6\x7b\x5a\x67\x65\xf8\x9a\xf4\xa9\xa0\xb6\xaf\xe8\x05\x86\xdd\x32\xd2\x39\x8c\xb5\xf8\x8d\x16\x99\x29\x4f\xa3\x82\x5a\xe5\x1c\xeb\xba\xf8\x82\x1d\xd1\xe1\x2a\x48\xc2\xb0\xcb\x5b\xf0\x67\xd0\x40\xcd\xbc\xb5\xb6\x8a\x6b\xb7\x3a\xf5\x4e\x31\x46\xe1\x3c\x1a\x79\x8e\x41\x15\xe5\x74\xa2\x8b\xe6\xb9\x77\x45\x7c\x11\x5e\x26\xc1\x6c\x06\x72\x64\x90\xb1\xe6\x55\x95\x09\x0a\xc8\x4e\x9f\x2a\x5e\x69\xb9\xab\x57\x73\xf5\xb1\x5c\xd9\xa4\xc3\x8f\xeb\x53\x51\x07\x92\x5b\x5f\xf6\x08\xa1\x87\xcb\xf8\x79\x52\x3d\xd7\x11\xa8\xbd\x65\x9d\xa5\x0e\xa1\xd1\x90\x52\x8d\x38\x60\xc8\x8b\x1d\xc7\xc1\x29\x2f\x44\x94\xe9\xbd\x08\x08\x75\x2d\x51\x4d\x99\xd8\xdc\xa0\x52\xec\xda\x81\xcc\x1b\xf3\xa6\xbb\x8b\x87\xaa\x54\x3e\x39\x8e\x3a\x39\xde\xe7\xac\x69\x6a\x83\xc2\x7e\x4b\xbf\xf3\xbf\x49\x0c\x17\xf7\x16\xb6\xf9\xd7\x6e\x60\x64\x59\xba\x35\x2a\xf6\xb2\x12\xfe\x95\xb6\x36\x42\x73\xb5\xf4\x9c\xc2\x1e\xae\x41\x19\xa4\xc6\x54\x27\x7c\xd3\xc6\x2b\x62\xb5\x79\xa4\x81\x1c\x65\x87\xc3\x39\xd6\xef\xc5\x7a\xbb\x10\x3a\x4b\x45\xcf\xd9\x76\xd9\xaf\x2b\xd1\x0d\x62\xe9\x7c\xe2\x0a\x80\xe6\xf4\x59\xb5\xc4\x12\xe9\x99\x21\x02\x24\xb0\xce\xde\x46\x32\xe9\x41\xff\x24\x4c\xb8\x02\xb6\xa0\x30\x7b\x23\xc2\x71\x85\x63\xae\x6f\x3f\x2a\xbe\x9d\xe6\x6d\xda\xda\xfe\x6a\x17\xe4\xaa\x45\xc7\x27\x42\x56\xa2\x6f\xd5\xf0\xc2\x51\x44\xd1\x11\x32\x7a\xb1\xcb\x50\xad\xa0\x04\x04\x17\xa2\x76\x31\xa1\x0f\x94\x25\xd9\x2b\x47\x61\x45\x17\x68\x5a\x58\x3b\x4a\x2b\x7a\x41\x42\x7a\x23\xc7\x71\xed\xa6\xf0\xb1\x85\xdd\x43\xa7\x62\xe2\x84\xe2\x4b\xbd\x96\x41\x0f\xb6\x3d\xa9\x04\x20\x76\x28\xe3\xa2\x49\x79\x84\xd4\xde\x7f\xc7\x7d\xca\x08\xd0\x22\x22\x1d\x7f\x83\xbd\x49\x46\x55\x5e\xcc\xa6\xb9\xf7\xbc\x83\x4d\x73\xb2\x63\x61\x14\x14\x8f\xfa\x5b\xb3\xec\xfb\x46\xd1\xdc\x97\xee\x71\x4b\xf1\xc6\x2e\xf0\x44\x18\xf8\x06\xbb\x0a\xd3\x38\x28\xaa\x05\x75\x31\x19\x80\xd5\x9d\x82\xdd\x7e\xc3\xf9\x55\x45\x5e\x72\x13\x57\x73\x8c\x53\xd8\x1b\x86\x3a\x79\xda\x26\xa6\x45\x5d\xa4\xc3\x22\xf7\x26\x85\xc9\x68\x0a\x1f\xe7\x36\x21\x9a\x58\x5a\x1b\xe3\x64\x6b\xe6\x58\xe9\xf7\x2f\xa0\x63\x0a\xd2\x74\x3e\xc5\x43\xfd\x3e\x31\x98\x24\x38\x18\x5e\x2b\xfb\x9d\x76\x20\x9b\x47\x34\x6d\x65\x81\x88\x66\xcb\xb1\x3d\x37\xff\x5a\xea\xd0\x44\x18\x17\x98\xa8\x27\x29\x5e\x9a\xd7\xfb\xf5\x45\xf3\x68\x59\x58\x7f\xa1\xc4\x6d\x91\x3c\x55\x21\x1d\x70\x2a\x40\x82\xf8\xdd\x3c\xe0\x93\xa5\x53\x52\x57\x0f\xab\xec\x4a\xe5\xcd\x62\xd7\xa8\x8b\x70\x41\x08\x1b\x6e\x13\x42\xd9\x93\xbd\x54\xcd\x8b\x0d\x94\xab\x1d\x65\xd0\x72\x94\xa2\x96\x66\xc2\x79\x43\xf2\xce\x6d\x22\xb1\xe8\xca\xe4\xcb\x70\x04\xf7\x25\xf4\xdf\xfc\xcb\x92\x45\x56\x18\xf6\x85\xc9\x3b\x0a\x9d\xb4\x52\xec\x9e\x64\x8b\x80\x87\x3b\x7d\xd2\x18\x59\xcb\x7b\xbf\x70\x85\xc1\x8c\xc5\x0b\x2a\xae\x8e\xe5\x35\x98\xe5\x05\x7b\x00\x39\x85\x34\x03\x80\xf3\xbd\x42\x64\xa0\x72\x4c\x6d\x2b\xc2\x88\x59\xf2\x32\x3b\x00\x66\x32\x73\x8e\x23\x30\xe6\xcd\x87\x26\xa2\x94\x7b\x80\xd1\xd0\xd9\xf9\xb0\x6c\x9d\x01\xa8\xb0\x14\x21\x69\x13\x75\x5a\x60\x72\x0c\x1f\xb8\xfd\xec\xde\x08\xc5\xd3\x90\xc8\x08\x15\x14\xd0\x4f\x97\xe1\x64\x82\xfa\x58\x34\x38\x44\x49\x10\x0d\xe3\xe9\xe4\xfa\x9e\x0e\xf7\xd4\x6a\x82\x0d\x53\x05\xed\xfd\x52\x81\x29\x25\x8d\x7f\x03\x2e\x44\x27\x79\x68\xb3\x20\x85\x1a\xab\xf8\x0a\x0f\xe6\x19\x2e\xad\xf0\x68\x54\x2b\x15\x96\xb8\xa3\xc2\xcc\xb7\x3c\x62\xd1\x3d\x41\xaf\xa0\x15\x32\x1c\xe4\xff\x57\xfc\x67\x66\x0a\x46\xe5\x6e\x9c\x9a\x2b\x9c\x44\x2b\x8c\xba\xa8\x62\xd3\x6d\xd4\x4f\xa7\x99\xcd\xb2\x47\x51\xfd\x83\xf7\x2a\xc9\x52\x22\x53\x38\xa5\x4e\x6b\xd5\x4a\x6b\xee\x70\xab\xa3\x4b\x5b\x59\xd7\xb6\xb4\x42\xe3\xcd\xd2\xc4\x03\x52\x81\x2b\x62\xdc\xc9\x34\xc8\x6c\x21\xdd\x94\xab\x2c\x91\xb7\x32\x1e\x80\xbf\x33\x60\x2d\xa1\xcd\x2c\x1f\x03\xb0\x9b\xb6\xd4\xe4\x22\x19\x34\x53\x90\xf3\x64\xb2\x7c\xcc\xd1\x4f\xb6\x3e\x5b\x4b\x0d\x2d\x53\x38\xbb\x9d\xa5\x8e\x98\x28\xb5\xe4\x61\x5c\x1e\xa9\x85\x14\x7d\x3b\xad\xb6\x4b\x33\xa0\xa9\xb8\x87\x8c\x2f\x73\x96\x67\xb0\xe4\x8a\x80\xe5\x11\xbf\x6e\xaf\x0f\x77\x44\x89\x13\x0a\x71\xf7\x37\x97\x86\xeb\x01\xf5\xe3\xef\xb6\x76\x6e\x10\xd9\x3e\xb9\x05\xa5\x6b\x17\x96\x52\x1e\x67\xb6\xf9\x5b\xdc\x52\x5a\x71\x47\x87\xfd\xce\x0f\x5f\x86\xa3\xae\xb2\x3d\x2b\x14\xb2\xa4\x7a\x9c\xb9\x54\x2d\xb3\x2f\x7f\x1f\xfa\xf2\x5c\xe9\xe0\x3b\x50\x47\xfc\x4d\xd4\xe6\x8e\xc5\x57\x48\x93\xbc\xc2\x87\xda\x17\x56\xf6\xe1\x1b\xae\xa0\x3f\x1f\x58\x83\x2d\xb7\xa3\x6f\xa4\x70\x30\x76\xd7\x38\xf3\x29\x77\x5d\xb2\x0b\x01\x4f\xc4\x16\x2e\xae\x28\xd8\xd3\xe1\x15\x32\x06\x7b\xa6\xdb\x9e\xcf\xbb\x93\x8a\xb1\xb4\x6f\x56\x97\xaa\xb0\xc5\x6a\x18\x54\x9d\x21\x09\xbc\x8a\x79\x4d\x5f\xe2\xbf\xce\x50\x03\x40\x58\xf3\xa3\xb7\xaf\xe8\xf1\x2d\x34\xf6\xc3\x2b\x9a\x0c\x04\x2a\x38\x87\x54\x39\x5b\x53\xc3\x4c\x0d\xba\x4f\x6f\xe2\x3c\xf1\xdd\x41\x1f\xfc\x17\xf0\xe3\x7b\x56\x10\x7f\xef\x8c\xf9\x7b\xd4\x13\xbb\x98\xe1\xb2\x8a\xe2\x3b\x31\xc6\x7b\x47\xd1\x56\x14\xdf\x17\xe3\x2e\xa8\x27\xfe\xe6\xbc\xfb\x9b\x2b\x8b\xbf\xfd\x56\x51\xd1\x6c\x7b\x3c\x27\xb4\xfb\xdb\x3b\x0a\xe9\xc3\xfd\xf7\x17\xae\xad\x43\x1d\xdf\x82\xbb\x47\x9e\x82\x5c\xaa\xf2\x44\xa6\x4b\x35\xa5\x25\xcb\x5f\x79\x73\x56\x69\x37\xbf\xd7\xa4\x94\xf7\x9e\x83\x72\xd9\xdc\x93\x5a\xce\x49\x0b\x31\x3b\xfd\xa4\x91\x76\x92\x57\xf4\x24\x9e\x04\xfd\xa8\x04\x2e\x7e\xea\xc9\x27\xf7\x83\x6c\x5c\x41\x8e\x14\x94\xf2\x78\xfd\x3e\x1e\x04\x13\x34\x8b\x27\xd7\xa3\x70\x82\xe2\x11\xa2\x9b\x16\x3b\xc5\x3b\x8e\xbc\x2c\xb6\xfd\x86\x5e\xd0\x68\x58\x63\x4c\xe2\xf5\x0e\x79\x7f\xf3\xca\x8e\x1d\xa4\xd8\x5a\xf6\x3f\x5b\x4c\x0d\x6c\x04\xe7\x7d\x32\x83\x26\x11\xef\x54\x67\x49\x9c\xc5\xe4\x13\xda\x20\xa7\x0f\xb3\x00\xab\x87\x36\x50\x84\x2f\x09\x02\xf9\x10\xa2\xf9\x64\xe2\x59\x28\x02\x03\xb9\x4c\x94\x78\x47\xae\x48\x9e\x7c\x4e\xf2\x95\xdc\x5e\xc5\xf6\xfb\xb0\x9f\x04\xc9\xf5\x22\x1d\xb9\x92\x1f\xd4\x0b\x0a\xb2\x85\x32\xad\x27\x11\x2e\x78\x97\x83\x09\x0a\xa3\x31\x4e\x42\x2d\x80\xab\x16\xd1\xc1\xcc\x33\x6a\x47\x18\xb5\xa7\xb3\x40\xd8\x3f\x1e\x63\x18\xdc\xe3\x84\x9f\xc1\x38\xc8\x38\x42\x2c\x94\x07\x15\x83\xac\x53\x25\x42\x79\x71\x00\xb9\xdc\x15\x5f\xe0\x24\x09\x87\x38\x45\x87\x54\x21\x12\xe2\x94\x32\xf0\xd9\x35\x0a\x23\x96\xcd\x58\x22\x50\xa0\x05\x33\x57\xc3\xc9\xb2\x00\x2c\x99\xcb\x53\x6e\x99\xa8\x81\x64\xa2\xf6\xaf\x4f\x28\x09\x6b\xd2\x4d\x8e\x49\xa2\xea\x2f\x16\xe2\xc9\xb0\x8b\x56\x20\x53\xd6\x8a\x69\x38\xe2\x6e\x93\xfc\x4d\x71\x36\x8e\x87\xb9\x3e\xf2\x4a\x69\x33\x46\xbe\xcb\xf1\x0c\x21\x3b\x9c\x21\x45\x5f\x33\xc8\xe6\xf3\xea\x0d\x62\x38\x0b\x2e\x23\xfb\x8b\xc2\x48\x88\xb0\x20\xd3\xea\xf9\xcc\x89\x37\xe7\xe7\x53\x1c\x39\x4c\x87\xc9\x8e\x92\x8f\x05\x92\xcc\x87\x9d\xbb\x64\x79\x67\xfa\x07\x27\x02\xcc\x4c\x8a\xbb\x7e\x85\xc2\xb1\x34\x71\xe3\xf4\x03\x6f\x72\x1c\xa4\x07\x97\x11\x23\xfb\xeb\xd2\x0a\xa9\xb9\x52\x16\x3e\x4f\xe4\x11\x36\x41\x5e\x9e\xbc\x58\xd8\x0f\x5a\x2b\x77\xba\x1d\xb5\xfe\x9f\x74\x3e\x23\xa2\x56\x14\x66\xd5\x80\x08\xa7\x6c\xeb\x0b\x92\xf3\x39\x19\x5d\xe7\x78\x20\x47\x06\x85\x9c\x71\x92\x1e\xb7\xc9\x4a\x8a\x24\x47\x0f\xa9\x52\x98\x4f\x3a\x5d\xa5\x36\x04\xb5\x83\xda\x7e\xe0\xd9\x76\x10\x57\x8c\x8f\x70\x82\xa3\x01\x69\x00\xc6\x79\x66\xae\x57\x6b\x18\x98\x5c\xec\x02\xe8\xdd\x67\x90\x2b\x35\x86\x8b\xa9\x6e\xc3\x4a\x49\x55\xa6\x49\x55\xde\xf3\x88\x8e\x03\x4c\x20\x5d\xb5\x76\x08\xd4\x4d\x3e\x1f\x32\x83\x4d\xa9\x2c\xae\xe1\x88\x28\x0d\x21\xe5\x00\x48\xa9\xfc\x77\xe6\x95\x3c\x62\x39\xda\x60\x6c\x93\xdf\x59\x2c\xe4\x45\xb4\x5c\x3e\xc7\xb3\x1b\x81\x25\x27\xe3\x64\xdb\x2b\x97\x47\x50\x57\xd6\x08\x7f\xa7\xaf\x13\x2f\xd5\xf0\xe2\xb7\x21\x9b\x3c\x77\x75\xcf\x5c\xa1\x03\xc6\xcc\x58\x92\x00\x20\x29\x30\xa1\x1f\x0e\x51\x1a\x4f\x31\x4d\x3d\x85\x2e\xc7\x38\x42\xd7\xf1\x3c\x11\x66\xf6\x01\x11\x67\x29\xf0\x7b\x8e\x9d\x7b\xd7\x5d\xd0\x74\x74\xce\xdb\xcb\x10\x65\x00\xd5\xaa\x3d\x32\x62\xe8\x6f\xb9\xdd\x2d\x44\xa3\xd0\x9c\xf6\xe2\x19\x11\x76\x66\x52\xee\x61\xf2\xce\x1d\xc4\x29\x05\x18\x68\x98\x34\x99\x6a\x0a\x9a\xc8\x7b\x9e\x52\xb6\x3a\xe9\xfe\x59\x54\x7e\xb9\xe5\xb8\x43\x23\xda\x25\xb6\xe8\x9f\x73\x8d\x8b\x88\x87\xfc\xb2\xed\x43\x30\x05\xa3\x89\x05\xf5\x10\xdb\xaa\x65\x31\x73\xb3\x56\x01\x96\x73\xb7\x58\x32\x9d\xa7\x6a\xf1\x33\xb4\xa1\xb4\xaf\x7f\x5a\x22\x75\x91\x67\x93\xdd\x46\x97\x71\xb4\x92\x51\xf9\x99\xbb\x3b\x2a\xc1\x0b\x27\x71\x3c\x43\x41\x3f\xbe\x70\x6c\x83\xf9\x5d\x5e\xe1\xd0\x56\xfc\x1d\x06\x2e\x2a\x5a\x55\xfb\x29\xde\x16\xc8\xab\x55\x68\xf1\x88\xc3\x09\xf4\x14\xec\x5f\x96\x59\x37\xae\x8d\x6f\x30\x89\x23\xfc\x00\x1c\x0f\xe0\xa2\x0d\xb9\x87\xc0\x8b\x02\x3b\x19\x29\xb6\x70\x23\x53\x73\x91\xe8\xc2\x11\xe7\xa7\x4e\x7b\x32\xf7\x19\xd9\x79\xbb\x1f\xa1\x00\x3c\x6f\x8d\x58\x84\xb9\x91\x85\xac\x38\xef\xf9\x20\x5c\xe1\x69\x84\xf1\x83\x1e\x0e\x31\x0d\xcf\xa3\x70\x14\x0e\x82\x28\x63\x01\x25\x43\xda\x7b\x00\x49\xdb\x71\x1d\x93\x7f\x55\x3c\x88\xe9\x59\x59\x7d\x73\x0f\x61\x63\xec\xe6\x4d\xb2\xf0\x84\xc1\x57\x4d\xaf\x16\x8c\x35\x72\x9a\x85\x89\x91\x32\x6e\x30\x16\x0e\x1a\xbe\xb7\x54\x2f\xaa\x7f\xb6\xb6\xb1\x5b\xb6\x30\x1e\xed\x7f\x71\x00\xa7\xb5\xab\x5a\xad\x56\xaf\x35\x6a\xcd\x0a\xaa\x5d\xd5\x5a\xb5\x76\xad\x53\x5b\x3b\x7b\x30\xc0\x15\xd4\x29\x1c\x7a\x85\x85\xaf\xe3\x33\x62\xad\xd8\x4b\xe6\x10\x0c\xcb\x95\x3f\xd0\x7f\xbf\x7e\x85\x98\xbd\x86\xa8\x31\x42\x25\x31\xbd\x3f\x6c\x38\x14\x85\xea\x1f\x40\x55\x8c\x86\xf8\xcf\xc2\xc6\xa4\x26\x00\x4a\x1e\x13\x1c\x9d\x67\x63\x6a\x7a\xe4\xe5\x22\xc5\x63\xc6\xc8\x85\xb2\x5c\xa4\x98\xed\x68\x10\x0f\x09\xbd\x63\xfa\xc3\x24\x77\x78\x9d\x1f\xfb\x53\x10\x00\x8e\x06\xd5\x5d\x7c\xe5\x6f\x73\x51\x00\x99\x42\xab\x7d\xe9\xe0\x2e\x92\x58\x0b\x44\x76\x71\xc4\x35\x58\x14\xd6\xc5\x51\x45\x1b\x92\x8f\xd9\x68\x7d\xa9\x68\x2e\x6c\x2a\xbc\xb1\x5c\xf8\x54\x7d\xfd\x8a\x76\xf1\x55\x6e\xf8\x96\x05\x04\x34\x08\x32\x1c\xb1\x3d\x5f\xa7\x20\x0f\xf3\xf7\x13\x92\x72\x0f\x2b\x07\xfc\x84\x71\x43\x85\x32\x21\xcd\xef\xb2\xf7\xba\x45\x71\x29\x42\x1b\x02\xbb\x3a\x8f\x9f\x21\xde\x34\xfc\x29\xcd\xa0\xa4\xc9\x94\x68\x60\xe7\xe5\xc2\x91\x90\x81\xfd\xd5\x62\x58\x0e\x5f\xc5\x6c\x1c\x88\x50\x07\x92\xc4\xfc\xa5\xc3\xf4\x58\xf2\x18\x8d\xe7\x78\x80\x1f\xeb\x2c\x89\xc2\x97\x75\xac\x4e\xf5\x26\xc1\x74\x86\xf0\x15\x44\x92\xec\x87\x66\xe7\xe8\xbd\x2a\x29\x63\xdf\x36\xd0\xfb\xd4\x81\x2b\x48\x8a\x86\xf8\xbf\x3c\x81\xd2\xa1\x3e\x11\x49\x23\x0c\x5b\x2d\x0a\x32\x14\xa0\x2c\x9c\x3a\x24\x6e\x57\x48\x76\xb5\xbb\xfe\xa4\x10\xea\xe0\x90\xa2\x68\x83\xa0\xc7\x66\xe1\x34\xe4\x51\xb1\xc9\x3f\xa5\x46\x0b\xbd\x40\xa5\x90\x62\xfc\x13\x5a\x2f\x97\x45\xb4\x6c\xaf\x14\x4f\xe1\xe8\x3d\x7e\x8e\x42\x11\x6e\xfb\xeb\x86\x6c\xfa\xf5\x6b\xde\x86\xa3\xbc\x68\xb4\x80\xe0\xef\xdd\x96\xd4\x31\xa5\x8b\xeb\x4e\x63\xea\x8f\x72\x5f\xb4\xfb\x1b\xc8\x1e\xec\x22\x19\x83\x6d\x2a\x14\x9b\xed\xf3\x0d\x1d\x4d\x57\x8e\x95\x20\x8c\x82\xbe\x79\xf2\x50\x0e\x00\x45\xd9\x29\x8d\xc1\x41\x84\x40\x4d\x30\x0c\xb3\xbb\x8a\x82\x72\x71\x8a\xd5\xe5\x61\x52\xe4\x73\xd1\xd0\xbd\x0e\xd6\x64\xcb\x51\xae\xb8\x48\x5e\x26\xe3\x66\x18\x0e\x51\xed\x54\xc0\xe0\x71\xe6\x37\x60\xe9\xd0\x3f\x20\xfd\x66\x83\x90\x7e\xaa\xf1\x05\x07\xc1\x6b\xa2\xd4\x06\xda\x0f\xb2\x71\x75\x80\xc3\x89\xac\xb9\x8a\x96\x88\x48\xe4\x3e\xff\x16\xda\x79\x3c\xe6\x48\xd6\xf1\xf7\xb6\x76\x9f\xec\xb8\xab\xd2\x82\x75\xde\xd5\x69\x61\xd1\x39\x57\x05\x0b\x27\x35\x8a\xab\x1a\xfd\xdc\x3e\x39\x57\x6d\x1a\x61\xe6\xf7\x35\xaf\x49\x1d\xa9\xb7\xfc\x14\x28\x62\xc3\x28\x9c\x4c\x78\xd8\x59\xe6\x26\x01\xe7\xad\xc5\x42\x09\x3f\xcc\x45\xae\x43\xaf\x0a\xca\xeb\xe2\x53\x68\x96\x19\xa4\x42\x84\x72\x5f\xc6\x67\x05\x8e\x60\xcc\x15\xa4\xee\x3f\x69\xd1\x12\x2a\x99\x44\xee\x23\x96\xca\x1e\xec\x03\x15\xf9\x9a\xe8\x37\xe4\xd3\x4f\x97\xfe\x28\xf3\x9f\x2e\xd1\x06\xf9\xaf\x27\x81\xda\xf4\xd3\x1f\x64\x9b\xb9\x6a\x06\x43\xdc\x59\xef\x9b\xe1\xd7\x45\xb1\x20\xfd\x82\x54\xce\x91\x73\x4f\x50\xe0\xee\x8e\xb6\x5a\xaa\x5d\xbd\xac\x75\x5e\xa2\x9f\x48\x17\xfe\x80\x3d\x7d\x67\x67\x67\xa7\x8c\x9e\xd3\x17\x3f\xff\x8c\x6a\x57\xf5\x1a\x6c\xf7\x04\x01\xcf\x76\x4f\xbb\x58\xaa\x5d\xb5\x3a\xed\x1a\x05\x76\x69\x02\xbb\x2c\x0a\x0c\x86\x17\xa7\x73\xf0\xf4\x29\x01\x1a\xaf\x5f\xd3\x9a\xe8\x39\x82\x91\xce\xad\xcf\xea\xae\x6e\x40\x1d\xf6\x97\x5f\xf6\xf9\x06\xaa\x55\xdb\xde\x32\x30\xa6\xac\xe8\x4f\xd4\xde\x86\x53\x5b\x19\xfd\x8c\xaa\x6d\xf4\x1f\xa8\x8e\xba\xe8\x45\xbd\x88\x88\x62\x71\x0e\x5d\xdc\xa8\xa0\x64\x10\x0c\xc6\x98\x65\xd7\x59\x2c\x70\x90\x9a\x9f\x08\x3d\x26\xa5\x12\xad\x4a\x8e\x4a\x1a\x92\x64\x37\x51\x06\xc3\x7d\xc5\x44\xab\x6e\xa0\x4f\x49\x89\x96\x07\x82\x5c\xeb\xaf\x39\xfa\x74\x29\x73\xf8\x94\x44\x79\x09\x1f\x7d\x45\xb5\x82\x61\xcd\x23\x7c\xa9\x38\x3b\xc1\xad\x23\x53\x80\x44\x3c\x7d\xcf\x13\x63\x24\xdd\xce\xa7\xec\x68\xbf\xc8\x90\x06\x47\x03\x30\xa4\xa1\xff\xba\x0d\x69\x76\xf1\x95\xad\x09\x70\x81\x23\x05\x37\x28\xd0\x2a\xfd\x5d\x2c\xfe\xa6\xa9\xbe\x18\xe3\xab\xc2\x2a\x8c\x02\x27\xcf\x25\xa3\x6a\x16\x6a\xfd\xbe\x18\xf9\x18\x5f\xd9\x21\x34\xd9\xf8\x29\x47\xfb\xc5\x89\x84\x9c\x81\x33\x6f\x7b\x4c\xbd\x2c\x7c\xf2\x4c\x97\x3d\x46\xd2\x59\xb7\x01\x8d\xf1\x55\x6f\x1c\x24\x85\xf3\x6c\xa5\x0b\x0f\x74\x90\x23\x2d\xa4\x07\xb9\xcb\x3b\x1e\xe2\x38\x76\x6c\x8d\x03\x58\x02\xa4\x55\x96\x6a\x9f\x7a\xa7\xec\xe2\x77\xae\xaa\xa4\x9d\xda\x28\xbf\xae\x87\x41\x08\x70\x9f\xe3\x30\x2a\xad\xac\xdc\x22\xe2\xa6\x42\xe1\x74\xbd\x2d\xa3\xe9\xe1\x2b\x85\x12\x6e\xf1\x05\xe3\x11\x9e\xfe\x7a\xa9\x89\x2f\x36\x6a\xb3\x2d\xd6\x63\xf1\x48\x99\xb4\xca\x72\x89\x52\x68\x9d\xf7\xfc\xe8\x42\x1f\xd9\x51\x66\x99\x55\x73\xb9\x4c\x6a\x3a\xb5\x51\xb6\x85\x36\x72\xf2\x63\xd2\xd5\xd2\x04\xcd\x04\x74\x7a\x2f\xca\x58\x67\xab\xe9\xbc\x9f\x66\x49\x29\xac\xa0\x46\xb9\x02\x49\xf8\xa4\xca\x82\xac\xa8\xf5\xb2\xcb\x01\x77\xe9\x3d\x4f\x1b\xa6\x55\xd4\x28\xea\x3e\xfb\x3e\xc8\xc2\xa8\x5e\x6c\xd3\x62\x65\xf9\xbe\x25\x1e\x6f\xb7\x75\xb1\xea\x7f\xdd\xee\x55\x14\x81\xfb\x5a\x53\x13\x68\xcf\xbd\x87\x51\x5c\xfe\x47\x6d\x63\x74\x38\xbe\xe3\x9d\x4c\x41\x90\xee\x48\x74\xea\xaa\xa3\x24\x9e\x92\xb7\xbd\x78\x88\x61\x93\x2a\xba\x21\xa9\x00\xef\xb0\x27\x69\x74\x7b\xfb\x6d\x49\x90\xe3\x52\x8b\xe1\xbb\xde\x9c\xd8\x2a\xa2\xfb\x93\xba\xdc\x8a\x6f\x51\xa2\xd6\x72\xbb\x94\xa8\x26\x36\x2a\xf1\xe6\xa1\xf7\x2a\xa3\xe9\x45\xb9\x9c\x43\x45\x8b\x2e\x7b\x5b\x1d\x30\x82\xde\xcc\x4a\x21\x5f\x13\xe6\x56\xe5\xd6\x2d\x2e\xbd\x55\x19\x08\x17\xdd\xa9\x3e\x9e\xec\xbc\x58\x2f\xb6\x51\x7d\xcc\x46\xeb\x62\x9b\x62\x0f\xb7\xdb\xa4\x68\xa3\x7f\xdd\x1e\x55\xb0\xfd\xfb\x5a\x59\xf3\x6c\xb4\xee\xde\xa0\xc8\x28\x3e\xe4\xf6\x94\x25\xd7\x39\x06\x46\x43\x4c\x8e\xe8\x1f\x8f\xf6\x7a\xdc\xd3\xa9\x84\xd3\x41\x30\xc3\xa5\x9c\x8d\xd3\x66\xcb\x68\x10\x64\x83\x31\x2a\xd9\xe9\xa3\x01\x85\x71\x12\x5f\x02\xdd\x42\xc6\x95\xd2\xca\x7e\x30\x19\xc5\xc9\x14\x0f\xd9\x34\x0c\x83\x2c\xb0\x53\xd0\x2d\xcf\xc0\xd5\x49\xbd\x3d\xff\x66\x73\xb5\x0c\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\x6e\x49\x86\xc5\x19\x37\xab\xe3\x33\x06\xd0\xb6\x86\x79\xc4\xa8\x87\x5a\x08\x68\x74\xc5\xe1\x94\x0b\x07\xa0\x11\x29\x78\x21\x17\x26\x1e\xb2\x6c\x66\x8a\x17\xba\x37\x13\xaf\x62\x27\x7b\xad\xa4\x44\x9b\xce\xd3\x0c\xf5\x31\x0a\xc9\x88\x4e\x71\x94\xd1\x3c\x6b\x01\x5c\xaf\x27\x38\x13\x1e\x0b\x85\x72\xfb\x1a\x79\x3a\x75\xe5\x3e\xcd\x71\x48\x5d\xab\x64\x82\xf8\x2f\x78\x96\xa1\x79\x34\xe3\x49\x03\xf5\xec\xa0\x8a\x4d\x4b\xcd\xc1\x7d\xdf\xb0\x71\x80\x4c\x83\x9b\x62\x14\x84\x97\x98\xef\x73\x41\x33\x38\xc8\xee\xca\xac\x79\x8c\x91\x5e\x61\x49\xb4\x59\x12\xd3\x2c\x46\x61\x96\x72\xaf\x18\x44\x28\xf8\xae\x77\x4c\x7d\x27\xf2\x34\x21\xae\xff\x92\xa9\x50\xd6\x5d\x66\xde\x87\xc0\x4a\xd9\x65\x33\x00\x19\x38\x99\xa7\xa2\xb1\xb3\x9a\x4c\x89\x96\x8f\xb6\x82\x2c\xe0\xc2\x7a\xad\xa8\xa4\xb9\x39\x1c\xa6\xd0\x06\xcf\x0b\xee\x19\x69\x46\x0b\xc5\x37\x45\x11\x64\xc1\xca\x3c\xce\x8c\x5d\x10\x5d\xf3\xcc\x09\x80\xf2\x4b\xea\x53\x12\x28\x16\x94\xd4\x9e\x18\x38\xde\xc3\x4c\xe6\x27\x8a\x4e\x69\xc5\xe6\xf7\x85\xea\x2d\xde\x1b\x59\xc9\x22\xc9\xcc\x6d\xf7\x7a\x99\x8e\x4e\x0d\x28\xaa\x0c\x10\x0b\x26\xaa\x83\x52\x7d\x9c\x81\x8c\x16\xc4\x89\x64\xb4\xa6\x30\x65\xc0\x70\x71\xa4\xb4\x4d\xe8\x9a\x8f\x7c\xb9\x29\x91\x0b\x98\x45\xb4\xcf\x37\xf4\x24\xe9\x45\x29\x98\xe7\x3a\x4d\x51\x70\x11\x84\x13\x88\xd8\x45\xf9\x02\x30\x3b\x3f\xd5\x9c\x28\xce\x2a\x61\x74\x11\x7f\xc1\xa9\x99\x64\xb8\xc4\x92\x03\x57\xd0\xe5\x38\x1c\x8c\x9d\xac\xba\x7f\x9d\xc3\xaa\xed\x56\xf9\x42\xe9\xc7\xf1\x04\x07\xd1\x0d\x1a\xc6\x3b\x93\x79\x3a\x46\xbf\x8e\x71\x46\xe3\x99\xf0\x5c\xb4\xe0\xae\x35\x0b\x12\x60\x14\xec\x95\xe4\xda\x82\x5d\xdf\x22\x1c\x88\xe0\xf4\x30\xe2\x77\xdf\xe6\x05\xc0\x2d\x4a\x48\xbe\x35\xc3\x53\xe5\xfa\xe2\x72\x2c\x09\xc6\x9d\x29\x58\x8f\xb5\x4a\x8b\x6a\x8b\x8f\x0e\xf8\x92\x3a\x13\xb6\x44\x24\x71\x3b\xb4\x25\xe4\x35\x37\x4e\x83\x91\xf5\xa9\x55\xc8\x47\xc5\xd0\xcc\x47\xf7\xbc\xb8\x94\x15\x36\x8c\x94\xcc\x79\x85\x39\x74\x59\xdb\x1d\xd1\xaf\x17\xcf\xa3\x8c\xd3\x97\x83\x99\x10\xa0\x11\x4d\x24\x7c\x04\x71\x8b\x37\x74\xfc\x57\x8d\x26\x5f\xd9\xbc\xc8\x37\xe4\x0c\x83\xa3\x78\x1e\x0d\xd1\x7c\x46\x1d\x0a\x07\x93\xf9\x10\x1b\x74\x6f\x57\x33\x30\x92\x46\x2e\xea\x87\xe2\xb1\x6d\x05\x16\xc3\xf8\x32\x52\xf1\x88\xa3\xc9\x35\x1a\xcd\xc5\xa2\x74\x44\xd2\x5f\x5d\x45\x13\x9c\x52\xa7\x4a\xb7\xac\x05\x7c\x23\xc1\xd3\x20\x8c\x74\xe1\xaa\x58\xbf\xa6\xc1\x55\x49\xeb\x17\x5c\x9c\xa2\x17\xae\xcc\xec\x95\xc5\x57\xaa\x62\xce\xa9\xe6\xc1\x37\xe5\x40\xc9\x1c\x0f\xad\xf5\x9f\x90\x42\x80\x3e\x7a\x02\xda\xf0\x92\x13\xf9\xaa\xf7\x31\x8c\x4a\x6a\x93\x3f\xa1\x56\x45\xa3\x33\x97\xf9\x24\xcf\xe0\xed\x22\x12\x42\x77\x0a\xc0\x7c\xb7\x2d\xca\xe7\xa9\x9a\x85\xfd\x7e\xad\x8e\x80\x78\xfb\x5c\x59\x4f\x5e\xa3\x09\x82\x19\x4e\xc8\x69\x52\x6c\x0c\x2f\xe4\x01\x01\x9c\x21\xdd\x15\x19\x77\xd1\xf7\x20\xc1\x55\x5c\xb9\xea\x7d\x73\x8c\xb4\x14\x58\x92\xe1\xc3\x94\xdb\x45\x35\xee\xab\xb2\x30\x33\x19\x96\x3a\xa2\x0e\x34\x34\x4e\x86\x5e\x6c\xa8\x33\xbd\x98\x2a\x79\x6c\xd1\x3c\x6c\xfd\x0a\x27\x1d\xff\x8a\xda\xf4\x5d\x8d\xdd\x0a\x67\xa1\xcc\x75\xf2\xba\xa3\x95\x9b\x67\x37\xfc\x8b\x4c\xde\x3e\x59\x1b\xa2\xc4\xc4\x39\x63\xb9\x16\x6f\x3a\x0f\x13\x27\x4d\x4f\x26\x7a\x7e\x06\x1f\x07\x29\x64\xc8\xf5\x9e\xb8\x17\xa6\x22\x97\xec\x5a\xf5\x81\xa2\x93\xce\xa0\xd3\xb0\x6b\x38\x45\x71\xa4\x1c\x85\xeb\x1d\x54\x6a\xd7\x1b\x60\xc9\x5a\x76\x1c\x8b\x77\x69\x65\x7e\x0c\x16\x8f\xee\xf3\xf0\xbd\x44\x7d\xcd\xcb\x40\x96\x1b\x30\x35\xcf\xd5\x8c\x0e\xc2\x12\x39\xc9\x6f\x1b\xdd\x8e\x34\x84\x68\x88\xe4\x45\x41\xee\x0a\xdb\x90\x88\x39\xd0\x42\xb7\x1d\xef\x6e\x36\xda\x1d\xb7\x93\x58\x5e\xaa\xeb\x5b\x47\x58\xe3\xb1\xd5\x8a\x87\x59\x3b\xc6\x22\xbc\x87\x5f\x43\x60\xab\x21\x16\x58\x62\x4b\x4d\x0a\x5f\x38\xf7\xaf\x32\x61\xf4\x72\x1f\x2a\x12\x40\x58\x55\xf1\xe8\x25\x3c\x2b\x09\x40\x6b\xcc\xcb\x96\x1a\xcc\xbd\x99\x0d\x87\x63\x63\xe6\x1b\xf2\xd1\x72\x63\xfd\x71\x36\x04\x96\xa1\x0e\x36\x4d\xcb\x5f\x3c\x63\x9f\x37\x82\x30\x05\x6e\xc6\x11\x2e\xec\x42\x44\x59\x11\xf3\x1f\x5a\xb8\xbc\x97\x98\xf3\x39\xe0\x55\x5a\x61\x48\xb9\x74\x29\x7a\xc9\xc5\xaa\x13\x5a\x50\x25\x14\x6d\x0c\x3c\xeb\xd1\xa3\x91\x60\x0a\x1b\x1d\x82\x83\x3c\xd8\xf8\x12\x21\x9d\xe0\xeb\x02\xa5\x9c\x63\x6d\xf1\xf7\xde\x7c\x27\x76\x58\x92\x9b\x54\xe0\xe2\x65\x90\xe8\x43\x0c\x28\x07\x19\xcd\x17\xcf\x6a\xca\x98\xa1\x28\x4c\x11\x1e\x8d\xf0\x20\x0b\x2f\xf0\xe4\x1a\x05\x68\x88\xd3\x2c\x99\xc3\x73\x05\xe4\xf4\x17\x71\x34\xc0\x85\xa2\x8c\x16\xa4\x50\x2d\xd1\x03\xa0\x24\x03\x72\x43\x89\xe5\x35\x17\x64\x10\xee\x69\x67\x40\x1b\x9c\x1c\x45\x32\x21\x8f\x5a\xc2\x53\x3a\x8f\xd0\x73\xaa\x2d\xa6\x7a\x5e\x74\x29\xba\xdf\x71\x8c\xaf\x7d\x20\xca\x07\x83\x16\xad\x95\x45\x02\xfc\x12\x9c\x55\x19\x21\xce\x64\x77\x94\x79\x70\x2e\x1e\x52\xde\xb7\x78\x94\xe4\x77\xed\x7a\x63\xb5\xd9\x28\x26\xe6\xa7\x4c\xe3\xa3\xc5\xbf\x0f\xd8\xa4\xad\x88\xc0\x49\x61\x94\xe1\x64\xa4\x58\x0b\x23\xef\xaa\xe0\xfc\x95\x75\x9d\x53\x2d\xdd\x6e\x59\x7c\xc4\x00\x8d\xf1\x64\x86\x13\x22\xfe\x14\x58\x04\x3b\x0c\x37\xe6\x1b\x6c\xa2\xfc\x0d\xee\xf1\xa8\xcc\x64\x3a\x55\xd0\xae\x56\x3f\xd1\x5e\xed\x42\x97\x4a\x2e\x61\xcb\xaf\x9f\x53\xab\x6a\xc6\x83\x00\xda\x77\xbf\x67\xad\x0b\x77\x00\x5c\xa4\x9f\x17\xd9\x4a\x84\xc3\xa2\x9e\x45\x4c\x66\xb8\xd4\x29\x7c\xf9\x63\xa3\x93\x9e\x08\x4b\xde\xdd\xdf\xec\xdd\x3f\x3d\x11\x11\x9a\x07\xa5\x20\x2d\x30\xba\xfa\x5b\xd0\xd4\xee\x34\x18\x14\xa2\xab\x69\x30\xb8\x0b\x6d\x89\xea\x77\xa2\xaf\x2f\xd8\xad\x42\x52\xe8\xab\xf7\x09\xd0\x22\xf3\x40\x89\x8c\x36\x42\xeb\x2e\x47\x6c\xb9\xc7\x5f\xa1\x49\x5a\xe0\xc3\x40\xb0\x01\x27\x06\xf6\x43\x7a\x31\xf0\x4c\x2d\x10\xd2\x77\x3f\xc8\xc6\x34\xac\xef\x13\xfe\x9e\x0d\xf3\x2b\x19\xe9\xf7\xe6\xac\xd2\x6e\x7d\xaf\xe1\x7d\x19\x32\x25\x1e\x8e\xb8\x7c\xef\xf1\x7e\x39\xe4\x65\xe3\xfe\x0a\x0c\xd5\xf8\xbf\xbe\xa0\xbf\xe2\x3b\x04\xff\x75\x05\xd0\xb5\xaf\x28\x78\xd4\x58\x39\x65\x0a\x01\x28\xd1\x60\x95\xf7\x39\xe1\x69\xb4\xda\x8a\x0b\x8c\x2f\x8c\x6c\xa7\x55\xcc\x44\x8b\x95\xe5\x46\x5a\xe2\xf1\x76\x66\x5a\xac\xfa\x5f\x67\xa7\x55\x14\x81\xfb\xe2\x94\x7d\x68\xcf\x6d\xaa\x45\x71\xf9\x07\xd8\x12\x5b\xe5\xa7\xc1\x4c\x08\x87\xd3\x60\xb6\x7c\xec\x05\x87\x8b\xb8\x0d\xc2\x67\x95\x49\xc7\xfc\xb6\x06\xcb\xe8\xf9\x06\x6a\xfa\x6d\x96\xaf\x33\x5c\x77\x18\x2d\xd3\x3f\x9f\xe9\x32\xfd\xf3\x1a\x30\x73\xc0\x0d\x09\xb8\x14\xa2\xe7\xa8\x5e\x76\xd8\x44\xf3\x2f\x45\x2c\xa3\x39\xe0\xa6\x01\xb8\xe1\x05\xdc\x70\x02\x76\x43\xce\x92\x70\x36\x81\xab\x97\x12\x1d\x96\xd7\xaf\xc1\x6f\xe2\x2b\x7d\x6e\x90\xe7\x75\xf2\x08\x28\xb8\xa0\x88\xa9\xf8\x4c\xa7\xa2\xf4\x19\xbd\x26\xad\xff\xf8\x23\x02\x6c\x3e\xa3\x9f\x50\xad\xba\xd6\x56\x66\xa8\xfc\x0a\x7d\xce\x09\x77\xa1\xcc\x3d\xb5\x05\x9f\x06\x33\xb0\x99\xdd\xcc\x4a\x25\x8e\x30\x74\xba\x83\x7e\x42\xa5\x26\x7a\x81\x3e\x97\x59\x4f\x9b\x23\xa7\xb7\x93\x15\x9f\xc1\x56\x5c\x0c\x87\x3c\xdd\xb7\x4d\x8d\xec\x03\x41\x09\x6d\x20\x05\x9d\x8e\xe5\x4c\x02\xb1\xf5\x64\x71\xb7\x71\xf0\x38\x9c\x60\x54\x52\xfb\xc9\xc2\x05\xf8\x62\x8d\x38\x87\x45\x6d\x66\xf9\x3e\x33\xce\xaa\x42\xbd\x83\x9d\xbc\xc6\x93\x6f\x6f\x67\x29\x58\xed\x52\x8c\xfe\xbb\x36\xb5\x64\x3b\x04\xb5\xeb\x51\xb7\x92\xe2\xe6\x96\xa2\xd6\x92\x9b\x83\xa8\x27\x0c\xe5\xc5\x1b\x61\x28\xbf\x98\xef\x5b\x25\x12\x7c\x81\x93\x14\xef\x2b\x05\xe5\x2b\x57\x5c\xb3\x1f\xe4\x67\x2f\x75\xe7\x02\x75\x6d\x01\xfc\xcf\xe4\x3f\x84\xfd\x90\x15\xca\x3a\x98\xcb\x69\xf4\x86\x4f\xf9\xc2\x66\xb6\xf9\x9f\xcb\x67\x68\x03\x7d\x2e\x16\xab\xd3\xc1\x52\xf6\xce\xa3\x38\xc1\xdf\x8c\xab\x28\x20\xf7\xa2\x21\xf8\x39\xcb\xe9\x0e\xc9\x9b\x83\xd1\x22\x9e\xa1\xb4\x43\x61\xfc\xb0\xb1\x81\x5e\xd4\x17\xf0\x24\x95\xc2\xd4\xda\xb7\x62\xc4\x4e\x91\x20\x11\x69\x2f\x53\xfc\x3e\x8e\x67\x72\x49\x54\x4c\x1c\x2a\xca\x8c\x6a\x22\x87\x71\xe3\x19\xcc\xba\x68\x65\xf3\x4d\x6f\x6b\x7b\xe7\xed\xee\xde\x7f\xbd\x7b\xbf\xff\xe1\xe0\xf0\x7f\x1f\x1d\x9f\x7c\xfc\xe5\xd7\xdf\xfe\xfd\x7f\x82\xfe\x60\x88\x47\xe7\xe3\xf0\xf3\x97\xc9\x34\x8a\x67\xff\x9d\xa4\xd9\xfc\xe2\xf2\xea\xfa\x8f\x5a\xbd\xd1\x6c\xb5\x3b\x6b\xeb\x2f\x9f\xaf\x6e\xb0\x08\xb7\xe2\x68\x27\x16\xed\xd2\xa8\xca\x21\xf6\x78\xa5\x48\xcb\x0d\xcd\xc2\xd4\x25\x0a\x19\xed\xb8\xdc\x54\xc8\x4c\x87\x9e\xfd\x86\x39\x76\xa5\x44\x48\x52\x96\x87\xa4\x26\xd5\x81\x05\xbd\x40\xf5\xf2\x19\x78\xaf\x48\x81\xa9\x61\x13\x17\x07\xda\x28\x02\xb4\x7c\xc6\x37\x78\x55\x0c\x73\x40\xa5\x02\x51\xa4\x45\xee\xf9\x4a\x84\x19\x40\xff\x2b\x6d\x51\xf5\xad\x89\xf2\x83\xf7\x20\x36\xc4\xcf\x9f\x6b\x1f\x04\xd9\x8a\x1f\x8c\x22\xad\xd8\x92\xce\xb0\x08\x37\x32\x77\x8f\x79\xc8\x57\xf6\x88\x57\xde\xcc\x3e\xed\xc7\xa3\xff\xe3\xd1\x5f\x1c\xfd\x3f\x9e\xec\xbc\xa8\x77\xd0\x9b\xed\xc2\x0e\x5a\xf5\xce\x9b\x6d\xd5\x47\xab\xde\xd1\x9f\xe0\xeb\xed\x9d\xb6\x28\x32\x7f\xad\xe3\x56\x41\x1c\xee\xd1\x79\xab\xde\xf1\x7a\x6f\xd5\x3b\xff\x00\x8d\x40\xf1\xc3\x3a\x0c\xc6\x5d\xce\xea\x6e\x7f\x7f\xb0\x8c\x8a\x87\xf8\x30\x0e\xa3\xcc\xe7\x64\x5c\xef\x78\x9c\x8c\x9d\x87\x69\x89\xa9\xdf\xcb\x58\x34\x59\xd4\xd5\x58\x01\x7a\x87\x13\x94\x49\xc4\x77\x72\x56\x03\xda\x5c\x76\x6d\x7c\xd7\xc7\x28\xba\xaa\x84\xcb\x1a\x5f\x7c\x4b\xf9\xac\x41\xa5\xe5\x7c\x8d\x79\x2d\x21\xdf\xf2\x17\x0f\xed\x69\xac\x37\x5c\xcc\xd1\xb8\x0e\xb2\x8f\xc0\x50\x77\x33\x26\x22\x90\x5c\x2c\x0d\xb2\x58\x8c\x20\x6c\x7e\x0a\xf7\x49\x39\xc6\xe8\xfc\x54\x3c\x14\x06\x23\xcb\xf7\x05\xf6\x30\x65\x9f\x7a\x7f\xe7\x7d\xea\xfd\x77\xb0\x4f\x15\xc1\xe1\xbe\xf7\x29\xe7\x72\x7a\xbf\xfd\xb8\x4d\x89\xbf\x7b\xdb\xa6\xd2\xcb\x60\xb6\x1d\x0d\xc3\x20\x2a\x2d\xbb\x63\xb9\x8e\xe4\xdf\xff\x96\xf5\xfe\x61\xb6\xac\x22\xcb\xe4\xfb\xdf\xb2\xde\x6f\x1b\x9b\xd6\xe3\x8e\x65\xed\x58\xca\x8a\x59\x6a\xf3\xfa\xa6\xbb\x97\x98\x17\x05\x5b\x02\x48\xeb\x23\x8f\x86\x0f\x5f\xd8\xdd\x09\x5d\xdc\xb5\x1a\xf9\x7f\xb8\x58\xa1\x1f\x49\xf7\xd9\x57\xfa\x4d\x2e\xff\x45\xea\x02\x20\x2c\xbf\xb6\xa0\x73\x27\x6d\x01\xcb\x51\xfb\x2d\x95\x06\x15\xa4\xbc\x4a\xc7\x41\xdd\x78\x35\x9e\x06\x83\x07\x54\x2d\x54\x10\x6f\x16\x7e\x41\x6b\xff\x04\x75\x83\x95\x2f\xf6\x16\xaa\x08\xcd\x88\x45\xf9\xb2\xbf\xd5\x86\x9a\x60\x72\xb3\xbf\xd5\x76\xc9\x78\x60\xe2\xfc\x05\x5f\xd3\x2c\xd8\xd4\x0e\x56\xf4\x15\x9c\x7f\x83\x28\xe3\x49\xbc\xe3\x64\x4a\x6d\xb4\xb7\x7f\x39\xfc\x04\x9b\xee\x49\xfc\x0e\x4b\x61\x10\x5d\x5e\x5e\x56\xe3\x19\x8e\xd2\x74\x52\x8d\x93\xf3\xd5\x61\x3c\x48\x57\x21\x09\x77\xbc\x6a\xd4\x19\x67\xd3\x89\x43\x11\xb2\x7d\x31\x7b\xb7\xb5\x23\xd1\x16\xcf\x05\x83\x21\x2c\xf6\x01\x31\xf6\x38\xcb\xfb\x85\xa5\x3c\x87\x3d\x8a\x0c\x4c\x4a\x1e\xc2\x88\xbb\xbd\x28\xe1\x9e\xa5\xab\x4b\x0b\x95\xea\x8d\x75\xcd\xd3\xc5\x82\xef\x31\x52\x53\xc3\x62\x98\x09\x52\xf6\xb7\xda\x8b\xb0\x0d\x33\x66\x8b\x6c\x06\xa9\x56\x3e\x64\x31\x9a\x51\xab\x53\xd5\x3b\xc7\xb3\xc3\x59\x7e\x31\xc6\xee\xc0\x86\xa7\x8b\xea\x8d\x75\x30\x21\xd5\xbe\xd2\xce\x01\xe6\xc6\x17\x89\x8f\xd6\xf6\xcd\xad\xdd\x6e\x3c\x44\xfb\xd0\x7e\x38\x58\x69\xf4\x1e\xcc\xac\xbf\x0c\x47\x96\xf7\x0d\xa5\xf9\x05\x29\x9a\x16\x57\xfc\x53\xce\xd5\xba\x91\xcf\xef\xb6\x60\x2a\xfa\x34\xd6\x6a\x35\x13\xf0\x92\xde\x41\x0b\xfd\x7e\x8a\xc9\xbb\x5b\x90\xc2\x9f\xd0\x08\xa1\x0a\x48\x84\x1d\x40\x06\x56\xb2\x68\x6f\x63\xa5\xcf\xeb\xd2\x58\x00\x2e\x40\x39\x95\xd3\x60\x92\xa1\x4d\xf8\x67\x79\xb1\x18\xa8\x8b\x92\xf7\x7d\x90\x17\x26\x9b\xc7\x97\xe1\xa8\x4a\xdd\x22\x70\x89\x77\xa6\x02\xf8\xe5\xe4\xad\x81\xe2\x5a\x7e\x47\xbd\xe6\x52\x02\xaf\x3e\xc5\x0e\xf1\x96\xac\x74\xc6\x3d\xec\xda\xc2\x4b\x8d\x90\x07\x33\x51\x96\xab\xc3\x09\xcb\xe7\x16\x06\xa1\x05\xe8\x10\xbf\x83\xb1\x71\xa5\x44\x5b\xe6\x8c\x2c\x81\x09\x9f\x60\xf1\xc6\x7b\x5c\xe6\x7b\x0c\xed\x11\x7b\x72\x94\x53\x98\x38\x2d\x2a\x5f\x38\xb0\x7c\xcb\x36\x26\x02\x5e\xff\xc8\x8c\x59\x0c\x5c\xb9\x41\xcb\x6b\x8e\x8f\xf3\x28\x40\xc4\x38\xf0\x1c\xf0\x5e\x30\xeb\x2e\x4b\xb4\xec\xe2\x6b\x65\xa4\x06\x63\x90\x4e\x20\x0c\x0a\x27\x36\xc5\x28\xd8\xa2\x57\xbd\x79\xe1\x4f\x67\x97\x20\x34\x21\x06\xce\xfe\xac\x1d\x94\xea\xf4\xa0\xa4\x0c\x74\x6e\xda\x1f\x03\x7b\x81\xac\x77\x14\x5c\x18\x3b\x86\xca\x7e\xa7\x90\x15\x8b\x19\xe3\x6c\xc3\x18\x65\xa5\x96\xa2\xa3\xe1\xf4\xe7\x88\x76\x21\x02\xcc\xf1\x7a\x45\x6d\xae\x0b\xf1\x60\xd5\xef\xf8\x56\xbc\x77\x49\xbe\x7b\x8f\xde\xb7\x0e\xbf\x32\xa5\x37\xc5\xb9\xb9\x52\x49\xd3\x6e\x28\xef\x75\xee\x2e\x3f\x20\x8d\xab\x8b\x4d\x9b\xee\xd7\x3e\xce\xbe\x5c\xb5\x0a\xf2\x88\x0d\x77\x01\x93\x2b\x36\x08\x15\xb2\x94\xf5\x7d\x7b\x8e\xed\xc2\xc2\x86\x5d\x97\x58\xc0\x71\x25\x7f\xbf\xbb\x79\x95\x73\x7c\xa7\xd0\xdc\x67\xf7\x0a\x3f\x7c\x76\xdb\xeb\x15\x7e\x24\xed\xae\xad\x91\x33\xfd\xda\xdf\xfa\x4c\x3f\x08\x67\x63\x9c\xbc\x78\x60\x13\x01\x38\xbd\xab\x4d\xfd\x35\x87\x78\x3b\x73\xe7\xbd\x9c\xe6\x7b\xd0\xb1\x43\xc2\x71\x52\x71\x68\x57\x5f\xfa\x4d\x08\xc4\x7b\x23\x13\x86\x56\x83\x9c\xe1\x82\x0c\x2a\xd1\x9f\x9c\x11\xb3\x8a\x3b\xf0\x32\x63\x51\x15\x68\x91\x25\xd2\x69\x90\xd3\x0d\x9d\x9b\x0c\x5f\x65\xe4\x14\x19\xb0\x67\x34\xa3\x7d\x62\xbe\x59\x3c\xd5\x46\x30\xc4\x83\x70\x1a\x4c\x26\xd7\x2c\x0d\xe8\xb0\xf0\xcd\x8d\x3a\x2a\x37\xac\x15\x36\x70\x27\x02\x0d\xbd\xd9\xe5\x93\x71\xdc\x06\xbf\x07\x4d\xcf\x21\xa7\x44\xb9\xd5\x51\x3b\xbf\xdc\xc5\x8e\x56\xd3\xe3\xa8\xa5\x96\xa9\xca\xd9\x95\x09\x24\x76\xf1\xd5\x2d\x33\x41\x38\x86\x57\x21\x1f\xf5\xbe\x61\xc9\xe9\x34\x6e\x1e\xc2\x68\x36\xcf\xee\x32\xa7\x9c\x3c\x74\xa2\xbb\x05\x9d\xdd\x17\x71\x0c\x0c\x46\xe1\xa0\x8f\x5b\x27\x95\x80\xd1\x72\x87\xb0\x91\x93\xb3\x81\x64\x1b\xb4\xc2\x2b\x27\xf5\xf4\x34\xea\xe1\x1a\x01\x09\xa8\xab\x02\xbd\x71\xeb\xe6\xfd\x3b\xad\xec\xae\xb1\xdb\x2a\x1b\x44\xb7\xdd\xa8\x18\xca\xf3\xf5\x47\x53\xbb\x7f\xba\xee\xdb\xb7\x3b\x5a\x91\xcc\xf3\x34\xe1\xf6\x21\x05\x1c\x80\x85\xc6\xd5\x99\x88\x8a\x94\xd8\x50\x1d\x55\xef\x27\x21\x3d\xb8\xbc\x2e\xe4\x78\x85\x95\xc4\x05\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x70\x76\x4f\x4a\x25\x22\xff\xee\xba\x03\x07\x41\x2f\x19\x9b\x70\x79\x22\x53\x47\xdf\xa2\x1a\x43\xd5\x39\xd8\x13\x20\xd8\xa9\x33\x12\xfa\x22\xea\xa3\x20\x1e\x4d\x0f\xf7\x1c\x6f\xb7\xfb\x8c\x2f\x0b\x07\xa6\x05\xe1\x65\xe9\xa1\x4a\x89\x2e\x6b\x8e\x93\xdb\x10\x3f\x47\x31\x45\x3b\xfa\x46\x89\x8b\xc9\xba\x9e\x17\x19\xd3\xa8\xc4\xf5\x05\x26\x2c\x77\x94\xcc\xcd\xc9\x24\xbe\x44\x41\xd2\x0f\xb3\x24\x48\xae\x11\x53\x2f\x7d\xc1\xd7\x8e\xb8\x83\x5f\x54\x8d\xc4\xcf\xce\x86\x73\x06\xca\x54\xb7\x14\x1b\xad\x05\xce\x90\x04\xa5\x1c\x37\x48\x88\xff\x06\xba\x8d\x38\x41\x61\x14\xe1\x04\xa2\xcf\xc6\xf3\x0c\x04\x08\x33\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x05\xa4\xe3\x1a\x3f\xb5\x46\xe8\xa8\xb1\x0c\x09\xc4\x8a\x56\x32\xce\xd3\x47\x86\x4a\xc1\x50\x29\x68\x35\xf6\xdb\xc1\x11\xcc\x27\xbd\x06\x9c\x05\x43\x34\x88\xa3\x34\x0b\x22\xb3\x79\x67\x12\x29\x7d\x8e\xfd\x8a\x35\x81\xf7\x69\x78\x86\x7e\xdf\x40\xb5\xab\xf6\x80\xfe\xcf\xe5\x0e\x63\x15\x6e\x76\xe8\xff\xf2\x35\x63\xb1\xa1\x13\x0b\x8d\x67\x17\x45\xfe\x05\x71\xc8\x60\x07\x7a\x88\x28\x64\x82\x89\xdf\x4b\x24\xb2\x9c\x7c\x65\x2e\x66\xec\x18\x48\xe8\xb4\x8b\x8f\x7b\xf4\xa4\xba\xbe\x58\x2e\x98\xdb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x84\x25\x96\x1b\x09\x65\xc9\x29\x2f\x12\x88\xcc\x2a\x7d\xff\xc1\xc8\x54\x92\xe0\xad\x2c\x0c\x3e\xf6\x50\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x5b\x20\xa6\x2e\x23\x22\x12\x1e\x2a\x69\x68\x61\x04\x31\x7f\x8d\x85\x51\xc4\xfc\x55\x1f\x28\x92\xd8\xdd\xb9\x5d\x8f\xaa\xa7\x61\xbc\x1d\xfb\x31\x91\x2e\x76\xdd\xc1\xd1\x72\x03\x8e\xe5\x72\x4c\x75\xac\x0c\xa0\x52\x42\xe1\x92\x06\xbf\x64\x12\xa8\x94\xbd\x21\xc7\xa6\xc1\xc0\x7d\x49\x24\x0e\xfe\x1e\x23\xb8\x97\x7f\x6b\x85\xf9\x55\xa7\xf5\xc2\xf1\x7a\x12\xf6\x5f\x10\x54\x86\x60\xdb\x9a\x1a\x5f\x71\x34\x78\x01\x36\x8d\x8e\xf7\xd4\xcd\xd2\xf8\x30\x1d\xb6\x17\x1b\xdf\xa5\xe3\xa0\xd1\x36\x41\x92\x97\x0d\x13\x5c\x3a\x0e\xda\xf5\x86\xfd\xb2\xb9\xee\x28\xd9\x34\x5e\x25\xe1\x0c\x4f\x87\xf5\x4e\xcd\x69\xfb\xa7\xbd\x9a\xf5\xbf\x0c\x47\x66\x3b\xf8\x62\xf6\x65\x38\xca\xbb\x77\xd0\xbb\x1e\x0f\xf1\x8b\xc1\xa8\xef\x7c\x9d\x25\x9e\xd7\x2f\xce\x27\xc1\x70\x1a\x44\xae\xcf\xb1\x1b\x18\x1e\x98\xaf\x67\xc1\xf0\x45\x10\xa5\xe1\xd5\xcb\x86\x39\x08\xe4\x53\x98\xc6\xf5\x5a\xbd\x61\x8e\x38\xfb\xf4\x72\xed\xe5\x9a\x39\x43\xe4\xd3\x1f\x38\x89\x99\xeb\xb5\xe3\x6b\xe4\xf9\x46\x75\x64\x2f\xc6\xf8\xca\xf8\x10\x60\x93\xb8\x68\xdc\x8d\xa1\xf5\x3e\x19\x98\x93\x9b\x04\xfd\x7e\x98\x39\x5f\xbe\x98\xe0\xf3\x60\x70\xfd\xd0\x77\x40\x62\xf5\xc0\x93\xb9\x68\xe0\xa5\x5c\x2b\xe2\x91\x2d\x11\x78\x26\x2b\xc3\x30\x0b\x65\xeb\x40\xfc\x6e\xb4\xc4\x6f\x42\xf5\xfc\x37\x21\x76\xf1\x9b\xfe\x92\xa4\x2d\xed\x4b\xe1\x17\x23\x64\x8a\x01\xa5\x5f\xeb\x0e\x8b\xa2\xc3\xa9\x55\x79\xca\x12\xfd\x49\xd0\xa6\x7c\x1b\x6b\x35\x08\x25\xd2\x66\x55\x02\x14\x6f\x04\xdd\xa9\x6f\x28\xb9\x89\x37\x2a\x95\x89\x97\x91\xfe\x4a\xa1\x29\x78\x26\xa4\x04\x3f\x24\x05\xd1\x51\x19\xb0\x81\x62\xf4\xa2\xfc\xe6\x64\xb2\xac\x22\x52\x53\x40\xaa\xbc\x76\x79\xc5\xa4\x3f\x14\x1b\xeb\x52\xb7\x5d\xaf\xe4\x6b\x93\x2b\x3a\x5d\x75\xdb\xad\x8a\x46\x78\xdd\x76\xbb\x22\x27\xbe\xdb\xee\x54\xf4\xd1\xeb\xb6\xd7\xcc\x1b\x61\x93\x94\xbb\x9d\x5a\x85\x51\x6b\xb7\x03\xf8\x08\x4a\xe9\x76\x1a\x15\x95\x56\xba\x9d\x56\xc5\x45\x2d\xdd\x4e\xb3\xa2\x52\x48\xb7\xd3\xae\xa8\xf4\xd3\xed\x00\x5e\x1a\xcd\x74\x3b\x6b\x15\x93\x6a\xba\x9d\xf5\x8a\x49\x37\xdd\xce\xcb\x8a\x45\x24\xdd\xb5\x5a\xc5\x41\x4e\xdd\x35\xc0\x9f\x2d\x89\xee\x1a\x60\xcf\x48\xa3\xbb\xd6\xaa\x58\xc4\xd1\x5d\x03\xc4\x09\x19\x75\xd7\x00\x67\xb9\xce\xba\x6b\x1d\xf5\x02\xbd\x22\x97\x6c\x77\x8d\x5f\xad\x93\xc5\xdc\x5d\x7b\x59\xe1\x4b\xb5\xbb\x5e\xab\xc8\x25\xdc\x5d\xaf\x57\xe4\xe2\xee\xae\x03\x3a\x92\x82\xbb\xeb\xd0\xb8\x60\x34\xdd\xf5\xd6\xcd\x59\xa5\x53\x7b\xbc\x3c\xf8\xeb\x2f\x0f\x7a\x63\x3c\xf8\x42\x3a\x05\x2b\x85\xba\x01\xd1\x34\x67\xe9\x7c\x46\x06\x06\xb3\xf8\xd4\x4a\xbf\x41\x8e\xa7\x21\xcd\xd1\x0f\x1b\x68\x85\x43\x5e\x71\x58\x84\x08\x27\x8d\x7b\xbc\xae\xc8\x35\xc7\x17\xed\x1c\xe1\x11\x4e\x30\x1c\xf4\x92\xf0\x1c\xce\x64\x61\x14\x66\x12\x4c\x3a\x9f\xe1\x04\x54\xd7\x1b\x46\x7a\x0e\x05\xca\xe6\xfc\x7c\x8a\xa3\xcc\x28\x80\xb2\x18\x8d\x83\x68\x38\xc1\xda\xb8\xa9\xb0\xfb\x4e\xc8\x9a\x4d\x0d\x54\xb5\xdd\x01\x15\xdd\x37\x8d\x25\x4f\x4d\xa0\xc2\x28\x5b\x57\x34\xf4\x23\xb5\xbe\x50\x4c\xe8\xb3\x63\x1f\xf3\x65\x0d\xaa\x84\xff\x48\xa0\xc2\x0b\x15\x1b\xed\x10\xe1\x44\x2c\xa6\xe9\xbf\x00\xd2\x45\x88\x2f\x7d\x28\x7a\x9b\x57\x10\xde\xe3\x28\xa0\xaf\x5f\xf5\xf2\x9c\xe0\x00\x4b\xd0\x19\xf3\xea\x3f\x90\x35\x27\x6c\x47\x60\xd1\xb9\x81\x5b\x55\xcb\x56\x2b\x5e\xac\xea\x1d\x37\x5a\xfe\x96\x96\xab\xb1\x17\x65\xcd\xc6\xb2\x4d\x2c\x57\x63\x67\x12\x07\xb7\xa9\xd2\x69\xc1\x7b\x59\xfe\x96\xa4\x54\xa5\x14\x5c\x41\xea\xab\xeb\x0c\x1f\x40\x72\x20\xeb\xb5\x2b\xef\xb2\x46\x7f\xbb\x74\xd1\xc9\xb6\x8a\xac\x08\x59\x7a\x39\x15\x82\x84\xf6\x46\xe0\x86\x36\xdc\x38\x3b\x34\x0b\xdb\x57\x2c\xfb\xea\x75\xe6\x32\x7e\x5e\xca\x5d\xd0\x85\xca\x32\xf9\xb4\x65\xfd\xd3\xf0\xec\x56\xc9\xb3\xa5\x39\x77\xf8\x07\xa6\xaa\x5a\xe9\x38\xaa\x17\x15\x8c\x55\xa6\xb6\xa8\x20\xe6\x46\xe8\xea\x88\x36\xdf\xce\xac\x67\x64\x34\xc9\x6b\x02\x0f\x45\x44\xea\x53\x99\xb9\xdd\x6e\x30\x9b\x4d\xae\x59\xc3\x41\x72\x3e\x27\x2c\x3c\xcd\xf3\x57\x64\xfc\xba\x3a\x4b\xe2\x2c\x26\x38\xaa\x9c\x3b\xcf\x70\xc2\xdc\x7d\xdc\x0a\x96\x4e\xfd\x51\xd6\xf9\x6b\x64\x1d\x08\x18\xfd\x17\xc4\x25\x72\xe6\x54\x2a\x60\x22\x01\x5b\x2c\xbd\xc7\x43\x99\xd4\xad\x93\x2a\x27\x8c\x59\x28\x25\xa9\xea\xd2\xb8\xf9\x73\x49\x7a\x3e\xbe\xd2\x69\xb9\xb9\xc8\x09\x61\x13\x1b\x74\xf8\xaa\x41\x3f\xa5\x3f\xd2\x30\x62\xc1\x58\x09\xcb\xa8\x5d\xd5\x6b\xec\xaf\x8c\xbe\xea\x69\x7c\xd9\xf2\x2a\x95\x9d\x16\xea\xfb\x5b\x6d\xc3\x9a\xc2\x65\x00\x62\x7a\x4d\xa2\x0d\x36\xaa\x0e\x03\x10\x9e\xf6\x26\xf7\x76\x4c\x6a\x82\xdd\xb9\x8a\x4f\x6d\x4e\x5a\xbb\xea\xac\xb5\xda\x8d\x66\xad\x5e\x41\xb5\x2b\x3c\x1a\x0c\x83\xfe\xfa\x4b\x47\x5e\xc5\xda\xd5\xcb\xf5\x7e\x30\x1c\x8c\x70\x05\x06\xa6\xd9\x68\xb7\xd6\x3a\x7a\xb9\x33\xef\x8d\x98\x91\x46\x4f\xed\xc5\xbe\xc8\xa4\xe7\xda\xbb\x2e\x83\x19\xc2\xe0\x5e\xbd\x78\x0f\xa9\x77\xfc\x3b\x86\xff\xfa\x9a\xcf\x06\x45\xe2\x13\x81\xc7\xd3\x0b\xa2\xd0\x13\x81\x77\xff\x93\x52\x7a\xff\x94\x3f\x9c\xb9\x5c\x42\x94\xcf\x84\xe0\xec\x02\xe4\xaf\x54\x2a\x29\x30\xa9\xa7\x38\xfa\x8a\xd4\x97\xb0\xd7\xb5\xca\x86\x8f\x38\xfa\x5a\x10\x60\xa3\x55\x76\x00\x84\x50\xc6\x9a\x4b\xba\x0d\xee\x6e\xc6\x21\xbb\xda\x0d\x85\xfb\xba\x5f\x1b\xd2\x1a\x52\xc6\x14\x3d\x47\x35\x53\x7c\xd0\x4a\xd7\x8d\xd2\xf5\xdc\xd2\x0d\xa3\x74\x23\xb7\x74\xd3\x28\xdd\xcc\x2d\xdd\x32\x4a\xb7\x72\x4b\xb7\x8d\xd2\xed\xdc\xd2\x1d\xa3\x74\x27\xb7\xf4\x9a\x51\x7a\x2d\xb7\xf4\xba\x51\x7a\x3d\xb7\xf4\x4b\xa3\xf4\xcb\xfc\xd9\xa9\x19\xb3\xb3\x60\x32\xeb\x46\xf1\xfc\xd9\xac\x37\x8c\xe2\xf9\xd3\x59\x6f\x1a\xc5\xf3\xe7\xb3\xde\x32\x8a\xe7\x4f\x68\xbd\x6d\x14\x6f\x5b\xdc\x60\x75\x95\x30\xe4\x2f\x61\x74\x4e\xaa\x86\xc1\xa4\xef\x12\x9b\x03\xb2\x0d\x9c\x3a\x07\xaa\x0f\x9f\x9c\x83\x32\x80\x4f\xce\x01\x18\xc2\xa7\xa6\x0b\x9d\x9e\xbc\x83\xd6\xbf\x11\x24\x76\x76\x4a\x41\x05\xf5\x2b\x68\x50\x41\xc3\x8a\xb2\x40\x2b\x08\xad\x55\xc8\x16\x5a\x3b\x33\x79\xc3\x90\xd6\x1b\x56\x90\xa8\x2a\x47\xa8\x82\x50\xbd\x51\x41\x27\xa7\x75\xab\xde\x80\xd6\xa3\x2d\xd1\xaa\x72\xd1\x92\x7a\x6b\xa4\x5e\xc3\xaa\xd7\xa7\xf5\x04\x92\x81\x52\xaf\x59\x41\xa8\x01\xed\x35\xad\x7a\x79\xfd\x6b\x89\xfe\xb5\x96\xea\x5f\x5b\xf4\xaf\xbd\x54\xff\x3a\xa2\x7f\x9d\xa5\xfa\xb7\x26\xfa\xb7\xb6\x54\xff\xd6\x45\xff\xd6\x97\xea\xdf\x4b\xd1\xbf\x97\x4b\xf5\xaf\x5e\xab\xb0\xfe\xd5\x6d\x82\xc9\xeb\x60\xbd\x5e\x61\x1d\xac\xdb\x14\x93\xd7\x43\x82\x25\xed\x61\xdd\x26\x99\x5c\x12\x6d\x56\x38\x89\xda\x34\x93\xdb\xc7\x96\xe8\xa3\x4d\x34\xb9\x7d\x6c\x8b\x3e\x02\xd5\xd8\x9d\x7c\xfb\xd6\xd3\xc9\x0a\x42\x6d\xda\x49\x9b\x6e\x86\xb4\xa2\xb3\x93\x84\xde\x5e\xd2\x8a\x36\xe1\x0c\x68\x45\x77\x27\xeb\x15\x44\x3a\x7a\x72\x5a\xb7\x29\xa7\x4f\x2b\x3a\x3b\x49\x38\x46\xa3\x06\x15\x6d\xd2\xc9\xeb\x63\x5b\xf4\xb1\xe1\xe6\x35\xbe\x3e\x12\x9a\xa3\x7d\x6c\xb8\x99\x8d\xb7\x8f\x6d\xde\xc7\x86\x9b\xdb\xf8\xfa\xd8\x12\x7d\x6c\xb8\xd9\x8d\xaf\x8f\x2f\x65\x1f\xdd\xfc\xc6\xdb\xc7\x96\xe8\xa3\x9b\xe1\xf8\xfa\x48\x18\x23\xeb\xa3\x9b\xe3\xf8\xfa\xb8\x2e\xfb\xe8\x66\x39\x5e\x5a\x6d\x56\x78\x1f\xdd\x3c\xc7\xd7\xc7\x86\xa0\xd5\x86\x9b\xe9\xf8\xfa\xb8\x26\xfa\xd8\x74\x33\x1d\x5f\x1f\xc9\xf2\xa7\x7d\x6c\xd6\xdd\x0b\x72\x77\xd7\x4f\xac\x2d\xc0\xb5\xe9\xe6\x3a\xbb\xbb\xee\x4e\x92\x61\x25\x6b\xeb\xe4\xb4\xe9\xe6\x3a\xbb\xbb\x39\x0b\xb2\x03\x15\xdd\x5c\x67\x77\xd7\xd3\xc9\x56\x05\x35\x9a\x50\xd1\x26\x9d\xbc\x3e\xd6\x65\x1f\xdd\x4c\xc7\xd7\xc7\x96\xec\xa3\x9b\xe9\xf8\xfa\x08\x13\x49\xfb\xe8\x66\x3a\xde\x3e\xd6\x44\x1f\xdd\x4c\xc7\xdb\xc7\x66\x85\xf5\xb1\xe5\x66\x3a\xbe\x3e\xd6\x44\x1f\x5b\x6e\xa6\xe3\xeb\x63\x53\xf4\xb1\xe5\x66\x3a\xbe\x3e\x12\x56\x4e\xfb\xd8\x72\x33\x1d\x5f\x1f\x5f\x8a\x79\x6c\xb9\x99\x8e\xaf\x8f\x64\x79\xb0\x3e\xba\x99\x8e\x97\x56\xdb\x9c\x56\x5b\x6e\xa6\xe3\xeb\x63\x43\xf6\x71\xcd\xbd\x20\xf7\xf6\xfc\x82\x6a\x87\x76\xd2\xcd\x75\xf6\xf6\xdc\x9d\x04\x9a\x03\x1e\xd0\x72\x73\x9d\xbd\xbd\x1c\x31\xa0\x0d\x22\xa0\x9b\xeb\xec\xed\xb9\x3b\x49\x78\x47\x03\x86\xb5\xed\x16\x75\x7c\x7d\x24\xf3\x41\xfb\xd8\x76\x33\x1d\x5f\x1f\x9b\xa2\x8f\x6d\x37\xd3\xf1\xf6\xb1\x26\xfa\xe8\x66\x3a\xbe\x3e\xd6\x65\x1f\xdd\x4c\xc7\xd7\xc7\x75\x31\x8f\x6d\x37\xd3\xf1\xf5\x11\x68\x8e\xf6\xd1\xcd\x74\x7c\x7d\x04\x91\x9c\xf6\xd1\xcd\x74\xbc\x7d\x6c\x56\x78\x1f\xdd\x4c\xc7\xd7\xc7\x96\xe8\x63\xc7\xcd\x74\xbc\x7d\xac\xf3\x3e\x76\xdc\x4c\xc7\xd7\xc7\x86\xe8\x63\xc7\xcd\x74\x7c\x7d\x7c\x29\xe6\xb1\xd3\xb4\x17\x24\x5c\xa3\x64\x38\x99\xe2\x61\x18\x64\xcc\xa9\x0c\xdc\x15\xf4\x72\xe4\x88\x8b\x36\x50\x09\xfe\x7d\x8e\x02\x53\xc3\x4a\xcb\xd4\x59\x99\x3a\x29\xd3\x77\x97\x69\xb0\x32\x0d\x52\x66\xe0\x2e\xd3\x64\x65\x9a\xa4\xcc\xd0\xd2\xe6\x1a\xaa\xca\x1d\x87\xa5\xee\x92\x01\x6d\x21\x53\xba\xc8\xa6\x1b\x64\x81\xeb\x60\x1e\x64\x81\x08\xe5\x13\x64\x81\x5f\x39\x16\xbd\x09\xb3\xf4\x24\xce\x82\x89\x80\x19\x6d\x05\x59\x40\x3d\x48\x7e\x42\xeb\x0e\xe8\x50\xe7\x3d\x1e\x65\x1c\xba\xf0\x38\x81\xf2\x56\x67\xbc\x29\xaf\x04\x9a\xa7\x12\xe4\xcf\x3f\xff\x8c\xda\x70\xf1\x56\xbb\x5a\xaf\xc9\xfb\x36\x59\xe2\x5f\xa8\xd9\xb0\x88\x43\xef\xcb\x2e\xda\x40\xa0\x76\x1f\x4d\xe2\x38\x29\x29\x9d\x5c\xd5\x74\xef\xbe\xce\x41\xd9\xf7\x68\x43\x79\x32\x17\x8e\x40\xbd\x54\x2a\x49\xdc\x9e\xa3\x4e\x8b\xe6\x4b\x7b\x09\xc1\x44\x5b\x65\xaa\xb0\x71\xeb\x67\x79\x55\x86\xb3\x54\xce\xaa\x6f\x8b\x6b\x67\x6d\x70\x4c\x35\x6b\x82\x5b\xa4\x9b\xb5\xb8\xc4\x32\x9d\x6d\x15\xe9\xec\x7b\x67\x67\xdf\xdf\xb6\xb3\xef\x9d\x9d\x7d\x5f\xb4\xb3\x76\x6f\x55\x27\xaa\x92\xe8\x3e\x0f\x36\x05\x39\xf5\xdc\xfe\x83\x60\xf0\x4e\xdd\x18\xc0\x47\xd1\xe5\x49\x95\x9b\x57\x7e\x81\x37\xa4\xa6\xf3\x76\x90\xef\x2e\x33\x8c\xf7\x7a\xbf\x2d\x75\xef\xe1\xb9\xe2\x42\x79\xd7\xff\x02\x13\xb8\xc2\xd8\x3d\x75\xdf\x5d\xec\xb2\x5b\xb2\x52\x69\x57\xbb\x96\xd8\x5d\xfa\x3e\x82\xd2\xc2\xae\x76\x17\xb1\xeb\xbd\x84\x58\x7c\xe3\x70\xc4\x72\x03\xc3\x1c\xb2\x08\x3c\x43\x18\x53\xbd\x68\x81\x64\xe5\xe0\x86\x90\xcb\xea\x41\xc1\x0a\x4e\x99\xe2\x86\x0e\x1e\xe5\xf5\xbf\xb5\xf1\xc2\xe7\x4f\x16\x2d\xf8\xbc\x2b\x79\x04\x0d\xf2\xd5\xed\xe1\x40\x7f\x09\x24\x0d\xd5\xd7\x55\x05\xa5\x15\xa4\x5f\xa1\x01\x9f\x44\x1b\x28\x40\xcf\x51\xa9\xd4\x47\x3f\xd2\xcd\xb1\xf4\x7f\xc9\xcf\x61\x99\xb0\x81\x2b\xf4\x1c\x65\x4a\x7b\x22\x60\x71\x44\xa6\x29\xa5\x2b\x95\xc6\x29\x6f\x36\xd0\x0b\x94\x96\xa1\x5a\xdf\x30\x7a\x13\x58\x19\xe7\xff\x62\x58\xc1\x76\x5c\x1a\xa0\x1f\xd1\xff\x7d\x18\xac\x8c\x43\xd0\x42\xac\xfa\xe8\x77\x34\x40\xbf\x13\xc4\xee\x1f\x19\x43\x00\x5c\x88\x0c\x41\xa4\xd4\x47\x5f\xef\x79\x70\xd4\xdb\xea\x63\x5f\x9a\xf4\x85\x89\xf7\x8b\x04\x59\xe3\x7e\x62\x86\x8b\x22\xac\x06\x1b\x8c\xc7\x59\xcc\x53\xfa\xb6\x61\xcd\xd8\xba\x14\x46\x2e\xfb\x5b\x6d\x87\xef\x57\x7e\x79\xdb\xe1\x4b\xc6\x17\xd3\x2e\xf3\xf5\x8c\xfc\xfb\x5b\x6d\xa7\xc9\x80\x77\x12\x16\xe4\xaa\xbf\xaf\x29\xb8\x55\x68\x87\xc5\x13\xa7\x7a\xf9\xdd\xc7\xc4\x51\xa7\x32\x31\x11\xbb\xd3\x60\x40\x26\x43\xcb\x0c\x6f\xcf\x07\x2b\x66\xcf\x89\xcc\x66\x4f\xe7\x25\x37\x03\x3b\x8b\x6c\xed\xb1\x80\x6a\xfc\xad\x5d\xcc\xfe\xf9\x31\xd9\xe8\x62\xfb\x89\xc5\x19\x42\x3b\x18\x0f\xfb\xc1\xe0\x0b\x8b\xab\x39\x8d\x87\xb0\xa4\x08\xcd\x88\xf9\x86\x97\xbd\x9d\x37\x44\x04\x72\x88\x07\x60\xe6\x04\x5f\x35\x6b\x39\xb0\x70\xa1\xad\xec\x13\x00\xcc\x98\x47\xac\xfa\xde\xce\x9b\xea\x76\x44\x63\x95\x83\x01\xd5\xce\x1b\x87\xc1\xcf\xcc\x63\x2e\xc3\xcc\x0c\x73\x4c\x66\xfc\xa2\x29\x0b\x41\xc5\x05\x12\xfa\xe8\xba\x67\x56\x42\x79\xd0\x42\x6a\x28\x0f\xbd\x3c\x8f\x51\xfe\x0e\x5f\xa7\x59\x82\x83\xe9\x66\x34\x64\xbd\x73\x58\x47\xc6\xcc\x2c\x56\x80\xab\xb0\x06\x5c\x42\xf6\x11\x9e\x62\x08\x32\x0e\xc6\x98\x74\x9e\x58\xac\x4c\xf0\x9f\x8f\xf0\x55\x46\x5f\xbb\xc5\x77\x7c\xf1\x86\xc5\x4c\x85\xd6\xab\xe9\x24\x1c\xe0\x12\x47\x41\xdc\xd4\x0b\x5c\x5c\xf6\x93\xda\xac\x6d\xe1\x7f\xca\xac\xdd\x61\x74\xc1\x70\x78\x1c\xa6\x4b\x8f\xed\x37\xa3\x9b\x13\xd9\xa1\x3e\x1e\xc4\x53\xe6\x75\x4f\x08\x22\x8c\xe7\x69\x31\x92\x11\x5d\x2c\x24\x8e\xe7\xf4\xa6\xb4\xb0\x0b\x86\x6f\x84\x7d\x60\x83\xf3\xde\x85\x0c\xd6\x72\xf1\x4a\x37\x1a\x57\xc3\x31\xd3\xe6\xe5\x67\xc8\xec\x7a\xe1\x3c\xd2\x88\xd2\x68\x03\x85\x17\x6c\x0a\x6b\x9e\x95\x18\x5f\x60\xb4\xf7\x0b\x9c\x3f\xd3\x79\x3f\xc5\xff\x3d\xc7\x51\x96\x73\x7a\x06\x7c\x85\x03\xc3\x42\x03\x68\x13\x1f\x63\x42\xec\x49\x20\x7f\x8c\xca\x31\x1d\x68\x28\x58\x12\x40\x2a\x48\xef\xca\xea\x2a\x62\x33\x22\xdf\x39\xb3\xe5\xe6\x47\x8d\xa1\xa6\xe7\xd2\x42\x10\x22\xc1\x88\x46\xe1\x1c\x6d\xd1\x0b\xc3\x82\x8b\x13\x3b\x6f\xf2\x0c\xae\xf9\xa6\xb3\x4c\x9c\xba\x4e\xf3\x51\xf8\xf8\xde\x85\x0f\xf4\x9f\xb3\x04\xa7\x38\xb9\xc0\x54\x0c\x89\xe7\x44\x94\x57\xc4\x0f\x50\x63\x04\x59\xd8\x9f\x30\x0e\x8c\xb6\x12\xf4\x26\x09\x83\x08\xbd\xa5\xee\x99\x68\x14\x4e\x30\x8e\x06\xd5\x01\x80\xe0\x21\x9f\x21\x02\xb6\x41\x3f\x27\x47\x50\xe4\xbf\x82\x08\xed\x26\xf3\xfe\x35\xfa\x3c\x26\xff\x54\x2f\x71\xff\x3f\xcf\xa7\x41\x38\xa9\x0e\xe2\xa9\x5b\xde\x39\x39\xe2\xcd\xe5\x88\x3d\x6a\xa1\xc2\xd2\xcf\x13\x99\xef\x25\x1a\x90\x83\x02\x4d\x99\xf4\xf4\xc9\x13\x32\xe8\x40\x7a\x22\x1d\x12\x28\x89\xa8\x52\xa8\x0c\xb3\x4e\x7f\xfd\x89\x56\x57\xe3\x0b\x9c\x8c\x26\xf1\x25\xa9\x03\x1b\x5f\x9d\xa7\x03\x25\xf5\xea\x9d\xf2\x8f\xa4\xec\x2b\xf1\xb9\xa1\x7e\x5e\x37\xbf\x36\xd9\x1e\xc6\x1a\x03\x3c\x01\x15\x02\x56\xb4\xbb\xba\x8a\x78\xb3\xa8\x5f\x27\x45\x00\x65\x68\xba\xf6\x4a\x54\x69\xc8\x2a\xa2\xcc\x13\x40\x80\x16\xa2\xa5\x9a\x7a\x29\x56\xec\x09\xa0\xc2\xca\xdd\xc0\x7f\x09\x41\xaa\x25\x9e\x3f\xef\x37\x95\xef\xf0\x1f\x5e\x86\x16\x79\xfe\xbc\xdf\x78\xf5\xd4\x5f\xe0\xf9\xf3\x7e\x9d\x7d\x27\xff\x85\x8e\xf3\x46\xe1\xe1\xf9\x06\xf4\xfc\xf5\x6b\x96\x0f\x52\x7d\xdd\xa0\x2a\x40\xed\x2d\x43\xc8\x6e\x49\x54\xab\x5d\xd5\xea\x4c\xeb\x27\x8b\x32\xae\x47\x0a\x91\x97\x37\x26\x75\xb0\xe5\x51\x1a\xd0\x7f\x75\x1a\x61\x2f\xe9\x0d\x12\x27\x25\xf9\xb2\xcc\x08\x46\x99\x82\xd5\x55\x44\x76\x09\xb8\x89\x41\xa1\xb2\x90\xe8\xe2\xb1\x56\xda\x4a\x8a\x00\x5e\x8a\xe2\x68\x72\x4d\x97\xe3\xd6\xaf\x07\x47\x5b\xe8\x33\x7a\x8d\xd6\x01\x26\x6f\xb0\xee\xc2\x82\xde\xc5\xe9\x9d\x65\xdf\x78\x7f\xf9\x5a\xd2\xce\x02\x62\x5d\x55\x3d\xaf\xff\x42\x99\x73\x59\x91\xd3\x2a\x6e\xc8\x30\x76\xab\x8c\x27\x8a\x66\xf9\x80\x59\xa8\xe7\x49\x3c\xc8\x2f\xf5\x80\xd0\xe0\x6e\x24\x5f\x06\x42\xb7\x90\x83\xd0\x62\x59\x88\x4b\x07\x84\xb0\x6d\x9a\xa7\xac\xe8\x89\x29\x1a\xb1\xcf\x0a\xae\xba\xea\x79\x19\xa1\x08\x79\x04\x23\x74\x3b\xe1\x08\x2d\x29\x20\x21\x5d\x9e\xb3\x0f\x5d\x92\xee\xd5\xb3\x97\x58\x1a\xaf\x0c\xc9\x4a\x14\x57\x04\x2c\xaf\x88\xa5\x14\x5e\x42\xd2\x6a\x3d\x4a\x5a\xdf\xbb\xa4\xe5\x91\xaf\x3c\xea\x9d\x93\xa3\x7c\x39\x67\x59\xf5\x8e\x83\xa5\x9b\xbc\xfc\x91\x89\xff\xf3\x98\x78\xee\x69\xf6\x01\x58\xf6\x5e\x34\x48\x30\x44\x6e\x60\xc0\x0d\x90\x4c\x0e\x91\x93\xfb\x02\x51\x63\x1a\xcf\x17\xb8\x2d\xff\x8a\x6a\x7f\xab\xcd\xa1\xe8\xae\xb0\xf8\xbc\x4d\xca\x2c\xb1\x0b\xb4\x1f\x77\x81\xbf\xc5\x2e\xb0\x3d\xc1\x83\x2c\x89\xa3\x70\x80\x7a\xf1\x10\xf7\xe3\x78\xb1\xc2\x7f\xbb\x97\xa7\xf0\xa7\x5f\x97\xda\x11\xb6\x7b\xba\xc2\x9f\x3c\xdf\xd7\x0e\xa0\xb2\x76\x9d\x81\xe8\xf5\xf2\xb4\x98\x04\x1f\x6d\x21\x3d\x14\x7e\x43\x7c\x2b\xfc\x78\xea\xa5\xde\x62\xbd\x19\x94\x59\x62\x1d\xff\xbd\x93\x23\xff\xcf\x59\xc7\x07\xf3\x6c\x36\xcf\x8a\x5f\xda\x1d\xe4\x5e\xda\x1d\x2c\x7f\x69\x67\x4a\x75\x07\xc6\x25\xde\xc1\x5f\x7b\x1d\xf4\xe0\x52\x9d\xad\x9b\x17\x6f\xee\x57\xb2\xcb\x69\xe8\x7b\x91\xee\xfe\x49\x27\xec\x03\xe3\x5a\xd3\x27\x44\x1d\x14\xb8\xb4\x38\x58\xf2\xd2\xe2\x31\x8b\xdd\xdf\x83\xf9\x6e\x7e\x38\xde\x43\xbf\x55\x5f\x36\x9a\xdc\x40\x1c\xa5\x19\x59\xde\xe7\xd7\x16\xf7\x9d\x05\xc3\xea\x66\x94\x86\xbf\x91\xd2\x22\x17\xdc\x2c\x18\xaa\xec\x6f\x18\x64\x81\x72\x11\xea\xbb\x00\x4d\xf5\x1b\x50\x52\xeb\x58\x1a\xfc\x6a\x06\xc0\xaf\xf4\xa2\x7d\x33\xad\x48\xdf\x97\x50\x04\x88\x62\x1e\x65\xa2\x67\x46\x30\x2b\xb0\xc5\x3b\xa4\xdf\x2c\x60\xf4\xc5\x0b\x1d\xb3\x7f\x19\xdf\xad\xd6\x68\x4c\x9b\x49\x90\xd2\xc8\x59\x68\x16\xa7\xa1\xee\x81\x4f\x1a\x25\xdf\x49\xfd\xc3\x98\x77\x56\xb4\xf0\xdc\xc0\xe8\x05\xaa\x1b\x8d\x1c\x06\x43\xf9\x0c\x03\x25\xb2\x8d\xe8\xaf\x29\x2b\x51\xdb\x92\x21\xb5\xf4\x46\x64\x48\x2d\xb5\xb4\x2b\xb8\x96\x6e\x99\xfd\xdc\x00\xc4\xed\x10\xb9\x05\xee\x3c\x72\x10\x87\x49\x11\x6f\x71\xa6\x24\x9c\xd7\xa6\x8a\x2a\xf0\xc5\x68\xe6\xcf\x9c\xd2\xe7\x92\x8e\xe6\x0b\x72\xfc\x65\x7d\x97\x17\x41\x0a\x0a\x6c\x5f\xb1\x3c\x24\x0c\x30\x9e\xde\x3e\x7d\x72\xe3\xe4\x9b\x7c\xb9\x5c\xbd\x6c\x34\x97\xe2\x9d\x77\x4b\x4c\xf6\xc8\x3b\xbf\x15\xef\xdc\x3b\x3e\x40\x10\x12\xb7\x18\xeb\xdc\x63\x01\x74\xef\xca\x3a\xff\x72\x76\x28\x97\xc4\x02\x7e\xe8\x60\x55\x34\x1d\x80\x3b\x02\x5d\x35\x09\xa2\x61\x3c\x2d\x59\x1c\xb0\x5c\xae\x1a\x92\x52\x3e\x1c\x96\x3a\xec\xd4\xe2\x72\x8d\xd6\x59\x85\x80\x7b\x64\x54\x26\xa3\xe2\xc4\xb9\x14\xa3\xfa\x7b\x67\x5e\xf8\x1f\xc5\xa8\x56\xf7\xb6\x7b\xe8\xe5\xda\xcb\xb5\x17\x75\xc4\x68\x03\xed\xe3\x6c\x1c\x0f\x51\xc3\xc7\xad\x20\xb4\xf7\x6d\xb9\xd5\xe6\x70\x48\xfd\x07\xf5\x05\x51\x80\x0b\xf0\xd5\x4b\x6a\xd3\x3f\xbe\x68\xb5\x06\xfe\x0f\x4e\x62\xc8\x1d\x96\x8d\x31\x4a\x70\xaa\xf0\x45\xad\x23\xa4\x1c\xeb\x31\x79\xb6\xf0\xbe\x15\x2f\x60\x0b\xf1\x0f\x86\x83\xbe\x1a\xbd\xcd\x03\x68\x0a\xcf\xbd\xb0\xe3\x08\xa3\x69\x9c\x60\x2a\x3c\xbe\x78\x01\x7d\xf3\x8d\x22\x5f\xef\x2f\x5e\x14\x5c\xe0\x30\x9f\xcb\x2c\xf0\xb5\xbb\x45\x39\x7f\x5c\xe0\xdf\xec\x14\x87\xa2\x38\x9e\x15\x13\x43\x3e\x70\x72\xf4\xae\x6c\x41\xec\xfe\x35\x21\x8b\xe4\xd1\x9c\x68\x6a\x29\xa2\xbb\x5b\xb8\xd9\x47\xa2\xfb\x56\x44\xf7\x7f\x14\xe6\x97\x4f\x72\x0a\x0f\xfc\x0b\x85\xdf\xc2\x07\x67\xf5\x7c\x6b\x09\xc0\xa5\x52\xbe\x08\x5c\x46\x5f\xbf\x9a\xaf\x6e\xb5\xc5\xb8\x7b\xbc\x38\xae\xc0\xea\x2a\xfa\x48\xe0\xeb\xf5\x42\x2b\x52\x00\x68\x16\x44\x99\xcb\x71\x38\xc1\xa8\xf4\x43\x49\xfa\x5a\xcb\x18\xdc\xe0\x71\x68\xc5\xdc\x16\x26\x9c\x96\x22\x33\x14\x5b\x12\xd2\x55\x94\xa6\x63\x37\xc4\xe3\x2d\xb2\x7b\x29\x14\xb4\x14\x2f\xf9\x7b\x3b\x6e\x39\x72\x74\xd1\x24\x59\x0f\xcb\x57\x64\x26\x24\x68\xed\xaf\xcf\xf3\xf1\xb0\x49\xc2\x8b\xc5\xc4\xb6\x62\x5e\x8b\x2f\xc7\xbb\x9b\x75\x19\xeb\x99\x3c\x29\x1f\xed\x44\xe0\x2e\x07\xd1\xc3\x20\x4d\xc9\x42\x7e\x41\x50\x1b\xa2\x77\xf8\x1a\x6d\xe1\x24\xbc\xa0\x39\x21\x77\xf8\xa0\x34\xf2\x63\x4e\x1f\xbe\x79\xb7\xb5\xd3\x90\xad\x89\xe7\x82\x89\xc7\x7b\x71\x34\x0a\xcf\xe7\x2c\x13\x65\x0c\x59\x21\xd3\xbc\xfc\x92\x49\x3c\xc3\x49\x76\x8d\xfe\xa4\xc7\x62\xf0\x26\x05\xe6\x7b\x32\xa6\x39\x8e\x53\xf2\x10\x46\x2c\x5d\x40\x16\x0b\x5f\x9a\x2a\xda\xc2\xa3\x60\x3e\xc9\xba\xa8\x85\x4a\xf5\xc6\x3a\x24\x52\x2e\xfb\xe0\x7b\x12\x9a\xe3\x84\x27\x32\x97\xe0\xc8\xf8\x2f\x42\x33\xcc\x58\xf2\xcc\x14\x40\xc9\x43\xbd\xf2\x21\x8b\xd1\x0c\x27\xa3\x38\x99\x2a\xc0\x35\xc8\x4a\xfa\xc7\xc1\xe8\xbc\xeb\x1b\x65\x44\x2f\xbe\x8e\x21\xe6\x4c\xbd\xb1\xbe\xda\x6c\x18\x21\xb8\x69\x57\x28\xea\xc6\x27\x89\x90\xd6\xf8\x4d\x39\x2f\x21\x69\x5e\x02\x79\x32\x2b\x43\x49\x5a\x7c\xbd\x2d\xce\x22\x7a\x00\x7c\xee\x86\x74\x55\xcd\x18\x4a\xc6\x6f\xe0\xa2\x1b\xee\x6f\x36\x8a\x13\x38\xc5\xc8\x46\xef\x21\x31\xe8\x97\xe1\xc8\x4a\x1a\x4f\xa9\x9d\x9f\x1e\x35\x33\xac\x65\x2a\xfe\x29\x27\x6b\x9d\xa6\x9f\xbc\x33\x98\x8a\x3e\x8d\xb5\x5a\xcd\x04\x9c\x93\xbd\x7e\x30\x3a\x77\x1b\x5e\x90\x89\xd8\x10\x3f\x39\xe1\x91\xe2\xbe\x60\x18\xf6\x7a\x87\xeb\x0a\xea\x41\x57\x94\x05\xdd\x26\xdf\xec\x8c\xc1\x06\x6a\xe1\x0f\xd5\x82\x95\xd3\x60\x92\xa1\x4d\xf8\x67\xf9\x44\xb4\xdc\x8d\x46\xf1\x6b\xbf\x0b\xd9\xd1\x44\xea\xc3\x51\x95\x45\x25\x29\xf1\xce\x54\x00\x3f\xef\xa4\xb2\xe2\xea\xbc\x1a\x35\x97\xca\xed\xa2\x4f\xbd\xd3\x80\x30\xcc\x3c\x49\x61\x99\x97\x3d\xf8\xee\x33\x5a\x25\xe4\x43\x79\x50\x45\xcc\x8e\xdb\x2c\xd1\x9f\xa0\x1c\x64\x53\x3a\xd8\x34\xdd\xbc\xa5\xcf\x71\x85\x7a\x02\x39\x79\x2f\x1a\xe2\x2b\x57\x8d\xd3\xda\x15\x53\x00\x39\xa2\x75\x2e\x08\xd1\x25\x50\x11\xc2\xb2\x78\xe3\xcd\x5f\x2f\xb1\xe1\x95\xe4\x1b\x6f\x25\xbe\xe5\x6d\x90\x59\xa9\xb2\x27\x97\x11\x86\xdc\x5a\x68\x51\xf9\x62\x81\x91\x85\xfe\x91\x09\xea\x46\x07\x79\x5c\xa4\xd7\x1c\x1f\xa7\x71\x81\xe8\x24\xcb\x73\xcc\x93\x65\x03\x05\xca\x34\xbe\xb2\xd7\xe6\x9c\x21\x96\xd1\x5b\xa6\x06\xb6\xbf\x2f\xce\xc6\x00\xf0\xb5\x21\x76\x8e\xae\x5d\x5c\x64\x31\x92\xaf\x58\xc7\x3d\x88\xec\x89\x31\x76\x83\x0e\xd5\x68\x76\x0c\xac\x03\x0b\xcd\x96\xa3\x4e\x6d\x39\x94\xe9\xf3\x1a\x73\x20\xe0\xe7\x5a\x13\x30\x7a\x62\xa4\xd5\x8f\xae\xb1\x2e\x32\xde\x68\x51\x28\x28\x57\x67\xf9\xe8\xab\xef\xdc\x01\xab\x94\x26\x7e\x3b\x38\xd2\xbb\x03\xae\x53\x0e\x8f\x6b\x6b\xdc\x3e\x53\x1b\x98\xcf\xdc\x06\x46\x99\xcd\x57\xe8\x73\xce\xe8\x91\x3f\x59\xe3\xf4\x33\x98\xc3\x58\x1d\x39\xfd\x6c\x9a\xc5\xf0\xbf\x1b\xfb\xb5\x19\x70\x8a\xfc\x29\xcc\x81\xe9\xa6\xa1\x51\xd7\x94\x18\x4c\xe2\xb4\x76\xf6\xfc\x79\xbe\x49\x91\x02\x5c\x39\xfa\x72\xbe\xe1\x08\x62\xc6\xf6\x32\x59\x2f\xcf\x80\x52\x3d\x46\xdc\x69\x43\x2f\x12\x6c\x26\x77\x23\x5f\x72\x13\xbf\x2f\xd1\x32\x4c\x5d\xe9\xf6\x17\x47\xaf\x71\x88\x06\xf7\x10\xc4\x86\x8a\x08\x42\x32\xa4\x42\xa1\x4f\x4c\x58\xae\x5a\x05\x79\x64\xd3\xbb\x80\xc9\x95\x4d\x65\x90\x1d\x71\x94\xf4\x09\x30\x15\x64\x0a\xaa\x6c\xd8\x75\xb1\x98\x14\x5a\x20\x3c\xdd\xe4\xd9\xa2\x51\x68\xee\x40\x3d\x66\x0a\x5d\x9e\x13\xf6\xe6\xac\xb2\xf6\xf7\xf6\xa1\x5f\x22\xad\xfb\xe2\xe4\xe8\x0f\xab\x3b\xf2\xa6\xd7\xf6\x65\xbd\xfe\x27\x68\x97\x8e\xc1\x38\xb3\xc7\x8d\x77\xa9\x12\x49\x7d\x99\xa7\x47\x12\x78\x1c\xe1\x79\x1a\xf4\x27\x98\x85\x03\x53\xd0\x39\x46\x6a\xaa\x45\x0a\xc5\x7c\xf3\x16\xe9\x19\xd6\x94\x6d\xe1\x08\xb2\x29\x23\x66\x68\xcb\x6c\x8c\x6d\x4d\x92\x28\x0f\x31\x56\xc2\x14\x05\x88\x26\x60\x46\x17\x38\x49\x21\x6a\xd9\x38\xc8\x50\x84\xcf\x27\x78\x90\xe1\x21\x61\xc3\x03\x96\x52\x35\x63\x0a\x9f\x2c\x46\x93\x30\xcb\x26\xf8\x05\x0d\x70\x59\xd5\x81\xe2\x24\x89\x13\x34\x8c\x71\x1a\xad\x64\x28\x18\x8d\xf0\x80\xd6\xa5\x48\xad\xa4\x28\xc5\x83\x79\x12\x66\xd7\x15\x51\xb1\x3f\xcf\x50\x98\x41\x25\x5e\x23\xcc\x52\x11\x50\x21\x9c\x84\x19\x73\xe2\xa6\x79\x5d\x43\xc2\x9f\xa7\x38\xa2\xfb\x41\xea\x52\x94\xd1\x01\x79\x4f\x3b\x27\xd4\x65\xc6\x5b\x75\xfe\x6e\x9b\xb4\x2d\xff\x90\xf2\x4e\x35\x83\xf6\x1e\x30\xa4\xf5\x36\x9c\x1a\x2e\xf2\x4e\x0b\x21\x3b\xa1\x91\xdd\x0b\x7b\xcf\x69\xbf\x89\x76\xc9\x2f\x47\xe2\xb8\x77\xa7\xb5\xb3\x0a\x2a\xbd\x3b\x6d\x9e\xb1\x60\x01\xe8\x2b\x79\x64\x57\x01\xf5\x4e\xd9\x91\x44\xee\xdd\x69\x9d\x56\xaa\xe9\x95\x9a\xf9\x95\x1a\xb4\x52\x5d\xaf\x54\xcb\xaf\xd4\xa4\x95\x1a\x7a\xa5\xba\xa8\xa4\xd7\x71\x65\x47\xb2\x86\x8c\x7b\x19\xfa\x06\xad\x27\x06\xad\xe7\x1e\x34\x1b\x1f\x65\xb8\x58\x9f\xe8\x85\xc9\x68\xc4\xd3\x0e\x52\xa4\x69\x90\xd5\x5a\x8d\x7c\x71\xf5\xd7\x9e\x88\xa6\x0e\xb9\xee\x84\xdc\x28\x04\xb9\xe6\x1d\x78\x05\x86\x01\xb9\x59\x08\x72\xdd\x37\x3b\x15\x05\x86\x01\xb9\x66\x40\x5e\x3c\x91\xbd\x20\x49\xae\x51\xdf\x4c\xa7\x4a\xa7\xaa\x4f\xe3\x5f\xd8\x9a\x8c\x8c\x4e\x3e\x61\x3d\xe9\x75\x9a\xe1\x29\x1a\xc5\xf3\x04\x65\xe1\xd4\x9c\xfb\x25\x83\xf2\x46\xf8\x2a\x3b\x26\xab\xcf\x1f\x3f\xd6\x11\xf1\x76\x3f\x1e\x86\xa3\x6b\xca\x09\x29\x1d\x16\xc0\x62\xdd\x8f\x45\xef\x94\x3a\x0e\xfc\x76\x0a\x29\x2f\x21\xda\x8a\x95\x29\xce\x95\x24\xf7\x17\x94\xe2\x6c\x3e\xd3\x3f\xe4\x78\x74\x2c\x3e\xec\xef\xfd\x42\x5d\x3b\xf2\x4e\xf8\x7b\xbf\x7c\xaa\xa1\x0d\xb4\xf7\x8b\x9d\x1a\x4d\x29\x52\xa7\x45\xea\xce\x68\xc6\xea\x92\x86\xa9\x4c\xe7\xfd\x0b\x4c\x44\x05\xdf\xd1\xbf\x46\x83\x1f\x43\xdb\x34\xfa\xf1\x57\x44\x9f\x7c\xd1\x8f\xd5\xe2\x2c\xcc\xb1\x28\x2f\xaf\x43\xdd\x61\x8e\x45\xb3\x0d\xd1\x6c\x5d\x6b\xb6\xbe\xa8\xd9\xba\xde\x6c\x7d\xb9\x66\x21\x8c\x4e\x58\xe3\x4b\x90\x00\x09\x1b\xfa\x0a\xf4\x55\x6d\x42\xd5\x06\x5f\xcc\x50\xb5\xa6\x2f\x53\xcf\x8c\x30\xb2\xce\x63\xad\x08\xa8\xb5\x46\xcf\xf5\x66\x6c\x7f\xfa\xb1\x4e\x3f\xd6\x9d\x1f\x1b\xf4\x63\xc3\xf9\xb1\x49\x3f\x36\x9d\x1f\x5b\x79\x6d\xb6\xf3\xda\xec\xe4\xb5\xb9\x26\xda\xcc\xd1\x48\x15\xe2\x3c\x68\x79\xee\x83\x8a\x71\x20\x64\x2b\x29\x54\x3f\xa2\x7b\x49\xee\xea\x55\x5e\x2b\xd2\x47\x21\xce\xac\x17\x71\xf7\xce\xbf\xbd\xc3\xe0\x4a\x2f\x33\xe0\x42\x7a\xe9\x63\x1a\x6a\xe8\x37\x20\x42\x54\xfa\x8d\xcc\x3d\x5f\x25\xf0\x2c\xf6\xde\x57\x66\xc5\x3a\xad\xd8\x60\x15\xd7\x8c\x8a\x6d\x6f\xc5\x06\xad\xd8\x62\x15\xeb\x46\xc5\x35\x6f\xc5\x26\xad\xd8\x39\x13\xa8\x69\x15\xeb\xb2\xe2\x9d\x76\xb1\xbc\x28\xf5\x14\x11\x1e\x3b\xfe\x98\xa5\x64\x67\xc1\xe3\xe1\xf1\x36\xd1\xe3\x39\x1c\xc6\xe0\x04\x1c\x57\xfc\x78\x27\xbe\x4e\x27\x3c\xa4\xe4\xe8\x15\xde\x74\xc7\xf9\x5e\x74\x2a\xf5\x0b\x3b\x1e\x79\x73\x2b\x3f\x86\x17\xf4\x4b\xa7\xb5\xda\x6c\x98\x6a\x39\xb1\x4c\x04\xc1\x96\x0a\xba\x42\x69\xeb\x43\xfb\xa2\x88\xa0\x86\xc1\xcf\x71\x70\x81\x51\x3c\x19\x7a\x59\xed\x12\xf2\x43\xef\x13\x9d\xdc\x9e\x19\xef\x50\x6b\xb1\x17\x4c\x06\xf3\x09\x59\x61\x11\xbe\xf4\x36\xdb\x63\x89\x60\x7a\x34\x11\x4c\xed\xaa\x35\x6c\xc2\xff\xa1\xe7\x5c\x42\x33\xf3\xb5\xf4\x58\x5e\x98\x1e\xcd\x0b\x53\xbb\x62\x35\x9a\x10\x53\xbe\xc7\x05\xd4\x5a\x19\xbd\x46\xa5\xde\x27\xe5\xf9\x3f\x50\x1d\x75\x51\xad\x6c\x43\x6c\x30\x88\x0d\x0a\x91\x01\x6c\x31\x88\x75\x03\x62\xbd\x00\xc4\x26\x83\xd8\xb4\xba\x55\xa2\xed\x68\x10\x1b\x05\x20\xb6\x18\xc4\x96\xb3\xd7\x4d\x03\x62\xb3\x00\xc4\x36\x83\xd8\x76\xf6\xba\x65\x40\x6c\x15\x80\xd8\x61\x10\x3b\xce\x5e\xb7\x0d\x88\xed\x02\x10\xd7\x18\xc4\x35\x67\xaf\x3b\x06\xc4\xce\x42\x88\x52\xec\xa7\x40\xb5\xea\x6b\x66\x75\xd3\x3b\x46\xd0\x34\xd9\x7d\xce\x5f\xdc\x61\x11\x91\x52\xe7\x57\xc0\xab\x43\xd2\xb5\x9e\x23\x09\x07\x4f\x97\x9f\xcc\x07\x19\x1a\x87\xe7\x63\x14\x44\x43\x34\x89\x2f\x51\x90\x9c\xcf\x21\xfc\x0b\xb8\x39\xff\xf7\x3c\x48\xac\xc4\x3d\xd0\x40\x80\x36\x48\x2b\x5c\x8a\x73\x28\x0f\xce\xfb\xb4\x08\xdd\x25\x9c\xc7\x27\xde\x67\x0d\x83\x04\xa7\xf3\x49\x86\xe2\x51\x5e\xf3\x63\xba\x05\x94\xce\x03\xf4\x13\x3a\x0f\xa8\xeb\x4a\x7d\xad\x8c\x9e\x23\xfa\xaa\xcf\x5e\xb5\xe1\x55\x1f\x5e\xb9\x90\x9c\x50\x40\x4a\x57\xe8\x91\xf0\x27\x74\x7e\x05\x33\x5c\x06\x82\xe0\x05\x84\xd8\xa9\x14\x70\x25\x82\x21\x1d\xfa\xed\xe0\x08\x41\x38\x49\xf5\xe3\x5b\xca\xe1\xce\xc7\xe8\x77\x74\x3e\x29\xca\xe4\xdc\x4a\x95\xdf\x18\x8b\x7b\x4b\x59\x5c\xa9\xf4\x56\x6e\xdf\x64\x27\x7b\xab\x88\x05\x65\x56\xa0\xa3\x17\xe8\xc8\x02\x26\x3d\xff\xc6\xb8\xe1\x5b\xca\x0d\x4b\xb4\x19\xb9\xdf\xbe\xe5\xfc\x0f\xf6\xdb\xe7\x88\xb4\x66\xc3\x68\x30\x18\x0d\x0e\xa3\xae\x23\x50\xb7\x30\xac\xe9\x05\x6a\x79\x18\x36\x19\xf4\x26\x87\xde\xd0\x31\x6c\x18\x18\xd6\x1d\x18\xb6\x18\x8c\x16\x87\xd1\xd4\x11\x68\x5a\x18\x36\xf4\x02\x8d\x3c\x0c\xdb\x0c\x7a\x9b\x43\x6f\xe9\x18\xb6\x0c\x0c\x9b\x0e\x0c\x3b\x0c\x46\x87\xc3\x68\xeb\x08\xb4\x2d\x0c\x5b\x7a\x81\x56\x1e\x86\x6b\x0c\xfa\xda\x99\x46\x22\x02\xc3\x8e\x81\x61\x5b\xc3\xb0\x50\xe2\x8f\x94\x27\x9d\x10\xba\xd6\x02\x69\x27\x16\x5d\x77\x51\x58\x19\xbe\xca\xd4\x7b\x27\x55\x93\xca\x43\x29\x68\x69\x1c\xe8\x6d\x91\x7d\x7f\x35\x9b\x04\x04\x9b\xab\x0c\x79\xc1\xb1\x38\x33\x25\xd9\xb2\x0b\xa2\xb8\xb8\xca\x53\xea\xea\xc9\x3b\xd4\x92\xe5\xbc\x3b\x28\xb5\x60\x61\x63\xe4\x8a\x7e\x37\xd2\x6d\xb7\x2a\xf2\x52\xa4\xdb\xee\x54\xd8\x5d\x49\xb7\x53\xbf\x39\xab\xac\xfd\xbd\x23\x11\x3e\xde\x57\x3d\xde\x57\x3d\xd8\x7d\x95\xb1\xc4\xe5\x7d\x8e\x79\x93\xf3\xf7\xba\xc3\xb9\xaf\xac\x70\xef\xc4\xd1\xfc\x9d\x7e\x34\x7f\x77\xdb\xa3\xf9\x3b\xfd\x68\xfe\x2e\xef\x68\xbe\x48\xc1\xfc\x78\x53\xf5\x78\x53\xf5\x78\x53\xa5\x7d\x79\xbc\xa9\x7a\xbc\xa9\x7a\xbc\xa9\x92\xcd\x3e\xde\x54\x99\x1f\x1f\x6f\xaa\x3c\x8f\x8f\x37\x55\x8f\x37\x55\x8f\x37\x55\xf0\xf7\x78\x53\x55\x4c\x89\xfb\x78\x53\xf5\x78\x53\xf5\x78\x53\xa5\xfc\x3d\xde\x54\x3d\xde\x54\x3d\xde\x54\x3d\xde\x54\xfd\x4f\xbe\xa9\xba\xb7\x3b\xaa\xdb\xdd\x4e\x15\xb9\x97\x2a\x70\x23\xf5\x50\x77\x51\x7f\xef\x7c\x28\x8f\x77\x51\xff\xfc\xbb\x28\xf5\xee\xa8\xd7\x5a\xe8\xe8\xa4\xde\x1c\xf5\x5a\xca\xb5\x11\x3c\x3c\xfc\x9d\x11\xf5\xd2\x14\xb7\x46\xee\xa0\x02\xdc\x43\x3b\xef\x5a\x09\xdc\x38\x55\x8f\x62\x25\x66\xba\xad\xaf\x88\xc2\x0c\xa5\xfd\xf8\xca\x86\x73\x2c\xd0\x39\x56\xaf\xe9\xf8\x9f\x4b\x9a\x6c\xb4\x3b\xfe\x43\x39\x3b\x74\x87\x8b\xd5\xb8\xef\xf0\xb5\x4b\x8f\xab\xb7\x58\xe1\xfe\xe3\x0b\x1b\x66\x83\x42\x86\x80\x47\x95\x08\xd1\xbf\xd4\x71\xf2\xa8\x0e\x59\x25\xb2\xb5\xf1\xb1\x3f\xd5\x00\xd9\x91\xd0\xb4\xcf\x56\x50\x34\xd7\xd9\x9f\xf4\xa2\xf4\x19\x3d\xa7\xe3\xf3\x9c\x37\x5a\x46\xff\x82\x5e\x79\x62\x29\x5c\x06\x33\x37\xce\xb0\x6f\xd8\x1a\x02\x65\x02\x8e\xdd\x8e\xf1\xe4\x35\x99\xf1\xc5\xd3\xd3\x73\xaa\xf8\x59\x56\x0d\x41\x34\x9f\x59\x96\x59\x01\xe8\xce\x6a\x39\xae\x09\x01\x2d\x88\x95\x7f\x9d\x4c\x8f\x5b\x65\xa8\xb5\x2c\x9c\x9c\x1b\xed\x8e\x47\x21\x52\xf3\x2a\x43\x9c\x8d\x16\x55\x8c\x28\xeb\xc9\x50\x8c\xc8\x41\x0b\x8d\x2f\x9f\xe5\x70\x2e\xcc\x00\x0f\xca\x41\xbd\xfa\x17\x15\x4f\x63\x3e\xc4\x6a\x8a\xe8\x32\x8a\xa8\x4a\x2d\x72\x2c\xa2\x10\x34\xe8\x34\x61\x1c\xa3\x4a\xed\xbb\x46\xc2\x1e\xc2\x75\x12\x6d\x0e\xc1\xfa\x89\x55\x12\xaa\xfe\x5e\xef\xec\x57\x52\xb7\xc4\xd6\x14\xa9\xc2\xf0\x3a\x93\x79\x0d\x22\x33\x8f\x81\x71\x7c\xfa\x08\x71\x50\x1c\x37\x5a\x92\xd4\x43\xeb\xec\x4e\xc6\x42\x9b\x2b\x26\x96\x69\xd8\x7d\xaf\x72\x6f\xaf\x75\x1f\x42\x6f\xaf\xb5\xb4\xc4\x6b\xef\xb1\x86\xb8\xdb\x6b\x39\x63\x5b\xc0\x0d\x4d\x88\x87\xb7\xd8\xe1\xb7\x92\x78\xa6\xed\xf2\xec\x05\x0c\xc2\x37\x88\x8a\x37\x24\xcd\xe9\x81\xe6\x0c\x3d\x3f\x99\x78\x52\x4a\x84\x9a\x43\xf5\x97\x0d\x15\xac\x19\x6b\x8e\xa0\xae\x44\xfd\x32\x56\x31\x01\xd5\xd5\x41\xe8\x11\xe3\x0a\x09\x31\xa4\x0d\x5e\x30\xff\x0e\x83\x8c\x67\xce\x06\x2e\x0c\x5f\x08\x5e\x64\x17\xff\x19\x36\xf3\x17\x2f\x9c\x7b\xf8\x12\xec\x1e\x2d\x48\x80\xf4\x1d\xad\x36\x32\x44\xf7\xb3\xe2\x00\xd2\xf2\xab\x8e\xd1\x7c\xfe\xca\x23\x85\xf2\x4f\x9a\xbd\xd6\x43\x1d\x33\xef\x96\xae\xef\x5b\x9e\x2f\x1f\xec\x14\xf8\x6d\x83\x38\x13\x56\x85\x53\x9c\x5c\xe0\xa7\x4f\x4a\x83\x32\x6a\xd4\xea\x0d\xd4\xbf\x46\xbd\xff\xef\xff\x1d\x26\xe1\x00\xed\xe3\x34\x0a\x27\x55\xb4\x39\x99\xa0\x24\x3c\x1f\x67\x29\x62\xe5\x87\xd5\xa7\x4f\x9f\x1c\xe1\x61\x98\x66\x49\xd8\x9f\x03\xfc\x20\x1a\x42\x50\x9e\x30\x42\x69\x3c\x4f\x06\x18\xde\xf4\xc3\x28\x48\xae\x09\x3b\x98\xa6\x15\x16\xa5\x21\x81\x7f\xe3\x79\x86\xa6\xc0\xd3\x07\xc0\x59\x2b\x28\x48\x30\x9a\xe1\x64\x1a\x66\x19\x1e\xa2\x59\x12\x5f\x84\x43\x3c\xa4\x41\x27\xc8\x3a\x1d\xc5\x93\x49\x7c\x19\x46\xe7\x68\x10\x47\xc3\x90\xae\x61\x52\x69\x8a\xb3\x2e\x5b\xf1\x2f\x90\x8e\x56\x0a\x8a\x61\x8a\xcf\x20\x1e\x62\x34\x9d\xa7\x19\xd9\xa8\x83\x30\x02\xa0\x41\x3f\xbe\x20\x9f\x66\xd7\xd0\x45\x14\xc5\x59\x38\xc0\x15\x1a\x57\x68\x12\xa6\xa0\x59\x56\xdb\x8b\x86\x06\x32\xc3\x30\x1d\x4c\x82\x70\x8a\x93\xaa\x0f\x87\x30\x52\x07\x82\xe3\x30\x4b\xe2\xe1\x7c\x80\xef\x1d\x0d\xc4\xba\x36\x8c\x07\x73\x11\x07\x83\xd4\x58\x8d\x13\x16\x23\x63\x1a\x64\x38\x09\x83\x49\x2a\x87\x19\xe6\x06\xaa\x29\xa8\x93\x79\x3e\xd9\xdd\x3b\x46\xc7\x07\x3b\x27\xbf\x6e\x1e\x6d\xa3\xbd\x63\x74\x78\x74\xf0\xcb\xde\xd6\xf6\x16\x7a\xf3\x6f\x74\xb2\xbb\x8d\x7a\x07\x87\xff\x3e\xda\x7b\xbb\x7b\x82\x76\x0f\xde\x6f\x6d\x1f\x1d\xa3\xcd\x0f\x5b\xa8\x77\xf0\xe1\xe4\x68\xef\xcd\xc7\x93\x83\xa3\x63\xf4\x6c\xf3\x18\xed\x1d\x3f\x83\x0f\x9b\x1f\xfe\x8d\xb6\x7f\x3b\x3c\xda\x3e\x3e\x46\x07\x47\x68\x6f\xff\xf0\xfd\xde\xf6\x16\xfa\x75\xf3\xe8\x68\xf3\xc3\xc9\xde\xf6\x71\x05\xed\x7d\xe8\xbd\xff\xb8\xb5\xf7\xe1\x6d\x05\xbd\xf9\x78\x82\x3e\x1c\x9c\xa0\xf7\x7b\xfb\x7b\x27\xdb\x5b\xe8\xe4\xa0\x02\x8d\xda\xd5\xd0\xc1\x0e\xda\xdf\x3e\xea\xed\x6e\x7e\x38\xd9\x7c\xb3\xf7\x7e\xef\xe4\xdf\xd0\xde\xce\xde\xc9\x07\xd2\xd6\xce\xc1\x11\xda\x44\x87\x9b\x47\x27\x7b\xbd\x8f\xef\x37\x8f\xd0\xe1\xc7\xa3\xc3\x83\xe3\x6d\x44\xba\xb5\xb5\x77\xdc\x7b\xbf\xb9\xb7\xbf\xbd\x55\x45\x7b\x1f\xd0\x87\x03\xb4\xfd\xcb\xf6\x87\x13\x74\xbc\xbb\xf9\xfe\xbd\xb3\x97\x04\x77\xad\x8f\x6f\xb6\xd1\xfb\xbd\xcd\x37\xef\xb7\x69\x4b\x1f\xfe\x8d\xb6\xf6\x8e\xb6\x7b\x27\xa4\x3b\xf2\x57\x6f\x6f\x6b\xfb\xc3\xc9\xe6\xfb\x0a\x3a\x3e\xdc\xee\xed\x91\x1f\xdb\xbf\x6d\xef\x1f\xbe\xdf\x3c\xfa\x77\x85\xc1\x3c\xde\xfe\xdf\x1f\xb7\x3f\x9c\xec\x6d\xbe\x47\x5b\x9b\xfb\x9b\x6f\xb7\x8f\x51\x69\xc1\x90\x1c\x1e\x1d\xf4\x3e\x1e\x6d\xef\x13\x9c\x0f\x76\xd0\xf1\xc7\x37\xc7\x27\x7b\x27\x1f\x4f\xb6\xd1\xdb\x83\x83\x2d\x18\xe8\xe3\xed\xa3\x5f\xf6\x7a\xdb\xc7\xaf\xd0\xfb\x83\x63\x18\xad\x8f\xc7\xdb\x15\xb4\xb5\x79\xb2\x09\x0d\x1f\x1e\x1d\xec\xec\x9d\x1c\xbf\x22\xbf\xdf\x7c\x3c\xde\x83\x41\xdb\xfb\x70\xb2\x7d\x74\xf4\xf1\xf0\x64\xef\xe0\x43\x19\xed\x1e\xfc\xba\xfd\xcb\xf6\x11\xea\x6d\x7e\x3c\xde\xde\x82\xd1\x3d\xf8\x00\x5d\x3d\xd9\xdd\x3e\x38\xfa\x37\x01\x4a\xc6\x00\x06\xbf\x82\x7e\xdd\xdd\x3e\xd9\xdd\x3e\x22\x03\x0a\x23\xb5\x49\x86\xe0\xf8\xe4\x68\xaf\x77\xa2\x16\x3b\x38\x42\x27\x07\x47\x27\x4a\x1f\xd1\x87\xed\xb7\xef\xf7\xde\x6e\x7f\xe8\x6d\x93\xaf\x07\x04\xca\xaf\x7b\xc7\xdb\x65\xb4\x79\xb4\x77\x4c\x0a\xec\xd1\x66\x7f\xdd\xfc\x37\x3a\xf8\x08\x5d\x26\x73\xf4\xf1\x78\x9b\xfe\x54\x28\xb6\x02\x33\x89\xf6\x76\xd0\xe6\xd6\x2f\x7b\x04\x6d\x56\xf8\xf0\xe0\xf8\x78\x8f\xd1\x09\x0c\x59\x6f\x97\x0d\x77\xf5\xe9\x93\x9f\x56\x75\x9d\xd7\x7e\x90\x8d\xef\x57\xef\x55\x2c\xea\x34\x0d\x7c\x2c\x8a\xd0\xc7\x42\xd6\xd9\x70\x61\x17\x44\x59\x8a\xb2\xa0\xcf\x25\x16\x52\xe5\xd3\x1f\x13\x67\xb0\x4d\x29\x47\xd5\x2a\x08\xd5\x2b\x08\x35\x2a\x08\x35\x2b\x08\xb5\x2a\x08\xb5\x2b\x08\x75\x2a\x08\xad\x55\x10\x5a\xaf\x20\xf4\xb2\x82\xea\xb5\x0a\xaa\xd7\x2b\xa8\xde\xa8\xa0\x7a\xb3\x82\xea\xad\x0a\xaa\xb7\x15\x0b\xcb\x35\x5a\x97\x7c\x23\xf0\x48\x79\x02\xa3\xde\xa6\x70\x49\x3d\x68\xeb\x25\x83\xdf\x60\x30\xea\xd0\x86\x84\xd3\x64\x6d\xb5\x18\x2e\x2f\x19\x8c\x75\x05\xcf\x35\x06\xab\xc3\x70\xa9\x53\x98\x75\x35\xd6\x72\x9d\xd5\xe5\xb8\xd4\x28\x0c\xc0\x83\xe3\xd9\xa4\xb0\x08\xfc\xba\xda\x6f\x15\x4e\x8b\xd5\x6d\x33\xdc\xd7\x18\x8c\x86\x82\x67\x9d\xc1\x5a\x67\xb8\xb0\x7e\xd7\x9b\x67\xe5\x57\xea\x5c\x24\x0b\xe6\x82\xe3\xb1\xa6\x8c\x55\x83\xc1\xe4\x38\x77\xf4\xf1\x80\xbe\x35\x8d\xbe\x77\x58\x9d\xa6\x84\x05\x75\xdb\x12\x67\x0e\x83\x8f\x07\xb4\x55\x37\xfa\x0e\x85\xda\x4a\x07\xd7\x18\x82\x1d\x39\xb8\x02\x48\x43\x19\x68\x8a\xac\x04\xb4\xce\xea\x28\x83\x05\x13\xd3\x96\x83\x2b\x60\x34\x95\x81\xa6\xc8\x2a\x08\x35\xd8\xc8\xd6\x14\x60\x7c\x34\xd6\xc4\xec\x09\x0a\x45\x6c\x74\x28\xb2\xfa\x6c\xa4\x8b\x56\x06\x45\x91\x8d\x15\xa0\xa7\xb6\xc4\x69\xab\xa9\x8c\x67\x47\x7e\xd3\x68\x7a\xad\x02\x9f\x60\xa8\x38\xbd\xbe\x94\xb4\xc7\x69\xaa\xde\x56\x86\x75\x8d\x95\xd5\xe6\xa3\x2e\x89\x40\xcc\xc5\x4b\x56\x90\x13\xcf\xba\x52\x86\x23\xbe\x06\xbf\xd5\xb3\x94\x58\xcb\x2d\x59\x95\xb7\x2f\xd6\xbc\xba\x26\xd6\x35\x90\x12\x14\x5f\x9f\x6d\x49\xfb\xa2\x9f\x0d\x89\x82\x18\x27\x46\x32\x14\x2e\x32\xa6\x64\xd1\x02\x61\x88\x69\x83\xdf\x96\x08\x40\x3f\xd7\xe4\x42\x84\x06\x5b\x0c\x91\x8e\x81\x74\x53\x1f\x7c\xd1\xe9\xba\x84\x23\xc6\x4e\x2c\x68\xf8\xae\xc1\x11\x0c\xa4\xae\x0c\x52\x47\xb6\x2b\x16\x1e\x5b\xc0\xf5\xa6\x63\x3e\x44\x07\x0c\xc4\x39\x20\xb1\xe0\x1a\xca\xbf\x6d\xb1\x8a\xf5\x01\x6a\x3b\xca\xb5\xf4\x99\x11\x33\x29\x3b\x85\xea\x75\x74\xa6\x65\xc9\xfe\x34\x26\x2b\xc4\x31\x1f\x48\x84\x6a\xae\x55\x50\xed\xaa\xbd\xb9\xde\x58\x7b\xf9\xf2\x25\xf9\xdd\xd9\xde\x7a\xb9\xfd\x66\xb3\x4e\x7e\xaf\xef\xd4\xdf\xbc\xe9\x6d\xf5\xc8\xef\xcd\x97\xed\xe6\xce\x56\x6b\x5b\x9f\xef\x71\xe2\x6d\xa0\x5d\xdb\x6c\xac\xbf\xd9\xee\x40\x03\xbd\xd6\xd6\x56\xbd\xd1\x82\x06\xb6\xd6\x6a\xcd\xed\x9d\x26\xf9\xbd\xb6\xd9\xd9\x5a\xeb\x6c\x43\xc3\x1c\xa1\x33\xa7\x3e\xe0\x68\xef\x70\x7b\x7f\xab\xde\xa9\x41\xf8\xfd\x05\x3a\x24\x51\x56\x6a\x91\x94\x57\x74\x57\xbe\xed\x5d\x11\x55\x26\x02\x12\x9e\x20\xd8\x9d\xb5\x56\xbb\xd1\xac\xc1\x08\x6e\xef\xf4\xb6\x36\xdf\xac\x43\x07\x5f\xae\xbf\xd9\xdc\xea\xed\x6c\x93\xdf\xf5\x5a\xb3\xd1\x6e\xad\xc1\xe0\xf4\x9a\x5b\x8d\xed\xfa\x4e\xed\xcc\xab\x1a\x2f\xaa\x94\x77\x2a\x76\x0b\x7b\x29\xd5\x73\x6e\x6a\x16\x9b\xe3\x53\x2c\x40\xf7\x2a\xcd\x22\x3d\xd7\x37\xfb\x9f\x94\xd2\xfc\xf2\xe0\x93\x6d\xc8\x84\xf2\xee\x54\x94\x7a\x68\x03\x95\xec\x02\x88\x1a\x80\x2a\x8d\x49\xc3\x07\xe5\xe5\x72\x46\xa5\x16\x40\x66\x57\x6a\x00\xb4\xad\x4b\x6d\x70\x39\xaa\x31\xb4\xc8\xd6\x79\x17\x89\xfb\x07\x42\x8a\xde\x2b\x47\x60\x00\x9f\xc6\x13\x7f\x81\x04\x0a\x24\xde\x02\x20\x7e\x7e\xfa\xc3\x0f\x01\x64\xa2\x4f\x7f\xf8\x21\xc0\x36\xfd\xff\xb3\xf7\xf6\x5b\x52\xdb\xd8\xa2\xf8\xdf\xe1\x29\x34\xf3\x5b\x03\xd5\x74\xd1\x6d\xc9\x5f\x32\xd0\xf9\x5d\x42\xe0\x74\x6e\x20\xb0\x80\xb9\xe1\x2c\x16\x64\x64\x5b\xee\x72\xa8\xae\xea\x53\xe5\xa6\xab\x93\x90\x75\x5f\xe3\xbe\xde\x7d\x92\xbb\xb4\x25\xdb\xb2\x2d\xc9\x55\x4d\x93\x33\x99\xa1\x67\x0d\xa9\x2a\x49\x7b\x6f\xed\x2f\x6d\x7d\x6d\xfd\xb4\xb6\x43\x80\x41\xe3\xa7\xf5\xca\x9c\xd1\xfa\xf0\x50\x58\xd9\x7b\x31\x69\xfe\xc0\x56\xa5\x88\x8e\x0d\x9b\xb4\x6c\x3e\x45\xe9\x7c\x8a\xb2\xf9\x14\xe5\xf3\x29\xe2\x73\x03\x22\xb6\x9a\xa2\x74\x35\x45\xd9\x6a\x8a\xf2\xd5\x14\xf1\x55\x1f\x19\x13\xa4\x30\x41\xf0\xf1\xf0\xca\x48\xba\x82\xa4\xe3\x50\x88\xfb\x85\x99\x28\xcc\x64\x21\xe9\x17\xe6\xa2\x30\x97\x85\x7e\xbf\x10\x26\x0c\x5c\x16\x06\xfd\xc2\xe6\x99\x6a\xd6\x7d\x97\xba\xee\x52\x7f\x57\xd0\x78\x94\x10\xfe\xbb\x7f\x84\xb0\xd1\xb6\x2b\x61\x3e\x6c\x8e\xf6\x5b\x9b\xda\xff\x65\xfe\xa6\x7c\xfb\x76\xef\x37\xd3\x25\x06\xb8\xb5\x73\x1f\x47\x7b\xbf\xde\xf8\xaa\xeb\x1a\x05\x0e\x54\xe0\x49\x3a\x9f\x66\xf3\x69\x3e\xdf\x43\xfb\x68\x36\x37\xdf\xbd\xf9\x88\x9a\x05\xb9\xf2\xbe\x4f\xe4\x52\x9b\x01\x1a\xe9\x43\x1b\x70\x7e\x00\x2d\xa0\x56\x68\x7e\x1f\xda\x40\x54\x03\x68\x51\x60\x85\x16\xf4\xa1\x0d\x64\xab\x41\xfb\xf5\xf0\x50\x41\xa4\x9e\x15\x62\xd8\x87\x38\x50\x08\x64\x4e\x93\x2e\x84\x58\x19\xc5\x25\x4a\xd0\x6a\x59\xcd\x27\xd5\x74\x2d\xc4\x6a\xba\xb4\x01\x3a\x50\xed\xf3\xb9\x59\xe4\x60\x11\x03\x93\x12\x7f\xa0\xb7\xb9\xa9\x04\xd4\x1d\xf0\x0a\x9b\xc4\xc6\x6b\x40\x60\x2f\xa9\xa9\x35\x98\xd9\x60\x27\xb1\x21\x95\xad\xd0\xbe\xa6\xad\xab\xab\x6b\x6b\x38\x49\x57\xd3\x6c\x35\xcd\x57\xc0\xf1\xd5\xa7\x69\x6b\xd0\x87\xf6\xa9\xda\xda\x85\xf6\x49\xda\x4a\xfa\xd0\x3e\x59\x5b\x71\x1f\xe2\x35\x6b\xeb\x0a\x76\xad\x1d\xea\xba\xb2\xa8\x2b\x78\xd4\x95\x49\x5d\xc1\x11\x9b\x4a\xc0\x45\x4b\x75\x5d\x59\xd5\x15\x06\x00\x53\x6b\x18\x1a\x86\x27\x34\xfa\xae\xfc\x3b\xfd\x39\x06\x88\x21\xe1\xd4\x6f\x2f\xc2\x14\xff\x1c\xa1\xc9\xb1\x3c\x9a\x9b\x09\xcf\x9c\x1b\x7a\x7a\xac\x8e\xf0\x1e\xcb\xe3\xb7\xb9\xa8\x67\xe2\xc8\xb1\x3a\xa6\x7b\x2c\x0f\xd2\x72\x51\x8f\x19\xeb\xf9\xaa\x1e\x1c\x96\x85\x11\x21\x35\xd6\x0b\x54\x3d\x38\x98\x9c\x8a\x7a\x99\xb1\x1e\x1c\x60\xee\xb0\xa5\x1f\xd6\x3e\x56\x4f\x6b\x7c\xc2\xf1\xac\x9c\x55\xac\x09\x86\xc4\x17\xc3\xc0\x3f\xfe\x0c\x63\x5d\x73\xf1\x4d\x59\xad\x5f\x2d\x2b\xf0\x78\x12\xe6\xe2\x5b\x56\x31\x79\x6a\xeb\x36\xa2\x06\xe8\xd0\xe6\x09\x2f\xaa\xc1\xa3\x8d\x50\x7f\xd0\x99\x07\x79\x3e\x7c\x85\x18\xa9\xf7\x16\xe5\x61\xa6\x16\xa4\x88\x26\xc3\xb7\xe8\xb7\x23\xf9\xb0\x70\x7b\x46\xa2\xa9\xf1\x37\xe4\x93\xbe\xb6\xb6\x90\x26\x93\x49\x5b\x75\x1f\x09\xff\x20\x40\x26\x7b\x02\x54\x20\xec\x16\x07\x96\x00\xba\x6e\x2a\xd9\xd1\x06\xcf\xda\x8f\xdb\x07\xcf\x03\x60\x2a\x70\xee\x01\x1b\x0b\x9c\x4d\x1d\xd5\xdf\xe9\x68\xdf\xc3\xac\xdf\xd8\x81\xc3\x31\x86\x67\x3b\x0e\x0f\x61\x26\x88\xe0\x75\x17\x79\x21\xcb\x78\x70\xea\x4c\xce\xbc\x86\xaf\xb9\xb8\xd5\x12\xac\x5b\x8f\xd1\x0d\x8a\x73\x8c\x8e\x90\x1e\xbe\x7f\xda\xfc\x2d\xdc\x6a\xfa\x66\x9e\x91\x1d\xc3\x54\xec\xd8\x70\x99\x04\xb9\xe6\x60\xc7\xcd\x75\xbd\xe3\xce\xf4\xea\x78\xe7\x79\x95\xd4\x90\xe3\xce\x9c\xea\xd8\x3a\x99\x1a\x3f\x0a\xf7\x42\xee\x84\x4b\xe1\xaa\x17\x2c\x72\x60\x76\xb7\xaa\xda\x31\xef\x09\xa8\xe3\xa6\xb2\xf9\x72\xe1\x76\x50\x70\x94\x40\xd4\x6a\x57\x17\xe0\xab\xfd\x18\x84\x2c\xfe\x69\xa0\x24\xb2\xdd\x50\xd7\x14\x99\x50\xda\x39\x17\x05\x1f\x3f\xca\xdd\x7f\xa4\x9f\x88\x2b\xf0\x64\x33\x45\x97\x53\xf4\x8b\xe9\x99\x8f\xc9\x64\x03\x37\x3b\x2f\xe1\xdf\x5f\xda\xd7\xda\x3f\x0e\xe0\x10\x37\x9c\xc9\x66\xef\xe6\xe4\x72\x4f\x5e\x27\xff\x5d\x7c\xf9\x65\x6f\x6f\xef\x9e\x0d\x9a\x3f\x0a\x4d\x00\xfa\x5d\x40\x6c\x49\xb3\xc0\x0a\xc6\x61\xdd\x04\x08\x40\xdb\xe5\xde\xcd\xc9\xef\x40\x9c\x1d\x62\xb8\x0d\xcf\x04\xd3\x7e\x6b\x41\x59\x60\x41\x28\xb1\x99\x2e\x8c\x90\x36\xf7\xef\x2f\x80\xaa\xcd\xd7\x5f\x7f\x3d\xf1\xc9\x9d\x85\x4e\x94\xfc\xe0\x3c\x0d\x53\x1f\x86\x91\xef\xc0\x6d\x77\x18\xc6\xfa\xda\x8f\x3a\xdf\x02\x67\x9e\xea\xcf\xd5\x52\x7a\xa6\x21\x18\xcb\xfb\x3c\x96\xda\x57\x7d\x98\x47\x59\x46\x7b\x92\xa5\x5e\xc0\x9b\xdc\x52\x24\xde\x32\x9c\xc2\xb1\xb7\xba\xa8\xa9\x35\x1d\xb7\x19\x2e\x0e\xf6\x8e\xda\xd4\x15\xb6\x3b\xaa\x54\x0b\xe7\xf8\xe9\x83\x87\x7f\x80\x68\x1c\xcd\xdf\xf3\x4b\x68\xba\xe6\xd9\x8a\x57\x96\xb7\x93\x2c\x02\x85\x27\x07\xaf\x51\xa0\xf2\x21\xc3\x46\x34\xc7\xa7\x2c\x6b\xc5\xa3\x1f\xb1\x32\x48\xa8\x53\x79\x28\xa5\x53\x96\x19\x24\xf5\xd5\x47\xb9\x0f\x6c\x39\x1a\x55\xd7\x34\xbf\x4e\xf4\xf1\xed\x34\x8e\xbf\x1c\x71\xfa\x57\xb8\xb2\xf2\xb9\xb7\xee\x7b\x89\xd5\x34\xc4\xd6\x94\x69\x2f\x8f\x1f\xdc\xc1\x5b\xec\x64\x0c\xdf\xaa\xbe\xce\xfd\x8b\x23\xb8\x7d\xda\x6e\x61\x94\x8b\xb2\x9a\x18\x12\x50\x75\xb7\x34\x78\x91\xe5\x2c\xa5\x89\x21\x37\x93\xb7\x49\x68\xca\xf2\xac\xe0\x9d\x3d\x0e\x53\xc5\xcc\xcf\x09\xc7\x85\xd7\x2d\xfb\xf4\x2d\x10\x5b\x84\x6e\x0e\xbe\x87\x2b\xe8\x03\x00\xdb\xac\x3d\x9b\x97\x8b\x45\x51\x6a\x5e\x2c\x86\x80\xd1\xbc\x54\x0c\xd3\x55\xf3\x42\xb1\x28\xe2\xcd\x32\xf1\x80\x52\xeb\x3a\xb1\x75\x4d\xd8\x32\x5b\x80\x75\x1f\x24\x6f\x98\x5a\x72\xc1\xfc\x28\x03\xff\x6e\x0a\x8c\xee\xdd\xd3\xfa\xaf\x5e\x50\x32\x03\xaa\xef\x39\xfc\xf8\xa6\x44\x77\x90\xff\x16\xbd\x53\x1f\x69\xfb\x11\x07\xda\xe7\xc8\xf6\x76\xa4\x22\x69\xb2\x80\xcb\xb1\x72\x6e\x09\xd3\x07\x1f\x9b\xd3\xd4\x98\x67\x42\xb0\xb4\x34\x61\x02\x48\x08\x40\x98\x9c\xc9\xc4\x70\x41\x96\xa3\x7d\x40\x64\x5b\x68\x44\xf7\x11\xf1\xac\x5c\x83\x65\xb3\xc9\x24\x45\x37\x51\x26\xe3\x5c\xf1\x31\x07\xc8\xde\x26\x64\x72\x17\x76\x64\x89\x0f\xdd\x47\xc1\x18\x8a\x14\xbd\x43\x19\x7a\x87\x72\x09\x39\xe2\x79\xc2\x53\x66\x4a\x3a\xd4\x83\x1c\xed\x40\xbc\xa4\x5d\x7c\xca\x54\x2f\xee\x20\x6f\x13\x7b\x3c\x08\x7c\x12\xd8\x71\x1d\xde\x6e\xd0\x51\x6f\x0f\xdd\x3e\xdc\xba\x2f\x02\xbe\x1f\x26\xb9\xcf\x49\x7f\x95\x07\x59\x44\x2a\xec\x25\x37\x2d\xf7\xa1\x23\x94\x99\x96\xf8\x10\xa0\xbc\x7f\x1f\xf9\x9e\xea\x25\x88\xdf\xf8\xb6\x28\x3a\x42\x26\x3a\xd8\x76\xb7\xb5\xb6\x5a\x0c\x54\x8b\x68\xf5\x62\x1b\xeb\xdf\xf0\x46\x9d\x85\x40\x58\x30\x1c\x64\x3e\x41\x9d\x45\x40\x58\x2c\xcc\xcc\x75\x7c\x7d\xa1\x30\x37\xd7\x09\xf4\x45\x42\xde\xaf\xf3\x65\x81\xef\x9f\x75\x81\x4f\xc4\xc2\x07\xc5\x7c\xb9\x5c\xe9\x6b\x6e\x87\x30\x50\xab\xbf\x4f\x42\x02\xb9\x10\x5a\xc8\x23\xeb\x74\x83\x65\xba\xcf\xb4\x42\xb7\xe3\x3a\x90\x71\xb9\xee\xcf\xb8\x1a\xf4\x65\x09\x61\xb0\x18\x20\xc2\xe7\x9d\x56\x0f\xa0\x81\x6b\xe1\xa0\x1b\x90\x77\xd7\x0c\x44\xd9\x97\xe5\x82\x6b\x5d\x2e\x00\x79\x6c\xb1\x52\x60\x16\x4b\xbb\x48\xa0\x44\x63\xbf\x36\x25\x2a\xd8\x97\x05\xe8\x9f\x3a\xc1\xc6\x7a\xc6\x48\x18\x7d\xee\xdc\x18\x0a\xcb\xbf\xcf\xf2\xc1\x60\x79\x40\x9f\xc3\x93\x30\xea\xcc\xe2\xb5\x5b\xd8\xfd\x55\x01\x42\x82\xed\xd6\x05\x44\xc5\x0e\x4c\xf8\x2e\x81\xff\xa1\x6b\x03\x19\xf6\xc2\x84\xe7\x54\x4c\xf9\xfd\x28\xce\xf2\xd0\x8b\xe1\xb3\x17\x7b\x79\x8e\xe1\x73\x11\x7b\x3c\x4c\x7c\xf3\x9a\x41\x51\x64\x9e\x97\xfa\xb0\xb8\x10\xd1\x90\xe2\x10\xcb\xcf\x41\x91\xd0\x82\x01\x80\x94\x17\x2c\x28\x58\xb0\xc3\x72\xc1\x56\x91\xa7\xe6\xf6\x15\xeb\xb4\x96\x8e\x5b\xb4\xe0\x51\x9b\x70\xe6\xce\xd1\x30\x78\xb1\x6c\x2c\x7d\x19\xa2\x47\x46\x5c\x42\x82\x5d\x07\x69\xd1\x64\x64\x98\xee\x58\xc7\x60\xa0\x26\xc4\x7c\x89\xfd\xcb\x50\xfd\x09\x43\xb5\x90\xca\x76\x83\xb5\x51\x38\x9d\xe1\x5a\x0a\xc8\x39\x60\x13\xd2\xbf\xea\xac\xdd\x6b\x56\xc3\xd1\xdd\x38\x11\x03\x78\xf2\x65\x5d\xff\xbf\x67\x60\xfe\xf3\x5d\xcb\xfb\x4e\x3e\xe2\x50\xfe\xd2\xdc\xca\x45\xab\xe5\xf9\x22\x47\x59\xf7\xbe\x9e\xd6\x83\xe3\xfe\xd3\x29\xdf\x77\xb7\x01\xea\x85\x5a\xde\xc2\x90\x25\xa6\x08\x06\xe9\x5b\xca\xe5\xfa\xf9\xaa\x3c\xe5\x93\x85\x71\x18\x5b\xff\xd7\xaa\xfa\xa1\x9e\xe7\x8b\x2f\x93\x45\x7f\x9e\xd9\x2c\x04\x4b\x71\xa2\x23\x44\xee\xd5\x9f\xef\x1f\x49\x08\xf5\x0f\x8e\xb5\xe1\xbf\x4c\x16\xe8\x6f\xaa\xda\x9e\x75\xbd\x50\xd9\x68\xc1\xe6\x6b\x3e\x7e\x2a\xb0\xbf\x3e\x56\xcf\xc7\x57\xe7\xdd\x19\xae\x81\x2d\x27\xbc\x7a\xbc\x62\xf0\x99\xcd\xbf\x29\xab\xb5\x81\x41\xcd\x16\xfe\x02\xdd\x41\x93\x05\x64\xf6\xdc\x43\xb7\x3b\x8b\x1f\xfd\x95\x2c\x0d\x57\xbd\x4a\xad\x67\x66\x87\xdf\x40\x20\xbd\xfc\x3d\x17\xb3\x72\xce\xd1\x44\x95\xdd\x47\xea\x48\x66\x9f\x8b\xad\x34\xad\x8c\x6e\x40\x50\x2b\x97\x8f\xdf\xc8\x4a\x90\x76\x74\xc0\x08\xd0\x85\xb3\xe5\xc5\x64\x31\x45\x18\x1d\x22\xb2\xb7\x45\xc6\x76\x04\x2f\xa1\xec\x02\xd6\xdf\x33\x26\xcf\x96\x20\xf6\xf7\x47\x96\x42\x17\x9d\x1a\x75\x84\x34\x69\x61\x5e\x7d\x8f\x4d\x04\xde\xdb\x45\xd3\xc3\x08\xfd\xb3\xef\xb4\x1d\x1f\xac\xe7\x65\xc6\x27\xde\xde\x97\x5d\xaf\xad\x77\xbd\x06\x45\x05\x14\x85\xa6\xa2\x13\x28\x1a\x6c\x18\x41\xcc\x02\x45\xf1\x27\x6f\xa3\x45\x8e\x5c\xf7\x7f\xf4\x36\xda\x09\x3b\x3d\x65\xde\xa6\xd9\x4c\xc3\x03\xa6\x0c\x6b\xc3\x41\xe3\x49\xdd\xf2\xfe\x7d\x44\xe4\xa6\x57\xfd\xcb\xd7\x5f\x7f\x8d\xe2\xbd\x3d\x84\xde\x99\x21\x75\xff\x3a\x90\x70\x30\x80\x84\xe9\xde\xde\x76\x90\xba\xed\x7c\xa3\x7b\xe9\xf4\x04\xb7\xfd\x36\x1e\x92\xef\x56\xd6\xba\x8d\x25\xb1\x5a\xb7\xf1\xa6\xce\x37\xbd\x25\xb1\x5d\x48\xfe\x10\x52\xb2\x63\xb7\xeb\x76\xe6\x37\x09\x50\xab\x38\x4a\x88\xfb\xaa\xe7\x90\xe4\x57\xf5\x70\xdf\xb9\x61\x6a\xdb\xfd\xcc\xe0\x56\xe3\x84\xa3\x9b\xa8\x80\xc3\x6e\xbf\x8b\x8f\x27\xb6\x27\x5c\x4e\x19\x64\x98\x63\xe8\x26\x4a\xa1\x3a\x93\xbb\x83\xef\x90\xda\x27\x34\xd1\x0f\xc1\x4a\x79\x22\x08\x6f\xb6\x5a\xd5\x66\x9b\xda\x6b\x95\x47\xff\x64\x09\x4e\xb4\x12\xec\x77\x8a\x3a\x8d\xcc\x63\x5b\x83\x0c\xde\xa9\x99\x70\xd0\x71\x99\x39\x99\x43\xbb\x48\x41\x94\x25\x58\x2b\xc1\x58\x2f\x8a\xe5\xc9\x56\x59\x44\x42\xf3\x88\x07\x1b\xc8\x02\xd3\x0c\xed\xd7\x68\xf7\x05\x53\xf7\xe5\x43\x6f\xd6\xcd\x63\x68\x48\xd0\x51\xcd\x98\x7d\xc1\x5a\x13\x06\xe1\xb8\x4e\x0c\x00\x84\xaf\xeb\xe7\x69\x17\x7f\xc2\x3d\x9a\xc2\x2f\xc8\x9d\x09\xaf\x25\x60\xd3\x36\x1f\x1a\xd9\x22\xed\x67\x5b\x47\x23\xdb\xa1\x93\x4a\x30\xa2\x22\x26\x5c\xff\x2e\x5b\xa3\xb2\x4e\xa8\xea\x40\xca\xf0\xc2\x5c\x27\x52\x75\x20\x25\xf8\x89\xb9\x4e\xac\xea\x80\xcd\xcf\xbe\x6c\xc3\x7e\xd9\x86\xfd\xb2\x0d\x3b\x8c\x36\xbf\x6c\xc3\xfe\x53\xae\xf1\x86\xd1\xce\x6b\xbc\x61\x34\xba\xc6\xab\xcf\xd9\x86\x6b\xbc\x61\xf4\x65\x8d\xf7\xda\xd7\x78\xc3\x68\xdb\x35\x5e\x93\x70\xba\x6b\xbc\x20\x20\xf7\xa1\xed\x66\xef\xcc\xbc\x35\x4b\xbd\x3f\xf5\xd6\xec\x26\x0a\xfe\x90\x87\x0b\x1a\x3c\x5f\x56\x81\xbb\xab\xc0\x9b\x08\xf6\x54\x0f\x36\x51\xa0\xfd\xfe\x3a\x0a\x54\x96\x6e\xa8\x71\xa0\xe5\x89\xde\x29\xa7\x9b\xd6\xbf\x17\xc7\xcf\x7e\x7a\xf6\xf8\xf1\xcb\x47\xaf\x5e\xf6\x57\x8b\x9f\x7f\xf7\xd3\x77\x3f\x7c\xfb\xe8\xf5\xa3\xe1\xab\xdc\x2f\x9e\xfd\xfd\x87\x6f\x7f\x7a\xf8\xec\x87\x97\xaf\x1e\xfc\xd0\xb4\xd4\xd0\xc9\x65\xe5\x87\xdb\x2d\x2b\x6b\x2d\x56\xb3\x65\x9d\xb4\xa5\xb7\x26\x5d\xa3\x16\xb3\x6b\x3c\x45\x97\xb6\x54\xe5\x95\x5c\x12\xa9\xd0\x7d\x44\x82\x7b\xa8\x32\x2c\x89\x68\x7d\x7e\xb3\x41\xfb\x28\x44\xb7\xd1\xa5\xbc\x3d\x58\xd5\x97\x34\xe1\x13\xd9\x83\x95\x4a\xf4\x37\x14\x0d\x62\x11\x08\x03\xf9\xc5\x6b\x74\x84\x2e\xd1\xdf\x50\x68\x8a\x12\xf9\xc5\x7f\x0a\xa8\x04\xdd\x46\x02\x8f\x2f\xf0\xec\x19\x2a\x6f\xe4\xb2\xdc\xeb\xde\xcf\x97\xf2\xe7\xff\xb4\x2c\x05\x6b\x6c\x3b\x2b\x51\x09\xcf\x09\x18\x98\xd6\x70\x66\x23\x39\xb3\x91\x17\x34\x37\x06\xc6\x34\x55\x25\x77\xd1\xa5\xac\x7a\x69\x59\x56\x6a\x15\xa4\xcb\xc6\x4b\x78\xe0\x67\xd8\x6b\xc1\xd7\x7e\xd7\x3f\x8e\xf6\xad\xb7\xcb\xd1\xd5\x86\x27\x8f\x5f\xbe\x10\xb4\x6e\x3c\x6c\x52\x06\xfd\xdd\x09\xcb\xfa\x98\xa8\x06\x28\x6a\x65\x7d\xba\xbe\xe8\xe9\x96\xb1\xda\x93\xba\x9a\x85\x85\xea\xe5\x89\x9f\xd1\x7d\x14\xdf\x43\x3f\x3b\x56\xe6\xa0\x0f\x70\x35\xd5\x9c\x15\xa5\x46\x9f\x96\xd5\xf3\xe5\x1a\xf2\xb8\x0a\xad\x82\xc7\x72\x7f\xde\x43\x77\x90\xe9\x34\x75\x0d\x5c\x6f\x74\x1f\xa9\x7c\x11\xa6\xca\xe2\x6f\xd0\xc1\x77\x47\x08\xd0\x68\x50\x2c\xb8\xba\x27\xaa\x75\xac\x5f\x1f\x01\x5a\xfb\xe1\xea\x01\xe6\xa7\x1a\xe6\x0e\xa8\x3b\x86\x79\x4f\x43\xc0\x76\x4b\x4b\x9a\x62\x2d\xf8\xa6\x02\x05\x1a\x11\x0b\xb5\x9f\x44\x3f\x3c\x44\xcf\x57\xe5\x69\x59\x95\x1f\x38\x3a\x5b\xce\x2f\x17\xcb\xd3\x92\xcd\xd1\xf2\x03\x5f\xa1\xff\x78\x3c\x21\x7b\x77\xd1\xe6\x1d\x45\xfb\x68\xf3\x2e\x82\x7f\x43\xf8\x37\x10\x6e\xc6\x0c\x52\x69\xb4\x44\x2f\xef\x0f\xbc\x43\xde\x26\x76\x1c\x99\xb7\x10\xa7\x20\x1c\x19\xf5\x63\x64\xd3\xab\xe7\xe0\xe5\x1a\x9f\x1a\x7e\xea\x04\x63\x7d\x99\x4d\x07\xfa\xb3\xb7\xeb\x6e\xca\x1a\xec\xa7\xe2\xa7\x67\xcb\x15\x5b\x5d\x76\x5e\xa2\x13\x26\xf0\x4a\x1f\x88\xac\xbb\x94\xc6\x57\x67\xcc\xd6\xff\xca\xd8\xb3\x31\xba\x7b\x7b\x3b\xfe\x76\x3b\x3b\x7e\x67\x5f\xc7\x77\xed\xea\x5c\xff\x53\x02\xcb\xf3\xea\xec\xbc\x7a\x02\x53\xeb\x4e\x5d\x04\x41\x7a\xce\xd7\xe5\x8a\xe7\xda\x43\x03\x69\x59\xad\xeb\x84\xd0\xb2\x71\x67\xb6\x50\x37\x7e\xb6\x98\xd7\x62\xd2\x72\x70\xb3\x15\xbf\x8b\x08\x09\xa6\x88\x84\xd1\x14\xf9\x34\x98\xa2\x10\x93\x7e\x63\xf5\x66\xc1\x5d\x51\xa6\x17\xf5\x1f\x2d\xa8\x27\xcd\xd6\x77\x0b\xf4\xde\xf5\xa0\x5d\xe1\xfd\x02\x58\xa9\x85\x97\x10\xeb\xb9\x77\xfd\xed\xcd\x5b\x8b\xb7\xdf\x42\xd5\xc4\x1f\xc0\x91\x2a\xb7\xe0\x17\x8d\xda\xc1\x26\xdc\x58\x2a\x01\xa0\xa4\x79\xad\x17\x46\x80\xc8\xf3\xd0\x1d\x24\x06\xda\xe6\xa5\x04\x9d\x13\x22\x7a\xf1\xc9\xe7\xda\xd1\x33\x2c\xcc\x19\x98\x66\x5c\x3c\xab\x3b\xf1\x84\x2d\x60\xed\xa7\xd7\xb5\x43\x44\x4c\x6b\x68\xe9\x7a\xb9\x4a\xc7\xf9\xdf\x03\xff\x29\x99\x04\x9f\x92\x12\x75\x37\xc5\x04\xaf\xad\xcb\xe6\x4f\x09\xbc\x41\xdf\xaf\x2e\x7c\xbd\x2b\x99\x85\xf5\x09\x6a\x81\xde\x99\x4f\x90\x74\x12\x09\x92\xab\x64\x10\x24\x9d\xd4\x81\xe4\xea\x39\x03\x15\xc1\x78\x8c\x62\xdc\x25\x19\x5f\x89\x66\xdc\x25\x1a\xef\x42\xb5\x51\x0e\x52\xb9\x9a\xa5\x91\x72\x51\x2d\xa5\x36\x9b\x25\x3d\x67\xb0\x98\x57\x9b\xb3\x81\x15\xa2\xc6\x01\xbc\x37\xfb\xee\x08\xf8\x62\xab\x33\x5f\x5e\x20\x55\x67\x7c\x37\xe2\x85\x18\x60\xd7\x16\x1b\x90\x81\x32\xd8\x81\xfc\x28\x83\x5e\xf8\x6c\x37\x81\x57\x33\x5e\xb1\x61\xc9\x0e\xb3\x06\x0d\xd8\xd3\x52\x4c\x41\xe6\xe7\xa7\x0b\xe8\x9c\xc1\xac\x6a\x0e\xd6\x61\xf6\x14\xb5\x91\xb4\xb1\xf2\x8e\x73\x12\x1d\x47\x47\x4a\xed\x0c\xc5\x82\x48\xfc\xd5\xa1\x67\x23\x3d\x57\xdd\x27\x5a\xdd\xf9\xf2\xc2\x1a\x97\x5a\xb9\xf5\xca\x18\xe7\x98\x7a\xf2\x4a\x48\xe1\xd5\x9b\x8d\x8d\xf6\x57\x1b\xa9\x6b\x47\xd0\x03\x7b\x25\x50\xb6\x23\x20\x7d\xbb\xd3\x37\x57\x53\x03\x87\x5b\x6d\x7b\x14\x40\x97\x26\x42\x2e\x01\x4c\x0f\x5d\x9b\xe5\xaf\x36\xb8\xad\x8e\xb7\xa9\x2e\xf5\xeb\xd5\x06\xbb\xe4\xa8\xea\x3e\x69\xea\x82\x1c\x9d\xea\xbd\x3e\x5f\x81\x45\xc9\xe7\x44\x84\xaa\x8f\x6b\xf9\xab\x4d\xa0\x7c\x01\x9a\x4c\x14\x6d\xcd\xd5\x60\x85\x5f\xdd\x0f\xb6\x4d\x6f\x00\xda\x93\x06\x9a\xf4\x1a\x12\xda\x93\x1e\xb4\xa7\xe3\xd0\xfe\x50\xa3\xea\xb8\x42\x87\x7e\xa2\xbe\x4b\xb4\xa8\x29\xda\x69\xb6\xf7\x62\xb6\x44\xcf\x4b\x87\x66\x0b\x94\xf5\x9b\x8f\xf8\x9e\xf6\x55\x86\x72\xcd\xf7\x4f\x56\xf9\x0e\xe7\x1a\xb0\x2e\x35\x16\x95\xa4\x06\x8d\x39\xa4\xba\xf6\x93\xb6\xb6\xdd\x25\xc1\x60\x31\x5b\x3e\x93\x51\xca\x51\x67\x3d\x4c\xa7\xcb\xda\xd9\x17\x4b\x08\xf4\x1c\x2e\x5e\x4c\xa0\x5b\x14\xa3\x0b\x0f\x9a\xad\x4c\xea\x4e\xdf\xbf\xdf\x12\x09\xaa\x5d\xf7\x0f\x9e\xd2\xf4\x09\xba\xa3\x95\xdb\x14\x1d\x75\x4d\xa7\x81\x61\x04\xfe\x74\x47\xe0\xdd\x35\x8f\xb6\xbb\x5b\xad\x78\xf4\xbb\xac\xa8\xd2\xc0\xc0\x6a\xc7\x90\xb8\x28\xb8\x72\xcf\x9f\x8e\xe0\x78\xb2\x23\x0e\xd7\xd8\xb6\x62\x8b\xf5\xd9\x72\xed\xd4\x12\x70\xbf\xcf\xcb\x27\xd2\x30\x5e\xbd\xd1\x16\x14\x5b\x3d\xb4\x8e\x79\xb2\xe1\x36\x03\x9f\xaa\x39\x36\xfa\x59\xfd\xc7\x59\x89\x58\x05\x43\x20\xf8\x4b\x73\x4c\xf8\xca\x83\x3e\x18\x93\xb6\x36\x93\x23\xaf\x71\x00\xc6\x7a\xaf\xbc\xba\x3b\xb2\xb6\xcd\xe4\x5f\x79\x75\x67\x54\x3d\xcb\xb8\x75\x78\x88\x1e\xce\x5c\xce\x6f\xfb\x61\xfd\x8a\x43\xc6\xb8\x6b\x44\x9a\xfb\xaa\xfd\x70\x33\xae\x8c\x28\xf7\x6e\x2e\xb5\x6e\xf5\xaa\x51\xb8\xed\x9b\x6c\x70\xd3\x68\xa2\x05\x21\x7b\xdb\x0c\x80\x12\x00\xe9\x01\x20\x03\x00\x4e\x2e\x8a\xd8\x63\xb5\xbc\x70\x30\x71\xae\x59\xc3\xab\xd6\x34\xde\xa1\xc9\xef\x8a\x7c\xf9\xc3\xcd\x9a\x18\xf8\xea\xf2\x1f\x73\xcd\x6a\x5e\xb5\x26\xa4\x43\x84\x1f\x5a\x88\xf3\xe5\xc5\xa7\x2f\xd0\x7e\xb7\x34\xcd\x48\x06\xf2\xb6\x5a\x5a\x67\x19\x52\x8c\x6f\xbd\xc5\x4c\x28\x1f\x9d\xb4\x75\xa0\xd8\x0c\xb1\x13\xaf\x74\x5b\x08\x93\x74\x6c\x76\xfc\x73\x1d\x8b\x32\x2c\xd2\x5c\xfb\xa9\xa8\x41\xfd\x66\xc5\x47\xb4\x1b\x2e\x03\xdd\x86\xc5\xab\xe1\x3a\xd0\x55\xcf\x52\xe1\xab\x1c\xa5\x82\x43\x52\x19\x2f\xe7\xdd\xf3\x4e\x78\x0f\x1d\x76\xe9\xdf\x43\xb7\xfb\x3f\x00\x72\xd8\xa0\x69\x4e\x73\xfd\x93\x1c\x82\xfa\xe4\x35\x3c\x7d\x99\xb1\x26\xde\xb8\x06\x89\x0e\x8d\xa2\xd7\xab\xd4\xab\x80\x43\x98\x87\xc6\xc3\x74\x2f\xff\xeb\x9c\xf3\x5f\xf8\x10\xe8\x8c\xad\x67\xb5\x72\x6f\xf5\x16\xfd\x80\x8a\x4f\x59\x2c\x1c\x5f\x13\xda\x3e\xa4\xb7\x85\xf3\xbb\xaf\x21\xb6\xf8\xec\xab\x72\x5a\x68\xa8\x16\xe6\xf4\x80\x73\xa7\xb5\x39\x0d\x94\x5a\x9e\xd3\x41\x5d\x75\x5d\xb1\x65\x85\xbb\x13\x4f\x06\x9d\x78\x72\xd5\x4e\x3c\x19\x74\xe2\xc9\x6e\x9d\x30\x8b\x4a\xaa\xae\x32\xb2\x6a\x89\x56\xbc\x5a\x95\xfc\x03\x37\x1c\x40\x44\xea\x72\xb7\xf4\x07\x67\xe7\xeb\x59\x4d\x86\x89\x45\x86\x9a\x4f\x87\x35\x3f\x3d\x3d\xb1\xe1\xf6\x50\x83\x7a\x3a\x34\x61\xeb\x7d\xa2\x6b\x3a\x35\x69\xf7\x5f\xea\x08\xa5\xc1\x9d\x35\x97\x9d\xb6\xf0\x10\x5b\x6e\xe6\xd4\x1f\xdb\xf3\x99\x4e\xb6\x7f\x39\xae\x79\xc5\xe3\x9a\xfe\xae\x87\x35\xfd\xb1\xa3\x9a\xbe\xe3\xa0\xa6\xff\xe5\x98\xe6\x75\x1f\xd3\xf4\xb7\x3c\xa4\x69\x10\x4b\xe7\x88\xa6\xbf\xcd\x01\x4d\xdf\x7e\x0d\xbf\x39\x78\x78\x97\x06\x1f\xdf\x4e\x29\xfe\x17\x39\xae\xd9\x4f\xb0\x13\x62\xf2\x87\x9d\xe1\xac\xd3\xed\x08\x9c\x7f\xae\x74\x3b\x57\x3a\x6d\xa9\x8a\xdb\xd3\x9e\x75\x9d\x9d\x12\xf2\x84\x98\x74\x8e\x85\x84\x98\x58\x8f\x99\xd0\x2d\x13\xf2\x88\x8a\x9d\xa3\x26\x54\x65\xb5\x08\x31\xb9\xb6\x2b\xc4\x7a\xf7\xad\x39\x79\x06\x87\x1c\xbc\x4d\x96\xa6\x69\x92\x87\xf9\x54\x4b\xd8\xb3\x37\x35\xd5\x8c\x48\xc2\x48\x42\x98\x9e\xce\x67\xcf\x90\xb7\xc7\xd0\x34\xc1\x61\xe2\xe1\x90\xe9\xd9\x7f\xcc\x48\x70\x48\x0a\x9e\xc9\x9c\x41\x75\x6e\xa0\x2d\x91\x44\xb1\xef\x93\x28\x92\x69\x85\x54\xe6\x20\x33\x12\xca\xd3\x20\x60\x34\xd6\xf3\x0a\x6d\x89\x24\x4f\xbd\x8c\x70\x2f\xd7\xd3\x10\x99\x91\x04\x71\x1a\x06\x14\xe7\x7a\x92\xa2\x5e\x68\x7a\xdd\x59\x8a\x84\x3e\x5d\x31\x4b\x11\x8e\xbe\xa4\x29\xba\xa6\x98\x88\xee\x9c\xa6\x48\x34\x19\x8b\x8b\x74\x9f\x31\x8c\x8c\xe8\x97\x34\x45\xd7\x1f\x1b\xd1\x6d\xd3\x14\x19\x85\xd3\x8d\x8f\xe8\x68\x9a\x22\x9f\xba\xd3\x14\x89\x61\xfc\x2e\x25\xa6\x68\x89\xfc\x8b\x44\x4b\xff\xd2\x97\x5b\xae\xf7\x62\xcb\x67\xba\xb2\x72\xf5\x20\x4a\x16\x35\xdd\x55\x80\x7e\xaa\x4f\xf0\x1a\xde\xba\xe9\x1e\xf2\x3d\x60\x67\x67\xf3\xcb\x89\xfa\x71\x8a\xd8\xea\xe4\xfc\x94\x2f\xaa\x75\xff\x4d\x1e\xfd\xfa\x4c\x4b\x0f\xa4\x52\x6a\x51\xf4\xd0\x7b\x9b\x80\x50\x46\x8a\x04\xe2\x8a\x3c\x26\x94\x71\x42\xf6\xa6\xc3\x7a\x31\xf6\xe3\x20\x48\x20\xcd\x20\xf1\x79\x11\x85\x59\xae\x87\x06\x83\x06\x69\x98\x79\x45\x9a\x15\xf0\x00\x42\x16\xe4\x7e\x4a\x0a\x13\x60\x9e\xa4\x61\x9e\xb2\x10\x5e\xcf\xc6\x34\xc9\xd3\x34\x73\x02\xf6\x93\x30\xca\x48\x98\x42\x38\xe3\x07\x34\x0d\x7d\x6a\x02\x1c\x26\x05\xc6\xb8\x00\x8a\xd3\xc8\x0b\x73\x0f\x27\x4e\xc0\x09\xf1\x0b\x4a\x18\x3c\xb9\xcd\x0a\x9c\x04\x45\x92\x9a\x00\xb3\x14\x67\x21\xcf\x81\xe2\x9c\x45\x39\xc5\x98\x3a\x01\xe7\xd4\x8b\x19\x93\x3c\x66\xbe\xe7\x7b\x24\x30\xf2\x18\x13\xea\x87\xa9\x7c\x33\x22\x08\x63\x2f\x2a\x52\xee\x04\x4c\x02\x1f\xd3\x30\x85\xb7\x23\x02\xce\x83\x94\xd0\xcc\xc8\x8a\xd0\xcb\xe2\x3c\x83\x07\xc4\xf3\xb0\x28\xd2\x80\x13\x27\xe0\x98\xa4\x3c\xcc\x63\x60\x45\x41\xe2\x94\x26\x91\x51\x78\xd4\xcb\x79\x8a\xe5\xe3\x15\x7e\x8a\xa3\x24\x4a\xb1\x9b\xc7\x69\x9e\x79\x91\xcc\x50\x49\xc2\x2c\xc6\xc4\x0f\x4d\x80\x33\x9c\xa4\x05\x96\x04\x64\x45\x94\x90\x28\x09\x9c\x80\x79\x90\xa4\x51\x92\x01\xef\x12\x5e\xe0\x80\xe5\x46\x1e\xf3\x22\xe5\x41\x4c\xe1\x19\x71\x9f\x06\x05\x09\xb9\xef\x04\xec\x15\x19\x4e\xf2\x0c\x1a\xd0\x94\x66\x79\x98\x1a\x29\x26\x81\x97\x31\x9c\x65\xf0\x48\x7b\xcc\xb2\x24\x8b\x42\xb7\xf0\x72\x9e\x90\x2c\x02\x03\x09\x13\x92\x7a\x24\x36\x02\x0e\x58\x1c\xd0\x80\xc1\x1c\x21\xe2\x2c\xe2\x01\x75\x53\x1c\x66\xa9\xc7\x92\x1c\x28\x49\xf3\x00\x17\x69\x1e\x18\x4d\x3a\x2a\x12\x4a\x73\x00\x4c\x7d\x8c\x43\x3f\x75\x53\x9c\x50\x9f\x87\x38\x24\x60\xd2\x3c\x8a\xf2\x82\x99\x0d\x84\xfa\x38\x8b\x22\x88\xf0\x49\x9e\x06\x3e\xc1\x9e\xdb\x57\x78\x9e\x4f\xe2\x8c\xca\x37\xdf\x8b\x94\x60\xdf\xa8\x6e\x69\x11\x26\x71\x91\xa9\xfc\xa6\xbc\xf0\x38\x77\x6b\x45\x16\x71\xcf\x4b\x0b\x50\x7c\x3f\x67\x94\x16\x99\x51\x2b\xf2\x90\xc5\x09\x0e\x00\x70\xe2\x7b\x8c\xc5\xc4\xcd\x0a\x2f\xca\x58\xe4\x87\xf2\x79\x17\xcf\xf3\x29\x31\x1b\x08\x0e\x48\x42\x12\x39\xf7\xf2\x98\xc7\x23\x1e\xbb\x59\x41\xe2\x34\xf6\x18\x05\xe7\x12\x44\x39\x21\x45\x61\x34\x69\xc2\xb1\x60\x13\xb0\x2c\xcc\x48\x94\x25\x24\x72\x02\x0e\x72\x92\x45\x79\x01\x5a\x11\xb2\x2c\x20\x8c\xe7\x46\x5f\xe1\xfb\xd4\xcb\x31\xb0\x2c\xc9\x93\x30\xf5\xf3\xc2\x09\x38\x0a\x3d\x16\xfb\x61\x20\x0d\x84\x15\x91\x9f\x73\xb3\xba\x45\xcc\x63\x29\xf8\x6d\x3f\x8b\xe3\x94\x30\xb7\xdb\xa4\x38\x23\x59\x42\xa4\x77\x8b\x79\xce\x38\x8f\x4c\x80\x13\x12\x13\x92\x49\x96\xe1\x80\x12\x3f\xf4\x53\x27\x60\x46\xd2\x82\x53\x26\xfd\x6c\x56\x60\xcf\x8f\x8c\x06\xc2\x28\x66\x51\x14\x00\xc5\x69\x16\x10\xdf\xf3\xdc\xde\x2d\x23\x41\x4a\xd3\xd8\x03\x3f\xeb\x15\x34\x89\x13\x6c\xf4\x6e\x71\x94\x85\x98\x01\x8f\xbd\x28\x0c\x52\xee\xbb\xb5\x22\xc7\x09\xe1\x14\x27\x00\x38\xe2\x45\x48\xb0\x71\xcc\xcb\xa3\x24\xf1\x22\x02\xb2\x08\xc3\x28\x64\xc9\x88\xe5\x15\x81\xc7\xfd\x50\xf2\x2e\x8c\x63\x4c\x3c\xc2\x8c\x7a\xec\x45\x8c\x79\xb2\x67\x3e\x49\xd3\x1c\xa7\x6e\xe1\xe1\x84\x05\x19\xc6\xe0\x36\x53\x9a\x93\xdc\xcb\x8c\x14\x63\xee\xc7\x51\xe6\x49\x3d\xc6\x01\x66\x69\xe8\xf6\x6e\x24\x0e\x68\x1c\x07\xa0\xc7\x79\x41\x39\x4f\x93\xc4\x04\xd8\x0f\x52\x2f\xcd\x52\xe8\x19\xc7\x49\x1a\xd0\x11\x75\xf3\x13\x9c\x79\x59\x0a\x42\xc9\xc2\x2c\x09\x59\xe4\x1b\xfd\x31\xcf\x29\x63\x01\xb8\x4d\xee\x07\x98\xb2\xcc\xad\x6e\x61\x9a\x64\x19\x0b\x0a\x39\x32\x44\x3e\xf7\x63\x23\xe0\x88\x12\x1e\x15\xd2\x59\xe5\x51\x4a\x52\xca\xdc\xac\x88\x03\x5a\x50\xc2\xc1\x40\xc2\x9c\x17\x29\x31\xfb\x8a\x98\xb2\x30\xf2\xe5\x48\x13\xf8\x38\x26\x45\xe4\xd6\x0a\x1a\x64\x34\xa6\x58\x46\x42\xb8\xf0\x58\x1a\x1b\xdd\x26\xcd\xb2\xd8\x23\x52\x78\x98\x45\x81\x9f\x70\x77\xec\x96\x78\x29\x2f\x8a\x82\xc9\x28\x32\xf2\x31\x27\x46\xad\x60\x41\xe8\x45\x19\x07\xcb\xcb\x39\x25\x69\xce\xdd\xb1\x5b\xca\x8b\x84\xf9\x85\x1c\x19\x48\x16\xc5\x09\x36\xc7\x15\x51\x8c\x63\x5a\xc8\x21\xcc\x8f\x49\xe8\x13\xb7\xf0\x32\x46\x62\x9f\x67\xc0\x63\xce\x48\x14\xe1\xc4\xc8\xe3\x1c\xd3\x28\xa5\x72\x68\x22\x42\x91\x48\x77\x11\x70\x18\x88\xb0\x9c\xc5\x79\x0e\x06\x92\xe5\xdc\xe3\x29\x36\xba\xcd\x22\x8c\xf3\xa0\x88\x0b\x35\xe8\xf2\x1c\xc7\x6e\x3d\xf6\xa2\xc2\x8b\x62\x19\x2f\xc4\x04\xc7\x51\x91\x1a\x4d\xda\x63\x91\x1f\xe7\x19\x18\x08\x23\x19\x4d\x28\x73\x8f\x20\x18\xfb\x45\x42\xbd\x40\x2d\xdc\x25\x5e\xce\x8c\x14\xe3\x34\xc6\x5e\xea\x4b\x7f\xec\xe3\x2c\x88\xb1\x9b\xc7\x84\xe6\x69\x1c\x17\xa1\xd4\x0a\x2f\x88\x73\x6a\xf4\xc7\x3e\xc9\x18\x4b\x63\xd0\x8a\xc0\xcb\x62\x12\x24\x6e\x03\xf1\xb3\x84\xa7\xdc\x03\x56\xe0\x30\x4b\x52\x9e\x1a\x85\x17\xf8\x38\x8f\xe2\x0c\x7a\x96\x64\xd8\xf3\xf2\xc0\xad\xc7\x41\x96\x85\x79\x20\x03\xef\x2c\xf5\x79\x40\x52\xe3\xd0\x24\xc2\x15\x92\x24\xe0\xac\x8a\x2c\x0a\x63\x2e\xdc\xab\xcb\x57\x14\x59\x1a\x15\x4c\x0e\x92\x2c\x8f\x0a\xc6\x8d\x14\x47\x59\x10\xe0\x84\x02\xe0\x80\x05\x71\x48\x71\xac\x16\x51\xdf\x3a\xae\xad\xb6\xf3\xc2\x1f\xaf\x7a\x43\xd5\xf6\x0c\xda\x8f\x9d\x1b\xaa\x3f\x5d\xed\x86\x6a\x88\xc9\x76\x5b\x07\x86\xed\x88\xeb\xcf\x3e\x7a\xd5\xad\x83\x88\x79\x09\xaf\x17\xdc\xfd\x34\xcb\x12\xcf\xb2\x75\x90\xa6\x51\xcc\xb8\x1c\x7e\x69\x90\x31\x16\x77\x43\x17\x07\x12\x3f\x8b\x78\xe1\xc7\xe0\xc9\x0a\x9e\x04\x05\x15\x9e\xcc\x54\x93\x85\x41\x51\x84\x3e\x58\x41\x58\xe0\xdc\x8f\x8a\x6d\x57\xf5\x43\xec\xf1\x90\x48\xe7\xc3\x72\x1e\x51\x92\x5b\xb6\x0e\x92\xd4\x0b\x23\x2a\x15\x92\xa4\x3e\x8f\x32\x5c\x6c\x89\x04\x17\xd4\xcf\x13\xa9\xf3\x45\x1a\xe0\x34\x8f\x2c\x3d\x09\x53\xee\x65\xb9\x0c\x83\xb0\x1f\x73\x82\xe3\x64\x97\xad\x83\xeb\xbe\x47\xba\x4d\x6a\x58\xa8\xe7\xd9\x33\xbf\x1e\x63\x7b\xea\xd7\x63\x62\xcf\xfd\x7a\xec\xdb\x93\xbf\x1e\x07\xf6\xec\xaf\xc7\xa1\x3d\xfd\xeb\x71\x64\xcf\xff\x7a\x1c\x5b\x12\xc0\xca\x0e\x42\x7a\x58\xe3\x39\x70\x59\x3e\x97\xe5\xc3\xcb\x1e\x92\x07\xd0\xdc\x78\x05\x4a\x96\xcf\x65\xb9\xa5\x39\x81\xe6\xc4\xda\x9c\xcc\x65\xb9\xa5\xb9\x0f\xcd\x7d\x6b\x73\x7f\x2e\xcb\x2d\xcd\x03\x68\x1e\x58\x9b\x07\x73\x59\x6e\x69\x1e\x42\xf3\xd0\xda\x3c\x9c\xcb\x72\x4b\xf3\x08\x9a\x47\xd6\xe6\xd1\x5c\x96\x5b\x9a\xc7\xd0\x3c\xb6\x36\x8f\xe7\xb2\xdc\x70\xac\x6f\xcb\xa4\xc7\x52\x33\x4c\xc0\x99\x54\x8a\x7e\xc6\x3d\x38\x72\x2b\x15\xc2\xd4\x2a\x95\xba\x60\x6a\x95\x49\x3d\x30\xb5\xca\xa4\x0a\x98\x5a\xe5\x52\xfc\xa6\x56\xb9\x94\xbc\xa9\x15\x97\x52\x37\xb5\xe2\x52\xe0\xa6\x56\x85\x14\xb6\xa9\x55\x21\xe5\x6c\x6a\x75\x22\x65\x6c\x6a\x75\x22\xc5\x6b\x6a\x35\x93\xa2\x35\xb5\x9a\x49\xa9\xce\x4d\x79\x07\x5d\x57\x77\xb7\x7c\x0e\xd5\x9a\x4f\xbb\xc6\xff\x63\x29\x73\x0f\xdb\xae\x9b\x3f\x82\x11\xbc\xde\x3e\x1b\x56\xd9\x22\x51\xb4\x44\x23\x58\xf0\x63\x59\xdf\x36\xd0\xb3\x46\xa3\xdb\x88\xbc\x85\x9a\xe6\x5c\xae\x2d\x8c\xb9\x84\xa1\xee\x17\xf4\x61\xc0\xad\xf9\x2b\x65\xa0\x3e\x3c\x44\xff\x01\xd9\x88\xed\xc8\xeb\x94\xce\x3b\x65\xa8\xde\xcc\x9a\x3c\xc7\x9b\xb1\xbb\x78\xaa\xda\x5c\x6b\xe1\xbe\x8f\x27\x6b\xcd\x3a\x59\xb0\x67\x32\xf9\xaf\x9e\xbc\x7a\x0e\x29\x8a\xeb\x74\xc0\x9d\x7a\x74\x50\x0f\x0e\xbd\xbe\x43\xdd\x6a\xb1\xeb\x86\xa9\xac\x39\xef\x50\x31\x1f\x52\x31\x33\x51\x31\x1f\x52\x31\xd3\xa9\xe8\xd6\x8b\x87\xf5\x2c\x99\x8c\x75\x91\x5a\x72\xe6\x7c\xd0\x72\x6f\xef\x92\x7c\xbb\x95\x28\xde\x4e\xa2\xb8\x95\x28\xde\x4a\xa2\x78\xd6\x49\xf0\x3d\xab\xb3\x70\x6b\x89\xb9\xe7\x2a\x57\xb7\xc6\x24\xac\x38\xdc\xad\x06\xe7\x98\x13\x4d\xa4\x35\xbc\x68\x54\xa4\x78\xde\x21\x63\x6e\x20\x63\x66\x22\x63\x3e\x20\x63\xd6\x21\xa3\x0b\x30\x1a\xc0\x23\x91\x53\xa6\x3b\xe5\x0e\x77\xb9\x92\xb8\x15\x7b\xec\x12\xfb\x8f\x65\x2c\x3d\x97\x71\x60\xee\xd5\x9c\xab\x9a\x8e\x3b\xe1\xb2\x26\x8e\x34\x47\x62\x7d\x15\xba\xae\x2b\x09\xc0\xc6\xc8\xa2\x5f\x77\x5e\xd7\x1d\xa5\xa1\xf5\x34\x73\xc1\xb4\x32\xee\x8f\x5c\xdd\xea\xad\x2b\x9b\xc9\xea\x33\xc8\xd9\x26\xe0\x08\x49\x7a\x7b\xe8\x7e\x6d\x9d\xcd\x2f\xff\x3f\xc2\xe8\x2e\x1a\x1c\x9b\x1e\xd2\x21\xfe\xad\x25\x38\x4e\x86\xf8\x77\xbf\xb1\x16\x0b\x15\xf8\xaa\x54\x00\x17\xb7\xa4\x41\x4a\x67\x48\x81\x94\xc4\x00\xbf\x19\x68\x3b\x2a\xfe\x58\xda\xc4\xdb\x8e\x7a\x3f\x96\x26\xe2\xec\x39\xf1\x55\x52\xfc\x19\xba\x89\x8a\x99\x4a\x8b\x2f\xbe\x98\xef\xf1\xc9\x36\xd2\xf6\xf9\x5c\xb4\x99\xab\x36\xe2\xcb\xc9\xdc\x91\x4c\x7f\x06\xd9\xf4\x05\xe8\x54\xe2\x81\xcf\x99\xfc\x9c\xaa\xcf\xf6\xe6\x73\x68\x2e\xb0\xa4\x12\x25\x7c\xce\xe4\xe7\x54\x7d\x76\xa7\xe4\x9f\xc9\x9c\xfc\xca\xe1\xc8\x71\x85\xcd\x65\x7a\xe9\x3d\x99\xfc\x80\xcd\xea\x8c\xfd\xaa\xb0\x93\xb3\x7f\xa6\xbd\x22\xc1\xea\x51\xc7\x99\x99\x1f\x66\x53\x93\x06\x90\xc2\x39\xeb\xe2\x9c\x77\x70\xce\xba\x38\xe7\x3a\xce\xd9\x36\x38\xb1\xec\x27\x57\x43\x83\xbc\x6f\xc2\xe5\xa0\x40\xeb\xb4\xff\xb3\xfa\xd1\x0a\xad\x30\x68\x0b\x05\x4e\xbf\x2e\x93\x69\xb8\xdd\x38\x65\x3f\x55\xe5\x1a\xe7\xac\x8b\x73\xde\xc1\x39\xeb\xe2\x9c\xeb\x38\x67\x2d\x4e\x63\xd4\x39\xfe\x0e\x81\x99\xd6\xef\x21\xfb\xd2\xf7\xf6\xcb\x54\xdf\x83\xf1\x7e\x5f\xba\xae\x51\x7d\x0f\xce\xe0\xfb\xd2\xe6\x42\x3f\xc0\x43\x09\xa2\xce\x6c\xde\x90\x68\x32\x4a\x59\x51\x20\x9c\xb5\x7d\x91\xee\xa2\xc2\xba\xbb\x98\x6d\xe3\xab\x5a\xb4\xe2\x5f\xc1\x11\x37\xce\x0a\x50\x65\x33\x13\xc2\xec\x4a\x18\xbf\x37\xba\x9e\x3e\xc6\xef\x4b\x13\xc6\xef\xcb\xab\x60\x34\x3b\xbb\x3e\xc6\x1f\x8d\x18\x7f\x34\x61\x34\x6b\x5b\xff\xf1\x0a\x0b\x4a\x58\xbc\xa8\xcd\x1e\x2a\x5a\xa9\x83\x75\x90\xda\x2b\xed\x4b\xf7\x08\x24\x12\x9d\xc4\x1a\xd6\x76\x64\xfe\xfd\x2c\x67\x15\x47\x17\xee\x99\xbe\xf8\x83\xf9\xa6\x51\xbf\x61\xba\x79\x62\x22\x1b\x06\xa0\xc2\xd4\x06\x26\xb6\x85\xa9\x0d\xcc\xa1\xb9\xa9\x0d\x4c\xa1\xb9\xa9\x0d\x4c\xc9\x27\xf9\x1c\x9e\xef\x98\xdb\xde\xef\x80\x39\xfd\x24\x9f\x41\x2d\xc9\x3a\xae\x73\x2e\x1f\x30\xcd\xfa\x12\x88\x80\x94\x99\x68\x84\x25\x85\xcc\x44\x23\xac\x5e\xa4\xa6\x36\xb0\x78\x91\x9a\xda\xc0\x3a\x09\x33\xb5\x81\x65\x92\xc1\x6b\x06\xe2\x0f\x96\x5d\x26\x52\xd5\x2b\x62\x65\x06\x2c\xdc\x4c\x24\x1f\x84\x66\xed\xb7\x23\x8e\xe4\x46\x35\x0c\x76\xae\xf5\xb1\x12\x6d\xcd\x10\x22\x83\x63\xd0\x7f\x36\x88\x06\x8e\x9b\x64\x14\x93\x63\xd0\x7b\x26\x89\x3d\xf6\x74\x6a\xd9\x90\xd8\x3e\x1c\x6d\x95\x51\x22\x04\x16\xa5\x43\x84\xb8\x45\x08\xec\x49\x15\xc2\x8e\x27\x48\xc7\x11\x6a\xeb\x92\x12\x21\x01\x17\x3b\x44\x48\x5a\x84\x64\x56\x8f\x4b\x13\xa8\xaf\xb9\xd7\x71\x84\xda\x4a\xa6\x44\xe8\x0b\x84\xf9\x10\xa1\xdf\x22\xf4\x05\xae\x5c\x21\xf4\x47\xcc\xa1\x0f\x47\x5b\xfb\x94\x08\x03\x81\x90\x0f\x11\x06\x2d\xc2\x40\xe0\xe2\x0a\x61\xa0\x23\xe4\xe3\x08\xb5\xd5\x52\x89\x30\x14\x08\x8b\x21\xc2\xb0\x45\x18\x0a\x5c\x85\x42\x18\xea\x08\x8b\x71\x84\xda\xfa\xaa\x44\x18\xc1\xa4\x62\x88\x30\x6a\x11\x42\xf4\x7e\xa2\x10\x46\x9d\x49\xc4\x38\x42\x6d\x45\x56\x22\x8c\x05\xc2\xd9\x10\x61\xdc\x22\x84\x69\x93\x1a\x93\x45\x7d\x57\x10\xf0\xc9\x77\x2f\xbe\x3c\x8a\x73\x7d\x8f\xe2\x60\x11\xdc\xab\x97\xcd\x04\x30\xc8\xc3\xe2\x7b\xd7\xfd\x2c\x8e\x19\x0d\xfe\xa7\x7c\x18\xe7\xe1\x72\xf1\x81\xaf\x64\x96\x5f\x54\x2d\x91\x4f\xee\xa4\x65\x25\x02\x94\x1c\x31\x38\x9f\x9d\xf2\x62\xb9\xe2\xea\x38\xf5\x40\x6a\xda\x5d\x13\x6d\xef\xae\x5a\xbe\xf6\xc9\x75\x3c\xc4\xf3\x67\x7d\x82\x47\xa7\xb3\xc9\x0f\x72\x17\x61\x8f\x04\x87\xbe\xca\x53\xfc\xe5\x76\x93\xf5\xaa\x52\x88\xc9\xae\xb7\x9b\x44\x93\x91\xdb\x4d\x9d\x63\x0d\x83\xdb\x4d\x21\x26\x5f\x6e\x37\x5d\xf7\xed\x26\x21\x95\xed\x6e\x37\x19\x85\xd3\xb9\xdd\x24\x05\xe4\xbc\xdd\x24\xef\xd1\x6e\x79\xfb\xdb\xff\x53\xdf\x67\xe2\x8b\xec\x4e\xca\xd6\x3c\x0a\x7a\x05\xa7\x79\xd8\xaf\xfa\xe1\xec\x7d\x5e\xf4\x7e\xcc\xca\xb3\x19\x5f\xfd\x21\x57\xa2\x34\x52\xe1\xbb\xa0\x50\x16\x48\xc2\xe0\xb3\x4e\xcf\xbf\xc2\xd5\xa9\x1f\xb7\x7a\x13\x08\x0e\xcf\x3c\x84\xae\x37\xf5\xb4\xdf\xc6\xaf\x42\x1d\x1e\xa2\xe7\x7c\x75\x0a\xa3\xe8\xc3\xd9\xb2\xcc\x38\xc2\xfd\x67\x53\x44\xf3\xe7\x0f\x71\xf7\xee\x52\x18\x4f\x51\x90\x4c\x51\x80\xa7\xc8\xf7\xa7\x88\x84\x53\x84\xe3\x29\x4a\xa6\x08\x61\xed\xa8\x51\x48\xa7\x28\xf4\xa6\x28\x20\x53\xe4\x07\x53\x44\xa2\x29\xc2\x74\x8a\xb0\x37\x45\x44\xaf\x97\x4c\x51\x88\xa7\x28\xf0\xa7\xc8\x0f\xa7\x88\xc4\x53\x84\x93\x29\xc2\x02\xbe\x56\x2f\xf2\xa6\x28\x24\x53\x14\x04\x53\xe4\x47\x53\x14\xf9\x53\x14\x86\x53\x14\xc4\x53\xe4\x27\x5a\x45\x1f\x4f\x11\xf1\xa7\x08\x87\x53\x14\x4f\x11\x8a\xc8\x14\x85\xc1\x14\x05\xf0\xb4\x80\x5e\x51\x50\x42\xa6\x08\x07\x53\x14\x89\x8a\x78\x8a\x42\x7f\x8a\x82\x70\x8a\xfc\x58\xab\x48\x92\x29\x22\x78\x8a\xb0\x40\x39\x45\x88\xd0\x29\x22\xde\x14\x61\x41\x8e\xac\xf6\xd6\xc1\x57\x62\xe6\x2b\xe9\xf2\x55\x50\x21\xf8\x28\xfa\x4d\xc4\xe7\x29\x42\xa1\x4e\xad\x42\x2c\xba\x25\xa8\x05\x82\x3c\x9d\x4a\x5f\x31\x4e\x50\x25\x2a\x44\x53\xa4\x77\x17\x47\x92\x1f\x82\xc1\x40\xbd\xdf\x15\x84\x10\xa8\x60\xb0\xe0\x9f\x1f\x4b\xc6\x86\x61\x8f\x5f\x81\xa7\xa4\x15\x4a\xe9\x07\x3a\x06\x21\x1a\xa1\x1a\xbe\x10\x69\x24\xc5\x1e\xea\x32\x14\x22\x10\xfa\x20\xf4\x42\xc8\x50\x30\xb6\x8e\x6a\x3a\x2f\x42\x9d\x9f\x9e\xcf\x19\x3c\x93\x22\x82\xca\xf5\xac\x2c\x06\x2f\x3c\x81\x15\x7c\xf7\xea\xa7\x97\xc7\xdf\x3d\x96\x6f\x4a\x09\x8e\x91\x29\x82\xce\x0b\x0e\x51\xa1\x91\x4a\x4c\xc0\x5d\xa5\xa9\x58\x89\x93\x28\xed\x05\x86\x50\x1d\xff\xcb\x6f\x9e\xbd\xe6\x6b\xc4\x16\xb9\xca\x8d\x7e\x06\x22\x95\xef\x69\x18\xe8\x10\xf5\x7f\x7a\xde\x95\x67\x2f\xa4\xf4\x36\xde\x5d\x98\x8c\x50\xe2\x79\xd3\x7e\x59\x3d\x57\x90\x55\x0c\x15\x48\xa7\x02\xf5\x3c\x32\xa8\xe2\x6b\x55\x86\xa5\x81\x5e\x6a\x40\x10\x76\x11\x10\x03\x82\xa8\x4b\xa4\xa9\x4a\xdc\xeb\x87\x01\x11\xed\x10\x32\x04\x91\xf4\xb1\x0c\x41\x30\xbd\x8a\xa9\x42\xda\xe7\xd6\xb0\x4a\xd6\x43\x33\xa8\x90\xf7\xbb\x32\xac\xc2\xb5\x2a\x43\x0c\x45\x97\xca\x61\x73\xea\x6a\x8d\xe9\xa8\x3c\x08\x1d\x41\xe0\xd3\x11\xad\x0a\xfa\x48\x0c\x7a\x41\xdd\x7a\x13\xd1\x51\xc5\x8c\xa9\x4b\x31\x29\x1d\x95\x77\x42\x47\xe4\xcd\xfa\x44\x18\x54\xa2\x8f\x66\x48\x49\x46\x47\x25\x9e\xd3\x11\xad\xe1\xd4\xad\xdd\x45\x1f\x87\x41\xf2\x56\x71\x29\x2f\x81\xcd\x8c\x24\x5a\xa9\x45\x98\x7e\xa7\x8a\x11\x7b\xd0\x85\x62\xea\x63\xa8\x57\x31\xea\x84\x4e\xa7\xa1\x3c\xee\x92\xe1\xb0\x0d\xec\x50\xff\xa4\x4f\xa9\xd5\x51\x60\x87\x44\xd3\x6e\x67\x0c\x5a\xd1\xe9\x8c\xd5\x4f\x60\x87\xfe\xf2\x5e\x15\x9b\xab\xc0\x66\x57\x40\x47\x59\x81\xe9\x28\x2b\x08\x1d\x15\xbd\x4f\xdd\x62\x0b\x7a\x20\x6c\xbe\xc2\xc5\xee\x88\xba\x54\x38\xa6\x23\xc2\xa0\x74\x84\x93\x09\x1d\x55\x2d\x46\xdd\x02\x4d\xfb\xfc\x36\x0c\x1e\x7d\x2c\xc3\x2a\x39\x75\x89\x94\xd3\x11\x13\x2a\xfa\x12\xd5\xdf\xa8\x9a\x8e\x45\x19\x81\xe7\xd1\xc0\xc3\x56\x0f\xa2\xea\x58\xc3\x8c\x46\x80\x36\x0f\x52\x23\xf1\x4c\x48\x82\x2e\x12\x63\x9d\xb0\x0b\xc7\x48\x4c\xd4\x85\x63\xac\x13\xb7\x75\x0c\x58\x74\x67\x6b\x6c\x9e\xf4\x51\x18\x80\xb0\x7e\x77\xec\x01\x87\x42\x64\x00\x92\x75\x18\x6b\xa8\x90\xb7\x15\xac\x0e\x44\x92\x60\x68\x5c\xf4\xa5\x62\x8d\xbb\x9c\xcc\xc4\x74\xa4\x17\x84\xba\xb8\xed\xf7\x51\x98\x74\x83\xf6\xe4\x6e\xd2\x0d\x3a\xce\xf0\x88\x8e\x28\x6a\x4c\xc7\x15\x95\xd2\x11\xa1\x24\xd4\x21\x14\x46\xdd\xb6\x94\xf6\x29\xb0\x3b\x12\xa7\xa9\xe4\x74\x44\x89\x79\x9f\xa7\x76\x7f\x62\xd5\x20\x7d\x02\x62\x28\xc5\x5b\x98\x3d\x26\x5b\x18\x13\xf6\xb7\x30\x7c\x1c\x6c\xa1\xcf\x38\x74\x9a\x3e\x8e\xc6\x4c\x12\xc7\x23\xce\x50\x0f\xc1\xcd\x10\x92\x31\x77\x89\xd9\x98\xdd\xe3\x74\x0b\x6f\x89\xb3\x31\x47\x86\xf3\x2d\x9c\x25\xe6\x5b\xb8\x32\x5c\xf4\x25\x64\x54\x97\x31\x57\x81\xf1\x98\x85\x62\xb2\x85\x81\x60\x7f\xc4\xca\x70\xb0\x8d\x63\x0b\xb7\x70\x3b\x38\x72\x7a\x37\x1c\x6f\xe1\x96\x30\xdd\xc2\x16\x71\xb2\x85\xd5\x63\xb6\x85\x37\xc5\xe9\x98\x07\xc3\x99\xcb\x85\xe1\x7c\xcc\x2d\xf0\x2d\xdc\x28\x2e\x7a\x1e\x6a\x97\x50\x05\x7b\x81\xc5\x19\x99\x49\x26\x1d\xae\x60\x6b\x88\x22\x61\x9b\xa0\x07\x5a\xb9\x67\x28\x0f\x7b\xc2\x19\xd6\x88\x3a\x4c\x33\xe1\x88\x3b\x35\xc6\x87\x63\x7b\x6c\xd2\x62\xb1\x45\x26\x75\x4f\x6d\x51\x49\x4b\xc5\x90\xce\xac\xc7\xcd\x61\x8d\xbc\xc3\x2d\x5b\x68\x02\x10\x2c\x61\x89\x6a\x6b\xe6\x80\xab\x7b\x98\x8e\x91\x4f\xa8\x5d\x51\x7c\x3a\xa6\x28\x01\x1d\x13\x74\x48\xdd\x9d\x8f\xa8\x5b\x95\x62\xad\x7c\x58\x4a\xa9\x9d\x75\x09\x75\xb1\x8e\xd1\x31\xf5\x4a\xa9\xdb\x08\x32\xea\x56\x9d\x9c\x8e\x29\x06\xa7\x63\x46\x50\xd0\x31\x15\xef\x84\x15\x16\x25\xc0\x23\xe6\x8a\xc9\x88\x86\x62\x7f\xd4\x65\xe0\xc0\xa9\xa9\x38\x1c\x35\x78\x1c\x8d\x7a\x0d\x1c\xbb\x3c\x31\x1d\xb5\x44\x9c\x8c\xba\x0c\xcc\x1c\xd6\x88\xd3\x11\x77\x81\xb3\x51\xaf\x85\x75\x77\x60\x40\xc1\x47\x7c\x2f\x2e\x46\x5d\x92\x0a\x2d\x9c\xdd\xc4\x4e\xbb\xc2\x64\xdc\xb5\xf8\x0e\xcf\x81\x83\x11\xb3\xc6\xe1\xa8\x6f\xc1\x91\xd3\x80\x71\x3c\xea\xdb\x30\x1d\x71\x3e\x38\x19\xb5\x40\xcc\x46\xdc\x00\x4e\x47\x7d\x20\xce\x46\x5d\x01\xce\x47\xfd\x11\xe6\x0e\x67\x87\x8b\xae\x37\xda\x25\x7e\xa0\x9e\x44\x69\xf6\x2d\x75\xf4\x89\xbd\xc0\x12\x4a\xd4\x44\x1b\xca\xfd\x16\x42\x60\x56\xc4\xc0\xae\x44\x61\x97\x23\xe6\x18\xa2\x09\x8e\x4d\xe8\x63\xaf\x13\xfe\xd9\xc7\xcf\x7a\x47\xc5\x1c\x41\xb4\xb2\x35\xc7\x0f\xb2\xdc\x1c\x3b\xb4\xec\xb3\xed\xa0\xb4\xec\x31\xc0\xc8\x35\x2b\xb5\x44\x0e\xb5\x7a\x9b\x63\x87\x56\xc0\x96\xfe\x3b\xe5\x8b\xa9\xbd\x7b\x84\x8e\x11\xef\xd3\x31\x06\x04\xd4\x2d\xe2\x90\x8e\x75\x21\xa2\x56\xfd\x89\xe9\x98\xf2\x51\xea\xe2\x5f\xd2\x45\x6e\x0b\x22\x1c\xda\x91\x52\x97\xf4\x32\x3a\xa6\x7d\x39\x75\xeb\x2f\xa7\x6e\xf3\x2b\xe8\x98\x85\x60\x6f\xc4\x44\x30\x1e\xb1\x42\x4c\x46\xcd\x10\xfb\xae\x91\xc2\xa9\xe1\x38\x1c\x35\x11\x1c\x79\x63\x72\xc2\xf1\xa8\x27\xc3\x74\xd4\x5a\x70\x32\xea\x2e\x30\x1b\x75\x78\x38\x1d\xf1\x99\x38\x1b\xf5\x1b\x38\x1f\x71\x4b\x98\x3b\xfc\x12\x2e\x9c\x6e\x43\x46\x0f\xee\x3e\xe0\x51\xbb\xc4\xc4\x6e\x98\xd8\x1f\x31\x7b\x1c\x8c\x28\x3e\x0e\x47\x6d\x07\x47\xe3\xde\x2d\x76\xb8\x37\x4c\xc7\x8d\x27\x71\xfa\x0f\xcc\x46\xfd\x1f\x4e\x47\x9d\x28\xce\x9c\x4e\x04\xe7\xa3\x5e\x0a\xf3\x11\x37\x85\x8b\xae\x1f\xd9\x2d\x78\x30\xfa\x94\x9a\x5e\xdb\x0e\x49\x43\x8d\x31\x64\xb8\xab\x1d\xd7\x30\x46\x0c\xaa\x02\xac\xa7\x18\xe3\x86\x26\xe6\x33\x94\x47\x35\x00\x5b\x85\xb8\x25\xd0\x50\xaa\xcb\xdc\x16\x32\xb4\xf4\x59\x62\x86\xb6\x87\x06\x0c\x69\x4b\xa0\x99\x84\xac\x53\xc1\x34\x70\x58\x6d\x8f\xeb\xc2\x31\x80\x2e\x3a\xcc\x31\xaf\x39\xb8\xda\x63\x3a\xc2\x5c\x42\x3d\x9b\xe2\xf8\xd4\xad\x38\x01\x75\x29\x4e\x48\x47\xf4\x22\xa2\x23\x5c\x8b\xe9\x88\xea\x51\x3a\x22\xda\x84\xda\xf8\xce\xe8\x88\x4c\x53\xea\xd6\xda\x8c\x8e\x68\x4d\x4e\x47\x24\xc7\xa9\x5b\x71\x0b\xea\x52\x7b\xec\x39\xcd\x16\x63\xcf\x2a\x57\x4c\xc6\x6c\x1a\xfb\x63\x36\x89\x83\x11\xab\xc6\xe1\x98\x51\xe0\x68\xcc\x73\xe0\x78\xc4\xb6\x9b\x71\xcf\x2a\x46\x9c\x8c\x19\x10\x66\x23\xfe\x11\xa7\x63\x1e\x04\x67\x4e\x0f\x85\xf3\x31\x0f\x83\xb9\x7d\x70\x2e\x46\x3c\x04\xc4\x07\x6e\x59\xe1\x11\x4d\xc3\x64\xc4\xd2\xb1\x3f\x66\xcc\x38\x18\x33\x56\x1c\x8e\xb9\xaa\xc8\xee\x8a\x70\x3c\xe6\x2c\x30\x75\x9b\x4b\x32\x66\xf0\x98\x59\x9d\x05\x4e\xc7\x6c\x19\x67\x23\xee\x02\xe7\x4e\x67\x89\xf9\x98\x2b\xc3\x45\xcf\xe1\xec\x12\x15\x28\xb2\xa9\xc9\x8b\xd4\x30\x4d\x71\x81\x6c\x4b\xcc\x7d\xf6\xdb\x72\x62\x82\x1d\xb4\x1c\x31\xc2\x0f\xf5\xfe\x98\xa2\x82\xa6\x74\x08\x3b\xee\x28\xb4\x75\x54\x34\x46\x03\x1a\x51\x43\xc0\xac\x46\x6b\x24\x39\x55\x0a\x6a\x8a\x00\x34\x5e\x0d\xcb\x73\x0d\xec\xb0\x94\x37\x7d\x1d\x96\x15\x1d\x2e\x9b\x7a\xea\x14\x12\xa6\x6e\x21\x11\x6a\xe9\x91\x4f\x5d\xd2\x09\xa8\xab\x3f\x21\x75\x6b\x5d\x44\xdd\x9a\x11\x53\x3b\x3f\x28\x75\xe9\x45\x42\xed\xfa\xcc\xa8\x5b\xf4\x29\x75\xcb\x30\xa3\x16\x9d\xca\xa9\x5b\x44\x9c\xba\x74\xaa\xa0\x6e\x55\xc6\xde\x88\x1d\x61\x3c\xa2\x7c\x98\x8c\x58\x2a\xf6\x1d\x0a\x88\x03\xa7\x9d\xe2\x70\xc4\x14\x71\xe4\x8d\xf8\xa0\xd8\x69\x73\x4d\x04\x6b\xa1\x3d\xb1\x7a\x6d\x66\xb3\x56\x9c\x8e\xb8\x36\x9c\x39\xfc\x22\xce\x47\x7c\x08\xe6\x23\x36\x8b\x0b\xa7\x73\x13\x23\xba\x85\x70\xec\x54\x25\x4c\x9c\x46\x8b\xfd\x11\xbb\xc4\xc1\x88\x61\xe2\xd0\x61\x99\x38\x1a\xf1\x35\x38\x1e\x75\x56\x23\x96\x84\x93\x11\x1b\xc5\xcc\xe1\x00\x70\xea\xf4\x5a\x38\x73\xba\x16\x9c\xdb\xec\x1f\xf3\x31\x13\x2e\xba\xae\x67\xf7\xa1\xdb\xa0\x23\x35\xa9\x81\x87\x0d\x43\xb7\x0a\x35\x0c\x83\xb6\x02\x6a\x6a\x16\x34\x41\x8e\xa9\x34\xb4\x74\x3f\x92\x20\x0d\x63\x74\x1b\x32\x0d\x4b\xa9\xd6\x01\xd3\x30\xdd\xf4\x7d\xd8\x94\x69\x4a\x3e\x2c\x4d\xb5\x4e\x98\xa6\xea\x5a\x1c\x67\x18\xa6\x25\xdf\x86\x50\x79\xcb\x37\xd3\x24\x5d\x8b\x7c\x87\x3d\x75\xb1\x01\x53\x33\x53\x09\x75\xc9\xd7\xa7\xae\x3e\x06\xd4\xa1\x38\x21\x75\x31\x2f\xa2\xae\x9e\xc4\xd4\xc6\x1e\x4a\x1d\x6a\x95\x50\x97\xa8\x19\x75\x49\x24\xa5\x0e\x45\xc8\xa8\x4d\xcd\x73\xea\xd2\x64\x4e\xcd\x1a\x5b\x50\x87\x90\xb1\xe7\x94\x32\xc6\x4e\x73\x25\x4e\x7b\xc5\xbe\xd3\x56\x70\xe0\x32\x07\x1c\x3a\x4d\x09\x47\x4e\x83\xc0\xb1\xcb\x23\xa8\xf1\xc6\x58\x94\x38\xbd\x05\x66\x2e\x8b\xc1\xa9\xc5\x69\xe0\xcc\xe6\x64\x73\xa7\xe5\x62\xee\x74\x0a\xb8\xb0\x7a\x44\xec\x39\xa5\x8e\x9d\x86\x88\x89\xdb\xba\x7d\x8b\xa6\xe1\xc0\x69\x68\x38\x74\x99\x30\x8e\xac\x76\x88\x63\xa7\x67\xc0\xd4\x69\xfd\x38\x71\xda\x22\x66\x16\x67\x85\x53\xa7\xb9\xe1\xcc\xe5\x1d\x70\x6e\xb5\x62\xcc\x9d\x9e\x03\x17\x9a\x73\xd8\x65\x4c\xa5\x62\x80\x27\x06\x80\x0d\x73\x86\xfe\xf8\x6e\xbb\xb9\x31\x74\xc7\xb2\xdd\xd0\x11\x2b\x78\x86\xa2\x50\xc2\x23\x46\x3a\xa2\xa6\xd0\xe4\x84\x15\x25\xe6\x71\x86\x7a\x66\xfa\x93\xa6\xdf\x26\x17\x2c\xe9\x34\x15\xa5\x0d\x50\x03\x9d\xd9\x5d\x79\xd9\x63\xe8\x7e\xcd\x7a\xc2\x1b\x26\x1a\xda\x14\x8a\x08\x43\x51\xbd\xa9\x64\xed\xb9\x2c\xc6\x2e\x9e\xaa\x3a\xc4\x25\x7f\x55\xc7\x77\xc9\x5a\xfd\x1e\xb8\x98\xad\xea\x84\x76\xb6\xaa\x1a\xd1\x68\x9f\x63\x8b\x6a\xa9\x62\xea\xe2\xa8\xaa\x93\xd8\xa4\xa4\xca\x99\x5d\x4b\x55\x8d\xd4\xa5\x8f\xaa\x4e\x66\x16\xb9\x2a\xcd\x5d\x6a\xa4\xea\x70\x97\x8a\xaa\x3a\x85\xdd\x42\xeb\x88\xd8\x68\xd8\xd8\xd5\x03\x4c\x2c\x4c\xc6\xbe\x4d\xe3\x70\xe0\x22\x16\x87\x2e\xb1\xe0\xc8\xc5\x0c\x1c\x3b\xba\x68\xf3\xbf\x89\x5d\x84\x98\xb9\x34\x15\xa7\x4e\x7f\x98\xb9\x2c\x0a\xe7\x76\xfd\xc6\xdc\xa6\x74\xb8\x18\xb7\xae\x76\x72\x63\xad\x81\xdd\xbe\x00\x93\x71\x85\xc3\xfe\x98\xf5\xe1\xc0\x69\x7d\x38\x1c\x77\x02\xb5\xb0\x9d\xdd\x8d\xc7\x9d\x12\xa6\xe3\xce\x0d\x27\xe3\xde\xa0\x56\x07\x97\x95\x49\xa5\xb0\x96\x66\x63\x6e\x4d\x2a\x86\x83\x4e\x3e\xe6\x71\x6a\x25\x01\x2c\xda\xc8\x2e\x3f\xea\x79\x0d\x9e\xb2\xf5\xfb\x35\xaa\x66\xac\x42\x6b\x3e\xe7\x59\x05\xf9\x88\x5e\x7e\xf3\xec\x35\x2a\x17\x67\xf5\x33\x11\x4d\x46\x83\xa7\x0f\x5e\xf6\x1e\x2e\x6e\x2f\x26\x4e\x51\x7b\xf0\x1f\x1e\x50\x54\x5f\xe0\xb3\xfa\x32\xd5\x1b\x7a\xea\x57\x59\x41\x7e\xa9\x3f\x8b\x2f\x53\xad\x3f\x7d\xca\xb5\xac\x4a\xdf\x3e\x7a\x29\x13\x63\x21\x99\xf8\xc5\xfd\x46\x95\xa8\xdd\x3c\x50\x25\xbf\x68\x59\x52\xae\xfa\x44\x95\x3b\xb5\xde\x7b\x7e\xd9\xa4\x00\x7b\xcf\x2f\x0d\xa9\xef\xde\xf3\xcb\x3a\xaf\xde\x7b\x7e\x69\x4e\xab\x27\x70\x48\x11\x85\x11\x4a\xcb\x6a\x8d\x58\x96\x2d\x57\x79\xb9\x38\x41\xd5\x12\x3d\x7f\x88\x8d\x70\xbf\x29\x21\x15\xd0\x9b\x7e\x0e\x64\xd3\xdb\x21\x61\x64\x7f\x3b\xa4\x05\xf7\x7c\x29\x00\x3e\x7f\x88\xdf\x94\x6f\xd1\x1d\x84\x0d\x39\x4a\x15\x5e\x99\x9e\x7f\x52\xf7\xee\x4d\xdb\x5e\xa5\xe3\x13\xff\x99\xf8\x18\xdd\xd1\x40\x43\x1e\xbe\x3d\x74\x73\x00\xd8\x90\xb0\xf4\xc1\x7a\xcd\x4f\xd3\x39\x47\x38\x42\xeb\xf3\xf4\x3d\xbf\x34\xb0\x7f\x7d\x9e\x7e\xcf\x2f\xd7\x8d\x08\xda\xef\x76\xa6\x2c\x5e\x42\x25\xc9\x9a\xfa\xcb\x7d\x84\xa3\xe6\x9b\xfd\x89\x95\x87\x90\x71\x4a\xd1\x63\x66\xe4\xba\x86\xae\x68\x79\xa3\x80\xbe\x55\x44\x19\xe1\xba\x9f\x6e\x49\xcb\xea\x25\x64\x45\x39\xd2\x92\xa0\x34\x70\x6d\x20\xa5\x42\x05\xd4\xa8\x50\x64\xd8\xc6\xa4\x35\x24\xb0\x6b\x4d\x17\x4f\xb1\x5a\x9e\x82\x83\x99\xf3\xa2\x42\x84\x82\x65\x08\xcc\xe6\x86\x92\x39\x6f\x26\x25\x3a\x94\x6f\x43\x78\x90\xc0\xb1\x56\xae\xc9\xe4\xf9\x43\xa2\x74\x70\x0f\xed\x37\x1c\xd8\x43\x7f\x43\x84\xbe\x85\x1c\x8f\xa0\x5b\x25\xfa\x1b\xbc\x71\xb1\x35\x79\xab\xf2\x64\xb6\x3d\x7d\x01\xa4\xef\x6c\x89\xdc\xeb\x50\x49\x28\x14\x4b\x5a\xd1\x3e\x22\x81\x85\xe0\x3d\x03\xc5\x03\xb4\xa6\xcc\xfe\xa2\x03\xe5\x22\xe3\x88\xb3\x6c\xa6\xd4\x0e\x95\x6b\xc4\xce\xce\xe6\x25\xcf\x85\x2c\xd9\x02\xf1\xcd\x19\x5b\xe4\x3c\xaf\xf3\x32\x82\x7b\x9f\x1a\xa1\x09\x16\x28\x30\x19\x5b\xa0\x94\xa3\x74\xb5\x7c\xcf\x17\xa8\x5c\x54\x4b\x44\x65\x52\xe0\x35\x5a\x67\x6c\x2e\xc1\x4b\x90\x6b\x33\xb4\x8b\x59\x99\xcd\x10\x9b\xcf\x97\x17\x6b\x00\x2d\xe0\x56\x4b\x01\xf6\x7c\xcd\x73\x74\x51\x56\xb3\xe5\x79\x25\x09\x5c\x97\xcb\xc5\x10\x8a\x62\x34\xa4\xd7\x9c\xb4\x5f\xee\xdf\x57\xcf\xca\xb4\x3f\x09\x87\xe2\x63\x13\xe7\x3a\x9a\x8b\xa5\xe6\xc6\x6e\xc5\x55\x60\xc1\x89\xb5\x9f\xc1\x67\x4d\x4a\x29\xc4\xdb\x48\x48\xdf\x37\x8b\xca\xd6\x8f\x58\xef\x47\xfc\x56\x25\xf6\xfc\x4d\xff\x09\x1e\x05\x18\x3c\xb5\x63\xf0\x80\x0f\x65\xe2\x4b\x54\x2e\x3e\xf0\xd5\x9a\xdb\xbd\x60\xb9\xf8\xf0\xb2\xe7\x08\x3b\x3f\x6d\x35\x40\x60\xc7\x00\xd1\x42\xd3\x39\xb6\x7e\x83\x43\xa1\xd0\x7d\xe8\x1f\x3b\x0b\x0e\xed\x17\xbe\xc8\x56\x97\x67\xd5\x0e\x4f\x01\xaa\x8c\xb5\xcb\x87\x4d\xbb\xb6\xf2\xb4\xeb\xf2\xad\x29\x74\x73\xfe\x39\xb0\xb6\x1c\x71\xe5\xee\x7d\xe8\xc6\x3c\xad\x19\x69\x0a\x3a\xfe\x83\x57\x7a\x9c\xd6\x25\x6e\x0e\x40\xb5\xa7\xb1\xfa\x32\x90\xd5\x56\xfd\x6a\xf0\x72\x96\x21\xfa\xf8\x6e\x51\x56\x25\x9b\xeb\xa9\xaf\xba\x75\xf8\x26\x9b\xb1\xc5\x09\x7f\xf2\xa2\x4d\x8b\x2a\x33\x8f\x79\x1b\xaf\x90\xff\xeb\xab\xb4\xb9\x8d\x7c\x9f\x1a\x66\xac\x45\x61\x6d\xf3\xe2\x89\xde\x86\x00\x1e\x5f\xfd\x6d\xd7\x86\x4a\xda\xbc\xa2\x10\xff\xdf\x92\x36\x68\x13\xaa\x3f\x63\x66\x5a\xd7\x53\x6d\x32\x7d\x18\x58\x94\xfc\x28\xad\x0a\x3e\x8f\x3f\xdb\x66\x18\x89\x8c\xf1\x04\x80\xb3\x3d\x7b\xd1\x28\x86\xae\x27\x96\xba\xab\x6e\xdd\x95\xaa\x6b\x24\xf2\x31\x2f\xd7\x15\x9f\x37\x5a\x6c\x86\x58\x40\xe7\xb7\x0b\x2d\xa8\xdb\x41\x17\x62\xa0\x95\xa9\xd6\xde\x94\x6f\xdf\x4c\x26\x8a\xda\x77\xad\xbb\x16\x81\x64\x33\x75\x81\xef\x90\x56\xdb\xc4\x1a\x83\xc3\xee\x19\xd2\xca\xc6\xa9\x9e\x25\xcd\x6b\x32\x8a\x71\x07\xfe\xf7\x45\xbe\x44\xeb\x0b\x76\x26\xc3\x8f\x39\x5b\x57\x52\x19\x86\x2e\xbc\x72\x8b\xac\x47\x6c\x57\x60\x2e\xc3\xaf\x0c\x3a\x0c\x19\xc5\x77\x35\xf5\x81\x69\x5c\x9b\x09\x5e\xc5\xd4\xaf\xe2\x52\x46\x5c\x97\x61\x46\x56\xa1\xe5\x79\x35\xf0\xc0\x8d\xcb\x75\x8b\xac\xe3\x72\xed\x32\xeb\x0c\x19\xef\xf9\xa5\x4c\x01\x1d\x05\x87\x3e\xd1\x4b\xca\x0f\x96\x02\x2d\x6f\x74\x64\xcc\x1a\x7d\x88\x5e\x0a\x0d\x54\x93\x80\xd5\x72\xbd\x6e\xc3\x74\xc8\x79\x08\x01\x31\x4c\x4b\x65\x8b\x66\xa0\x6a\x19\x37\xa9\xc7\xab\x53\xb6\x7e\xdf\x31\xd9\x5a\x77\x27\x93\x8e\x8a\x0a\x43\xac\x47\xd7\x77\x9d\xae\x0b\xa3\x15\x50\x34\x16\x74\x54\xf6\x1d\xe8\xec\x57\x46\xc5\x17\x65\x22\xa2\x92\x90\x55\xad\xda\xee\x06\x64\xbf\x78\xb2\x3d\xd9\x2b\x3b\xd9\x73\x37\xd9\x73\x07\xd9\xab\x2d\xc8\x76\x26\x91\x5e\xd7\x59\xa4\xe5\xf2\xc7\x76\x79\xa4\xc7\x92\x30\x4b\x58\x15\xdf\x54\x7a\x2a\xe6\x6f\x1f\xbd\x3c\x50\x01\x5a\x27\x17\xf3\x14\x65\xc5\x89\x21\xb9\xf6\xd9\x9c\x09\x22\x36\x15\xea\x43\x51\x01\xd7\xa4\xc5\x63\x02\xd4\x64\x76\x1e\x2e\xd4\x74\x93\x6e\x7f\xfb\xe8\xa5\x31\xe3\xf6\xab\x55\x79\x36\xe7\x77\x76\x5b\x22\x92\x8d\x3a\x0b\x45\xfa\x4f\x7f\x9e\xe5\x22\xb5\x10\x21\xc8\x2e\x21\x43\x69\xd6\x7f\x1e\x48\x45\xb1\x7c\x8d\xd1\x91\xa8\x77\x20\xb9\xfa\x48\xca\x78\xb9\x9a\xb4\xef\xac\xab\x87\xe3\x6b\xd4\x07\xeb\x79\x99\xf1\x89\x37\x45\x64\x6f\xf0\x16\x46\x03\x96\x5c\x11\x2c\x99\xa2\xc0\x01\xd6\xbf\x22\xd8\x60\x8a\xa2\x3d\xfb\x43\x1a\x57\x9e\x7b\xf0\x35\x3e\xd0\x1b\x6b\x2d\xac\x9c\x39\xd0\xe7\x1c\x5b\x34\xf0\xb7\xc0\x70\x3d\x73\x1a\x81\x6b\x47\xe2\xc8\xae\xdd\xc7\x5b\x60\x30\x8f\x7a\x38\x21\xd7\x36\xec\xfd\x93\xb8\xd5\xc6\xbb\x5c\x83\x73\x6d\x61\xed\xe8\x62\x6d\x2e\xae\xeb\x68\x9b\x5a\xce\xfc\xf9\x4d\xad\x5e\x0a\x7d\x2d\x31\xfb\xdd\x90\x4c\x7b\x59\xf5\xb5\xe4\xee\x77\xc3\x60\xda\x66\x75\xbf\x1b\x46\x53\x95\xec\xfd\x6e\x84\x3f\xbe\x9d\xd2\xe0\x93\x12\xee\xff\x91\x99\xf6\x3f\x5b\x3e\xfc\xff\x9e\xcc\xf6\xf0\x52\x41\xb9\xe0\xf9\xf5\xa6\xb8\xff\x86\xad\x79\x9b\xb5\x9e\xad\xb9\x56\xf6\xda\x27\xce\x0c\xf8\x43\x5b\xde\x44\x01\x5a\xb0\x53\xbe\x3e\xd3\xad\xf4\x50\x27\x43\x54\x11\x64\xc8\xff\xfe\xfa\xd1\x04\xe6\x01\x8a\x82\xe6\x09\x1b\x13\x98\xd7\x51\x20\xe8\x00\xa2\x36\x51\x70\xa0\xbe\x08\xfa\x0d\x91\x41\x0b\x5a\x82\x57\xcb\x29\xe5\x2f\x7c\x8d\x18\x5a\xf0\x8b\xf9\x25\x92\xb6\x96\x9b\x10\xeb\x0e\x05\x75\x5e\xf3\x58\x9c\x9f\xa6\x7c\xf5\x11\xc1\xab\x52\xf0\xaa\x8a\xf8\xe0\x13\x08\xe7\x0f\x9c\x4d\xe6\xcb\x0b\x68\x21\xfe\x6b\x6a\xd0\x6d\xdc\xf5\x6e\xc3\x0a\x35\x5f\x36\x2d\x5f\x6a\x8f\x50\xb3\xa7\x1e\x98\xe5\xee\x9f\x47\x3c\x1f\x66\x65\x81\x17\x7a\x91\xd7\x5d\xef\xac\x39\x0d\x2e\x7e\x51\x76\x22\x2a\xd1\xc3\xa9\xa0\xda\x3c\x86\xa9\xf7\xb5\x0c\xaf\x7a\x42\xb1\xe8\xed\x11\xea\xbe\xbe\xad\xcf\xcc\xfb\x92\xfa\xa6\xac\x2e\xca\x35\x47\x3f\x3c\x7b\xb5\x06\x08\x63\x82\xa9\x1f\x4a\x51\x0a\xf2\x11\x3d\x10\xf2\x15\x7c\xb9\x03\x8c\x51\x23\x09\x2b\x2a\xbe\x42\x0b\x7e\xc2\xaa\x72\x71\x72\x0d\x8c\x07\x50\x5c\x30\x5e\x89\xe0\x60\xb1\xac\x26\x56\xae\x1e\x1e\xa2\xc5\x72\x34\x52\x85\x37\x59\x24\x43\x7f\x6f\xb8\x7b\xcf\x58\x4d\x32\xf6\xf7\x9a\xc9\x86\x90\x54\x71\x46\x31\xa6\xd6\x86\x56\x9c\xf7\x3a\xd4\x75\x22\x00\x9b\x54\x1e\xfc\xf0\xad\x26\x15\xd8\x4e\x80\x71\xfb\x8c\xad\x61\x7b\x61\x2b\x1b\x6a\x24\x05\x30\x84\x49\x34\xc2\xaa\x96\x02\x45\x0d\xf7\x9a\x85\xff\xe0\x87\x6f\xaf\x47\xf4\x72\x6f\xa7\x15\x3c\x5b\xe4\x13\xb6\x58\x56\x33\xbe\x52\x84\xb8\xd4\x80\x2d\x72\x5d\x0d\x44\x0f\x47\x54\xa1\xb5\xb3\x9b\x92\x21\x63\x5a\xd1\x58\x9e\xaa\xff\x87\xe9\xc7\xb3\x17\x9f\x5b\x3d\x9e\xbd\xf8\x4c\xda\xf1\xec\xc5\xf5\x28\xc7\x72\xd5\xd1\x8d\xe5\x6a\x07\xd5\x58\xae\xae\xac\x19\xbf\xed\xa8\x19\xbf\xfd\xc1\x9a\xf1\xfa\xf3\xab\xc6\xeb\xcf\xa6\x1b\xaf\xaf\x4b\x39\x36\x3d\xed\xd8\xec\xa4\x1e\x9b\x4f\xd0\x8f\x77\x3b\xea\xc7\xbb\x3f\x48\x3f\x60\x53\x5e\xd7\x8c\x85\x5c\x19\x55\x13\xc2\x39\x2f\xaa\xed\xa3\xb2\x05\xe8\x84\xfc\x86\x96\x45\x03\x09\x9e\xb0\xb9\x2e\x65\x00\x60\xd7\xa3\x0e\x00\xaa\xa3\x10\xf0\xcb\x93\x09\x09\x5d\x7a\x20\x2b\xe9\xaa\xb0\x30\xe9\x81\x98\x02\x2d\xd0\x7d\xe4\x13\xdb\x4e\x97\xa6\x29\x93\x56\x55\xee\xdf\x47\x0b\xd8\x22\x6f\x94\x41\x1e\x1d\x22\xe8\x0e\x5a\x18\x1f\xab\x37\xab\x90\x80\x33\xd4\xb5\x8f\xa8\x9e\x3c\xb9\x09\xd2\xc1\x4c\x16\xe8\x8e\xe1\xc5\xd0\x01\xea\xfe\x56\x97\x40\xf7\xdf\xa9\xbd\xb0\x94\xff\x6f\xa7\xbe\x2f\x26\xf6\xc9\x45\xad\xbd\x2f\xae\x49\x7b\xa5\xdc\xbb\x9a\xaa\x29\x6f\xad\xcf\x5b\x28\xef\xc0\x63\x02\xa8\x2b\xe8\xaf\x66\x05\x0d\x9c\x71\x05\x56\xe8\xff\x70\x0d\x7e\xb1\xac\x58\xc5\x3f\xb7\x03\x5e\x01\x96\xeb\x52\x61\x80\x76\x3d\x2a\x2c\x09\xd3\x55\x78\xb5\x1c\xf5\xbf\xa2\xca\xa8\xfe\xaa\x1e\x81\x1e\x28\xaf\xbe\xd8\x13\xe1\x60\xfb\xcb\x8b\x49\x14\x0c\xd4\xf2\x53\x05\x76\x4d\x3e\xe7\xcf\x25\xb1\x11\x97\x23\x6a\xec\x2e\xb0\x17\x03\x81\x3d\xb9\x8a\xc0\x1e\xe4\xf9\xe7\x8e\x7c\x59\x9e\x7f\xa6\xc8\x57\x3e\xf9\x7d\x1d\x73\xe6\xbc\x37\x67\xce\x77\x9a\x33\xe7\x5b\xcf\x99\xfb\x23\xc2\x7e\x13\xc8\xc2\x81\x51\x73\xf0\x9b\xb1\xd5\xea\x52\x34\xab\xc7\x10\xf9\x30\x7c\x67\x58\x69\x9f\x87\x37\xc3\x18\x06\x52\xfb\x6d\xcc\x8d\xf6\x25\x0e\x45\xc3\xa7\x7a\x74\xf9\xcd\xbc\xbb\xf2\x60\xa1\x9e\x00\x5f\x16\xfa\xda\xe6\xda\xf4\xc2\xf1\x6a\x79\xc6\x57\xd5\x25\xfa\x55\x3d\x31\x0c\x15\x41\xbd\x1a\x10\x83\x65\x45\xa5\x20\xeb\x03\x13\x9c\xda\xad\x34\x6f\xa2\x77\xbd\xcb\xba\x3c\x59\x94\x45\x99\xb1\x45\x85\x52\x28\x2f\x17\x9a\x6d\x00\x52\xc7\xea\x6f\xbb\x2e\x5d\x13\x53\xff\x72\x0d\xeb\xc0\x43\x0a\xec\xe6\xd8\x61\xd7\xe4\xd9\x99\x50\x4b\x36\xdf\xeb\xf0\x7e\x94\x71\xc8\xe8\x90\x1b\xce\x69\x60\xb7\x62\x22\xef\x8a\xf9\x13\x6c\xf5\x42\x67\x75\xbf\x17\x9d\x3d\xdf\xae\xcd\x7e\x22\xb0\x37\x83\xf6\xe2\x6f\xd7\x65\xed\xe9\xae\x50\x30\xc5\x09\x66\x38\x85\x3b\x35\x19\xce\x31\xc7\xc5\xde\x00\xc8\xdb\x7f\xa3\xae\x4e\x11\xf6\xb6\xde\x1e\x00\xa5\x9b\x36\x6a\x3b\x70\xcb\x17\xea\xf0\x04\xb8\xc5\xfa\x8b\xfc\xef\x6f\xbf\x19\x2e\x60\x88\xb8\xbf\xb1\x81\xbf\x1c\xa1\xe1\x2e\x98\xfe\x27\xc7\xe6\xba\xfa\x51\x43\x46\xff\x2c\xa0\x35\x68\xef\x03\x90\x36\x34\xe7\x8b\x93\x6a\x86\x6e\x23\xba\xe5\x51\xea\xbe\xa3\x79\xb8\x5c\x7c\xe0\xab\x7a\x6a\xa8\xb9\x61\xe5\x1f\xc4\xa0\x5d\xdf\x0e\xd8\xca\xf1\xd4\xa3\x76\x23\xdd\xce\xce\xdc\x47\xf4\xaa\xeb\x44\x6f\xad\x51\xce\x2a\x86\xd8\x7a\x47\x3c\x5b\xaf\x64\x75\x77\x0a\x37\x9a\x83\x3e\xa8\x96\xaf\x7d\x62\xdf\x0a\x81\xe2\x4f\x38\xb3\xa3\x70\x75\x95\xca\x70\x72\xa7\xae\xf7\x44\x0a\xb3\x21\xb2\x16\xaf\xe9\x14\x8f\x14\x9b\x01\x96\xec\xee\xd6\x87\xf7\xbb\xb8\xdd\x37\xbd\xda\x2d\xbc\xba\xd5\x9b\xc1\x11\x7e\xf1\x57\xd3\x70\x70\x76\xbe\x9e\x4d\xea\x40\x4a\xc4\x08\xa6\x79\xa5\xb9\x76\x2f\x96\x40\x86\x73\xb2\x75\x28\xa2\x09\xb8\xf6\x20\x35\xcc\x69\xd7\x6c\xac\x07\x49\x06\x56\x01\x60\x84\x4a\x66\xcb\x33\x18\x24\x2d\x63\x3f\x1a\x0d\x5b\x1b\xb5\xe7\x28\x9b\x2f\x17\xae\x99\xca\xb6\x2a\x0d\x70\xfa\xba\x0c\x3f\xda\x75\x19\x8a\x9d\xba\xac\x43\x86\x28\x45\x92\xdb\x9c\x7c\x35\x9d\x74\x7d\x08\xf5\xff\x0a\x8a\xfd\x57\xc9\x99\x21\xd0\xda\x97\x4a\x78\x43\x37\x5b\x9f\x1a\xb3\x23\x80\x3b\x4c\xf5\xc6\xba\x0c\x4e\x2c\x68\x1a\x13\xba\xe8\xd8\xcf\xa8\x19\x5c\x6c\x63\x03\x17\x4a\xe5\x6b\xf0\x6f\xca\xb7\x26\xb6\xdb\x55\x15\x2a\x77\xf6\x97\x9b\xf0\xd8\x7a\x6e\xa6\x77\x5a\x46\x1d\x8d\xf9\xf8\x76\x4a\xc3\x6d\xce\xbb\x1c\xde\xfe\x0b\x9a\x55\xd5\xd9\xfa\xee\xe1\xe1\x69\x35\x5b\x1f\xa4\xfc\xf0\xbc\x2a\xe8\xcf\x6b\xf4\x81\x1c\xe0\x03\x82\xd2\x4b\xf4\x3f\x4e\x59\x35\x2b\xd9\x5a\x68\x4c\x7b\x40\x06\x4e\x85\xc8\xc3\x1e\x87\x87\xe8\x5b\x5e\xc9\xeb\x70\x9c\x0b\x76\x97\x2c\x9d\xf3\x35\xfa\x87\xc2\xf4\x8f\x1b\x5f\xc1\x31\xfe\x15\xe7\x8f\x9a\xf3\x2f\x83\x93\x34\xe8\x96\x14\xde\x2d\x74\xf3\x66\xfd\xf3\x3d\x3b\x78\xf4\x0f\xd9\x1d\x0d\xf8\x53\xf8\xa1\x85\x7d\xaa\xbe\x77\x41\xab\x5f\x6f\xde\x34\x9c\xcf\x39\xea\x10\xd9\x54\x76\x92\x71\x02\x27\x67\xfe\x31\x95\xa7\xf1\x7f\x58\xe6\xfc\xe0\xe7\x35\x5a\xae\xd0\x37\xf2\x28\x4d\x59\x94\x3c\x47\xd9\x32\xe7\x53\x80\xc2\x16\x39\x3a\x5f\x73\x54\x56\x62\x5c\xfb\x87\xe0\xa3\xd6\x07\x75\x0e\xa7\xe9\xc3\x89\xfa\xde\xed\x83\xfc\xf5\x9e\x3c\x93\xd4\x36\x3b\x68\x6a\x1f\xe9\xc0\x7e\xfb\x4d\xfb\x76\x70\x51\x2e\x72\x31\xbb\xec\xd4\x91\x47\x87\x04\x2d\x48\xff\x19\x0e\xfb\xdc\xf8\xea\xf0\xf6\x9d\x6b\xfb\xbb\x7d\x78\x43\xf6\x76\x5d\xad\xca\xc5\xc9\xe3\xd5\xf2\xf4\xe1\x8c\xad\x1e\x2e\x73\x21\xb9\x97\xf0\xe3\x41\xa1\xfd\xaa\x98\xff\x8a\xbd\xe7\x0b\xc9\xe3\xbe\xca\x9e\x9d\x2f\x2e\x05\x7f\x6f\x7c\xd5\x78\xb0\xf3\x6c\x4d\x72\x2e\x7e\x9c\x48\x3c\xb2\x83\xb0\xb5\x09\x87\xef\xeb\x21\x10\x7e\xca\x96\xe7\x8b\x8a\xaf\xd4\xca\x25\xfc\x34\xaf\x7d\x85\x6c\xde\x3a\x0b\x28\x85\xfb\x8c\xf5\x17\xbe\xa9\x56\x4c\x7c\xb9\x98\x95\x73\x8e\x26\x35\xb4\xfb\x0a\x88\x44\xfd\x15\xb4\x69\x01\x66\xaa\x7b\x0f\xaa\xba\xc1\xfe\xbe\x30\xf5\xaf\x40\xa6\xb2\xf2\xd7\x47\xc8\xdb\x7c\x4b\x3d\x4f\xc8\x5c\xfe\x74\x1f\x7e\xfa\xe6\xf1\x63\xf1\x93\x05\x93\x60\x17\x4c\xd7\xd7\xe7\xab\xd5\xf2\x84\x55\x7c\x0a\x5a\x57\xcd\xf8\x8a\xc3\x3d\x4f\xb4\xe0\x9b\x0a\x09\x12\x58\x56\xf1\x15\x34\x82\x6e\x6c\x43\x1f\x10\x38\x91\xd5\x6f\x22\x6f\xf3\xf8\xa1\xe7\xed\x09\x0d\xf5\x36\xdf\xc2\xc7\x5f\x85\x73\x9e\x2f\x2f\x5a\xfc\xd0\xec\x2b\xc9\x79\x39\x94\x4f\x54\x17\x05\x00\xff\xf1\xe3\x3d\xb8\x9a\xe9\xed\xa1\x7d\xa4\x41\x86\x82\xfd\x3a\xe3\x90\xc2\xde\x46\xc1\xaa\xab\xe7\x8b\x53\x56\x65\x33\x9e\xb7\xf8\xee\xa1\xe5\x62\x7e\x89\xd8\xd9\x19\x87\x7e\x97\x6b\x30\x40\x74\xbe\x28\xab\xa9\x98\x68\x66\x6c\xcd\x61\xb6\x29\x18\xd1\x40\x6a\xea\x08\x26\x55\xf5\xb9\xa8\x06\xaa\x18\xea\x99\xf6\xf5\x8c\x95\xab\x61\xcf\xa0\x5f\x8a\xd6\xaf\x14\xeb\xee\xdc\x51\xb4\xdf\xe8\x77\xc0\xd2\x52\x54\x14\xff\x57\xfe\x5e\xd6\xaa\xad\xf1\x2a\xc6\xc0\x17\x60\x0c\x30\x0a\xb7\xb6\xd0\x68\xb9\x8c\x5b\xba\x4a\x5e\x2e\x72\xbe\x41\x47\xe8\x0e\x36\xaa\x7d\x63\x47\xb7\x6e\x69\xca\xbf\xbf\x2f\x9b\x59\x94\x1f\xf0\xbc\x81\x2a\x6f\xfb\xca\x2e\x54\xe9\xb1\x90\xb8\xe4\x8c\xfc\xf5\xce\x51\x2d\xfe\x7b\x1a\xbf\xd0\xfe\x91\xc1\x7f\xd4\x80\xbe\xfe\x1a\x61\xaf\x56\x20\xf4\x9b\xb2\x21\x25\x92\x9a\x12\xa9\xac\xe8\x37\xd4\xd1\xc3\x86\xf9\x5b\x20\x02\x80\x36\x21\x35\xcc\xcf\x66\x3c\x7b\xff\x32\x63\x73\xb6\xfa\x5f\xa2\xd5\x44\xc8\xe1\xf9\xb2\x5c\xc8\xd3\xd4\xc0\x80\xe6\xa7\xae\xc5\xb7\x3f\x4b\xab\x6f\x99\x53\xcd\x56\xcb\x0b\xf4\x68\xb5\x5a\xae\x26\xd0\xab\x5b\x4f\x44\x28\xd4\xaa\xe6\xdf\xf7\x6f\xa1\xfd\x16\xc0\x41\xb5\x94\x9e\x75\x82\xa3\xbd\x83\x6a\xf9\xf7\xb3\x33\xbe\x7a\xc8\xd6\x7c\xb2\x87\xf6\x25\x00\xa1\xf2\x8b\x65\x25\x14\x1c\x88\x95\x7c\xb9\x25\x0a\xeb\x8e\x7e\xfc\x0c\x23\x41\xcb\x27\x88\xaa\x45\x24\xde\xb2\x63\x2a\xb7\xd9\xd4\xe0\x24\xb9\x6c\x90\xc6\x44\x67\xe0\xd7\x75\x1b\x29\x51\x58\xaa\xdc\x50\x6f\xaf\x2f\x17\x69\x10\x0f\xeb\x86\x26\xb1\x68\x60\x6f\x2a\xe5\x7c\xfc\x98\x2a\x5f\xa7\xdc\x1c\xbe\x93\x5e\x56\x1c\xad\xf9\x7f\x9d\xf3\x45\x06\x8e\xce\x4e\x68\x8b\xa3\x56\x1d\x18\x08\x2f\x4f\xd3\xe5\xbc\x31\x24\x1b\x66\xea\x75\x31\x93\x21\xe6\x06\xd2\x38\x93\x22\xc9\x20\xac\x18\xf4\xd0\x6b\x48\x6a\x0e\x1e\x1b\x88\x00\x37\xac\x13\xe1\x0f\x89\x70\x28\xfc\xbd\x1d\x89\xc4\x44\x52\xe9\x29\x2a\x1f\x79\x1d\x10\xfb\x47\x16\xad\x89\xb6\xe8\xcc\x23\x6f\xd0\x99\xe0\x93\x38\x8a\xa9\x22\x36\x96\xc4\x3e\xde\x92\x58\x4c\x76\xed\x54\x5b\xd3\x44\x55\xb7\xa3\x5d\x0b\x68\x74\x13\x20\xf4\x4d\x42\x84\xfe\x6a\x9c\xe8\x07\x4d\x0d\x50\x11\xba\x0f\x83\xab\x41\xd4\xd4\xd6\x1f\x1d\x54\x9a\xaa\xf5\x0f\x42\x08\xd2\x5b\x6d\x39\xb8\xb4\x3d\xd6\x11\xeb\xa3\x8c\x06\x72\xff\xc8\x61\xfa\x3d\x8f\xde\x36\xfb\x5c\x81\x70\xc3\xfb\x15\x67\xf9\xc3\xe5\xa2\x2a\x17\xe7\x70\x79\x16\xa4\xdf\xba\x22\x41\xc9\x77\xd0\xf7\xaf\x8f\x80\xac\x87\x22\xb0\x30\x8c\x06\xb7\xbe\x5b\x7c\x60\xf3\x32\x87\x4a\x92\xdb\xb7\x54\xb7\x1a\x7e\x77\xb1\x20\x09\x10\x16\x0a\xde\x34\x78\xde\x2a\x33\x11\x4d\x9b\x1f\xf7\xf7\x45\x30\x5e\x7b\xa8\x1e\x98\x9b\xd2\x8d\xc8\x40\x50\x78\xc9\x5f\x35\x67\x68\xac\xed\x3f\x6e\x08\x3b\x3c\x44\xdf\x15\xe8\x82\x23\x11\xaf\x9d\x9f\x21\x11\xa9\x4e\x51\x59\xfd\xdf\xff\xfd\x7f\xea\x61\x49\x07\x01\x14\xdf\xb0\xf4\x7c\x50\xf1\xd6\xc0\xf9\x4b\xed\x7d\x09\x56\x30\x69\xb5\x5c\x54\xc6\xba\x1a\x12\xfd\x8b\xaf\x7f\x09\x0c\xea\x3b\x94\xd5\x27\x88\xaa\x0b\xe9\x68\x28\x75\xc5\xd9\x82\xcd\xe1\xf2\x43\xc3\xc7\x17\x9c\xe5\xa8\x28\x57\xeb\xaa\xe6\x12\x74\x6b\x77\x31\x0f\x47\x37\x34\x59\x2c\x87\xec\x5d\xef\xd5\x3a\x21\x11\xdd\x54\xf2\x57\x9e\x55\xa3\xb5\xe1\x6f\x4d\xeb\x70\x0c\xeb\xc1\x79\x54\x2b\xd4\xc3\x1a\x14\x88\x05\x1d\x59\x0c\xe6\x5e\xdf\x1f\xe8\xc0\xb0\x9c\x66\x40\xce\x9d\x46\xba\xa6\x00\xac\xd1\xde\x56\x7d\x35\x1f\xd5\x0d\xe0\x77\x50\xc1\x3a\xac\x97\x7d\xf7\xfb\xbc\x3d\x65\x97\xa8\x5c\x64\xf3\x73\x98\x84\x88\xc9\x85\x3e\xa5\x31\x71\xf9\x71\xcd\x9d\x47\x3b\x70\x07\x54\xf9\x6a\x0c\xf4\xd4\x3c\x8d\xc0\xd9\x24\x89\x4b\x67\xa8\x6f\x63\xa8\x07\xc1\x8b\x64\xd8\x58\x7c\xf0\x39\x79\x3e\x1c\xe1\xfb\x1c\xa5\x8a\xa3\x8f\xaf\x97\xa3\xe0\x32\xae\xc8\xf4\x18\x98\xee\x6d\xfa\x6c\xf7\x36\xde\xc3\x3d\xf4\x1b\x70\x64\x22\x69\x90\xbf\x36\xf2\x08\xac\xf2\x80\x19\x95\x61\x8e\x81\x3d\x7d\x0a\x66\x96\x44\xcd\x4f\xa3\x14\xfe\xfe\xea\xf1\x1d\x8a\x72\x58\x29\xe3\x79\xe3\x79\x6b\xb7\xa9\x6e\x60\x35\xdf\xc1\xa1\x69\xdf\xc1\xff\xdc\xeb\xc5\x24\x2a\xd6\x68\x47\x63\x49\x5f\x03\xaf\x1b\x92\x68\xd5\x6a\xaf\x06\x58\x74\x07\xa8\x05\x25\x9a\x8f\x6d\x57\x7f\x3a\xe1\x4e\xbb\x4e\x54\x9d\x9e\x69\xd1\xc8\xa4\x3a\x3d\x43\x47\xbd\xb1\x64\x0f\xfd\xe5\xe8\x48\x3a\xe5\x7e\x74\xa2\x36\x31\xaa\xd3\xb3\x7e\x9c\xa1\x4d\xd0\xdb\xda\x7b\x9f\x73\xf1\x4d\xb0\x15\x1d\x01\x81\xb7\x3e\xf0\xd5\xba\x5c\x2e\x6e\xdd\x45\xb7\x60\xd1\xf7\xd6\x54\xfc\x2a\xe9\xb9\x75\x57\x8b\x0a\xe1\x77\xd9\x5d\xf5\xbb\xfc\x72\xe3\xab\x8f\x6a\x91\xee\xe5\xf2\x94\xa3\x07\x4f\xbf\x45\xe9\x79\x39\xcf\xd1\xf2\xac\x2a\x4f\xcb\x5f\xf8\x6a\x3d\x45\xf3\xf2\x3d\x47\xab\x83\x9f\xd7\x53\x39\x25\x86\x95\xf6\xf5\x19\xcf\xca\xa2\xcc\x84\xf1\xe6\x25\x08\xfc\x8c\x55\x15\x5f\x2d\xd6\x00\x0f\x1a\x55\x33\x8e\x8a\xe5\x7c\xbe\xbc\x28\x17\x27\x77\xe5\x9a\xa7\x50\xbf\xde\xbd\x48\x74\xab\x56\x9a\x5b\x72\x71\xb7\x53\xe1\x80\x9d\xe6\xbd\x55\xd4\xe6\x8a\xa4\x28\xbb\xf1\x95\x14\x97\xba\x34\xd9\x2c\x73\x77\x07\x30\xd1\x67\x90\x1d\x08\xa7\x9d\x5d\xf4\x56\x8d\xff\xa2\x7d\x3f\x58\x2c\x73\xfe\xea\xf2\x8c\xb7\xc1\x5c\xbb\x56\xad\x26\x1e\xe5\x42\x5f\x37\x7e\x51\x2e\x4e\x96\xff\xf3\x25\xfa\xe0\x1d\xd0\x03\x0f\xa6\xe7\x6d\x0b\xed\x2e\x69\x43\x8c\x72\x8d\x35\x24\xb6\xba\x98\xb1\x79\x0f\x52\x7c\xe0\xdd\x91\x0b\x31\xab\xfa\x6c\x94\xbc\xc5\xa8\x7e\x9b\xb1\xf5\xb3\x8b\xc5\xf3\xfa\x08\xcc\x91\xaa\x74\xd0\xfd\x1d\xaa\x37\x5b\x24\x90\x35\x4e\x32\xa5\xf6\x18\xdd\xea\x72\x7f\x48\x94\xc3\x45\xe2\x3d\xc1\x1b\x9d\x57\x6f\xde\xcb\x04\x86\xa2\x06\x7c\xee\x2c\x7e\xf5\xfa\xf5\x62\x56\x2e\x96\xa2\x57\x0c\x5d\xf0\x14\xa9\x8b\xaa\x6a\xd5\xfa\x40\x29\xb4\xe2\xc9\xc7\x1b\xea\x8a\x2a\x6c\x9b\x7c\x9c\xfe\xfa\xf1\xed\x94\x46\xdb\x6c\x89\x0c\x6e\xec\xbe\x7e\xfa\xe4\xb8\xaa\xce\x5e\x88\x21\x63\x5d\x35\xd0\xfe\x9a\x96\x27\xf2\x30\xcb\xc1\xcf\xeb\xbf\x6e\x03\xf9\xd6\xf9\x9a\xc3\x84\x2d\xab\x6e\xdd\xbb\x31\x44\xf4\x4d\x79\xf2\x03\x00\xbc\x27\x3a\xfc\xf3\x7a\x26\x9c\x72\x79\xb2\x58\xae\xf8\xdd\x79\xb9\xe0\x37\x1a\xd4\x17\x3c\xf5\xb7\x42\x29\x84\xf4\x23\x4f\xe5\xd8\x24\xaf\x19\xdf\x3a\x38\x9c\x97\xe9\xa1\x00\x21\x9c\xf3\x8d\xc3\x43\x94\x2f\x17\x15\x5a\x7e\xe0\xab\x55\x99\xf3\x7a\xc3\xa1\xde\xdf\xb8\xa1\x5d\x41\x56\x3b\x07\xc2\xc1\xdd\x6a\x0e\x34\xc0\x7e\x44\xa7\xc2\x81\x44\xd9\xad\x25\x14\x04\xb6\xc9\xf4\x2a\x40\xdc\xbd\x1b\x1f\x0d\xdc\x90\x25\x6a\x63\xab\xa6\xf8\xaf\x77\x09\xf9\xf8\x56\x70\x61\xfa\x46\x72\xe1\xed\xde\x8d\xc3\xc3\xff\x0f\xad\x97\xe7\xab\x8c\x3f\x65\x67\x67\xe5\xe2\xe4\xef\x2f\x9e\x1c\x89\xc2\x3b\x73\x38\x44\xfa\xf3\xfa\xe0\x94\x9d\xdd\xf8\x7f\x01\x00\x00\xff\xff\x78\x96\x95\xca\xe4\x2c\x06\x00")
func web3JsBytes() ([]byte, error) {
return bindataRead(
@@ -105,7 +114,7 @@ func web3Js() (*asset, error) {
}
info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
- a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe1, 0x52, 0x59, 0xdc, 0x62, 0x13, 0x62, 0xd3, 0x4c, 0x5, 0x90, 0x78, 0x59, 0x0, 0xbc, 0xf7, 0x73, 0xa4, 0x6d, 0x49, 0xdd, 0x77, 0x5a, 0x40, 0x5d, 0x5f, 0xe0, 0xc1, 0xb7, 0x0, 0xeb, 0x1d}}
+ a := &asset{bytes: bytes, info: info}
return a, nil
}
@@ -113,21 +122,15 @@ func web3Js() (*asset, error) {
// It returns an error if the asset could not be found or
// could not be loaded.
func Asset(name string) ([]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
- if f, ok := _bindata[canonicalName]; ok {
+ cannonicalName := strings.ReplaceAll(name, "\\", "/")
+ if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read Asset %s by error: %v", name, err)
}
return a.bytes, nil
}
- return nil, fmt.Errorf("Asset %s not found", name)
-}
-
-// AssetString returns the asset contents as a string (instead of a []byte).
-func AssetString(name string) (string, error) {
- data, err := Asset(name)
- return string(data), err
+ return nil, fmt.Errorf("not found Asset %s", name)
}
// MustAsset is like Asset but panics when Asset would return an error.
@@ -141,52 +144,19 @@ func MustAsset(name string) []byte {
return a
}
-// MustAssetString is like AssetString but panics when Asset would return an
-// error. It simplifies safe initialization of global variables.
-func MustAssetString(name string) string {
- return string(MustAsset(name))
-}
-
// AssetInfo loads and returns the asset info for the given name.
// It returns an error if the asset could not be found or
// could not be loaded.
func AssetInfo(name string) (os.FileInfo, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
- if f, ok := _bindata[canonicalName]; ok {
+ cannonicalName := strings.ReplaceAll(name, "\\", "/")
+ if f, ok := _bindata[cannonicalName]; ok {
a, err := f()
if err != nil {
- return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ return nil, fmt.Errorf("can't read AssetInfo %s by error: %v", name, err)
}
return a.info, nil
}
- return nil, fmt.Errorf("AssetInfo %s not found", name)
-}
-
-// AssetDigest returns the digest of the file with the given name. It returns an
-// error if the asset could not be found or the digest could not be loaded.
-func AssetDigest(name string) ([sha256.Size]byte, error) {
- canonicalName := strings.Replace(name, "\\", "/", -1)
- if f, ok := _bindata[canonicalName]; ok {
- a, err := f()
- if err != nil {
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
- }
- return a.digest, nil
- }
- return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
-}
-
-// Digests returns a map of all known files and their checksums.
-func Digests() (map[string][sha256.Size]byte, error) {
- mp := make(map[string][sha256.Size]byte, len(_bindata))
- for name := range _bindata {
- a, err := _bindata[name]()
- if err != nil {
- return nil, err
- }
- mp[name] = a.digest
- }
- return mp, nil
+ return nil, fmt.Errorf("not found AssetInfo %s", name)
}
// AssetNames returns the names of the assets.
@@ -204,9 +174,6 @@ var _bindata = map[string]func() (*asset, error){
"web3.js": web3Js,
}
-// AssetDebug is true if the assets were built with the debug flag enabled.
-const AssetDebug = false
-
// AssetDir returns the file names below a certain
// directory embedded in the file by go-bindata.
// For example if you run go-bindata on data/... and data contains the
@@ -218,15 +185,15 @@ const AssetDebug = false
// a.png
// b.png
//
-// then AssetDir("data") would return []string{"foo.txt", "img"},
-// AssetDir("data/img") would return []string{"a.png", "b.png"},
-// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
+// then AssetDir("data") would return []string{"foo.txt", "img"}
+// AssetDir("data/img") would return []string{"a.png", "b.png"}
+// AssetDir("foo.txt") and AssetDir("notexist") would return an error
// AssetDir("") will return []string{"data"}.
func AssetDir(name string) ([]string, error) {
node := _bintree
if len(name) != 0 {
- canonicalName := strings.Replace(name, "\\", "/", -1)
- pathList := strings.Split(canonicalName, "/")
+ cannonicalName := strings.ReplaceAll(name, "\\", "/")
+ pathList := strings.Split(cannonicalName, "/")
for _, p := range pathList {
node = node.Children[p]
if node == nil {
@@ -254,7 +221,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"web3.js": {web3Js, map[string]*bintree{}},
}}
-// RestoreAsset restores an asset under the given directory.
+// RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
@@ -268,14 +235,18 @@ func RestoreAsset(dir, name string) error {
if err != nil {
return err
}
- err = os.WriteFile(_filePath(dir, name), data, info.Mode())
+ err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
- return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+ err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+ if err != nil {
+ return err
+ }
+ return nil
}
-// RestoreAssets restores an asset under the given directory recursively.
+// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
@@ -293,6 +264,6 @@ func RestoreAssets(dir, name string) error {
}
func _filePath(dir, name string) string {
- canonicalName := strings.Replace(name, "\\", "/", -1)
- return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
+ cannonicalName := strings.ReplaceAll(name, "\\", "/")
+ return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}
diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js
index 5c7236ed14..83cbc36e78 100644
--- a/internal/jsre/deps/web3.js
+++ b/internal/jsre/deps/web3.js
@@ -3741,7 +3741,7 @@ var inputCallFormatter = function (options){
options.to = inputAddressFormatter(options.to);
}
- ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
+ ['maxFeePerGas', 'maxPriorityFeePerGas', 'gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
return options[key] !== undefined;
}).forEach(function(key){
options[key] = utils.fromDecimal(options[key]);
@@ -3766,7 +3766,7 @@ var inputTransactionFormatter = function (options){
options.to = inputAddressFormatter(options.to);
}
- ['gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
+ ['maxFeePerGas', 'maxPriorityFeePerGas', 'gasPrice', 'gas', 'value', 'nonce'].filter(function (key) {
return options[key] !== undefined;
}).forEach(function(key){
options[key] = utils.fromDecimal(options[key]);
@@ -3790,6 +3790,12 @@ var outputTransactionFormatter = function (tx){
tx.nonce = utils.toDecimal(tx.nonce);
tx.gas = utils.toDecimal(tx.gas);
tx.gasPrice = utils.toBigNumber(tx.gasPrice);
+ if(tx.maxFeePerGas !== undefined) {
+ tx.maxFeePerGas = utils.toBigNumber(tx.maxFeePerGas);
+ }
+ if(tx.maxPriorityFeePerGas !== undefined) {
+ tx.maxPriorityFeePerGas = utils.toBigNumber(tx.maxPriorityFeePerGas);
+ }
tx.value = utils.toBigNumber(tx.value);
return tx;
};
@@ -3808,7 +3814,9 @@ var outputTransactionReceiptFormatter = function (receipt){
receipt.transactionIndex = utils.toDecimal(receipt.transactionIndex);
receipt.cumulativeGasUsed = utils.toDecimal(receipt.cumulativeGasUsed);
receipt.gasUsed = utils.toDecimal(receipt.gasUsed);
-
+ if(receipt.effectiveGasPrice !== undefined) {
+ receipt.effectiveGasPrice = utils.toBigNumber(receipt.effectiveGasPrice);
+ }
if(utils.isArray(receipt.logs)) {
receipt.logs = receipt.logs.map(function(log){
return outputLogFormatter(log);
@@ -3828,6 +3836,9 @@ var outputTransactionReceiptFormatter = function (receipt){
var outputBlockFormatter = function(block) {
// transform to number
+ if (block.baseFeePerGas !== undefined) {
+ block.baseFeePerGas = utils.toBigNumber(block.baseFeePerGas);
+ }
block.gasLimit = utils.toDecimal(block.gasLimit);
block.gasUsed = utils.toDecimal(block.gasUsed);
block.size = utils.toDecimal(block.size);
diff --git a/internal/jsre/jsre.go b/internal/jsre/jsre.go
index 81c3e22177..14fc602aac 100644
--- a/internal/jsre/jsre.go
+++ b/internal/jsre/jsre.go
@@ -294,11 +294,11 @@ func (re *JSRE) loadScript(call Call) (goja.Value, error) {
file = common.AbsolutePath(re.assetPath, file)
source, err := os.ReadFile(file)
if err != nil {
- return nil, fmt.Errorf("Could not read file %s: %v", file, err)
+ return nil, fmt.Errorf("could not read file %s: %v", file, err)
}
value, err := compileAndRun(re.vm, file, string(source))
if err != nil {
- return nil, fmt.Errorf("Error while compiling or running script: %v", err)
+ return nil, fmt.Errorf("error while compiling or running script: %v", err)
}
return value, nil
}
diff --git a/internal/testlog/testlog.go b/internal/testlog/testlog.go
new file mode 100644
index 0000000000..855e8c9b7f
--- /dev/null
+++ b/internal/testlog/testlog.go
@@ -0,0 +1,207 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package testlog provides a log handler for unit tests.
+package testlog
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "log/slog"
+ "sync"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+)
+
+const (
+ termTimeFormat = "01-02|15:04:05.000"
+)
+
+// logger implements log.Logger such that all output goes to the unit test log via
+// t.Logf(). All methods in between logger.Trace, logger.Debug, etc. are marked as test
+// helpers, so the file and line number in unit test output correspond to the call site
+// which emitted the log message.
+type logger struct {
+ t *testing.T
+ l log.Logger
+ mu *sync.Mutex
+ h *bufHandler
+}
+
+type bufHandler struct {
+ buf []slog.Record
+ attrs []slog.Attr
+ level slog.Level
+}
+
+func (h *bufHandler) Handle(_ context.Context, r slog.Record) error {
+ h.buf = append(h.buf, r)
+ return nil
+}
+
+func (h *bufHandler) Enabled(_ context.Context, lvl slog.Level) bool {
+ return lvl <= h.level
+}
+
+func (h *bufHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ records := make([]slog.Record, len(h.buf))
+ copy(records[:], h.buf[:])
+ return &bufHandler{
+ records,
+ append(h.attrs, attrs...),
+ h.level,
+ }
+}
+
+func (h *bufHandler) WithGroup(_ string) slog.Handler {
+ panic("not implemented")
+}
+
+// Logger returns a logger which logs to the unit test log of t.
+func Logger(t *testing.T, level slog.Level) log.Logger {
+ handler := bufHandler{
+ []slog.Record{},
+ []slog.Attr{},
+ level,
+ }
+ return &logger{
+ t: t,
+ l: log.NewLogger(&handler),
+ mu: new(sync.Mutex),
+ h: &handler,
+ }
+}
+
+// LoggerWithHandler returns
+func LoggerWithHandler(t *testing.T, handler slog.Handler) log.Logger {
+ var bh bufHandler
+ return &logger{
+ t: t,
+ l: log.NewLogger(handler),
+ mu: new(sync.Mutex),
+ h: &bh,
+ }
+}
+
+func (l *logger) Handler() slog.Handler {
+ return l.l.Handler()
+}
+
+func (l *logger) Write(level slog.Level, msg string, ctx ...interface{}) {}
+
+func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
+ return l.l.Enabled(ctx, level)
+}
+
+func (l *logger) Trace(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Trace(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Log(level slog.Level, msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Log(level, msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Debug(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Debug(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Info(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Info(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Warn(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Warn(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Error(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Error(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) Crit(msg string, ctx ...interface{}) {
+ l.t.Helper()
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.l.Crit(msg, ctx...)
+ l.flush()
+}
+
+func (l *logger) With(ctx ...interface{}) log.Logger {
+ return &logger{l.t, l.l.With(ctx...), l.mu, l.h}
+}
+
+func (l *logger) New(ctx ...interface{}) log.Logger {
+ return l.With(ctx...)
+}
+
+// terminalFormat formats a message similarly to the NewTerminalHandler in the log package.
+// The difference is that terminalFormat does not escape messages/attributes and does not pad attributes.
+func (h *bufHandler) terminalFormat(r slog.Record) string {
+ buf := &bytes.Buffer{}
+ lvl := log.LevelAlignedString(r.Level)
+ attrs := []slog.Attr{}
+ r.Attrs(func(attr slog.Attr) bool {
+ attrs = append(attrs, attr)
+ return true
+ })
+
+ attrs = append(h.attrs, attrs...)
+
+ fmt.Fprintf(buf, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Message)
+ if length := len(r.Message); length < 40 {
+ buf.Write(bytes.Repeat([]byte{' '}, 40-length))
+ }
+
+ for _, attr := range attrs {
+ fmt.Fprintf(buf, " %s=%s", attr.Key, string(log.FormatSlogValue(attr.Value, nil)))
+ }
+ buf.WriteByte('\n')
+ return buf.String()
+}
+
+// flush writes all buffered messages and clears the buffer.
+func (l *logger) flush() {
+ l.t.Helper()
+ for _, r := range l.h.buf {
+ l.t.Logf("%s", l.h.terminalFormat(r))
+ }
+ l.h.buf = nil
+}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 1921ad6099..f726b5d620 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -197,6 +197,16 @@ web3._extend({
call: 'admin_removePeer',
params: 1
}),
+ new web3._extend.Method({
+ name: 'addTrustedPeer',
+ call: 'admin_addTrustedPeer',
+ params: 1
+ }),
+ new web3._extend.Method({
+ name: 'removeTrustedPeer',
+ call: 'admin_removeTrustedPeer',
+ params: 1
+ }),
new web3._extend.Method({
name: 'exportChain',
call: 'admin_exportChain',
@@ -290,11 +300,6 @@ web3._extend({
name: 'chaindbCompact',
call: 'debug_chaindbCompact',
}),
- new web3._extend.Method({
- name: 'metrics',
- call: 'debug_metrics',
- params: 1
- }),
new web3._extend.Method({
name: 'verbosity',
call: 'debug_verbosity',
@@ -538,6 +543,12 @@ web3._extend({
params: 2,
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter],
}),
+ new web3._extend.Method({
+ name: 'feeHistory',
+ call: 'eth_feeHistory',
+ params: 3,
+ inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, null]
+ }),
new web3._extend.Method({
name: 'getBlockReceipts',
call: 'eth_getBlockReceipts',
@@ -557,6 +568,11 @@ web3._extend({
return formatted;
}
}),
+ new web3._extend.Property({
+ name: 'maxPriorityFeePerGas',
+ getter: 'eth_maxPriorityFeePerGas',
+ outputFormatter: web3._extend.utils.toBigNumber
+ }),
]
});
`
@@ -1060,6 +1076,11 @@ web3._extend({
return status;
}
}),
+ new web3._extend.Method({
+ name: 'contentFrom',
+ call: 'txpool_contentFrom',
+ params: 1,
+ }),
]
});
`
diff --git a/les/api_backend.go b/les/api_backend.go
index 876a1dcbe9..fe8371c5a2 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -24,18 +24,17 @@ import (
"os"
"path/filepath"
+ "github.com/XinFinOrg/XDPoSChain/XDCx"
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
- "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
-
- "github.com/XinFinOrg/XDPoSChain/XDCx"
-
"github.com/XinFinOrg/XDPoSChain/accounts"
+ "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
@@ -66,12 +65,15 @@ func (b *LesApiBackend) SetHead(number uint64) {
b.eth.blockchain.SetHead(number)
}
-func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
- if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber {
+func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
+ if number == rpc.PendingBlockNumber {
+ return nil, nil
+ }
+ if number == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentHeader(), nil
}
- return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
+ return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number))
}
func (b *LesApiBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
@@ -168,7 +170,10 @@ func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t
}
func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
- return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash))
+ if number := rawdb.ReadHeaderNumber(b.eth.chainDb, blockHash); number != nil {
+ return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, *number)
+ }
+ return nil, nil
}
func (b *LesApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
@@ -184,8 +189,9 @@ func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
vmConfig = new(vm.Config)
}
state.SetBalance(msg.From(), math.MaxBig256)
- context := core.NewEVMContext(msg, header, b.eth.blockchain, nil)
- return vm.NewEVM(context, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil
+ txContext := core.NewEVMTxContext(msg)
+ context := core.NewEVMBlockContext(header, b.eth.blockchain, nil)
+ return vm.NewEVM(context, txContext, state, XDCxState, b.eth.chainConfig, *vmConfig), state.Error, nil
}
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
@@ -222,6 +228,10 @@ func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.txPool.Content()
}
+func (b *LesApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ return b.eth.txPool.ContentFrom(addr)
+}
+
func (b *LesApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) {
return make(map[common.Address]types.OrderTransactions), make(map[common.Address]types.OrderTransactions)
}
@@ -268,8 +278,12 @@ func (b *LesApiBackend) ProtocolVersion() int {
return b.eth.LesVersion() + 10000
}
-func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
- return b.gpo.SuggestPrice(ctx)
+func (b *LesApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
+ return b.gpo.SuggestTipCap(ctx)
+}
+
+func (b *LesApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
+ return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
}
func (b *LesApiBackend) ChainDb() ethdb.Database {
@@ -302,6 +316,10 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
}
}
+func (b *LesApiBackend) CurrentHeader() *types.Header {
+ return b.eth.blockchain.CurrentHeader()
+}
+
// func (b *LesApiBackend) GetIPCClient() (*ethclient.Client, error) {
func (b *LesApiBackend) GetIPCClient() (bind.ContractBackend, error) {
// func (b *LesApiBackend) GetIPCClient() (bind.ContractBackend, error) {
diff --git a/les/backend.go b/les/backend.go
index cc6fdf962d..08933f93a0 100644
--- a/les/backend.go
+++ b/les/backend.go
@@ -131,11 +131,7 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config) (*LightEthereum, er
return nil, err
}
leth.ApiBackend = &LesApiBackend{leth, nil}
- gpoParams := config.GPO
- if gpoParams.Default == nil {
- gpoParams.Default = config.GasPrice
- }
- leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
+ leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, config.GPO, config.GasPrice)
return leth, nil
}
@@ -147,7 +143,7 @@ func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic {
case lpv2:
name = "LES2"
default:
- panic(nil)
+ panic("lesTopic")
}
return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8]))
}
diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go
index 571b87bdb9..e22cd5ae92 100644
--- a/les/flowcontrol/manager.go
+++ b/les/flowcontrol/manager.go
@@ -35,35 +35,35 @@ type cmNode struct {
finishRecharge mclock.AbsTime
}
-func (node *cmNode) update(time mclock.AbsTime) {
- dt := int64(time - node.lastUpdate)
- node.rcValue += node.rcDelta * dt / rcConst
- node.lastUpdate = time
- if node.recharging && time >= node.finishRecharge {
- node.recharging = false
- node.rcDelta = 0
- node.rcValue = 0
+func (n *cmNode) update(time mclock.AbsTime) {
+ dt := int64(time - n.lastUpdate)
+ n.rcValue += n.rcDelta * dt / rcConst
+ n.lastUpdate = time
+ if n.recharging && time >= n.finishRecharge {
+ n.recharging = false
+ n.rcDelta = 0
+ n.rcValue = 0
}
}
-func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) {
- if node.serving && !serving {
- node.recharging = true
- sumWeight += node.rcWeight
+func (n *cmNode) set(serving bool, simReqCnt, sumWeight uint64) {
+ if n.serving && !serving {
+ n.recharging = true
+ sumWeight += n.rcWeight
}
- node.serving = serving
- if node.recharging && serving {
- node.recharging = false
- sumWeight -= node.rcWeight
+ n.serving = serving
+ if n.recharging && serving {
+ n.recharging = false
+ sumWeight -= n.rcWeight
}
- node.rcDelta = 0
+ n.rcDelta = 0
if serving {
- node.rcDelta = int64(rcConst / simReqCnt)
+ n.rcDelta = int64(rcConst / simReqCnt)
}
- if node.recharging {
- node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight)
- node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta))
+ if n.recharging {
+ n.rcDelta = -int64(n.node.cm.rcRecharge * n.rcWeight / sumWeight)
+ n.finishRecharge = n.lastUpdate + mclock.AbsTime(n.rcValue*rcConst/(-n.rcDelta))
}
}
@@ -89,16 +89,16 @@ func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager {
return cm
}
-func (self *ClientManager) Stop() {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (cm *ClientManager) Stop() {
+ cm.lock.Lock()
+ defer cm.lock.Unlock()
// signal any waiting accept routines to return false
- self.nodes = make(map[*cmNode]struct{})
- close(self.resumeQueue)
+ cm.nodes = make(map[*cmNode]struct{})
+ close(cm.resumeQueue)
}
-func (self *ClientManager) addNode(cnode *ClientNode) *cmNode {
+func (cm *ClientManager) addNode(cnode *ClientNode) *cmNode {
time := mclock.Now()
node := &cmNode{
node: cnode,
@@ -106,28 +106,28 @@ func (self *ClientManager) addNode(cnode *ClientNode) *cmNode {
finishRecharge: time,
rcWeight: 1,
}
- self.lock.Lock()
- defer self.lock.Unlock()
+ cm.lock.Lock()
+ defer cm.lock.Unlock()
- self.nodes[node] = struct{}{}
- self.update(mclock.Now())
+ cm.nodes[node] = struct{}{}
+ cm.update(mclock.Now())
return node
}
-func (self *ClientManager) removeNode(node *cmNode) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (cm *ClientManager) removeNode(node *cmNode) {
+ cm.lock.Lock()
+ defer cm.lock.Unlock()
time := mclock.Now()
- self.stop(node, time)
- delete(self.nodes, node)
- self.update(time)
+ cm.stop(node, time)
+ delete(cm.nodes, node)
+ cm.update(time)
}
// recalc sumWeight
-func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) {
+func (cm *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) {
var sumWeight, rcSum uint64
- for node := range self.nodes {
+ for node := range cm.nodes {
rc := node.recharging
node.update(time)
if rc && !node.recharging {
@@ -138,44 +138,44 @@ func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) {
}
rcSum += uint64(node.rcValue)
}
- self.sumWeight = sumWeight
- self.rcSumValue = rcSum
+ cm.sumWeight = sumWeight
+ cm.rcSumValue = rcSum
return
}
-func (self *ClientManager) update(time mclock.AbsTime) {
+func (cm *ClientManager) update(time mclock.AbsTime) {
for {
firstTime := time
- for node := range self.nodes {
+ for node := range cm.nodes {
if node.recharging && node.finishRecharge < firstTime {
firstTime = node.finishRecharge
}
}
- if self.updateNodes(firstTime) {
- for node := range self.nodes {
+ if cm.updateNodes(firstTime) {
+ for node := range cm.nodes {
if node.recharging {
- node.set(node.serving, self.simReqCnt, self.sumWeight)
+ node.set(node.serving, cm.simReqCnt, cm.sumWeight)
}
}
} else {
- self.time = time
+ cm.time = time
return
}
}
}
-func (self *ClientManager) canStartReq() bool {
- return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum
+func (cm *ClientManager) canStartReq() bool {
+ return cm.simReqCnt < cm.maxSimReq && cm.rcSumValue < cm.maxRcSum
}
-func (self *ClientManager) queueProc() {
- for rc := range self.resumeQueue {
+func (cm *ClientManager) queueProc() {
+ for rc := range cm.resumeQueue {
for {
time.Sleep(time.Millisecond * 10)
- self.lock.Lock()
- self.update(mclock.Now())
- cs := self.canStartReq()
- self.lock.Unlock()
+ cm.lock.Lock()
+ cm.update(mclock.Now())
+ cs := cm.canStartReq()
+ cm.lock.Unlock()
if cs {
break
}
@@ -184,41 +184,41 @@ func (self *ClientManager) queueProc() {
}
}
-func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (cm *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool {
+ cm.lock.Lock()
+ defer cm.lock.Unlock()
- self.update(time)
- if !self.canStartReq() {
+ cm.update(time)
+ if !cm.canStartReq() {
resume := make(chan bool)
- self.lock.Unlock()
- self.resumeQueue <- resume
+ cm.lock.Unlock()
+ cm.resumeQueue <- resume
<-resume
- self.lock.Lock()
- if _, ok := self.nodes[node]; !ok {
+ cm.lock.Lock()
+ if _, ok := cm.nodes[node]; !ok {
return false // reject if node has been removed or manager has been stopped
}
}
- self.simReqCnt++
- node.set(true, self.simReqCnt, self.sumWeight)
+ cm.simReqCnt++
+ node.set(true, cm.simReqCnt, cm.sumWeight)
node.startValue = node.rcValue
- self.update(self.time)
+ cm.update(cm.time)
return true
}
-func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) {
+func (cm *ClientManager) stop(node *cmNode, time mclock.AbsTime) {
if node.serving {
- self.update(time)
- self.simReqCnt--
- node.set(false, self.simReqCnt, self.sumWeight)
- self.update(time)
+ cm.update(time)
+ cm.simReqCnt--
+ node.set(false, cm.simReqCnt, cm.sumWeight)
+ cm.update(time)
}
}
-func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (cm *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) {
+ cm.lock.Lock()
+ defer cm.lock.Unlock()
- self.stop(node, time)
+ cm.stop(node, time)
return uint64(node.rcValue), uint64(node.rcValue - node.startValue)
}
diff --git a/les/handler.go b/les/handler.go
index 6a4ba688ea..7e7b99d419 100644
--- a/les/handler.go
+++ b/les/handler.go
@@ -27,12 +27,12 @@ import (
"sync"
"time"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/ethdb"
@@ -92,7 +92,7 @@ type BlockChain interface {
type txPool interface {
AddRemotes(txs []*types.Transaction) []error
AddRemotesSync(txs []*types.Transaction) []error
- Status(hashes []common.Hash) []core.TxStatus
+ Status(hashes []common.Hash) []txpool.TxStatus
}
type ProtocolManager struct {
@@ -1044,7 +1044,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
stats := pm.txStatus(hashes)
for i, stat := range stats {
- if stat.Status == core.TxStatusUnknown {
+ if stat.Status == txpool.TxStatusUnknown {
if errs := pm.txpool.AddRemotes([]*types.Transaction{req.Txs[i]}); errs[0] != nil {
stats[i].Error = errs[0].Error()
continue
@@ -1160,9 +1160,9 @@ func (pm *ProtocolManager) txStatus(hashes []common.Hash) []txStatus {
stats[i].Status = stat
// If the transaction is unknown to the pool, try looking it up locally
- if stat == core.TxStatusUnknown {
+ if stat == txpool.TxStatusUnknown {
if block, number, index := core.GetTxLookupEntry(pm.chainDb, hashes[i]); block != (common.Hash{}) {
- stats[i].Status = core.TxStatusIncluded
+ stats[i].Status = txpool.TxStatusIncluded
stats[i].Lookup = &core.TxLookupEntry{BlockHash: block, BlockIndex: number, Index: index}
}
}
@@ -1181,15 +1181,15 @@ type NodeInfo struct {
}
// NodeInfo retrieves some protocol metadata about the running host node.
-func (self *ProtocolManager) NodeInfo() *NodeInfo {
- head := self.blockchain.CurrentHeader()
+func (pm *ProtocolManager) NodeInfo() *NodeInfo {
+ head := pm.blockchain.CurrentHeader()
hash := head.Hash()
return &NodeInfo{
- Network: self.networkId,
- Difficulty: self.blockchain.GetTd(hash, head.Number.Uint64()),
- Genesis: self.blockchain.Genesis().Hash(),
- Config: self.blockchain.Config(),
+ Network: pm.networkId,
+ Difficulty: pm.blockchain.GetTd(hash, head.Number.Uint64()),
+ Genesis: pm.blockchain.Genesis().Hash(),
+ Config: pm.blockchain.Config(),
Head: hash,
}
}
diff --git a/les/handler_test.go b/les/handler_test.go
index bac89b5d33..85d9581b3d 100644
--- a/les/handler_test.go
+++ b/les/handler_test.go
@@ -18,7 +18,6 @@ package les
import (
"encoding/binary"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"math/rand"
"testing"
@@ -27,6 +26,8 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
@@ -493,10 +494,10 @@ func TestTransactionStatusLes2(t *testing.T) {
db := rawdb.NewMemoryDatabase()
pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db)
chain := pm.blockchain.(*core.BlockChain)
- config := core.DefaultTxPoolConfig
+ config := txpool.DefaultConfig
config.Journal = ""
- txpool := core.NewTxPool(config, params.TestChainConfig, chain)
- pm.txpool = txpool
+ txPool := txpool.NewTxPool(config, params.TestChainConfig, chain)
+ pm.txpool = txPool
peer, _ := newTestPeer(t, "peer", 2, pm, true)
defer peer.close()
@@ -520,20 +521,20 @@ func TestTransactionStatusLes2(t *testing.T) {
// test error status by sending an underpriced transaction
tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
- test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
+ test(tx0, true, txStatus{Status: txpool.TxStatusUnknown, Error: txpool.ErrUnderpriced.Error()})
tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
- test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
- test(tx1, true, txStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending
- test(tx1, true, txStatus{Status: core.TxStatusPending}) // adding it again should not return an error
+ test(tx1, false, txStatus{Status: txpool.TxStatusUnknown}) // query before sending, should be unknown
+ test(tx1, true, txStatus{Status: txpool.TxStatusPending}) // send valid processable tx, should return pending
+ test(tx1, true, txStatus{Status: txpool.TxStatusPending}) // adding it again should not return an error
tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
// send transactions in the wrong order, tx3 should be queued
- test(tx3, true, txStatus{Status: core.TxStatusQueued})
- test(tx2, true, txStatus{Status: core.TxStatusPending})
+ test(tx3, true, txStatus{Status: txpool.TxStatusQueued})
+ test(tx2, true, txStatus{Status: txpool.TxStatusPending})
// query again, now tx3 should be pending too
- test(tx3, false, txStatus{Status: core.TxStatusPending})
+ test(tx3, false, txStatus{Status: txpool.TxStatusPending})
// generate and add a block with tx1 and tx2 included
gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) {
@@ -545,19 +546,19 @@ func TestTransactionStatusLes2(t *testing.T) {
}
// wait until TxPool processes the inserted block
for i := 0; i < 10; i++ {
- if pending, _ := txpool.Stats(); pending == 1 {
+ if pending, _ := txPool.Stats(); pending == 1 {
break
}
time.Sleep(100 * time.Millisecond)
}
- if pending, _ := txpool.Stats(); pending != 1 {
+ if pending, _ := txPool.Stats(); pending != 1 {
t.Fatalf("pending count mismatch: have %d, want 1", pending)
}
// check if their status is included now
block1hash := core.GetCanonicalHash(db, 1)
- test(tx1, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
- test(tx2, false, txStatus{Status: core.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
+ test(tx1, false, txStatus{Status: txpool.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
+ test(tx2, false, txStatus{Status: txpool.TxStatusIncluded, Lookup: &core.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
// create a reorg that rolls them back
gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {})
@@ -566,15 +567,15 @@ func TestTransactionStatusLes2(t *testing.T) {
}
// wait until TxPool processes the reorg
for i := 0; i < 10; i++ {
- if pending, _ := txpool.Stats(); pending == 3 {
+ if pending, _ := txPool.Stats(); pending == 3 {
break
}
time.Sleep(100 * time.Millisecond)
}
- if pending, _ := txpool.Stats(); pending != 3 {
+ if pending, _ := txPool.Stats(); pending != 3 {
t.Fatalf("pending count mismatch: have %d, want 3", pending)
}
// check if their status is pending again
- test(tx1, false, txStatus{Status: core.TxStatusPending})
- test(tx2, false, txStatus{Status: core.TxStatusPending})
+ test(tx1, false, txStatus{Status: txpool.TxStatusPending})
+ test(tx2, false, txStatus{Status: txpool.TxStatusPending})
}
diff --git a/les/metrics.go b/les/metrics.go
index 4ba1af2475..9215c712eb 100644
--- a/les/metrics.go
+++ b/les/metrics.go
@@ -74,7 +74,7 @@ type meteredMsgReadWriter struct {
// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the
// metrics system is disabled, this function returns the original object.
func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter {
- if !metrics.Enabled {
+ if !metrics.Enabled() {
return rw
}
return &meteredMsgReadWriter{MsgReadWriter: rw}
diff --git a/les/odr_requests.go b/les/odr_requests.go
index 1ad2d106ba..38914156c2 100644
--- a/les/odr_requests.go
+++ b/les/odr_requests.go
@@ -196,7 +196,7 @@ func (r *TrieRequest) GetCost(peer *peer) uint64 {
case lpv2:
return peer.GetRequestCost(GetProofsV2Msg, 1)
default:
- panic(nil)
+ panic("TrieRequest GetCost")
}
}
@@ -356,7 +356,7 @@ func (r *ChtRequest) GetCost(peer *peer) uint64 {
case lpv2:
return peer.GetRequestCost(GetHelperTrieProofsMsg, 1)
default:
- panic(nil)
+ panic("ChtRequest GetCost")
}
}
diff --git a/les/odr_test.go b/les/odr_test.go
index 1234bd2853..031a1df5dc 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -133,16 +133,17 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
+ msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)}
- context := core.NewEVMContext(msg, header, bc, nil)
- vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{})
+ context := core.NewEVMBlockContext(header, bc, nil)
+ txContext := core.NewEVMTxContext(msg)
+ vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true})
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
- ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
- res = append(res, ret...)
+ result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
+ res = append(res, result.Return()...)
}
} else {
header := lc.GetHeaderByHash(bhash)
@@ -153,14 +154,15 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
- context := core.NewEVMContext(msg, header, lc, nil)
- vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{})
+ msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)}
+ context := core.NewEVMBlockContext(header, lc, nil)
+ txContext := core.NewEVMTxContext(msg)
+ vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
- ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
+ result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
if statedb.Error() == nil {
- res = append(res, ret...)
+ res = append(res, result.Return()...)
}
}
}
diff --git a/les/peer.go b/les/peer.go
index cbc7b99570..277750c365 100644
--- a/les/peer.go
+++ b/les/peer.go
@@ -279,7 +279,7 @@ func (p *peer) RequestProofs(reqID, cost uint64, reqs []ProofReq) error {
case lpv2:
return sendRequest(p.rw, GetProofsV2Msg, reqID, cost, reqs)
default:
- panic(nil)
+ panic("peer RequestProofs")
}
}
@@ -301,7 +301,7 @@ func (p *peer) RequestHelperTrieProofs(reqID, cost uint64, reqs []HelperTrieReq)
case lpv2:
return sendRequest(p.rw, GetHelperTrieProofsMsg, reqID, cost, reqs)
default:
- panic(nil)
+ panic("peer RequestHelperTrieProofs")
}
}
@@ -320,7 +320,7 @@ func (p *peer) SendTxs(reqID, cost uint64, txs types.Transactions) error {
case lpv2:
return sendRequest(p.rw, SendTxV2Msg, reqID, cost, txs)
default:
- panic(nil)
+ panic("peer SendTxs")
}
}
diff --git a/les/protocol.go b/les/protocol.go
index 273ccfcce9..888b0ac179 100644
--- a/les/protocol.go
+++ b/les/protocol.go
@@ -28,6 +28,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/secp256k1"
"github.com/XinFinOrg/XDPoSChain/rlp"
@@ -161,7 +162,7 @@ func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error {
if bytes.Equal(pbytes, recPubkey) {
return nil
} else {
- return errors.New("Wrong signature")
+ return errors.New("wrong signature")
}
}
@@ -223,7 +224,7 @@ type CodeData []struct {
type proofsData [][]rlp.RawValue
type txStatus struct {
- Status core.TxStatus
+ Status txpool.TxStatus
Lookup *core.TxLookupEntry `rlp:"nil"`
Error string
}
diff --git a/les/randselect.go b/les/randselect.go
index 1a9d0695bd..6150c3a263 100644
--- a/les/randselect.go
+++ b/les/randselect.go
@@ -109,7 +109,7 @@ func (n *wrsNode) insert(item wrsItem, weight int64) int {
for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) {
branch++
if branch == wrsBranches {
- panic(nil)
+ panic("wrsNode insert: branch == wrsBranches")
}
}
n.itemCnt++
@@ -169,5 +169,5 @@ func (n *wrsNode) choose(val int64) (wrsItem, int64) {
val -= w
}
}
- panic(nil)
+ panic("wrsNode choose")
}
diff --git a/les/retrieve.go b/les/retrieve.go
index 509b8a3e35..f73f4fe6c9 100644
--- a/les/retrieve.go
+++ b/les/retrieve.go
@@ -119,7 +119,7 @@ func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *dist
case <-ctx.Done():
sentReq.stop(ctx.Err())
case <-shutdown:
- sentReq.stop(errors.New("Client is shutting down"))
+ sentReq.stop(errors.New("client is shutting down"))
}
return sentReq.getError()
}
@@ -306,7 +306,7 @@ func (r *sentReq) tryRequest() {
s, ok := r.sentTo[p]
r.lock.RUnlock()
if !ok {
- panic(nil)
+ panic("sentReq tryRequest: !ok")
}
defer func() {
diff --git a/les/txrelay.go b/les/txrelay.go
index a78d9f4974..b5488a8ad5 100644
--- a/les/txrelay.go
+++ b/les/txrelay.go
@@ -50,47 +50,47 @@ func NewLesTxRelay(ps *peerSet, reqDist *requestDistributor) *LesTxRelay {
return r
}
-func (self *LesTxRelay) registerPeer(p *peer) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (l *LesTxRelay) registerPeer(p *peer) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
- self.peerList = self.ps.AllPeers()
+ l.peerList = l.ps.AllPeers()
}
-func (self *LesTxRelay) unregisterPeer(p *peer) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (l *LesTxRelay) unregisterPeer(p *peer) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
- self.peerList = self.ps.AllPeers()
+ l.peerList = l.ps.AllPeers()
}
// send sends a list of transactions to at most a given number of peers at
// once, never resending any particular transaction to the same peer twice
-func (self *LesTxRelay) send(txs types.Transactions, count int) {
+func (l *LesTxRelay) send(txs types.Transactions, count int) {
sendTo := make(map[*peer]types.Transactions)
- self.peerStartPos++ // rotate the starting position of the peer list
- if self.peerStartPos >= len(self.peerList) {
- self.peerStartPos = 0
+ l.peerStartPos++ // rotate the starting position of the peer list
+ if l.peerStartPos >= len(l.peerList) {
+ l.peerStartPos = 0
}
for _, tx := range txs {
hash := tx.Hash()
- ltr, ok := self.txSent[hash]
+ ltr, ok := l.txSent[hash]
if !ok {
ltr = <rInfo{
tx: tx,
sentTo: make(map[*peer]struct{}),
}
- self.txSent[hash] = ltr
- self.txPending[hash] = struct{}{}
+ l.txSent[hash] = ltr
+ l.txPending[hash] = struct{}{}
}
- if len(self.peerList) > 0 {
+ if len(l.peerList) > 0 {
cnt := count
- pos := self.peerStartPos
+ pos := l.peerStartPos
for {
- peer := self.peerList[pos]
+ peer := l.peerList[pos]
if _, ok := ltr.sentTo[peer]; !ok {
sendTo[peer] = append(sendTo[peer], tx)
ltr.sentTo[peer] = struct{}{}
@@ -100,10 +100,10 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) {
break // sent it to the desired number of peers
}
pos++
- if pos == len(self.peerList) {
+ if pos == len(l.peerList) {
pos = 0
}
- if pos == self.peerStartPos {
+ if pos == l.peerStartPos {
break // tried all available peers
}
}
@@ -130,46 +130,46 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) {
return func() { peer.SendTxs(reqID, cost, ll) }
},
}
- self.reqDist.queue(rq)
+ l.reqDist.queue(rq)
}
}
-func (self *LesTxRelay) Send(txs types.Transactions) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (l *LesTxRelay) Send(txs types.Transactions) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
- self.send(txs, 3)
+ l.send(txs, 3)
}
-func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (l *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
for _, hash := range mined {
- delete(self.txPending, hash)
+ delete(l.txPending, hash)
}
for _, hash := range rollback {
- self.txPending[hash] = struct{}{}
+ l.txPending[hash] = struct{}{}
}
- if len(self.txPending) > 0 {
- txs := make(types.Transactions, len(self.txPending))
+ if len(l.txPending) > 0 {
+ txs := make(types.Transactions, len(l.txPending))
i := 0
- for hash := range self.txPending {
- txs[i] = self.txSent[hash].tx
+ for hash := range l.txPending {
+ txs[i] = l.txSent[hash].tx
i++
}
- self.send(txs, 1)
+ l.send(txs, 1)
}
}
-func (self *LesTxRelay) Discard(hashes []common.Hash) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (l *LesTxRelay) Discard(hashes []common.Hash) {
+ l.lock.Lock()
+ defer l.lock.Unlock()
for _, hash := range hashes {
- delete(self.txSent, hash)
- delete(self.txPending, hash)
+ delete(l.txSent, hash)
+ delete(l.txPending, hash)
}
}
diff --git a/light/lightchain.go b/light/lightchain.go
index dd3e57f624..26b5df0917 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -25,6 +25,7 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/common"
+ "github.com/XinFinOrg/XDPoSChain/common/lru"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
@@ -35,7 +36,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
- lru "github.com/hashicorp/golang-lru"
)
var (
@@ -59,9 +59,9 @@ type LightChain struct {
mu sync.RWMutex
chainmu sync.RWMutex
- bodyCache *lru.Cache // Cache for the most recent block bodies
- bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
- blockCache *lru.Cache // Cache for the most recent entire blocks
+ bodyCache *lru.Cache[common.Hash, *types.Body]
+ bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
+ blockCache *lru.Cache[common.Hash, *types.Block]
quit chan struct{}
running int32 // running must be called automically
@@ -76,17 +76,13 @@ type LightChain struct {
// available in the database. It initialises the default Ethereum header
// validator.
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) {
- bodyCache, _ := lru.New(bodyCacheLimit)
- bodyRLPCache, _ := lru.New(bodyCacheLimit)
- blockCache, _ := lru.New(blockCacheLimit)
-
bc := &LightChain{
chainDb: odr.Database(),
odr: odr,
quit: make(chan struct{}),
- bodyCache: bodyCache,
- bodyRLPCache: bodyRLPCache,
- blockCache: blockCache,
+ bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
+ bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
+ blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
engine: engine,
}
var err error
@@ -116,45 +112,45 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
}
// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
-func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
- if self.odr.ChtIndexer() != nil {
- StoreChtRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot)
- self.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
+func (lc *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
+ if lc.odr.ChtIndexer() != nil {
+ StoreChtRoot(lc.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot)
+ lc.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
}
- if self.odr.BloomTrieIndexer() != nil {
- StoreBloomTrieRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot)
- self.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
+ if lc.odr.BloomTrieIndexer() != nil {
+ StoreBloomTrieRoot(lc.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot)
+ lc.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
}
- if self.odr.BloomIndexer() != nil {
- self.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
+ if lc.odr.BloomIndexer() != nil {
+ lc.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
}
log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.sectionIdx+1)*CHTFrequencyClient-1, "hash", cp.sectionHead)
}
-func (self *LightChain) getProcInterrupt() bool {
- return atomic.LoadInt32(&self.procInterrupt) == 1
+func (lc *LightChain) getProcInterrupt() bool {
+ return atomic.LoadInt32(&lc.procInterrupt) == 1
}
// Odr returns the ODR backend of the chain
-func (self *LightChain) Odr() OdrBackend {
- return self.odr
+func (lc *LightChain) Odr() OdrBackend {
+ return lc.odr
}
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
-func (self *LightChain) loadLastState() error {
- if head := core.GetHeadHeaderHash(self.chainDb); head == (common.Hash{}) {
+func (lc *LightChain) loadLastState() error {
+ if head := core.GetHeadHeaderHash(lc.chainDb); head == (common.Hash{}) {
// Corrupt or empty database, init from scratch
- self.Reset()
+ lc.Reset()
} else {
- if header := self.GetHeaderByHash(head); header != nil {
- self.hc.SetCurrentHeader(header)
+ if header := lc.GetHeaderByHash(head); header != nil {
+ lc.hc.SetCurrentHeader(header)
}
}
// Issue a status log and return
- header := self.hc.CurrentHeader()
- headerTd := self.GetTd(header.Hash(), header.Number.Uint64())
+ header := lc.hc.CurrentHeader()
+ headerTd := lc.GetTd(header.Hash(), header.Number.Uint64())
log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd)
return nil
@@ -162,128 +158,127 @@ func (self *LightChain) loadLastState() error {
// SetHead rewinds the local chain to a new head. Everything above the new
// head will be deleted and the new one set.
-func (bc *LightChain) SetHead(head uint64) {
- bc.mu.Lock()
- defer bc.mu.Unlock()
+func (lc *LightChain) SetHead(head uint64) {
+ lc.mu.Lock()
+ defer lc.mu.Unlock()
- bc.hc.SetHead(head, nil)
- bc.loadLastState()
+ lc.hc.SetHead(head, nil)
+ lc.loadLastState()
}
// GasLimit returns the gas limit of the current HEAD block.
-func (self *LightChain) GasLimit() uint64 {
- return self.hc.CurrentHeader().GasLimit
+func (lc *LightChain) GasLimit() uint64 {
+ return lc.hc.CurrentHeader().GasLimit
}
// Reset purges the entire blockchain, restoring it to its genesis state.
-func (bc *LightChain) Reset() {
- bc.ResetWithGenesisBlock(bc.genesisBlock)
+func (lc *LightChain) Reset() {
+ lc.ResetWithGenesisBlock(lc.genesisBlock)
}
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
// specified genesis state.
-func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
+func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
// Dump the entire block chain and purge the caches
- bc.SetHead(0)
+ lc.SetHead(0)
- bc.mu.Lock()
- defer bc.mu.Unlock()
+ lc.mu.Lock()
+ defer lc.mu.Unlock()
// Prepare the genesis block and reinitialise the chain
- if err := core.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
+ if err := core.WriteTd(lc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err)
}
- rawdb.WriteBlock(bc.chainDb, genesis)
- bc.genesisBlock = genesis
- bc.hc.SetGenesis(bc.genesisBlock.Header())
- bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
+ rawdb.WriteBlock(lc.chainDb, genesis)
+ lc.genesisBlock = genesis
+ lc.hc.SetGenesis(lc.genesisBlock.Header())
+ lc.hc.SetCurrentHeader(lc.genesisBlock.Header())
}
// Accessors
// Engine retrieves the light chain's consensus engine.
-func (bc *LightChain) Engine() consensus.Engine { return bc.engine }
+func (lc *LightChain) Engine() consensus.Engine { return lc.engine }
// Genesis returns the genesis block
-func (bc *LightChain) Genesis() *types.Block {
- return bc.genesisBlock
+func (lc *LightChain) Genesis() *types.Block {
+ return lc.genesisBlock
}
// State returns a new mutable state based on the current HEAD block.
-func (bc *LightChain) State() (*state.StateDB, error) {
+func (lc *LightChain) State() (*state.StateDB, error) {
return nil, errors.New("not implemented, needs client/server interface split")
}
// GetBody retrieves a block body (transactions and uncles) from the database
// or ODR service by hash, caching it if found.
-func (self *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) {
+func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) {
// Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := self.bodyCache.Get(hash); ok {
- body := cached.(*types.Body)
- return body, nil
+ if cached, ok := lc.bodyCache.Get(hash); ok && cached != nil {
+ return cached, nil
}
- body, err := GetBody(ctx, self.odr, hash, self.hc.GetBlockNumber(hash))
+ body, err := GetBody(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash))
if err != nil {
return nil, err
}
// Cache the found body for next time and return
- self.bodyCache.Add(hash, body)
+ lc.bodyCache.Add(hash, body)
return body, nil
}
// GetBodyRLP retrieves a block body in RLP encoding from the database or
// ODR service by hash, caching it if found.
-func (self *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) {
+func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) {
// Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := self.bodyRLPCache.Get(hash); ok {
- return cached.(rlp.RawValue), nil
+ if cached, ok := lc.bodyRLPCache.Get(hash); ok {
+ return cached, nil
}
- body, err := GetBodyRLP(ctx, self.odr, hash, self.hc.GetBlockNumber(hash))
+ body, err := GetBodyRLP(ctx, lc.odr, hash, lc.hc.GetBlockNumber(hash))
if err != nil {
return nil, err
}
// Cache the found body for next time and return
- self.bodyRLPCache.Add(hash, body)
+ lc.bodyRLPCache.Add(hash, body)
return body, nil
}
// HasBlock checks if a block is fully present in the database or not, caching
// it if present.
-func (bc *LightChain) HasBlock(hash common.Hash, number uint64) bool {
- blk, _ := bc.GetBlock(NoOdr, hash, number)
+func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool {
+ blk, _ := lc.GetBlock(NoOdr, hash, number)
return blk != nil
}
// GetBlock retrieves a block from the database or ODR service by hash and number,
// caching it if found.
-func (self *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) {
+func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) {
// Short circuit if the block's already in the cache, retrieve otherwise
- if block, ok := self.blockCache.Get(hash); ok {
- return block.(*types.Block), nil
+ if block, ok := lc.blockCache.Get(hash); ok && block != nil {
+ return block, nil
}
- block, err := GetBlock(ctx, self.odr, hash, number)
+ block, err := GetBlock(ctx, lc.odr, hash, number)
if err != nil {
return nil, err
}
// Cache the found block for next time and return
- self.blockCache.Add(block.Hash(), block)
+ lc.blockCache.Add(block.Hash(), block)
return block, nil
}
// GetBlockByHash retrieves a block from the database or ODR service by hash,
// caching it if found.
-func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- return self.GetBlock(ctx, hash, self.hc.GetBlockNumber(hash))
+func (lc *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
+ return lc.GetBlock(ctx, hash, lc.hc.GetBlockNumber(hash))
}
// GetBlockByNumber retrieves a block from the database or ODR service by
// number, caching it (associated with its hash) if found.
-func (self *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
- hash, err := GetCanonicalHash(ctx, self.odr, number)
+func (lc *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
+ hash, err := GetCanonicalHash(ctx, lc.odr, number)
if hash == (common.Hash{}) || err != nil {
return nil, err
}
- return self.GetBlock(ctx, hash, number)
+ return lc.GetBlock(ctx, hash, number)
}
func (bc *LightChain) SaveData() {
@@ -291,44 +286,44 @@ func (bc *LightChain) SaveData() {
// Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt.
-func (bc *LightChain) Stop() {
- if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
+func (lc *LightChain) Stop() {
+ if !atomic.CompareAndSwapInt32(&lc.running, 0, 1) {
return
}
- close(bc.quit)
- atomic.StoreInt32(&bc.procInterrupt, 1)
+ close(lc.quit)
+ atomic.StoreInt32(&lc.procInterrupt, 1)
- bc.wg.Wait()
+ lc.wg.Wait()
log.Info("Blockchain manager stopped")
}
// Rollback is designed to remove a chain of links from the database that aren't
// certain enough to be valid.
-func (self *LightChain) Rollback(chain []common.Hash) {
- self.mu.Lock()
- defer self.mu.Unlock()
+func (lc *LightChain) Rollback(chain []common.Hash) {
+ lc.mu.Lock()
+ defer lc.mu.Unlock()
for i := len(chain) - 1; i >= 0; i-- {
hash := chain[i]
- if head := self.hc.CurrentHeader(); head.Hash() == hash {
- self.hc.SetCurrentHeader(self.GetHeader(head.ParentHash, head.Number.Uint64()-1))
+ if head := lc.hc.CurrentHeader(); head.Hash() == hash {
+ lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1))
}
}
}
// postChainEvents iterates over the events generated by a chain insertion and
// posts them into the event feed.
-func (self *LightChain) postChainEvents(events []interface{}) {
+func (lc *LightChain) postChainEvents(events []interface{}) {
for _, event := range events {
switch ev := event.(type) {
case core.ChainEvent:
- if self.CurrentHeader().Hash() == ev.Hash {
- self.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block})
+ if lc.CurrentHeader().Hash() == ev.Hash {
+ lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block})
}
- self.chainFeed.Send(ev)
+ lc.chainFeed.Send(ev)
case core.ChainSideEvent:
- self.chainSideFeed.Send(ev)
+ lc.chainSideFeed.Send(ev)
}
}
}
@@ -344,28 +339,28 @@ func (self *LightChain) postChainEvents(events []interface{}) {
//
// In the case of a light chain, InsertHeaderChain also creates and posts light
// chain events when necessary.
-func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
+func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
start := time.Now()
- if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
+ if i, err := lc.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
return i, err
}
// Make sure only one thread manipulates the chain at once
- self.chainmu.Lock()
+ lc.chainmu.Lock()
defer func() {
- self.chainmu.Unlock()
+ lc.chainmu.Unlock()
time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation
}()
- self.wg.Add(1)
- defer self.wg.Done()
+ lc.wg.Add(1)
+ defer lc.wg.Done()
var events []interface{}
whFunc := func(header *types.Header) error {
- self.mu.Lock()
- defer self.mu.Unlock()
+ lc.mu.Lock()
+ defer lc.mu.Unlock()
- status, err := self.hc.WriteHeader(header)
+ status, err := lc.hc.WriteHeader(header)
switch status {
case core.CanonStatTy:
@@ -378,91 +373,91 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
}
return err
}
- i, err := self.hc.InsertHeaderChain(chain, whFunc, start)
- self.postChainEvents(events)
+ i, err := lc.hc.InsertHeaderChain(chain, whFunc, start)
+ lc.postChainEvents(events)
return i, err
}
// CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache.
-func (self *LightChain) CurrentHeader() *types.Header {
- return self.hc.CurrentHeader()
+func (lc *LightChain) CurrentHeader() *types.Header {
+ return lc.hc.CurrentHeader()
}
// GetTd retrieves a block's total difficulty in the canonical chain from the
// database by hash and number, caching it if found.
-func (self *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
- return self.hc.GetTd(hash, number)
+func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
+ return lc.hc.GetTd(hash, number)
}
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
// database by hash, caching it if found.
-func (self *LightChain) GetTdByHash(hash common.Hash) *big.Int {
- return self.hc.GetTdByHash(hash)
+func (lc *LightChain) GetTdByHash(hash common.Hash) *big.Int {
+ return lc.hc.GetTdByHash(hash)
}
// GetHeader retrieves a block header from the database by hash and number,
// caching it if found.
-func (self *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header {
- return self.hc.GetHeader(hash, number)
+func (lc *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header {
+ return lc.hc.GetHeader(hash, number)
}
// GetHeaderByHash retrieves a block header from the database by hash, caching it if
// found.
-func (self *LightChain) GetHeaderByHash(hash common.Hash) *types.Header {
- return self.hc.GetHeaderByHash(hash)
+func (lc *LightChain) GetHeaderByHash(hash common.Hash) *types.Header {
+ return lc.hc.GetHeaderByHash(hash)
}
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
-func (bc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
- return bc.hc.HasHeader(hash, number)
+func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
+ return lc.hc.HasHeader(hash, number)
}
// GetCanonicalHash returns the canonical hash for a given block number
-func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash {
- return bc.hc.GetCanonicalHash(number)
+func (lc *LightChain) GetCanonicalHash(number uint64) common.Hash {
+ return lc.hc.GetCanonicalHash(number)
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
// hash, fetching towards the genesis block.
-func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
- return self.hc.GetBlockHashesFromHash(hash, max)
+func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
+ return lc.hc.GetBlockHashesFromHash(hash, max)
}
// GetHeaderByNumber retrieves a block header from the database by number,
// caching it (associated with its hash) if found.
-func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {
- return self.hc.GetHeaderByNumber(number)
+func (lc *LightChain) GetHeaderByNumber(number uint64) *types.Header {
+ return lc.hc.GetHeaderByNumber(number)
}
// GetHeaderByNumberOdr retrieves a block header from the database or network
// by number, caching it (associated with its hash) if found.
-func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) {
- if header := self.hc.GetHeaderByNumber(number); header != nil {
+func (lc *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) {
+ if header := lc.hc.GetHeaderByNumber(number); header != nil {
return header, nil
}
- return GetHeaderByNumber(ctx, self.odr, number)
+ return GetHeaderByNumber(ctx, lc.odr, number)
}
// Config retrieves the header chain's chain configuration.
-func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() }
+func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }
-func (self *LightChain) SyncCht(ctx context.Context) bool {
- if self.odr.ChtIndexer() == nil {
+func (lc *LightChain) SyncCht(ctx context.Context) bool {
+ if lc.odr.ChtIndexer() == nil {
return false
}
- headNum := self.CurrentHeader().Number.Uint64()
- chtCount, _, _ := self.odr.ChtIndexer().Sections()
+ headNum := lc.CurrentHeader().Number.Uint64()
+ chtCount, _, _ := lc.odr.ChtIndexer().Sections()
if headNum+1 < chtCount*CHTFrequencyClient {
num := chtCount*CHTFrequencyClient - 1
- header, err := GetHeaderByNumber(ctx, self.odr, num)
+ header, err := GetHeaderByNumber(ctx, lc.odr, num)
if header != nil && err == nil {
- self.mu.Lock()
- if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
- self.hc.SetCurrentHeader(header)
+ lc.mu.Lock()
+ if lc.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
+ lc.hc.SetCurrentHeader(header)
}
- self.mu.Unlock()
+ lc.mu.Unlock()
return true
}
}
@@ -471,38 +466,38 @@ func (self *LightChain) SyncCht(ctx context.Context) bool {
// LockChain locks the chain mutex for reading so that multiple canonical hashes can be
// retrieved while it is guaranteed that they belong to the same version of the chain
-func (self *LightChain) LockChain() {
- self.chainmu.RLock()
+func (lc *LightChain) LockChain() {
+ lc.chainmu.RLock()
}
// UnlockChain unlocks the chain mutex
-func (self *LightChain) UnlockChain() {
- self.chainmu.RUnlock()
+func (lc *LightChain) UnlockChain() {
+ lc.chainmu.RUnlock()
}
// SubscribeChainEvent registers a subscription of ChainEvent.
-func (self *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
- return self.scope.Track(self.chainFeed.Subscribe(ch))
+func (lc *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
+ return lc.scope.Track(lc.chainFeed.Subscribe(ch))
}
// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
-func (self *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
- return self.scope.Track(self.chainHeadFeed.Subscribe(ch))
+func (lc *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
+ return lc.scope.Track(lc.chainHeadFeed.Subscribe(ch))
}
// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
-func (self *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
- return self.scope.Track(self.chainSideFeed.Subscribe(ch))
+func (lc *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
+ return lc.scope.Track(lc.chainSideFeed.Subscribe(ch))
}
// SubscribeLogsEvent implements the interface of filters.Backend
// LightChain does not send logs events, so return an empty subscription.
-func (self *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return self.scope.Track(new(event.Feed).Subscribe(ch))
+func (lc *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return lc.scope.Track(new(event.Feed).Subscribe(ch))
}
// SubscribeRemovedLogsEvent implements the interface of filters.Backend
// LightChain does not send core.RemovedLogsEvent, so return an empty subscription.
-func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
- return self.scope.Track(new(event.Feed).Subscribe(ch))
+func (lc *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
+ return lc.scope.Track(new(event.Feed).Subscribe(ch))
}
diff --git a/light/lightchain_test.go b/light/lightchain_test.go
index 0de612e3b9..7d762e2e3f 100644
--- a/light/lightchain_test.go
+++ b/light/lightchain_test.go
@@ -250,8 +250,8 @@ func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.
Number: big.NewInt(int64(i + 1)),
Difficulty: big.NewInt(int64(difficulty)),
UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyRootHash,
- ReceiptHash: types.EmptyRootHash,
+ TxHash: types.EmptyTxsHash,
+ ReceiptHash: types.EmptyReceiptsHash,
}
if i == 0 {
header.ParentHash = genesis.Hash()
diff --git a/light/odr_test.go b/light/odr_test.go
index 83e1c807ca..1562f955d9 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -44,7 +44,7 @@ import (
var (
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
- testBankFunds = big.NewInt(100000000)
+ testBankFunds = big.NewInt(math.MaxInt64)
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
@@ -157,6 +157,7 @@ func (callmsg) CheckNonce() bool { return false }
func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
config := params.TestChainConfig
+ config.Eip1559Block = big.NewInt(0)
var res []byte
for i := 0; i < 3; i++ {
@@ -184,13 +185,14 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
if value, ok := feeCapacity[testContractAddr]; ok {
balanceTokenFee = value
}
- msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, nil, false, balanceTokenFee, header.Number)}
- context := core.NewEVMContext(msg, header, chain, nil)
- vmenv := vm.NewEVM(context, st, nil, config, vm.Config{})
+ msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)}
+ txContext := core.NewEVMTxContext(msg)
+ context := core.NewEVMBlockContext(header, chain, nil)
+ vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
- ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
- res = append(res, ret...)
+ result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
+ res = append(res, result.Return()...)
if st.Error() != nil {
return res, st.Error()
}
@@ -203,17 +205,17 @@ func testChainGen(i int, block *core.BlockGen) {
switch i {
case 0:
// In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(90_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
block.AddTx(tx)
case 1:
// In block 2, the test bank sends some more ether to account #1.
// acc1Addr passes it on to account #2.
// acc1Addr creates a test contract.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
+ tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
nonce := block.TxNonce(acc1Addr)
- tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
+ tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key)
nonce++
- tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode), signer, acc1Key)
+ tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, block.BaseFee(), testContractCode), signer, acc1Key)
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
block.AddTx(tx1)
block.AddTx(tx2)
@@ -223,7 +225,7 @@ func testChainGen(i int, block *core.BlockGen) {
block.SetCoinbase(acc2Addr)
block.SetExtra([]byte("yeehaw"))
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
block.AddTx(tx)
case 3:
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
@@ -234,28 +236,33 @@ func testChainGen(i int, block *core.BlockGen) {
b3.Extra = []byte("foo")
block.AddUncle(b3)
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey)
+ tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
block.AddTx(tx)
}
}
func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
+ sdb = rawdb.NewMemoryDatabase()
+ ldb = rawdb.NewMemoryDatabase()
+ gspec = core.Genesis{
+ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
genesis = gspec.MustCommit(sdb)
)
gspec.MustCommit(ldb)
// Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
+ config := *params.TestChainConfig
+ config.Eip1559Block = big.NewInt(0)
+ blockchain, _ := core.NewBlockChain(sdb, nil, &config, ethash.NewFullFaker(), vm.Config{})
+ gchain, _ := core.GenerateChain(&config, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil {
t.Fatal(err)
}
odr := &testOdr{sdb: sdb, ldb: ldb}
- lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
+ lightchain, err := NewLightChain(odr, &config, ethash.NewFullFaker())
if err != nil {
t.Fatal(err)
}
diff --git a/light/odr_util.go b/light/odr_util.go
index d7ebd6739f..7b94ebe7a3 100644
--- a/light/odr_util.go
+++ b/light/odr_util.go
@@ -19,6 +19,7 @@ package light
import (
"bytes"
"context"
+ "errors"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -29,6 +30,13 @@ import (
var sha3_nil = crypto.Keccak256Hash(nil)
+// errNonCanonicalHash is returned if the requested chain data doesn't belong
+// to the canonical chain. ODR can only retrieve the canonical chain data covered
+// by the CHT or Bloom trie for verification.
+var errNonCanonicalHash = errors.New("hash is not currently canonical")
+
+// GetHeaderByNumber retrieves the canonical block header corresponding to the
+// given number. The returned header is proven by local CHT.
func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
db := odr.Database()
hash := core.GetCanonicalHash(db, number)
@@ -113,7 +121,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint
// Retrieve the block header and body contents
header := core.GetHeader(odr.Database(), hash, number)
if header == nil {
- return nil, ErrNoHeader
+ return nil, errNoHeader
}
body, err := GetBody(ctx, odr, hash, number)
if err != nil {
@@ -129,6 +137,13 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
// Retrieve the potentially incomplete receipts from disk or network
receipts := core.GetBlockReceipts(odr.Database(), hash, number)
if receipts == nil {
+ header, err := GetHeaderByNumber(ctx, odr, number)
+ if err != nil {
+ return nil, errNoHeader
+ }
+ if header.Hash() != hash {
+ return nil, errNonCanonicalHash
+ }
r := &ReceiptsRequest{Hash: hash, Number: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
@@ -144,7 +159,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
genesis := core.GetCanonicalHash(odr.Database(), 0)
config, _ := core.GetChainConfig(odr.Database(), genesis)
- if err := core.SetReceiptsData(config, block, receipts); err != nil {
+ if err := receipts.DeriveFields(config, hash, number, block.BaseFee(), block.Transactions()); err != nil {
return nil, err
}
core.WriteBlockReceipts(odr.Database(), hash, number, receipts)
diff --git a/light/postprocess.go b/light/postprocess.go
index 5e6e70b8d8..5f6799cf7a 100644
--- a/light/postprocess.go
+++ b/light/postprocess.go
@@ -19,13 +19,13 @@ package light
import (
"encoding/binary"
"errors"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/bitutil"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/log"
@@ -81,9 +81,9 @@ var trustedCheckpoints = map[common.Hash]trustedCheckpoint{
}
var (
- ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
- ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie")
- ErrNoHeader = errors.New("Header not found")
+ ErrNoTrustedCht = errors.New("no trusted canonical hash trie")
+ ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie")
+ errNoHeader = errors.New("header not found")
chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
ChtTablePrefix = "cht-"
)
@@ -164,7 +164,7 @@ func (c *ChtIndexerBackend) Process(header *types.Header) {
td := core.GetTd(c.diskdb, hash, num)
if td == nil {
- panic(nil)
+ panic("ChtIndexerBackend Process: td == nil")
}
var encNumber [8]byte
binary.BigEndian.PutUint64(encNumber[:], num)
diff --git a/light/trie_test.go b/light/trie_test.go
index 2332043d26..1dda0cc73e 100644
--- a/light/trie_test.go
+++ b/light/trie_test.go
@@ -21,12 +21,12 @@ import (
"context"
"errors"
"fmt"
+ "math/big"
"testing"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/params"
@@ -38,7 +38,10 @@ func TestNodeIterator(t *testing.T) {
var (
fulldb = rawdb.NewMemoryDatabase()
lightdb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
+ gspec = core.Genesis{
+ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
genesis = gspec.MustCommit(fulldb)
)
gspec.MustCommit(lightdb)
diff --git a/light/txpool.go b/light/txpool.go
index 292e91b92d..a1d9190a38 100644
--- a/light/txpool.go
+++ b/light/txpool.go
@@ -26,6 +26,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/event"
@@ -68,6 +69,7 @@ type TxPool struct {
homestead bool
eip2718 bool // Fork indicator whether we are in the eip2718 stage.
+ eip1559 bool // Fork indicator whether we are in the eip1559 stage.
}
// TxRelayBackend provides an interface to the mechanism that forwards transacions
@@ -113,25 +115,25 @@ func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBacke
}
// currentState returns the light state of the current head header
-func (pool *TxPool) currentState(ctx context.Context) *state.StateDB {
- return NewState(ctx, pool.chain.CurrentHeader(), pool.odr)
+func (p *TxPool) currentState(ctx context.Context) *state.StateDB {
+ return NewState(ctx, p.chain.CurrentHeader(), p.odr)
}
// GetNonce returns the "pending" nonce of a given address. It always queries
// the nonce belonging to the latest header too in order to detect if another
// client using the same key sent a transaction.
-func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
- state := pool.currentState(ctx)
+func (p *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
+ state := p.currentState(ctx)
nonce := state.GetNonce(addr)
if state.Error() != nil {
return 0, state.Error()
}
- sn, ok := pool.nonce[addr]
+ sn, ok := p.nonce[addr]
if ok && sn > nonce {
nonce = sn
}
if !ok || sn < nonce {
- pool.nonce[addr] = nonce
+ p.nonce[addr] = nonce
}
return nonce, nil
}
@@ -165,52 +167,52 @@ func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Has
// checkMinedTxs checks newly added blocks for the currently pending transactions
// and marks them as mined if necessary. It also stores block position in the db
// and adds them to the received txStateChanges map.
-func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error {
+func (p *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error {
// If no transactions are pending, we don't care about anything
- if len(pool.pending) == 0 {
+ if len(p.pending) == 0 {
return nil
}
- block, err := GetBlock(ctx, pool.odr, hash, number)
+ block, err := GetBlock(ctx, p.odr, hash, number)
if err != nil {
return err
}
// Gather all the local transaction mined in this block
- list := pool.mined[hash]
+ list := p.mined[hash]
for _, tx := range block.Transactions() {
- if _, ok := pool.pending[tx.Hash()]; ok {
+ if _, ok := p.pending[tx.Hash()]; ok {
list = append(list, tx)
}
}
// If some transactions have been mined, write the needed data to disk and update
if list != nil {
// Retrieve all the receipts belonging to this block and write the lookup table
- if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results
+ if _, err := GetBlockReceipts(ctx, p.odr, hash, number); err != nil { // ODR caches, ignore results
return err
}
- if err := core.WriteTxLookupEntries(pool.chainDb, block); err != nil {
+ if err := core.WriteTxLookupEntries(p.chainDb, block); err != nil {
return err
}
// Update the transaction pool's state
for _, tx := range list {
- delete(pool.pending, tx.Hash())
+ delete(p.pending, tx.Hash())
txc.setState(tx.Hash(), true)
}
- pool.mined[hash] = list
+ p.mined[hash] = list
}
return nil
}
// rollbackTxs marks the transactions contained in recently rolled back blocks
// as rolled back. It also removes any positional lookup entries.
-func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
- if list, ok := pool.mined[hash]; ok {
+func (p *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
+ if list, ok := p.mined[hash]; ok {
for _, tx := range list {
txHash := tx.Hash()
- core.DeleteTxLookupEntry(pool.chainDb, txHash)
- pool.pending[txHash] = tx
+ core.DeleteTxLookupEntry(p.chainDb, txHash)
+ p.pending[txHash] = tx
txc.setState(txHash, false)
}
- delete(pool.mined, hash)
+ delete(p.mined, hash)
}
}
@@ -220,60 +222,60 @@ func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
// timeout) occurs during checking new blocks, it leaves the locally known head
// at the latest checked block and still returns a valid txStateChanges, making it
// possible to continue checking the missing blocks at the next chain head event
-func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) {
+func (p *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) {
txc := make(txStateChanges)
- oldh := pool.chain.GetHeaderByHash(pool.head)
+ oldh := p.chain.GetHeaderByHash(p.head)
newh := newHeader
// find common ancestor, create list of rolled back and new block hashes
var oldHashes, newHashes []common.Hash
for oldh.Hash() != newh.Hash() {
if oldh.Number.Uint64() >= newh.Number.Uint64() {
oldHashes = append(oldHashes, oldh.Hash())
- oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1)
+ oldh = p.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1)
}
if oldh.Number.Uint64() < newh.Number.Uint64() {
newHashes = append(newHashes, newh.Hash())
- newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
+ newh = p.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
if newh == nil {
// happens when CHT syncing, nothing to do
newh = oldh
}
}
}
- if oldh.Number.Uint64() < pool.clearIdx {
- pool.clearIdx = oldh.Number.Uint64()
+ if oldh.Number.Uint64() < p.clearIdx {
+ p.clearIdx = oldh.Number.Uint64()
}
// roll back old blocks
for _, hash := range oldHashes {
- pool.rollbackTxs(hash, txc)
+ p.rollbackTxs(hash, txc)
}
- pool.head = oldh.Hash()
+ p.head = oldh.Hash()
// check mined txs of new blocks (array is in reversed order)
for i := len(newHashes) - 1; i >= 0; i-- {
hash := newHashes[i]
- if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil {
+ if err := p.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil {
return txc, err
}
- pool.head = hash
+ p.head = hash
}
// clear old mined tx entries of old blocks
- if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent {
+ if idx := newHeader.Number.Uint64(); idx > p.clearIdx+txPermanent {
idx2 := idx - txPermanent
- if len(pool.mined) > 0 {
- for i := pool.clearIdx; i < idx2; i++ {
- hash := core.GetCanonicalHash(pool.chainDb, i)
- if list, ok := pool.mined[hash]; ok {
+ if len(p.mined) > 0 {
+ for i := p.clearIdx; i < idx2; i++ {
+ hash := core.GetCanonicalHash(p.chainDb, i)
+ if list, ok := p.mined[hash]; ok {
hashes := make([]common.Hash, len(list))
for i, tx := range list {
hashes[i] = tx.Hash()
}
- pool.relay.Discard(hashes)
- delete(pool.mined, hash)
+ p.relay.Discard(hashes)
+ delete(p.mined, hash)
}
}
}
- pool.clearIdx = idx2
+ p.clearIdx = idx2
}
return txc, nil
@@ -285,66 +287,67 @@ const blockCheckTimeout = time.Second * 3
// eventLoop processes chain head events and also notifies the tx relay backend
// about the new head hash and tx state changes
-func (pool *TxPool) eventLoop() {
+func (p *TxPool) eventLoop() {
for {
select {
- case ev := <-pool.chainHeadCh:
- pool.setNewHead(ev.Block.Header())
+ case ev := <-p.chainHeadCh:
+ p.setNewHead(ev.Block.Header())
// hack in order to avoid hogging the lock; this part will
// be replaced by a subsequent PR.
time.Sleep(time.Millisecond)
// System stopped
- case <-pool.chainHeadSub.Err():
+ case <-p.chainHeadSub.Err():
return
}
}
}
-func (pool *TxPool) setNewHead(head *types.Header) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
+func (p *TxPool) setNewHead(head *types.Header) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout)
defer cancel()
- txc, _ := pool.reorgOnNewHead(ctx, head)
+ txc, _ := p.reorgOnNewHead(ctx, head)
m, r := txc.getLists()
- pool.relay.NewHead(pool.head, m, r)
+ p.relay.NewHead(p.head, m, r)
// Update fork indicator by next pending block number
next := new(big.Int).Add(head.Number, big.NewInt(1))
- pool.homestead = pool.config.IsHomestead(head.Number)
- pool.eip2718 = pool.config.IsEIP1559(next)
+ p.homestead = p.config.IsHomestead(head.Number)
+ p.eip2718 = p.config.IsEIP1559(next)
+ p.eip1559 = p.config.IsEIP1559(next)
}
// Stop stops the light transaction pool
-func (pool *TxPool) Stop() {
+func (p *TxPool) Stop() {
// Unsubscribe all subscriptions registered from txpool
- pool.scope.Close()
+ p.scope.Close()
// Unsubscribe subscriptions registered from blockchain
- pool.chainHeadSub.Unsubscribe()
- close(pool.quit)
+ p.chainHeadSub.Unsubscribe()
+ close(p.quit)
log.Info("Transaction pool stopped")
}
// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and
// starts sending event to the given channel.
-func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return pool.scope.Track(pool.txFeed.Subscribe(ch))
+func (p *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
+ return p.scope.Track(p.txFeed.Subscribe(ch))
}
// Stats returns the number of currently pending (locally created) transactions
-func (pool *TxPool) Stats() (pending int) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
+func (p *TxPool) Stats() (pending int) {
+ p.mu.RLock()
+ defer p.mu.RUnlock()
- pending = len(pool.pending)
+ pending = len(p.pending)
return
}
// validateTx checks whether a transaction is valid according to the consensus rules.
-func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
+func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
// Validate sender
var (
from common.Address
@@ -353,51 +356,51 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// check if sender is in black list
if tx.From() != nil && common.Blacklist[*tx.From()] {
- return fmt.Errorf("Reject transaction with sender in black-list: %v", tx.From().Hex())
+ return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
}
// check if receiver is in black list
if tx.To() != nil && common.Blacklist[*tx.To()] {
- return fmt.Errorf("Reject transaction with receiver in black-list: %v", tx.To().Hex())
+ return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex())
}
// validate minFee slot for XDCZ
if tx.IsXDCZApplyTransaction() {
- copyState := pool.currentState(ctx).Copy()
- if err := core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
+ copyState := p.currentState(ctx).Copy()
+ if err := core.ValidateXDCZApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
return err
}
}
// validate balance slot, token decimal for XDCX
if tx.IsXDCXApplyTransaction() {
- copyState := pool.currentState(ctx).Copy()
- if err := core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
+ copyState := p.currentState(ctx).Copy()
+ if err := core.ValidateXDCXApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
return err
}
}
// Validate the transaction sender and it's sig. Throw
// if the from fields is invalid.
- if from, err = types.Sender(pool.signer, tx); err != nil {
- return core.ErrInvalidSender
+ if from, err = types.Sender(p.signer, tx); err != nil {
+ return txpool.ErrInvalidSender
}
// Last but not least check for nonce errors
- currentState := pool.currentState(ctx)
+ currentState := p.currentState(ctx)
if n := currentState.GetNonce(from); n > tx.Nonce() {
return core.ErrNonceTooLow
}
// Check the transaction doesn't exceed the current
// block limit gas.
- header := pool.chain.GetHeaderByHash(pool.head)
+ header := p.chain.GetHeaderByHash(p.head)
if header.GasLimit < tx.Gas() {
- return core.ErrGasLimit
+ return txpool.ErrGasLimit
}
// Transactions can't be negative. This may never happen
// using RLP decoded transactions but may occur if you create
// a transaction using the RPC for example.
if tx.Value().Sign() < 0 {
- return core.ErrNegativeValue
+ return txpool.ErrNegativeValue
}
// Transactor should have enough funds to cover the costs
@@ -407,7 +410,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
}
// Should supply enough intrinsic gas
- gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, pool.homestead)
+ gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, p.homestead, p.eip1559)
if err != nil {
return err
}
@@ -419,80 +422,83 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error
// add validates a new transaction and sets its state pending if processable.
// It also updates the locally stored nonce if necessary.
-func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error {
+func (p *TxPool) add(ctx context.Context, tx *types.Transaction) error {
hash := tx.Hash()
- if self.pending[hash] != nil {
- return fmt.Errorf("Known transaction (%x)", hash[:4])
+ if p.pending[hash] != nil {
+ return fmt.Errorf("known transaction (%x)", hash[:4])
}
- err := self.validateTx(ctx, tx)
+ err := p.validateTx(ctx, tx)
if err != nil {
return err
}
- if _, ok := self.pending[hash]; !ok {
- self.pending[hash] = tx
+ if _, ok := p.pending[hash]; !ok {
+ p.pending[hash] = tx
nonce := tx.Nonce() + 1
- addr, _ := types.Sender(self.signer, tx)
- if nonce > self.nonce[addr] {
- self.nonce[addr] = nonce
+ addr, _ := types.Sender(p.signer, tx)
+ if nonce > p.nonce[addr] {
+ p.nonce[addr] = nonce
}
// Notify the subscribers. This event is posted in a goroutine
// because it's possible that somewhere during the post "Remove transaction"
// gets called which will then wait for the global tx pool lock and deadlock.
- go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
+ go p.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
}
// Print a log message if low enough level is set
- log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(self.signer, tx); return from }}, "to", tx.To())
+ if log.Enabled(log.LevelDebug) {
+ from, _ := types.Sender(p.signer, tx)
+ log.Debug("Pooled new transaction", "hash", hash, "from", from, "to", tx.To())
+ }
return nil
}
// Add adds a transaction to the pool if valid and passes it to the tx relay
// backend
-func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
- self.mu.Lock()
- defer self.mu.Unlock()
+func (p *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
data, err := tx.MarshalBinary()
if err != nil {
return err
}
- if err := self.add(ctx, tx); err != nil {
+ if err := p.add(ctx, tx); err != nil {
return err
}
//fmt.Println("Send", tx.Hash())
- self.relay.Send(types.Transactions{tx})
+ p.relay.Send(types.Transactions{tx})
- self.chainDb.Put(tx.Hash().Bytes(), data)
+ p.chainDb.Put(tx.Hash().Bytes(), data)
return nil
}
// AddTransactions adds all valid transactions to the pool and passes them to
// the tx relay backend
-func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
- self.mu.Lock()
- defer self.mu.Unlock()
+func (p *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
var sendTx types.Transactions
for _, tx := range txs {
- if err := self.add(ctx, tx); err == nil {
+ if err := p.add(ctx, tx); err == nil {
sendTx = append(sendTx, tx)
}
}
if len(sendTx) > 0 {
- self.relay.Send(sendTx)
+ p.relay.Send(sendTx)
}
}
// GetTransaction returns a transaction if it is contained in the pool
// and nil otherwise.
-func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
+func (p *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
// check the txs first
- if tx, ok := tp.pending[hash]; ok {
+ if tx, ok := p.pending[hash]; ok {
return tx
}
return nil
@@ -500,13 +506,13 @@ func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
// GetTransactions returns all currently processable transactions.
// The returned slice may be modified by the caller.
-func (self *TxPool) GetTransactions() (txs types.Transactions, err error) {
- self.mu.RLock()
- defer self.mu.RUnlock()
+func (p *TxPool) GetTransactions() (txs types.Transactions, err error) {
+ p.mu.RLock()
+ defer p.mu.RUnlock()
- txs = make(types.Transactions, len(self.pending))
+ txs = make(types.Transactions, len(p.pending))
i := 0
- for _, tx := range self.pending {
+ for _, tx := range p.pending {
txs[i] = tx
i++
}
@@ -515,14 +521,14 @@ func (self *TxPool) GetTransactions() (txs types.Transactions, err error) {
// Content retrieves the data content of the transaction pool, returning all the
// pending as well as queued transactions, grouped by account and nonce.
-func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
- self.mu.RLock()
- defer self.mu.RUnlock()
+func (p *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
+ p.mu.RLock()
+ defer p.mu.RUnlock()
// Retrieve all the pending transactions and sort by account and by nonce
pending := make(map[common.Address]types.Transactions)
- for _, tx := range self.pending {
- account, _ := types.Sender(self.signer, tx)
+ for _, tx := range p.pending {
+ account, _ := types.Sender(p.signer, tx)
pending[account] = append(pending[account], tx)
}
// There are no queued transactions in a light pool, just return an empty map
@@ -530,27 +536,46 @@ func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common
return pending, queued
}
+// ContentFrom retrieves the data content of the transaction pool, returning the
+// pending as well as queued transactions of this address, grouped by nonce.
+func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ // Retrieve the pending transactions and sort by nonce
+ var pending types.Transactions
+ for _, tx := range pool.pending {
+ account, _ := types.Sender(pool.signer, tx)
+ if account != addr {
+ continue
+ }
+ pending = append(pending, tx)
+ }
+ // There are no queued transactions in a light pool, just return an empty map
+ return pending, types.Transactions{}
+}
+
// RemoveTransactions removes all given transactions from the pool.
-func (self *TxPool) RemoveTransactions(txs types.Transactions) {
- self.mu.Lock()
- defer self.mu.Unlock()
+func (p *TxPool) RemoveTransactions(txs types.Transactions) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
var hashes []common.Hash
for _, tx := range txs {
//self.RemoveTx(tx.Hash())
hash := tx.Hash()
- delete(self.pending, hash)
- self.chainDb.Delete(hash[:])
+ delete(p.pending, hash)
+ p.chainDb.Delete(hash[:])
hashes = append(hashes, hash)
}
- self.relay.Discard(hashes)
+ p.relay.Discard(hashes)
}
// RemoveTx removes the transaction with the given hash from the pool.
-func (pool *TxPool) RemoveTx(hash common.Hash) {
- pool.mu.Lock()
- defer pool.mu.Unlock()
+func (p *TxPool) RemoveTx(hash common.Hash) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
// delete from pending pool
- delete(pool.pending, hash)
- pool.chainDb.Delete(hash[:])
- pool.relay.Discard([]common.Hash{hash})
+ delete(p.pending, hash)
+ p.chainDb.Delete(hash[:])
+ p.relay.Discard([]common.Hash{hash})
}
diff --git a/light/txpool_test.go b/light/txpool_test.go
index 4ada980daf..e52e8dff73 100644
--- a/light/txpool_test.go
+++ b/light/txpool_test.go
@@ -18,12 +18,13 @@ package light
import (
"context"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math"
"math/big"
"testing"
"time"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
@@ -36,19 +37,19 @@ type testTxRelay struct {
send, discard, mined chan int
}
-func (self *testTxRelay) Send(txs types.Transactions) {
- self.send <- len(txs)
+func (r *testTxRelay) Send(txs types.Transactions) {
+ r.send <- len(txs)
}
-func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
+func (r *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
m := len(mined)
if m != 0 {
- self.mined <- m
+ r.mined <- m
}
}
-func (self *testTxRelay) Discard(hashes []common.Hash) {
- self.discard <- len(hashes)
+func (r *testTxRelay) Discard(hashes []common.Hash) {
+ r.discard <- len(hashes)
}
const poolTestTxs = 1000
@@ -77,13 +78,16 @@ func txPoolTestChainGen(i int, block *core.BlockGen) {
func TestTxPool(t *testing.T) {
for i := range testTx {
- testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
+ testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
}
var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
+ sdb = rawdb.NewMemoryDatabase()
+ ldb = rawdb.NewMemoryDatabase()
+ gspec = core.Genesis{
+ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ }
genesis = gspec.MustCommit(sdb)
)
gspec.MustCommit(ldb)
diff --git a/log/CONTRIBUTORS b/log/CONTRIBUTORS
deleted file mode 100644
index a0866713be..0000000000
--- a/log/CONTRIBUTORS
+++ /dev/null
@@ -1,11 +0,0 @@
-Contributors to log15:
-
-- Aaron L
-- Alan Shreve
-- Chris Hines
-- Ciaran Downey
-- Dmitry Chestnykh
-- Evan Shaw
-- Péter Szilágyi
-- Trevor Gattis
-- Vincent Vanackere
diff --git a/log/LICENSE b/log/LICENSE
deleted file mode 100644
index 5f0d1fb6a7..0000000000
--- a/log/LICENSE
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2014 Alan Shreve
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/log/README.md b/log/README.md
deleted file mode 100644
index 0951b21cb5..0000000000
--- a/log/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-# log15 [](https://godoc.org/github.com/inconshreveable/log15) [](https://travis-ci.org/inconshreveable/log15)
-
-Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package.
-
-## Features
-- A simple, easy-to-understand API
-- Promotes structured logging by encouraging use of key/value pairs
-- Child loggers which inherit and add their own private context
-- Lazy evaluation of expensive operations
-- Simple Handler interface allowing for construction of flexible, custom logging configurations with a tiny API.
-- Color terminal support
-- Built-in support for logging to files, streams, syslog, and the network
-- Support for forking records to multiple handlers, buffering records for output, failing over from failed handler writes, + more
-
-## Versioning
-The API of the master branch of log15 should always be considered unstable. If you want to rely on a stable API,
-you must vendor the library.
-
-## Importing
-
-```go
-import log "github.com/inconshreveable/log15"
-```
-
-## Examples
-
-```go
-// all loggers can have key/value context
-srvlog := log.New("module", "app/server")
-
-// all log messages can have key/value context
-srvlog.Warn("abnormal conn rate", "rate", curRate, "low", lowRate, "high", highRate)
-
-// child loggers with inherited context
-connlog := srvlog.New("raddr", c.RemoteAddr())
-connlog.Info("connection open")
-
-// lazy evaluation
-connlog.Debug("ping remote", "latency", log.Lazy{pingRemote})
-
-// flexible configuration
-srvlog.SetHandler(log.MultiHandler(
- log.StreamHandler(os.Stderr, log.LogfmtFormat()),
- log.LvlFilterHandler(
- log.LvlError,
- log.Must.FileHandler("errors.json", log.JsonFormat()))))
-```
-
-Will result in output that looks like this:
-
-```
-WARN[06-17|21:58:10] abnormal conn rate module=app/server rate=0.500 low=0.100 high=0.800
-INFO[06-17|21:58:10] connection open module=app/server raddr=10.0.0.1
-```
-
-## Breaking API Changes
-The following commits broke API stability. This reference is intended to help you understand the consequences of updating to a newer version
-of log15.
-
-- 57a084d014d4150152b19e4e531399a7145d1540 - Added a `Get()` method to the `Logger` interface to retrieve the current handler
-- 93404652ee366648fa622b64d1e2b67d75a3094a - `Record` field `Call` changed to `stack.Call` with switch to `github.com/go-stack/stack`
-- a5e7613673c73281f58e15a87d2cf0cf111e8152 - Restored `syslog.Priority` argument to the `SyslogXxx` handler constructors
-
-## FAQ
-
-### The varargs style is brittle and error prone! Can I have type safety please?
-Yes. Use `log.Ctx`:
-
-```go
-srvlog := log.New(log.Ctx{"module": "app/server"})
-srvlog.Warn("abnormal conn rate", log.Ctx{"rate": curRate, "low": lowRate, "high": highRate})
-```
-
-## License
-Apache
diff --git a/log/README_ETHEREUM.md b/log/README_ETHEREUM.md
deleted file mode 100644
index f6c42ccc03..0000000000
--- a/log/README_ETHEREUM.md
+++ /dev/null
@@ -1,5 +0,0 @@
-This package is a fork of https://github.com/inconshreveable/log15, with some
-minor modifications required by the go-ethereum codebase:
-
- * Support for log level `trace`
- * Modified behavior to exit on `critical` failure
diff --git a/log/doc.go b/log/doc.go
deleted file mode 100644
index 83ad8c54f6..0000000000
--- a/log/doc.go
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
-Package log15 provides an opinionated, simple toolkit for best-practice logging that is
-both human and machine readable. It is modeled after the standard library's io and net/http
-packages.
-
-This package enforces you to only log key/value pairs. Keys must be strings. Values may be
-any type that you like. The default output format is logfmt, but you may also choose to use
-JSON instead if that suits you. Here's how you log:
-
- log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
-
-This will output a line that looks like:
-
- lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
-
-Getting Started
-
-To get started, you'll want to import the library:
-
- import log "github.com/inconshreveable/log15"
-
-
-Now you're ready to start logging:
-
- func main() {
- log.Info("Program starting", "args", os.Args())
- }
-
-
-Convention
-
-Because recording a human-meaningful message is common and good practice, the first argument to every
-logging method is the value to the *implicit* key 'msg'.
-
-Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so
-will the current timestamp with key 't'.
-
-You may supply any additional context as a set of key/value pairs to the logging function. log15 allows
-you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for
-logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
-in the variadic argument list:
-
- log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
-
-If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
-
- log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
-
-
-Context loggers
-
-Frequently, you want to add context to a logger so that you can track actions associated with it. An http
-request is a good example. You can easily create new loggers that have context that is automatically included
-with each log line:
-
- requestlogger := log.New("path", r.URL.Path)
-
- // later
- requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
-
-This will output a log line that includes the path context that is attached to the logger:
-
- lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
-
-
-Handlers
-
-The Handler interface defines where log lines are printed to and how they are formated. Handler is a
-single interface that is inspired by net/http's handler interface:
-
- type Handler interface {
- Log(r *Record) error
- }
-
-
-Handlers can filter records, format them, or dispatch to multiple other Handlers.
-This package implements a number of Handlers for common logging patterns that are
-easily composed to create flexible, custom logging structures.
-
-Here's an example handler that prints logfmt output to Stdout:
-
- handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
-
-Here's an example handler that defers to two other handlers. One handler only prints records
-from the rpc package in logfmt to standard out. The other prints records at Error level
-or above in JSON formatted output to the file /var/log/service.json
-
- handler := log.MultiHandler(
- log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JsonFormat())),
- log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
- )
-
-Logging File Names and Line Numbers
-
-This package implements three Handlers that add debugging information to the
-context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
-an example that adds the source file and line number of each logging call to
-the context.
-
- h := log.CallerFileHandler(log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
-
-Here's an example that logs the call stack rather than just the call site.
-
- h := log.CallerStackHandler("%+v", log.StdoutHandler)
- log.Root().SetHandler(h)
- ...
- log.Error("open file", "err", err)
-
-This will output a line that looks like:
-
- lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
-
-The "%+v" format instructs the handler to include the path of the source file
-relative to the compile time GOPATH. The github.com/go-stack/stack package
-documents the full list of formatting verbs and modifiers available.
-
-Custom Handlers
-
-The Handler interface is so simple that it's also trivial to write your own. Let's create an
-example handler which tries to write to one handler, but if that fails it falls back to
-writing to another handler and includes the error that it encountered when trying to write
-to the primary. This might be useful when trying to log over a network socket, but if that
-fails you want to log those records to a file on disk.
-
- type BackupHandler struct {
- Primary Handler
- Secondary Handler
- }
-
- func (h *BackupHandler) Log (r *Record) error {
- err := h.Primary.Log(r)
- if err != nil {
- r.Ctx = append(ctx, "primary_err", err)
- return h.Secondary.Log(r)
- }
- return nil
- }
-
-This pattern is so useful that a generic version that handles an arbitrary number of Handlers
-is included as part of this library called FailoverHandler.
-
-Logging Expensive Operations
-
-Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
-the price of computing them if you haven't turned up your logging level to a high level of detail.
-
-This package provides a simple type to annotate a logging operation that you want to be evaluated
-lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
-filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
-
- func factorRSAKey() (factors []int) {
- // return the factors of a very large number
- }
-
- log.Debug("factors", log.Lazy{factorRSAKey})
-
-If this message is not logged for any reason (like logging at the Error level), then
-factorRSAKey is never evaluated.
-
-Dynamic context values
-
-The same log.Lazy mechanism can be used to attach context to a logger which you want to be
-evaluated when the message is logged, but not when the logger is created. For example, let's imagine
-a game where you have Player objects:
-
- type Player struct {
- name string
- alive bool
- log.Logger
- }
-
-You always want to log a player's name and whether they're alive or dead, so when you create the player
-object, you might do:
-
- p := &Player{name: name, alive: true}
- p.Logger = log.New("name", p.name, "alive", p.alive)
-
-Only now, even after a player has died, the logger will still report they are alive because the logging
-context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
-of whether the player is alive or not to each log message, so that the log records will reflect the player's
-current state no matter when the log message is written:
-
- p := &Player{name: name, alive: true}
- isAlive := func() bool { return p.alive }
- player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
-
-Terminal Format
-
-If log15 detects that stdout is a terminal, it will configure the default
-handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
-logs records nicely for your terminal, including color-coded output based
-on log level.
-
-Error Handling
-
-Becasuse log15 allows you to step around the type system, there are a few ways you can specify
-invalid arguments to the logging functions. You could, for example, wrap something that is not
-a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries
-are typically the mechanism by which errors are reported, it would be onerous for the logging functions
-to return errors. Instead, log15 handles errors by making these guarantees to you:
-
-- Any log record containing an error will still be printed with the error explained to you as part of the log record.
-
-- Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily
-(and if you like, automatically) detect if any of your logging calls are passing bad values.
-
-Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers
-are encouraged to return errors only if they fail to write their log records out to an external source like if the
-syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
-like the FailoverHandler.
-
-Library Use
-
-log15 is intended to be useful for library authors as a way to provide configurable logging to
-users of their library. Best practice for use in a library is to always disable all output for your logger
-by default and to provide a public Logger instance that consumers of your library can configure. Like so:
-
- package yourlib
-
- import "github.com/inconshreveable/log15"
-
- var Log = log.New()
-
- func init() {
- Log.SetHandler(log.DiscardHandler())
- }
-
-Users of your library may then enable it if they like:
-
- import "github.com/inconshreveable/log15"
- import "example.com/yourlib"
-
- func main() {
- handler := // custom handler setup
- yourlib.Log.SetHandler(handler)
- }
-
-Best practices attaching logger context
-
-The ability to attach context to a logger is a powerful one. Where should you do it and why?
-I favor embedding a Logger directly into any persistent object in my application and adding
-unique, tracing context keys to it. For instance, imagine I am writing a web browser:
-
- type Tab struct {
- url string
- render *RenderingContext
- // ...
-
- Logger
- }
-
- func NewTab(url string) *Tab {
- return &Tab {
- // ...
- url: url,
-
- Logger: log.New("url", url),
- }
- }
-
-When a new tab is created, I assign a logger to it with the url of
-the tab as context so it can easily be traced through the logs.
-Now, whenever we perform any operation with the tab, we'll log with its
-embedded logger and it will include the tab title automatically:
-
- tab.Debug("moved position", "idx", tab.idx)
-
-There's only one problem. What if the tab url changes? We could
-use log.Lazy to make sure the current url is always written, but that
-would mean that we couldn't trace a tab's full lifetime through our
-logs after the user navigate to a new URL.
-
-Instead, think about what values to attach to your loggers the
-same way you think about what to use as a key in a SQL database schema.
-If it's possible to use a natural key that is unique for the lifetime of the
-object, do so. But otherwise, log15's ext package has a handy RandId
-function to let you generate what you might call "surrogate keys"
-They're just random hex identifiers to use for tracing. Back to our
-Tab example, we would prefer to set up our Logger like so:
-
- import logext "github.com/inconshreveable/log15/ext"
-
- t := &Tab {
- // ...
- url: url,
- }
-
- t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
- return t
-
-Now we'll have a unique traceable identifier even across loading new urls, but
-we'll still be able to see the tab's current url in the log messages.
-
-Must
-
-For all Handler functions which can return an error, there is a version of that
-function which will return no error but panics on failure. They are all available
-on the Must object. For example:
-
- log.Must.FileHandler("/path", log.JsonFormat)
- log.Must.NetHandler("tcp", ":1234", log.JsonFormat)
-
-Inspiration and Credit
-
-All of the following excellent projects inspired the design of this library:
-
-code.google.com/p/log4go
-
-github.com/op/go-logging
-
-github.com/technoweenie/grohl
-
-github.com/Sirupsen/logrus
-
-github.com/kr/logfmt
-
-github.com/spacemonkeygo/spacelog
-
-golang's stdlib, notably io and net/http
-
-The Name
-
-https://xkcd.com/927/
-
-*/
-package log
diff --git a/log/format.go b/log/format.go
index 6785935117..54c071b908 100644
--- a/log/format.go
+++ b/log/format.go
@@ -2,69 +2,26 @@ package log
import (
"bytes"
- "encoding/json"
"fmt"
+ "log/slog"
+ "math/big"
"reflect"
"strconv"
- "strings"
- "sync"
- "sync/atomic"
"time"
"unicode/utf8"
+
+ "github.com/holiman/uint256"
)
const (
- timeFormat = "2006-01-02T15:04:05-0700"
- termTimeFormat = "01-02|15:04:05"
- floatFormat = 'f'
- termMsgJust = 40
+ timeFormat = "2006-01-02T15:04:05-0700"
+ floatFormat = 'f'
+ termMsgJust = 40
+ termCtxMaxPadding = 40
)
-// locationTrims are trimmed for display to avoid unwieldy log lines.
-var locationTrims = []string{
- "github.com/XinFinOrg/XDPoSChain/",
-}
-
-// PrintOrigins sets or unsets log location (file:line) printing for terminal
-// format output.
-func PrintOrigins(print bool) {
- if print {
- atomic.StoreUint32(&locationEnabled, 1)
- } else {
- atomic.StoreUint32(&locationEnabled, 0)
- }
-}
-
-// locationEnabled is an atomic flag controlling whether the terminal formatter
-// should append the log locations too when printing entries.
-var locationEnabled uint32
-
-// locationLength is the maxmimum path length encountered, which all logs are
-// padded to to aid in alignment.
-var locationLength uint32
-
-// fieldPadding is a global map with maximum field value lengths seen until now
-// to allow padding log contexts in a bit smarter way.
-var fieldPadding = make(map[string]int)
-
-// fieldPaddingLock is a global mutex protecting the field padding map.
-var fieldPaddingLock sync.RWMutex
-
-type Format interface {
- Format(r *Record) []byte
-}
-
-// FormatFunc returns a new Format object which uses
-// the given function to perform record formatting.
-func FormatFunc(f func(*Record) []byte) Format {
- return formatFunc(f)
-}
-
-type formatFunc func(*Record) []byte
-
-func (f formatFunc) Format(r *Record) []byte {
- return f(r)
-}
+// 40 spaces
+var spaces = []byte(" ")
// TerminalStringer is an analogous interface to the stdlib stringer, allowing
// own types to have custom shortened serialization formats when printed to the
@@ -73,291 +30,334 @@ type TerminalStringer interface {
TerminalString() string
}
-// TerminalFormat formats log records optimized for human readability on
-// a terminal with color-coded level output and terser human friendly timestamp.
-// This format should only be used for interactive programs or while developing.
-//
-// [TIME] [LEVEL] MESAGE key=value key=value ...
-//
-// Example:
-//
-// [May 16 20:58:45] [DBUG] remove route ns=haproxy addr=127.0.0.1:50002
-//
-func TerminalFormat(usecolor bool) Format {
- return FormatFunc(func(r *Record) []byte {
- var color = 0
- if usecolor {
- switch r.Lvl {
- case LvlCrit:
- color = 35
- case LvlError:
- color = 31
- case LvlWarn:
- color = 33
- case LvlInfo:
- color = 32
- case LvlDebug:
- color = 36
- case LvlTrace:
- color = 34
- }
- }
-
- b := &bytes.Buffer{}
- lvl := r.Lvl.AlignedString()
- if atomic.LoadUint32(&locationEnabled) != 0 {
- // Log origin printing was requested, format the location path and line number
- location := fmt.Sprintf("%+v", r.Call)
- for _, prefix := range locationTrims {
- location = strings.TrimPrefix(location, prefix)
- }
- // Maintain the maximum location length for fancyer alignment
- align := int(atomic.LoadUint32(&locationLength))
- if align < len(location) {
- align = len(location)
- atomic.StoreUint32(&locationLength, uint32(align))
- }
- padding := strings.Repeat(" ", align-len(location))
-
- // Assemble and print the log heading
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s|%s]%s %s ", color, lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
- } else {
- fmt.Fprintf(b, "%s[%s|%s]%s %s ", lvl, r.Time.Format(termTimeFormat), location, padding, r.Msg)
- }
- } else {
- if color > 0 {
- fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %s ", color, lvl, r.Time.Format(termTimeFormat), r.Msg)
- } else {
- fmt.Fprintf(b, "%s[%s] %s ", lvl, r.Time.Format(termTimeFormat), r.Msg)
- }
- }
- // try to justify the log output for short messages
- length := utf8.RuneCountInString(r.Msg)
- if len(r.Ctx) > 0 && length < termMsgJust {
- b.Write(bytes.Repeat([]byte{' '}, termMsgJust-length))
- }
- // print the keys logfmt style
- logfmt(b, r.Ctx, color, true)
- return b.Bytes()
- })
-}
-
-// LogfmtFormat prints records in logfmt format, an easy machine-parseable but human-readable
-// format for key/value pairs.
-//
-// For more details see: http://godoc.org/github.com/kr/logfmt
-//
-func LogfmtFormat() Format {
- return FormatFunc(func(r *Record) []byte {
- common := []interface{}{r.KeyNames.Time, r.Time, r.KeyNames.Lvl, r.Lvl, r.KeyNames.Msg, r.Msg}
- buf := &bytes.Buffer{}
- logfmt(buf, append(common, r.Ctx...), 0, false)
- return buf.Bytes()
- })
-}
-
-func logfmt(buf *bytes.Buffer, ctx []interface{}, color int, term bool) {
- for i := 0; i < len(ctx); i += 2 {
- if i != 0 {
- buf.WriteByte(' ')
- }
-
- k, ok := ctx[i].(string)
- v := formatLogfmtValue(ctx[i+1], term)
- if !ok {
- k, v = errorKey, formatLogfmtValue(k, term)
- }
-
- // XXX: we should probably check that all of your key bytes aren't invalid
- fieldPaddingLock.RLock()
- padding := fieldPadding[k]
- fieldPaddingLock.RUnlock()
-
- length := utf8.RuneCountInString(v)
- if padding < length {
- padding = length
-
- fieldPaddingLock.Lock()
- fieldPadding[k] = padding
- fieldPaddingLock.Unlock()
- }
- if color > 0 {
- fmt.Fprintf(buf, "\x1b[%dm%s\x1b[0m=", color, k)
- } else {
- buf.WriteString(k)
- buf.WriteByte('=')
- }
- buf.WriteString(v)
- if i < len(ctx)-2 {
- buf.Write(bytes.Repeat([]byte{' '}, padding-length))
+func (h *TerminalHandler) format(buf []byte, r slog.Record, usecolor bool) []byte {
+ msg := escapeMessage(r.Message)
+ var color = ""
+ if usecolor {
+ switch r.Level {
+ case LevelCrit:
+ color = "\x1b[35m"
+ case slog.LevelError:
+ color = "\x1b[31m"
+ case slog.LevelWarn:
+ color = "\x1b[33m"
+ case slog.LevelInfo:
+ color = "\x1b[32m"
+ case slog.LevelDebug:
+ color = "\x1b[36m"
+ case LevelTrace:
+ color = "\x1b[34m"
}
}
+ if buf == nil {
+ buf = make([]byte, 0, 30+termMsgJust)
+ }
+ b := bytes.NewBuffer(buf)
+
+ if color != "" { // Start color
+ b.WriteString(color)
+ b.WriteString(LevelAlignedString(r.Level))
+ b.WriteString("\x1b[0m")
+ } else {
+ b.WriteString(LevelAlignedString(r.Level))
+ }
+ b.WriteString("[")
+ writeTimeTermFormat(b, r.Time)
+ b.WriteString("] ")
+ b.WriteString(msg)
+
+ // try to justify the log output for short messages
+ //length := utf8.RuneCountInString(msg)
+ length := len(msg)
+ if (r.NumAttrs()+len(h.attrs)) > 0 && length < termMsgJust {
+ b.Write(spaces[:termMsgJust-length])
+ }
+ // print the attributes
+ h.formatAttributes(b, r, color)
+
+ return b.Bytes()
+}
+
+func (h *TerminalHandler) formatAttributes(buf *bytes.Buffer, r slog.Record, color string) {
+ writeAttr := func(attr slog.Attr, first, last bool) {
+ buf.WriteByte(' ')
+
+ if color != "" {
+ buf.WriteString(color)
+ buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
+ buf.WriteString("\x1b[0m=")
+ } else {
+ buf.Write(appendEscapeString(buf.AvailableBuffer(), attr.Key))
+ buf.WriteByte('=')
+ }
+ val := FormatSlogValue(attr.Value, buf.AvailableBuffer())
+
+ padding := h.fieldPadding[attr.Key]
+
+ length := utf8.RuneCount(val)
+ if padding < length && length <= termCtxMaxPadding {
+ padding = length
+ h.fieldPadding[attr.Key] = padding
+ }
+ buf.Write(val)
+ if !last && padding > length {
+ buf.Write(spaces[:padding-length])
+ }
+ }
+ var n = 0
+ var nAttrs = len(h.attrs) + r.NumAttrs()
+ for _, attr := range h.attrs {
+ writeAttr(attr, n == 0, n == nAttrs-1)
+ n++
+ }
+ r.Attrs(func(attr slog.Attr) bool {
+ writeAttr(attr, n == 0, n == nAttrs-1)
+ n++
+ return true
+ })
buf.WriteByte('\n')
}
-// JsonFormat formats log records as JSON objects separated by newlines.
-// It is the equivalent of JsonFormatEx(false, true).
-func JsonFormat() Format {
- return JsonFormatEx(false, true)
-}
-
-// JsonFormatEx formats log records as JSON objects. If pretty is true,
-// records will be pretty-printed. If lineSeparated is true, records
-// will be logged with a new line between each record.
-func JsonFormatEx(pretty, lineSeparated bool) Format {
- jsonMarshal := json.Marshal
- if pretty {
- jsonMarshal = func(v interface{}) ([]byte, error) {
- return json.MarshalIndent(v, "", " ")
- }
- }
-
- return FormatFunc(func(r *Record) []byte {
- props := make(map[string]interface{})
-
- props[r.KeyNames.Time] = r.Time
- props[r.KeyNames.Lvl] = r.Lvl.String()
- props[r.KeyNames.Msg] = r.Msg
-
- for i := 0; i < len(r.Ctx); i += 2 {
- k, ok := r.Ctx[i].(string)
- if !ok {
- props[errorKey] = fmt.Sprintf("%+v is not a string key", r.Ctx[i])
- }
- props[k] = formatJsonValue(r.Ctx[i+1])
- }
-
- b, err := jsonMarshal(props)
- if err != nil {
- b, _ = jsonMarshal(map[string]string{
- errorKey: err.Error(),
- })
- return b
- }
-
- if lineSeparated {
- b = append(b, '\n')
- }
-
- return b
- })
-}
-
-func formatShared(value interface{}) (result interface{}) {
+// FormatSlogValue formats a slog.Value for serialization to terminal.
+func FormatSlogValue(v slog.Value, tmp []byte) (result []byte) {
+ var value any
defer func() {
if err := recover(); err != nil {
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr && v.IsNil() {
- result = "nil"
+ result = []byte("")
} else {
panic(err)
}
}
}()
- switch v := value.(type) {
- case time.Time:
- return v.Format(timeFormat)
-
- case error:
- return v.Error()
-
- case fmt.Stringer:
- return v.String()
-
- default:
- return v
- }
-}
-
-func formatJsonValue(value interface{}) interface{} {
- value = formatShared(value)
- switch value.(type) {
- case int, int8, int16, int32, int64, float32, float64, uint, uint8, uint16, uint32, uint64, string:
- return value
- default:
- return fmt.Sprintf("%+v", value)
- }
-}
-
-// formatValue formats a value for serialization
-func formatLogfmtValue(value interface{}, term bool) string {
- if value == nil {
- return "nil"
- }
-
- if t, ok := value.(time.Time); ok {
+ switch v.Kind() {
+ case slog.KindString:
+ return appendEscapeString(tmp, v.String())
+ case slog.KindInt64: // All int-types (int8, int16 etc) wind up here
+ return appendInt64(tmp, v.Int64())
+ case slog.KindUint64: // All uint-types (uint8, uint16 etc) wind up here
+ return appendUint64(tmp, v.Uint64(), false)
+ case slog.KindFloat64:
+ return strconv.AppendFloat(tmp, v.Float64(), floatFormat, 3, 64)
+ case slog.KindBool:
+ return strconv.AppendBool(tmp, v.Bool())
+ case slog.KindDuration:
+ value = v.Duration()
+ case slog.KindTime:
// Performance optimization: No need for escaping since the provided
// timeFormat doesn't have any escape characters, and escaping is
// expensive.
- return t.Format(timeFormat)
- }
- if term {
- if s, ok := value.(TerminalStringer); ok {
- // Custom terminal stringer provided, use that
- return escapeString(s.TerminalString())
- }
- }
- value = formatShared(value)
- switch v := value.(type) {
- case bool:
- return strconv.FormatBool(v)
- case float32:
- return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
- case float64:
- return strconv.FormatFloat(v, floatFormat, 3, 64)
- case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
- return fmt.Sprintf("%d", value)
- case string:
- return escapeString(v)
+ return v.Time().AppendFormat(tmp, timeFormat)
default:
- return escapeString(fmt.Sprintf("%+v", value))
+ value = v.Any()
}
+ if value == nil {
+ return []byte("")
+ }
+ switch v := value.(type) {
+ case *big.Int: // Need to be before fmt.Stringer-clause
+ return appendBigInt(tmp, v)
+ case *uint256.Int: // Need to be before fmt.Stringer-clause
+ return appendU256(tmp, v)
+ case error:
+ return appendEscapeString(tmp, v.Error())
+ case TerminalStringer:
+ return appendEscapeString(tmp, v.TerminalString())
+ case fmt.Stringer:
+ return appendEscapeString(tmp, v.String())
+ }
+
+ // We can use the 'tmp' as a scratch-buffer, to first format the
+ // value, and in a second step do escaping.
+ internal := fmt.Appendf(tmp, "%+v", value)
+ return appendEscapeString(tmp, string(internal))
}
-var stringBufPool = sync.Pool{
- New: func() interface{} { return new(bytes.Buffer) },
+// appendInt64 formats n with thousand separators and writes into buffer dst.
+func appendInt64(dst []byte, n int64) []byte {
+ if n < 0 {
+ return appendUint64(dst, uint64(-n), true)
+ }
+ return appendUint64(dst, uint64(n), false)
}
-func escapeString(s string) string {
- needsQuotes := false
- needsEscape := false
+// appendUint64 formats n with thousand separators and writes into buffer dst.
+func appendUint64(dst []byte, n uint64, neg bool) []byte {
+ // Small numbers are fine as is
+ if n < 100000 {
+ if neg {
+ return strconv.AppendInt(dst, -int64(n), 10)
+ } else {
+ return strconv.AppendInt(dst, int64(n), 10)
+ }
+ }
+ // Large numbers should be split
+ const maxLength = 26
+
+ var (
+ out = make([]byte, maxLength)
+ i = maxLength - 1
+ comma = 0
+ )
+ for ; n > 0; i-- {
+ if comma == 3 {
+ comma = 0
+ out[i] = ','
+ } else {
+ comma++
+ out[i] = '0' + byte(n%10)
+ n /= 10
+ }
+ }
+ if neg {
+ out[i] = '-'
+ i--
+ }
+ return append(dst, out[i+1:]...)
+}
+
+// FormatLogfmtUint64 formats n with thousand separators.
+func FormatLogfmtUint64(n uint64) string {
+ return string(appendUint64(nil, n, false))
+}
+
+// appendBigInt formats n with thousand separators and writes to dst.
+func appendBigInt(dst []byte, n *big.Int) []byte {
+ if n.IsUint64() {
+ return appendUint64(dst, n.Uint64(), false)
+ }
+ if n.IsInt64() {
+ return appendInt64(dst, n.Int64())
+ }
+
+ var (
+ text = n.String()
+ buf = make([]byte, len(text)+len(text)/3)
+ comma = 0
+ i = len(buf) - 1
+ )
+ for j := len(text) - 1; j >= 0; j, i = j-1, i-1 {
+ c := text[j]
+
+ switch {
+ case c == '-':
+ buf[i] = c
+ case comma == 3:
+ buf[i] = ','
+ i--
+ comma = 0
+ fallthrough
+ default:
+ buf[i] = c
+ comma++
+ }
+ }
+ return append(dst, buf[i+1:]...)
+}
+
+// appendU256 formats n with thousand separators.
+func appendU256(dst []byte, n *uint256.Int) []byte {
+ if n.IsUint64() {
+ return appendUint64(dst, n.Uint64(), false)
+ }
+ res := []byte(n.PrettyDec(','))
+ return append(dst, res...)
+}
+
+// appendEscapeString writes the string s to the given writer, with
+// escaping/quoting if needed.
+func appendEscapeString(dst []byte, s string) []byte {
+ needsQuoting := false
+ needsEscaping := false
for _, r := range s {
- if r <= ' ' || r == '=' || r == '"' {
- needsQuotes = true
+ // If it contains spaces or equal-sign, we need to quote it.
+ if r == ' ' || r == '=' {
+ needsQuoting = true
+ continue
}
- if r == '\\' || r == '"' || r == '\n' || r == '\r' || r == '\t' {
- needsEscape = true
+ // We need to escape it, if it contains
+ // - character " (0x22) and lower (except space)
+ // - characters above ~ (0x7E), plus equal-sign
+ if r <= '"' || r > '~' {
+ needsEscaping = true
+ break
}
}
- if !needsEscape && !needsQuotes {
+ if needsEscaping {
+ return strconv.AppendQuote(dst, s)
+ }
+ // No escaping needed, but we might have to place within quote-marks, in case
+ // it contained a space
+ if needsQuoting {
+ dst = append(dst, '"')
+ dst = append(dst, []byte(s)...)
+ return append(dst, '"')
+ }
+ return append(dst, []byte(s)...)
+}
+
+// escapeMessage checks if the provided string needs escaping/quoting, similarly
+// to escapeString. The difference is that this method is more lenient: it allows
+// for spaces and linebreaks to occur without needing quoting.
+func escapeMessage(s string) string {
+ needsQuoting := false
+ for _, r := range s {
+ // Allow CR/LF/TAB. This is to make multi-line messages work.
+ if r == '\r' || r == '\n' || r == '\t' {
+ continue
+ }
+ // We quote everything below (0x20) and above~ (0x7E),
+ // plus equal-sign
+ if r < ' ' || r > '~' || r == '=' {
+ needsQuoting = true
+ break
+ }
+ }
+ if !needsQuoting {
return s
}
- e := stringBufPool.Get().(*bytes.Buffer)
- e.WriteByte('"')
- for _, r := range s {
- switch r {
- case '\\', '"':
- e.WriteByte('\\')
- e.WriteByte(byte(r))
- case '\n':
- e.WriteString("\\n")
- case '\r':
- e.WriteString("\\r")
- case '\t':
- e.WriteString("\\t")
- default:
- e.WriteRune(r)
- }
- }
- e.WriteByte('"')
- var ret string
- if needsQuotes {
- ret = e.String()
- } else {
- ret = string(e.Bytes()[1 : e.Len()-1])
- }
- e.Reset()
- stringBufPool.Put(e)
- return ret
+ return strconv.Quote(s)
+}
+
+// writeTimeTermFormat writes on the format "01-02|15:04:05.000"
+func writeTimeTermFormat(buf *bytes.Buffer, t time.Time) {
+ _, month, day := t.Date()
+ writePosIntWidth(buf, int(month), 2)
+ buf.WriteByte('-')
+ writePosIntWidth(buf, day, 2)
+ buf.WriteByte('|')
+ hour, min, sec := t.Clock()
+ writePosIntWidth(buf, hour, 2)
+ buf.WriteByte(':')
+ writePosIntWidth(buf, min, 2)
+ buf.WriteByte(':')
+ writePosIntWidth(buf, sec, 2)
+ ns := t.Nanosecond()
+ buf.WriteByte('.')
+ writePosIntWidth(buf, ns/1e6, 3)
+}
+
+// writePosIntWidth writes non-negative integer i to the buffer, padded on the left
+// by zeroes to the given width. Use a width of 0 to omit padding.
+// Adapted from pkg.go.dev/log/slog/internal/buffer
+func writePosIntWidth(b *bytes.Buffer, i, width int) {
+ // Cheap integer to fixed-width decimal ASCII.
+ // Copied from log/log.go.
+ if i < 0 {
+ panic("negative int")
+ }
+ // Assemble decimal in reverse order.
+ var bb [20]byte
+ bp := len(bb) - 1
+ for i >= 10 || width > 1 {
+ width--
+ q := i / 10
+ bb[bp] = byte('0' + i - q*10)
+ bp--
+ i = q
+ }
+ // i < 10
+ bb[bp] = byte('0' + i)
+ b.Write(bb[bp:])
}
diff --git a/log/format_test.go b/log/format_test.go
new file mode 100644
index 0000000000..d4c1df4abc
--- /dev/null
+++ b/log/format_test.go
@@ -0,0 +1,24 @@
+package log
+
+import (
+ "math/rand"
+ "testing"
+)
+
+var sink []byte
+
+func BenchmarkPrettyInt64Logfmt(b *testing.B) {
+ buf := make([]byte, 100)
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink = appendInt64(buf, rand.Int63())
+ }
+}
+
+func BenchmarkPrettyUint64Logfmt(b *testing.B) {
+ buf := make([]byte, 100)
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ sink = appendUint64(buf, rand.Uint64(), false)
+ }
+}
diff --git a/log/handler.go b/log/handler.go
index d5594b853d..56eff6671f 100644
--- a/log/handler.go
+++ b/log/handler.go
@@ -1,361 +1,199 @@
package log
import (
+ "context"
"fmt"
"io"
- "net"
- "os"
+ "log/slog"
+ "math/big"
"reflect"
"sync"
+ "time"
- "github.com/go-stack/stack"
+ "github.com/holiman/uint256"
)
-// A Logger prints its log records by writing to a Handler.
-// The Handler interface defines where and how log records are written.
-// Handlers are composable, providing you great flexibility in combining
-// them to achieve the logging structure that suits your applications.
-type Handler interface {
- Log(r *Record) error
+type discardHandler struct{}
+
+// DiscardHandler returns a no-op handler
+func DiscardHandler() slog.Handler {
+ return &discardHandler{}
}
-// FuncHandler returns a Handler that logs records with the given
-// function.
-func FuncHandler(fn func(r *Record) error) Handler {
- return funcHandler(fn)
+func (h *discardHandler) Handle(_ context.Context, r slog.Record) error {
+ return nil
}
-type funcHandler func(r *Record) error
-
-func (h funcHandler) Log(r *Record) error {
- return h(r)
+func (h *discardHandler) Enabled(_ context.Context, level slog.Level) bool {
+ return false
}
-// StreamHandler writes log records to an io.Writer
-// with the given format. StreamHandler can be used
-// to easily begin writing log records to other
-// outputs.
+func (h *discardHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
+}
+
+func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return &discardHandler{}
+}
+
+type TerminalHandler struct {
+ mu sync.Mutex
+ wr io.Writer
+ lvl slog.Level
+ useColor bool
+ attrs []slog.Attr
+ // fieldPadding is a map with maximum field value lengths seen until now
+ // to allow padding log contexts in a bit smarter way.
+ fieldPadding map[string]int
+
+ buf []byte
+}
+
+// NewTerminalHandler returns a handler which formats log records at all levels optimized for human readability on
+// a terminal with color-coded level output and terser human friendly timestamp.
+// This format should only be used for interactive programs or while developing.
//
-// StreamHandler wraps itself with LazyHandler and SyncHandler
-// to evaluate Lazy objects and perform safe concurrent writes.
-func StreamHandler(wr io.Writer, fmtr Format) Handler {
- h := FuncHandler(func(r *Record) error {
- _, err := wr.Write(fmtr.Format(r))
- return err
- })
- return LazyHandler(SyncHandler(h))
+// [LEVEL] [TIME] MESSAGE key=value key=value ...
+//
+// Example:
+//
+// [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002
+func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler {
+ return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor)
}
-// SyncHandler can be wrapped around a handler to guarantee that
-// only a single Log operation can proceed at a time. It's necessary
-// for thread-safe concurrent writes.
-func SyncHandler(h Handler) Handler {
- var mu sync.Mutex
- return FuncHandler(func(r *Record) error {
- defer mu.Unlock()
- mu.Lock()
- return h.Log(r)
- })
-}
-
-// FileHandler returns a handler which writes log records to the give file
-// using the given format. If the path
-// already exists, FileHandler will append to the given file. If it does not,
-// FileHandler will create the file with mode 0644.
-func FileHandler(path string, fmtr Format) (Handler, error) {
- f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
- return nil, err
+// NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs
+// records which are less than or equal to the specified verbosity level.
+func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler {
+ return &TerminalHandler{
+ wr: wr,
+ lvl: lvl,
+ useColor: useColor,
+ fieldPadding: make(map[string]int),
}
- return closingHandler{f, StreamHandler(f, fmtr)}, nil
}
-// NetHandler opens a socket to the given address and writes records
-// over the connection.
-func NetHandler(network, addr string, fmtr Format) (Handler, error) {
- conn, err := net.Dial(network, addr)
- if err != nil {
- return nil, err
+func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ buf := h.format(h.buf, r, h.useColor)
+ h.wr.Write(buf)
+ h.buf = buf[:0]
+ return nil
+}
+
+func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool {
+ return level >= h.lvl
+}
+
+func (h *TerminalHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
+}
+
+func (h *TerminalHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return &TerminalHandler{
+ wr: h.wr,
+ lvl: h.lvl,
+ useColor: h.useColor,
+ attrs: append(h.attrs, attrs...),
+ fieldPadding: make(map[string]int),
}
-
- return closingHandler{conn, StreamHandler(conn, fmtr)}, nil
}
-// XXX: closingHandler is essentially unused at the moment
-// it's meant for a future time when the Handler interface supports
-// a possible Close() operation
-type closingHandler struct {
- io.WriteCloser
- Handler
+// ResetFieldPadding zeroes the field-padding for all attribute pairs.
+func (h *TerminalHandler) ResetFieldPadding() {
+ h.mu.Lock()
+ h.fieldPadding = make(map[string]int)
+ h.mu.Unlock()
}
-func (h *closingHandler) Close() error {
- return h.WriteCloser.Close()
+type leveler struct{ minLevel slog.Level }
+
+func (l *leveler) Level() slog.Level {
+ return l.minLevel
}
-// CallerFileHandler returns a Handler that adds the line number and file of
-// the calling function to the context with key "caller".
-func CallerFileHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "caller", fmt.Sprint(r.Call))
- return h.Log(r)
+// JSONHandler returns a handler which prints records in JSON format.
+func JSONHandler(wr io.Writer) slog.Handler {
+ return JSONHandlerWithLevel(wr, levelMaxVerbosity)
+}
+
+// JSONHandlerWithLevel returns a handler which prints records in JSON format that are less than or equal to
+// the specified verbosity level.
+func JSONHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
+ return slog.NewJSONHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceJSON,
+ Level: &leveler{level},
})
}
-// CallerFuncHandler returns a Handler that adds the calling function name to
-// the context with key "fn".
-func CallerFuncHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- r.Ctx = append(r.Ctx, "fn", formatCall("%+n", r.Call))
- return h.Log(r)
+// LogfmtHandler returns a handler which prints records in logfmt format, an easy machine-parseable but human-readable
+// format for key/value pairs.
+//
+// For more details see: http://godoc.org/github.com/kr/logfmt
+func LogfmtHandler(wr io.Writer) slog.Handler {
+ return slog.NewTextHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceLogfmt,
})
}
-// This function is here to please go vet on Go < 1.8.
-func formatCall(format string, c stack.Call) string {
- return fmt.Sprintf(format, c)
-}
-
-// CallerStackHandler returns a Handler that adds a stack trace to the context
-// with key "stack". The stack trace is formated as a space separated list of
-// call sites inside matching []'s. The most recent call site is listed first.
-// Each call site is formatted according to format. See the documentation of
-// package github.com/go-stack/stack for the list of supported formats.
-func CallerStackHandler(format string, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- s := stack.Trace().TrimBelow(r.Call).TrimRuntime()
- if len(s) > 0 {
- r.Ctx = append(r.Ctx, "stack", fmt.Sprintf(format, s))
- }
- return h.Log(r)
+// LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs
+// records which are less than or equal to the specified verbosity level.
+func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler {
+ return slog.NewTextHandler(wr, &slog.HandlerOptions{
+ ReplaceAttr: builtinReplaceLogfmt,
+ Level: &leveler{level},
})
}
-// FilterHandler returns a Handler that only writes records to the
-// wrapped Handler if the given function evaluates true. For example,
-// to only log records where the 'err' key is not nil:
-//
-// logger.SetHandler(FilterHandler(func(r *Record) bool {
-// for i := 0; i < len(r.Ctx); i += 2 {
-// if r.Ctx[i] == "err" {
-// return r.Ctx[i+1] != nil
-// }
-// }
-// return false
-// }, h))
-//
-func FilterHandler(fn func(r *Record) bool, h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- if fn(r) {
- return h.Log(r)
- }
- return nil
- })
+func builtinReplaceLogfmt(_ []string, attr slog.Attr) slog.Attr {
+ return builtinReplace(nil, attr, true)
}
-// MatchFilterHandler returns a Handler that only writes records
-// to the wrapped Handler if the given key in the logged
-// context matches the value. For example, to only log records
-// from your ui package:
-//
-// log.MatchFilterHandler("pkg", "app/ui", log.StdoutHandler)
-//
-func MatchFilterHandler(key string, value interface{}, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- switch key {
- case r.KeyNames.Lvl:
- return r.Lvl == value
- case r.KeyNames.Time:
- return r.Time == value
- case r.KeyNames.Msg:
- return r.Msg == value
- }
-
- for i := 0; i < len(r.Ctx); i += 2 {
- if r.Ctx[i] == key {
- return r.Ctx[i+1] == value
- }
- }
- return false
- }, h)
+func builtinReplaceJSON(_ []string, attr slog.Attr) slog.Attr {
+ return builtinReplace(nil, attr, false)
}
-// LvlFilterHandler returns a Handler that only writes
-// records which are less than the given verbosity
-// level to the wrapped Handler. For example, to only
-// log Error/Crit records:
-//
-// log.LvlFilterHandler(log.LvlError, log.StdoutHandler)
-//
-func LvlFilterHandler(maxLvl Lvl, h Handler) Handler {
- return FilterHandler(func(r *Record) (pass bool) {
- return r.Lvl <= maxLvl
- }, h)
-}
-
-// A MultiHandler dispatches any write to each of its handlers.
-// This is useful for writing different types of log information
-// to different locations. For example, to log to a file and
-// standard error:
-//
-// log.MultiHandler(
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StderrHandler)
-//
-func MultiHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- for _, h := range hs {
- // what to do about failures?
- h.Log(r)
- }
- return nil
- })
-}
-
-// A FailoverHandler writes all log records to the first handler
-// specified, but will failover and write to the second handler if
-// the first handler has failed, and so on for all handlers specified.
-// For example you might want to log to a network socket, but failover
-// to writing to a file if the network fails, and then to
-// standard out if the file write fails:
-//
-// log.FailoverHandler(
-// log.Must.NetHandler("tcp", ":9090", log.JsonFormat()),
-// log.Must.FileHandler("/var/log/app.log", log.LogfmtFormat()),
-// log.StdoutHandler)
-//
-// All writes that do not go to the first handler will add context with keys of
-// the form "failover_err_{idx}" which explain the error encountered while
-// trying to write to the handlers before them in the list.
-func FailoverHandler(hs ...Handler) Handler {
- return FuncHandler(func(r *Record) error {
- var err error
- for i, h := range hs {
- err = h.Log(r)
- if err == nil {
- return nil
+func builtinReplace(_ []string, attr slog.Attr, logfmt bool) slog.Attr {
+ switch attr.Key {
+ case slog.TimeKey:
+ if attr.Value.Kind() == slog.KindTime {
+ if logfmt {
+ return slog.String("t", attr.Value.Time().Format(timeFormat))
} else {
- r.Ctx = append(r.Ctx, fmt.Sprintf("failover_err_%d", i), err)
+ return slog.Attr{Key: "t", Value: attr.Value}
}
}
-
- return err
- })
-}
-
-// ChannelHandler writes all records to the given channel.
-// It blocks if the channel is full. Useful for async processing
-// of log messages, it's used by BufferedHandler.
-func ChannelHandler(recs chan<- *Record) Handler {
- return FuncHandler(func(r *Record) error {
- recs <- r
- return nil
- })
-}
-
-// BufferedHandler writes all records to a buffered
-// channel of the given size which flushes into the wrapped
-// handler whenever it is available for writing. Since these
-// writes happen asynchronously, all writes to a BufferedHandler
-// never return an error and any errors from the wrapped handler are ignored.
-func BufferedHandler(bufSize int, h Handler) Handler {
- recs := make(chan *Record, bufSize)
- go func() {
- for m := range recs {
- _ = h.Log(m)
+ case slog.LevelKey:
+ if l, ok := attr.Value.Any().(slog.Level); ok {
+ attr = slog.Any("lvl", LevelString(l))
+ return attr
}
- }()
- return ChannelHandler(recs)
-}
+ }
-// LazyHandler writes all values to the wrapped handler after evaluating
-// any lazy functions in the record's context. It is already wrapped
-// around StreamHandler and SyslogHandler in this library, you'll only need
-// it if you write your own Handler.
-func LazyHandler(h Handler) Handler {
- return FuncHandler(func(r *Record) error {
- // go through the values (odd indices) and reassign
- // the values of any lazy fn to the result of its execution
- hadErr := false
- for i := 1; i < len(r.Ctx); i += 2 {
- lz, ok := r.Ctx[i].(Lazy)
- if ok {
- v, err := evaluateLazy(lz)
- if err != nil {
- hadErr = true
- r.Ctx[i] = err
- } else {
- if cs, ok := v.(stack.CallStack); ok {
- v = cs.TrimBelow(r.Call).TrimRuntime()
- }
- r.Ctx[i] = v
- }
- }
+ switch v := attr.Value.Any().(type) {
+ case time.Time:
+ if logfmt {
+ attr = slog.String(attr.Key, v.Format(timeFormat))
}
-
- if hadErr {
- r.Ctx = append(r.Ctx, errorKey, "bad lazy")
+ case *big.Int:
+ if v == nil {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.String())
}
-
- return h.Log(r)
- })
-}
-
-func evaluateLazy(lz Lazy) (interface{}, error) {
- t := reflect.TypeOf(lz.Fn)
-
- if t.Kind() != reflect.Func {
- return nil, fmt.Errorf("INVALID_LAZY, not func: %+v", lz.Fn)
- }
-
- if t.NumIn() > 0 {
- return nil, fmt.Errorf("INVALID_LAZY, func takes args: %+v", lz.Fn)
- }
-
- if t.NumOut() == 0 {
- return nil, fmt.Errorf("INVALID_LAZY, no func return val: %+v", lz.Fn)
- }
-
- value := reflect.ValueOf(lz.Fn)
- results := value.Call([]reflect.Value{})
- if len(results) == 1 {
- return results[0].Interface(), nil
- } else {
- values := make([]interface{}, len(results))
- for i, v := range results {
- values[i] = v.Interface()
+ case *uint256.Int:
+ if v == nil {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.Dec())
+ }
+ case fmt.Stringer:
+ if v == nil || (reflect.ValueOf(v).Kind() == reflect.Pointer && reflect.ValueOf(v).IsNil()) {
+ attr.Value = slog.StringValue("")
+ } else {
+ attr.Value = slog.StringValue(v.String())
}
- return values, nil
}
-}
-
-// DiscardHandler reports success for all writes but does nothing.
-// It is useful for dynamically disabling logging at runtime via
-// a Logger's SetHandler method.
-func DiscardHandler() Handler {
- return FuncHandler(func(r *Record) error {
- return nil
- })
-}
-
-// The Must object provides the following Handler creation functions
-// which instead of returning an error parameter only return a Handler
-// and panic on failure: FileHandler, NetHandler, SyslogHandler, SyslogNetHandler
-var Must muster
-
-func must(h Handler, err error) Handler {
- if err != nil {
- panic(err)
- }
- return h
-}
-
-type muster struct{}
-
-func (m muster) FileHandler(path string, fmtr Format) Handler {
- return must(FileHandler(path, fmtr))
-}
-
-func (m muster) NetHandler(network, addr string, fmtr Format) Handler {
- return must(NetHandler(network, addr, fmtr))
+ return attr
}
diff --git a/log/handler_glog.go b/log/handler_glog.go
index f8b932fd1b..739f8c5b42 100644
--- a/log/handler_glog.go
+++ b/log/handler_glog.go
@@ -17,8 +17,11 @@
package log
import (
+ "context"
"errors"
"fmt"
+ "log/slog"
+ "maps"
"regexp"
"runtime"
"strconv"
@@ -30,28 +33,24 @@ import (
// errVmoduleSyntax is returned when a user vmodule pattern is invalid.
var errVmoduleSyntax = errors.New("expect comma-separated list of filename=N")
-// errTraceSyntax is returned when a user backtrace pattern is invalid.
-var errTraceSyntax = errors.New("expect file.go:234")
-
// GlogHandler is a log handler that mimics the filtering features of Google's
// glog logger: setting global log levels; overriding with callsite pattern
// matches; and requesting backtraces at certain positions.
type GlogHandler struct {
- origin Handler // The origin handler this wraps
+ origin slog.Handler // The origin handler this wraps
- level uint32 // Current log level, atomically accessible
- override uint32 // Flag whether overrides are used, atomically accessible
- backtrace uint32 // Flag whether backtrace location is set
+ level atomic.Int32 // Current log level, atomically accessible
+ override atomic.Bool // Flag whether overrides are used, atomically accessible
- patterns []pattern // Current list of patterns to override with
- siteCache map[uintptr]Lvl // Cache of callsite pattern evaluations
- location string // file:line location where to do a stackdump at
- lock sync.RWMutex // Lock protecting the override pattern list
+ patterns []pattern // Current list of patterns to override with
+ siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations
+ location string // file:line location where to do a stackdump at
+ lock sync.RWMutex // Lock protecting the override pattern list
}
// NewGlogHandler creates a new log handler with filtering functionality similar
// to Google's glog logger. The returned handler implements Handler.
-func NewGlogHandler(h Handler) *GlogHandler {
+func NewGlogHandler(h slog.Handler) *GlogHandler {
return &GlogHandler{
origin: h,
}
@@ -61,13 +60,13 @@ func NewGlogHandler(h Handler) *GlogHandler {
// and a file pattern to match.
type pattern struct {
pattern *regexp.Regexp
- level Lvl
+ level slog.Level
}
// Verbosity sets the glog verbosity ceiling. The verbosity of individual packages
// and source files can be raised using Vmodule.
-func (h *GlogHandler) Verbosity(level Lvl) {
- atomic.StoreUint32(&h.level, uint32(level))
+func (h *GlogHandler) Verbosity(level slog.Level) {
+ h.level.Store(int32(level))
}
// Vmodule sets the glog verbosity pattern.
@@ -77,14 +76,14 @@ func (h *GlogHandler) Verbosity(level Lvl) {
//
// For instance:
//
-// pattern="gopher.go=3"
-// sets the V level to 3 in all Go files named "gopher.go"
+// pattern="gopher.go=3"
+// sets the V level to 3 in all Go files named "gopher.go"
//
-// pattern="foo=3"
-// sets V to 3 in all files of any packages whose import path ends in "foo"
+// pattern="foo=3"
+// sets V to 3 in all files of any packages whose import path ends in "foo"
//
-// pattern="foo/*=3"
-// sets V to 3 in all files of any packages whose import path contains "foo"
+// pattern="foo/*=3"
+// sets V to 3 in all files of any packages whose import path contains "foo"
func (h *GlogHandler) Vmodule(ruleset string) error {
var filter []pattern
for _, rule := range strings.Split(ruleset, ",") {
@@ -103,11 +102,13 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
return errVmoduleSyntax
}
// Parse the level and if correct, assemble the filter rule
- level, err := strconv.Atoi(parts[1])
+ l, err := strconv.Atoi(parts[1])
if err != nil {
return errVmoduleSyntax
}
- if level <= 0 {
+ level := FromLegacyLevel(l)
+
+ if level == LevelCrit {
continue // Ignore. It's harmless but no point in paying the overhead.
}
// Compile the rule pattern into a regular expression
@@ -125,103 +126,89 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
matcher = matcher + "$"
re, _ := regexp.Compile(matcher)
- filter = append(filter, pattern{re, Lvl(level)})
+ filter = append(filter, pattern{re, level})
}
// Swap out the vmodule pattern for the new filter system
h.lock.Lock()
defer h.lock.Unlock()
h.patterns = filter
- h.siteCache = make(map[uintptr]Lvl)
- atomic.StoreUint32(&h.override, uint32(len(filter)))
+ h.siteCache = make(map[uintptr]slog.Level)
+ h.override.Store(len(filter) != 0)
return nil
}
-// BacktraceAt sets the glog backtrace location. When set to a file and line
-// number holding a logging statement, a stack trace will be written to the Info
-// log whenever execution hits that statement.
+// Enabled implements slog.Handler, reporting whether the handler handles records
+// at the given level.
+func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
+ // fast-track skipping logging if override not enabled and the provided verbosity is above configured
+ return h.override.Load() || slog.Level(h.level.Load()) <= lvl
+}
+
+// WithAttrs implements slog.Handler, returning a new Handler whose attributes
+// consist of both the receiver's attributes and the arguments.
+func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ h.lock.RLock()
+ siteCache := maps.Clone(h.siteCache)
+ h.lock.RUnlock()
+
+ patterns := []pattern{}
+ patterns = append(patterns, h.patterns...)
+
+ res := GlogHandler{
+ origin: h.origin.WithAttrs(attrs),
+ patterns: patterns,
+ siteCache: siteCache,
+ location: h.location,
+ }
+
+ res.level.Store(h.level.Load())
+ res.override.Store(h.override.Load())
+ return &res
+}
+
+// WithGroup implements slog.Handler, returning a new Handler with the given
+// group appended to the receiver's existing groups.
//
-// Unlike with Vmodule, the ".go" must be present.
-func (h *GlogHandler) BacktraceAt(location string) error {
- // Ensure the backtrace location contains two non-empty elements
- parts := strings.Split(location, ":")
- if len(parts) != 2 {
- return errTraceSyntax
- }
- parts[0] = strings.TrimSpace(parts[0])
- parts[1] = strings.TrimSpace(parts[1])
- if len(parts[0]) == 0 || len(parts[1]) == 0 {
- return errTraceSyntax
- }
- // Ensure the .go prefix is present and the line is valid
- if !strings.HasSuffix(parts[0], ".go") {
- return errTraceSyntax
- }
- if _, err := strconv.Atoi(parts[1]); err != nil {
- return errTraceSyntax
- }
- // All seems valid
- h.lock.Lock()
- defer h.lock.Unlock()
-
- h.location = location
- atomic.StoreUint32(&h.backtrace, uint32(len(location)))
-
- return nil
+// Note, this function is not implemented.
+func (h *GlogHandler) WithGroup(name string) slog.Handler {
+ panic("not implemented")
}
-// Log implements Handler.Log, filtering a log record through the global, local
-// and backtrace filters, finally emitting it if either allow it through.
-func (h *GlogHandler) Log(r *Record) error {
- // If backtracing is requested, check whether this is the callsite
- if atomic.LoadUint32(&h.backtrace) > 0 {
- // Everything below here is slow. Although we could cache the call sites the
- // same way as for vmodule, backtracing is so rare it's not worth the extra
- // complexity.
- h.lock.RLock()
- match := h.location == r.Call.String()
- h.lock.RUnlock()
-
- if match {
- // Callsite matched, raise the log level to info and gather the stacks
- r.Lvl = LvlInfo
-
- buf := make([]byte, 1024*1024)
- buf = buf[:runtime.Stack(buf, true)]
- r.Msg += "\n\n" + string(buf)
- }
- }
+// Handle implements slog.Handler, filtering a log record through the global,
+// local and backtrace filters, finally emitting it if either allow it through.
+func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
// If the global log level allows, fast track logging
- if atomic.LoadUint32(&h.level) >= uint32(r.Lvl) {
- return h.origin.Log(r)
- }
- // If no local overrides are present, fast track skipping
- if atomic.LoadUint32(&h.override) == 0 {
- return nil
+ if slog.Level(h.level.Load()) <= r.Level {
+ return h.origin.Handle(context.Background(), r)
}
+
// Check callsite cache for previously calculated log levels
h.lock.RLock()
- lvl, ok := h.siteCache[r.Call.PC()]
+ lvl, ok := h.siteCache[r.PC]
h.lock.RUnlock()
// If we didn't cache the callsite yet, calculate it
if !ok {
h.lock.Lock()
+
+ fs := runtime.CallersFrames([]uintptr{r.PC})
+ frame, _ := fs.Next()
+
for _, rule := range h.patterns {
- if rule.pattern.MatchString(fmt.Sprintf("%+s", r.Call)) {
- h.siteCache[r.Call.PC()], lvl, ok = rule.level, rule.level, true
- break
+ if rule.pattern.MatchString(fmt.Sprintf("+%s", frame.File)) {
+ h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true
}
}
// If no rule matched, remember to drop log the next time
if !ok {
- h.siteCache[r.Call.PC()] = 0
+ h.siteCache[r.PC] = 0
}
h.lock.Unlock()
}
- if lvl >= r.Lvl {
- return h.origin.Log(r)
+ if lvl <= r.Level {
+ return h.origin.Handle(context.Background(), r)
}
return nil
}
diff --git a/log/handler_go13.go b/log/handler_go13.go
deleted file mode 100644
index 0843ed0e5f..0000000000
--- a/log/handler_go13.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build !go1.4
-
-package log
-
-import (
- "sync/atomic"
- "unsafe"
-)
-
-// swapHandler wraps another handler that may be swapped out
-// dynamically at runtime in a thread-safe fashion.
-type swapHandler struct {
- handler unsafe.Pointer
-}
-
-func (h *swapHandler) Log(r *Record) error {
- return h.Get().Log(r)
-}
-
-func (h *swapHandler) Get() Handler {
- return *(*Handler)(atomic.LoadPointer(&h.handler))
-}
-
-func (h *swapHandler) Swap(newHandler Handler) {
- atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler))
-}
diff --git a/log/handler_go14.go b/log/handler_go14.go
deleted file mode 100644
index 05dedbf2a7..0000000000
--- a/log/handler_go14.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// +build go1.4
-
-package log
-
-import "sync/atomic"
-
-// swapHandler wraps another handler that may be swapped out
-// dynamically at runtime in a thread-safe fashion.
-type swapHandler struct {
- handler atomic.Value
-}
-
-func (h *swapHandler) Log(r *Record) error {
- return (*h.handler.Load().(*Handler)).Log(r)
-}
-
-func (h *swapHandler) Swap(newHandler Handler) {
- h.handler.Store(&newHandler)
-}
-
-func (h *swapHandler) Get() Handler {
- return *h.handler.Load().(*Handler)
-}
diff --git a/log/logger.go b/log/logger.go
index 15c83a9b25..016856c834 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -1,240 +1,216 @@
package log
import (
- "fmt"
+ "context"
+ "log/slog"
+ "math"
"os"
+ "runtime"
"time"
-
- "github.com/go-stack/stack"
)
-const timeKey = "t"
-const lvlKey = "lvl"
-const msgKey = "msg"
-const errorKey = "LOG15_ERROR"
-
-type Lvl int
+const errorKey = "LOG_ERROR"
const (
- LvlCrit Lvl = iota
- LvlError
- LvlWarn
- LvlInfo
- LvlDebug
- LvlTrace
+ legacyLevelCrit = iota
+ legacyLevelError
+ legacyLevelWarn
+ legacyLevelInfo
+ legacyLevelDebug
+ legacyLevelTrace
)
-// Aligned returns a 5-character string containing the name of a Lvl.
-func (l Lvl) AlignedString() string {
+const (
+ levelMaxVerbosity slog.Level = math.MinInt
+ LevelTrace slog.Level = -8
+ LevelDebug = slog.LevelDebug
+ LevelInfo = slog.LevelInfo
+ LevelWarn = slog.LevelWarn
+ LevelError = slog.LevelError
+ LevelCrit slog.Level = 12
+
+ // for backward-compatibility
+ LvlTrace = LevelTrace
+ LvlInfo = LevelInfo
+ LvlDebug = LevelDebug
+)
+
+// FromLegacyLevel converts from old Geth verbosity level constants
+// to levels defined by slog
+func FromLegacyLevel(lvl int) slog.Level {
+ switch lvl {
+ case legacyLevelCrit:
+ return LevelCrit
+ case legacyLevelError:
+ return slog.LevelError
+ case legacyLevelWarn:
+ return slog.LevelWarn
+ case legacyLevelInfo:
+ return slog.LevelInfo
+ case legacyLevelDebug:
+ return slog.LevelDebug
+ case legacyLevelTrace:
+ return LevelTrace
+ default:
+ break
+ }
+
+ // TODO: should we allow use of custom levels or force them to match existing max/min if they fall outside the range as I am doing here?
+ if lvl > legacyLevelTrace {
+ return LevelTrace
+ }
+ return LevelCrit
+}
+
+// LevelAlignedString returns a 5-character string containing the name of a Lvl.
+func LevelAlignedString(l slog.Level) string {
switch l {
- case LvlTrace:
+ case LevelTrace:
return "TRACE"
- case LvlDebug:
+ case slog.LevelDebug:
return "DEBUG"
- case LvlInfo:
+ case slog.LevelInfo:
return "INFO "
- case LvlWarn:
+ case slog.LevelWarn:
return "WARN "
- case LvlError:
+ case slog.LevelError:
return "ERROR"
- case LvlCrit:
+ case LevelCrit:
return "CRIT "
default:
- panic("bad level")
+ return "unknown level"
}
}
-// Strings returns the name of a Lvl.
-func (l Lvl) String() string {
+// LevelString returns a string containing the name of a Lvl.
+func LevelString(l slog.Level) string {
switch l {
- case LvlTrace:
- return "trce"
- case LvlDebug:
- return "dbug"
- case LvlInfo:
+ case LevelTrace:
+ return "trace"
+ case slog.LevelDebug:
+ return "debug"
+ case slog.LevelInfo:
return "info"
- case LvlWarn:
+ case slog.LevelWarn:
return "warn"
- case LvlError:
- return "eror"
- case LvlCrit:
+ case slog.LevelError:
+ return "error"
+ case LevelCrit:
return "crit"
default:
- panic("bad level")
+ return "unknown"
}
}
-// Returns the appropriate Lvl from a string name.
-// Useful for parsing command line args and configuration files.
-func LvlFromString(lvlString string) (Lvl, error) {
- switch lvlString {
- case "trace", "trce":
- return LvlTrace, nil
- case "debug", "dbug":
- return LvlDebug, nil
- case "info":
- return LvlInfo, nil
- case "warn":
- return LvlWarn, nil
- case "error", "eror":
- return LvlError, nil
- case "crit":
- return LvlCrit, nil
- default:
- return LvlDebug, fmt.Errorf("Unknown level: %v", lvlString)
- }
-}
-
-// A Record is what a Logger asks its handler to write
-type Record struct {
- Time time.Time
- Lvl Lvl
- Msg string
- Ctx []interface{}
- Call stack.Call
- KeyNames RecordKeyNames
-}
-
-type RecordKeyNames struct {
- Time string
- Msg string
- Lvl string
-}
-
// A Logger writes key/value pairs to a Handler
type Logger interface {
- // New returns a new Logger that has this logger's context plus the given context
+ // With returns a new Logger that has this logger's attributes plus the given attributes
+ With(ctx ...interface{}) Logger
+
+ // New returns a new Logger that has this logger's attributes plus the given attributes. Identical to 'With'.
New(ctx ...interface{}) Logger
- // GetHandler gets the handler associated with the logger.
- GetHandler() Handler
+ // Log logs a message at the specified level with context key/value pairs
+ Log(level slog.Level, msg string, ctx ...interface{})
- // SetHandler updates the logger to write records to the specified handler.
- SetHandler(h Handler)
-
- // Log a message at the given level with context key/value pairs
+ // Trace log a message at the trace level with context key/value pairs
Trace(msg string, ctx ...interface{})
+
+ // Debug logs a message at the debug level with context key/value pairs
Debug(msg string, ctx ...interface{})
+
+ // Info logs a message at the info level with context key/value pairs
Info(msg string, ctx ...interface{})
+
+ // Warn logs a message at the warn level with context key/value pairs
Warn(msg string, ctx ...interface{})
+
+ // Error logs a message at the error level with context key/value pairs
Error(msg string, ctx ...interface{})
+
+ // Crit logs a message at the crit level with context key/value pairs, and exits
Crit(msg string, ctx ...interface{})
+
+ // Write logs a message at the specified level
+ Write(level slog.Level, msg string, attrs ...any)
+
+ // Enabled reports whether l emits log records at the given context and level.
+ Enabled(ctx context.Context, level slog.Level) bool
+
+ // Handler returns the underlying handler of the inner logger.
+ Handler() slog.Handler
}
type logger struct {
- ctx []interface{}
- h *swapHandler
+ inner *slog.Logger
}
-func (l *logger) write(msg string, lvl Lvl, ctx []interface{}) {
- l.h.Log(&Record{
- Time: time.Now(),
- Lvl: lvl,
- Msg: msg,
- Ctx: newContext(l.ctx, ctx),
- Call: stack.Caller(2),
- KeyNames: RecordKeyNames{
- Time: timeKey,
- Msg: msgKey,
- Lvl: lvlKey,
- },
- })
+// NewLogger returns a logger with the specified handler set
+func NewLogger(h slog.Handler) Logger {
+ return &logger{
+ slog.New(h),
+ }
+}
+
+func (l *logger) Handler() slog.Handler {
+ return l.inner.Handler()
+}
+
+// Write logs a message at the specified level.
+func (l *logger) Write(level slog.Level, msg string, attrs ...any) {
+ if !l.inner.Enabled(context.Background(), level) {
+ return
+ }
+
+ var pcs [1]uintptr
+ runtime.Callers(3, pcs[:])
+
+ if len(attrs)%2 != 0 {
+ attrs = append(attrs, nil, errorKey, "Normalized odd number of arguments by adding nil")
+ }
+ r := slog.NewRecord(time.Now(), level, msg, pcs[0])
+ r.Add(attrs...)
+ l.inner.Handler().Handle(context.Background(), r)
+}
+
+func (l *logger) Log(level slog.Level, msg string, attrs ...any) {
+ l.Write(level, msg, attrs...)
+}
+
+func (l *logger) With(ctx ...interface{}) Logger {
+ return &logger{l.inner.With(ctx...)}
}
func (l *logger) New(ctx ...interface{}) Logger {
- child := &logger{newContext(l.ctx, ctx), new(swapHandler)}
- child.SetHandler(l.h)
- return child
+ return l.With(ctx...)
}
-func newContext(prefix []interface{}, suffix []interface{}) []interface{} {
- normalizedSuffix := normalize(suffix)
- newCtx := make([]interface{}, len(prefix)+len(normalizedSuffix))
- n := copy(newCtx, prefix)
- copy(newCtx[n:], normalizedSuffix)
- return newCtx
+// Enabled reports whether l emits log records at the given context and level.
+func (l *logger) Enabled(ctx context.Context, level slog.Level) bool {
+ return l.inner.Enabled(ctx, level)
}
func (l *logger) Trace(msg string, ctx ...interface{}) {
- l.write(msg, LvlTrace, ctx)
+ l.Write(LevelTrace, msg, ctx...)
}
func (l *logger) Debug(msg string, ctx ...interface{}) {
- l.write(msg, LvlDebug, ctx)
+ l.Write(slog.LevelDebug, msg, ctx...)
}
func (l *logger) Info(msg string, ctx ...interface{}) {
- l.write(msg, LvlInfo, ctx)
+ l.Write(slog.LevelInfo, msg, ctx...)
}
-func (l *logger) Warn(msg string, ctx ...interface{}) {
- l.write(msg, LvlWarn, ctx)
+func (l *logger) Warn(msg string, ctx ...any) {
+ l.Write(slog.LevelWarn, msg, ctx...)
}
func (l *logger) Error(msg string, ctx ...interface{}) {
- l.write(msg, LvlError, ctx)
+ l.Write(slog.LevelError, msg, ctx...)
}
func (l *logger) Crit(msg string, ctx ...interface{}) {
- l.write(msg, LvlCrit, ctx)
+ l.Write(LevelCrit, msg, ctx...)
os.Exit(1)
}
-
-func (l *logger) GetHandler() Handler {
- return l.h.Get()
-}
-
-func (l *logger) SetHandler(h Handler) {
- l.h.Swap(h)
-}
-
-func normalize(ctx []interface{}) []interface{} {
- // if the caller passed a Ctx object, then expand it
- if len(ctx) == 1 {
- if ctxMap, ok := ctx[0].(Ctx); ok {
- ctx = ctxMap.toArray()
- }
- }
-
- // ctx needs to be even because it's a series of key/value pairs
- // no one wants to check for errors on logging functions,
- // so instead of erroring on bad input, we'll just make sure
- // that things are the right length and users can fix bugs
- // when they see the output looks wrong
- if len(ctx)%2 != 0 {
- ctx = append(ctx, nil, errorKey, "Normalized odd number of arguments by adding nil")
- }
-
- return ctx
-}
-
-// Lazy allows you to defer calculation of a logged value that is expensive
-// to compute until it is certain that it must be evaluated with the given filters.
-//
-// Lazy may also be used in conjunction with a Logger's New() function
-// to generate a child logger which always reports the current value of changing
-// state.
-//
-// You may wrap any function which takes no arguments to Lazy. It may return any
-// number of values of any type.
-type Lazy struct {
- Fn interface{}
-}
-
-// Ctx is a map of key/value pairs to pass as context to a log function
-// Use this only if you really need greater safety around the arguments you pass
-// to the logging functions.
-type Ctx map[string]interface{}
-
-func (c Ctx) toArray() []interface{} {
- arr := make([]interface{}, len(c)*2)
-
- i := 0
- for k, v := range c {
- arr[i] = k
- arr[i+1] = v
- i += 2
- }
-
- return arr
-}
diff --git a/log/logger_test.go b/log/logger_test.go
new file mode 100644
index 0000000000..3ec6d2e19c
--- /dev/null
+++ b/log/logger_test.go
@@ -0,0 +1,191 @@
+package log
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "log/slog"
+ "math/big"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/holiman/uint256"
+)
+
+// TestLoggingWithVmodule checks that vmodule works.
+func TestLoggingWithVmodule(t *testing.T) {
+ out := new(bytes.Buffer)
+ glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false))
+ glog.Verbosity(LevelCrit)
+ logger := NewLogger(glog)
+ logger.Warn("This should not be seen", "ignored", "true")
+ glog.Vmodule("logger_test.go=5")
+ logger.Trace("a message", "foo", "bar")
+ have := out.String()
+ // The timestamp is locale-dependent, so we want to trim that off
+ // "INFO [01-01|00:00:00.000] a message ..." -> "a message..."
+ have = strings.Split(have, "]")[1]
+ want := " a message foo=bar\n"
+ if have != want {
+ t.Errorf("\nhave: %q\nwant: %q\n", have, want)
+ }
+}
+
+func TestTerminalHandlerWithAttrs(t *testing.T) {
+ out := new(bytes.Buffer)
+ glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}))
+ glog.Verbosity(LevelTrace)
+ logger := NewLogger(glog)
+ logger.Trace("a message", "foo", "bar")
+ have := out.String()
+ // The timestamp is locale-dependent, so we want to trim that off
+ // "INFO [01-01|00:00:00.000] a message ..." -> "a message..."
+ have = strings.Split(have, "]")[1]
+ want := " a message baz=bat foo=bar\n"
+ if have != want {
+ t.Errorf("\nhave: %q\nwant: %q\n", have, want)
+ }
+}
+
+// Make sure the default json handler outputs debug log lines
+func TestJSONHandler(t *testing.T) {
+ out := new(bytes.Buffer)
+ handler := JSONHandler(out)
+ logger := slog.New(handler)
+ logger.Debug("hi there")
+ if len(out.String()) == 0 {
+ t.Error("expected non-empty debug log output from default JSON Handler")
+ }
+
+ out.Reset()
+ handler = JSONHandlerWithLevel(out, slog.LevelInfo)
+ logger = slog.New(handler)
+ logger.Debug("hi there")
+ if len(out.String()) != 0 {
+ t.Errorf("expected empty debug log output, but got: %v", out.String())
+ }
+}
+
+func BenchmarkTraceLogging(b *testing.B) {
+ SetDefault(NewLogger(NewTerminalHandler(io.Discard, true)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Trace("a message", "v", i)
+ }
+}
+
+func BenchmarkTerminalHandler(b *testing.B) {
+ l := NewLogger(NewTerminalHandler(io.Discard, false))
+ benchmarkLogger(b, l)
+}
+func BenchmarkLogfmtHandler(b *testing.B) {
+ l := NewLogger(LogfmtHandler(io.Discard))
+ benchmarkLogger(b, l)
+}
+
+func BenchmarkJSONHandler(b *testing.B) {
+ l := NewLogger(JSONHandler(io.Discard))
+ benchmarkLogger(b, l)
+}
+
+func benchmarkLogger(b *testing.B, l Logger) {
+ var (
+ bb = make([]byte, 10)
+ tt = time.Now()
+ bigint = big.NewInt(100)
+ nilbig *big.Int
+ err = errors.New("oh nooes it's crap")
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ l.Info("This is a message",
+ "foo", int16(i),
+ "bytes", bb,
+ "bonk", "a string with text",
+ "time", tt,
+ "bigint", bigint,
+ "nilbig", nilbig,
+ "err", err)
+ }
+ b.StopTimer()
+}
+
+func TestLoggerOutput(t *testing.T) {
+ type custom struct {
+ A string
+ B int8
+ }
+ var (
+ customA = custom{"Foo", 12}
+ customB = custom{"Foo\nLinebreak", 122}
+ bb = make([]byte, 10)
+ tt = time.Time{}
+ bigint = big.NewInt(100)
+ nilbig *big.Int
+ err = errors.New("oh nooes it's crap")
+ smallUint = uint256.NewInt(500_000)
+ bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff}
+ )
+
+ out := new(bytes.Buffer)
+ glogHandler := NewGlogHandler(NewTerminalHandler(out, false))
+ glogHandler.Verbosity(LevelInfo)
+ NewLogger(glogHandler).Info("This is a message",
+ "foo", int16(123),
+ "bytes", bb,
+ "bonk", "a string with text",
+ "time", tt,
+ "bigint", bigint,
+ "nilbig", nilbig,
+ "err", err,
+ "struct", customA,
+ "struct", customB,
+ "ptrstruct", &customA,
+ "smalluint", smallUint,
+ "bigUint", bigUint)
+
+ have := out.String()
+ t.Logf("output %v", out.String())
+ want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig= err="oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
+`
+ if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) {
+ t.Errorf("Error\nhave: %q\nwant: %q", have, want)
+ }
+}
+
+const termTimeFormat = "01-02|15:04:05.000"
+
+func BenchmarkAppendFormat(b *testing.B) {
+ var now = time.Now()
+ b.Run("fmt time.Format", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat))
+ }
+ })
+ b.Run("time.AppendFormat", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ now.AppendFormat(nil, termTimeFormat)
+ }
+ })
+ var buf = new(bytes.Buffer)
+ b.Run("time.Custom", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ writeTimeTermFormat(buf, now)
+ buf.Reset()
+ }
+ })
+}
+
+func TestTermTimeFormat(t *testing.T) {
+ var now = time.Now()
+ want := now.AppendFormat(nil, termTimeFormat)
+ var b = new(bytes.Buffer)
+ writeTimeTermFormat(b, now)
+ have := b.Bytes()
+ if !bytes.Equal(have, want) {
+ t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want)
+ }
+}
diff --git a/log/root.go b/log/root.go
index 71b8cef6d4..aaf2fe56f6 100644
--- a/log/root.go
+++ b/log/root.go
@@ -1,61 +1,120 @@
package log
import (
+ "context"
+ "log/slog"
"os"
+ "sync/atomic"
)
-var (
- root = &logger{[]interface{}{}, new(swapHandler)}
- StdoutHandler = StreamHandler(os.Stdout, LogfmtFormat())
- StderrHandler = StreamHandler(os.Stderr, LogfmtFormat())
-)
+var root atomic.Value
func init() {
- root.SetHandler(DiscardHandler())
+ root.Store(&logger{slog.New(DiscardHandler())})
+}
+
+// SetDefault sets the default global logger
+func SetDefault(l Logger) {
+ root.Store(l)
+ if lg, ok := l.(*logger); ok {
+ slog.SetDefault(lg.inner)
+ }
+}
+
+// Root returns the root logger
+func Root() Logger {
+ return root.Load().(Logger)
+}
+
+// The following functions bypass the exported logger methods (logger.Debug,
+// etc.) to keep the call depth the same for all paths to logger.Write so
+// runtime.Caller(2) always refers to the call site in client code.
+
+// Trace is a convenient alias for Root().Trace
+//
+// Log a message at the trace level with context key/value pairs
+//
+// # Usage
+//
+// log.Trace("msg")
+// log.Trace("msg", "key1", val1)
+// log.Trace("msg", "key1", val1, "key2", val2)
+func Trace(msg string, ctx ...interface{}) {
+ Root().Write(LevelTrace, msg, ctx...)
+}
+
+// Debug is a convenient alias for Root().Debug
+//
+// Log a message at the debug level with context key/value pairs
+//
+// # Usage Examples
+//
+// log.Debug("msg")
+// log.Debug("msg", "key1", val1)
+// log.Debug("msg", "key1", val1, "key2", val2)
+func Debug(msg string, ctx ...interface{}) {
+ Root().Write(slog.LevelDebug, msg, ctx...)
+}
+
+// Info is a convenient alias for Root().Info
+//
+// Log a message at the info level with context key/value pairs
+//
+// # Usage Examples
+//
+// log.Info("msg")
+// log.Info("msg", "key1", val1)
+// log.Info("msg", "key1", val1, "key2", val2)
+func Info(msg string, ctx ...interface{}) {
+ Root().Write(slog.LevelInfo, msg, ctx...)
+}
+
+// Warn is a convenient alias for Root().Warn
+//
+// Log a message at the warn level with context key/value pairs
+//
+// # Usage Examples
+//
+// log.Warn("msg")
+// log.Warn("msg", "key1", val1)
+// log.Warn("msg", "key1", val1, "key2", val2)
+func Warn(msg string, ctx ...interface{}) {
+ Root().Write(slog.LevelWarn, msg, ctx...)
+}
+
+// Error is a convenient alias for Root().Error
+//
+// Log a message at the error level with context key/value pairs
+//
+// # Usage Examples
+//
+// log.Error("msg")
+// log.Error("msg", "key1", val1)
+// log.Error("msg", "key1", val1, "key2", val2)
+func Error(msg string, ctx ...interface{}) {
+ Root().Write(slog.LevelError, msg, ctx...)
+}
+
+// Crit is a convenient alias for Root().Crit
+//
+// Log a message at the crit level with context key/value pairs, and then exit.
+//
+// # Usage Examples
+//
+// log.Crit("msg")
+// log.Crit("msg", "key1", val1)
+// log.Crit("msg", "key1", val1, "key2", val2)
+func Crit(msg string, ctx ...interface{}) {
+ Root().Write(LevelCrit, msg, ctx...)
+ os.Exit(1)
}
// New returns a new logger with the given context.
// New is a convenient alias for Root().New
func New(ctx ...interface{}) Logger {
- return root.New(ctx...)
+ return Root().With(ctx...)
}
-// Root returns the root logger
-func Root() Logger {
- return root
-}
-
-// The following functions bypass the exported logger methods (logger.Debug,
-// etc.) to keep the call depth the same for all paths to logger.write so
-// runtime.Caller(2) always refers to the call site in client code.
-
-// Trace is a convenient alias for Root().Trace
-func Trace(msg string, ctx ...interface{}) {
- root.write(msg, LvlTrace, ctx)
-}
-
-// Debug is a convenient alias for Root().Debug
-func Debug(msg string, ctx ...interface{}) {
- root.write(msg, LvlDebug, ctx)
-}
-
-// Info is a convenient alias for Root().Info
-func Info(msg string, ctx ...interface{}) {
- root.write(msg, LvlInfo, ctx)
-}
-
-// Warn is a convenient alias for Root().Warn
-func Warn(msg string, ctx ...interface{}) {
- root.write(msg, LvlWarn, ctx)
-}
-
-// Error is a convenient alias for Root().Error
-func Error(msg string, ctx ...interface{}) {
- root.write(msg, LvlError, ctx)
-}
-
-// Crit is a convenient alias for Root().Crit
-func Crit(msg string, ctx ...interface{}) {
- root.write(msg, LvlCrit, ctx)
- os.Exit(1)
+func Enabled(level slog.Level) bool {
+ return Root().Enabled(context.Background(), level)
}
diff --git a/log/syslog.go b/log/syslog.go
deleted file mode 100644
index 71a17b30b3..0000000000
--- a/log/syslog.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// +build !windows,!plan9
-
-package log
-
-import (
- "log/syslog"
- "strings"
-)
-
-// SyslogHandler opens a connection to the system syslog daemon by calling
-// syslog.New and writes all records to it.
-func SyslogHandler(priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.New(priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-// SyslogNetHandler opens a connection to a log daemon over the network and writes
-// all log records to it.
-func SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) (Handler, error) {
- wr, err := syslog.Dial(net, addr, priority, tag)
- return sharedSyslog(fmtr, wr, err)
-}
-
-func sharedSyslog(fmtr Format, sysWr *syslog.Writer, err error) (Handler, error) {
- if err != nil {
- return nil, err
- }
- h := FuncHandler(func(r *Record) error {
- var syslogFn = sysWr.Info
- switch r.Lvl {
- case LvlCrit:
- syslogFn = sysWr.Crit
- case LvlError:
- syslogFn = sysWr.Err
- case LvlWarn:
- syslogFn = sysWr.Warning
- case LvlInfo:
- syslogFn = sysWr.Info
- case LvlDebug:
- syslogFn = sysWr.Debug
- case LvlTrace:
- syslogFn = func(m string) error { return nil } // There's no syslog level for trace
- }
-
- s := strings.TrimSpace(string(fmtr.Format(r)))
- return syslogFn(s)
- })
- return LazyHandler(&closingHandler{sysWr, h}), nil
-}
-
-func (m muster) SyslogHandler(priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogHandler(priority, tag, fmtr))
-}
-
-func (m muster) SyslogNetHandler(net, addr string, priority syslog.Priority, tag string, fmtr Format) Handler {
- return must(SyslogNetHandler(net, addr, priority, tag, fmtr))
-}
diff --git a/log/term/LICENSE b/log/term/LICENSE
deleted file mode 100644
index f090cb42f3..0000000000
--- a/log/term/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Simon Eskildsen
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/log/term/terminal_appengine.go b/log/term/terminal_appengine.go
deleted file mode 100644
index c1b5d2a3b1..0000000000
--- a/log/term/terminal_appengine.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build appengine
-
-package term
-
-// IsTty always returns false on AppEngine.
-func IsTty(fd uintptr) bool {
- return false
-}
diff --git a/log/term/terminal_darwin.go b/log/term/terminal_darwin.go
deleted file mode 100644
index d8f351b1b1..0000000000
--- a/log/term/terminal_darwin.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-// +build !appengine
-
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_freebsd.go b/log/term/terminal_freebsd.go
deleted file mode 100644
index cfaceab337..0000000000
--- a/log/term/terminal_freebsd.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package term
-
-import (
- "syscall"
-)
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-// Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
-type Termios struct {
- Iflag uint32
- Oflag uint32
- Cflag uint32
- Lflag uint32
- Cc [20]uint8
- Ispeed uint32
- Ospeed uint32
-}
diff --git a/log/term/terminal_linux.go b/log/term/terminal_linux.go
deleted file mode 100644
index 5290468d69..0000000000
--- a/log/term/terminal_linux.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !appengine
-
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TCGETS
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_netbsd.go b/log/term/terminal_netbsd.go
deleted file mode 100644
index f9bb9e1c23..0000000000
--- a/log/term/terminal_netbsd.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_notwindows.go b/log/term/terminal_notwindows.go
deleted file mode 100644
index c9af534f62..0000000000
--- a/log/term/terminal_notwindows.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build linux,!appengine darwin freebsd openbsd netbsd
-
-package term
-
-import (
- "syscall"
- "unsafe"
-)
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- var termios Termios
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
- return err == 0
-}
diff --git a/log/term/terminal_openbsd.go b/log/term/terminal_openbsd.go
deleted file mode 100644
index f9bb9e1c23..0000000000
--- a/log/term/terminal_openbsd.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package term
-
-import "syscall"
-
-const ioctlReadTermios = syscall.TIOCGETA
-
-type Termios syscall.Termios
diff --git a/log/term/terminal_solaris.go b/log/term/terminal_solaris.go
deleted file mode 100644
index 033c163246..0000000000
--- a/log/term/terminal_solaris.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package term
-
-import "golang.org/x/sys/unix"
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- _, err := unix.IoctlGetTermios(int(fd), unix.TCGETA)
- return err == nil
-}
diff --git a/log/term/terminal_windows.go b/log/term/terminal_windows.go
deleted file mode 100644
index df3c30c158..0000000000
--- a/log/term/terminal_windows.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Based on ssh/terminal:
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build windows
-
-package term
-
-import (
- "syscall"
- "unsafe"
-)
-
-var kernel32 = syscall.NewLazyDLL("kernel32.dll")
-
-var (
- procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
-)
-
-// IsTty returns true if the given file descriptor is a terminal.
-func IsTty(fd uintptr) bool {
- var st uint32
- r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
- return r != 0 && e == 0
-}
diff --git a/metrics/README.md b/metrics/README.md
index bc2a45a838..85b119470a 100644
--- a/metrics/README.md
+++ b/metrics/README.md
@@ -5,7 +5,7 @@ go-metrics
Go port of Coda Hale's Metrics library: .
-Documentation: .
+Documentation: .
Usage
-----
@@ -100,24 +100,6 @@ go influxdb.InfluxDB(metrics.DefaultRegistry,
)
```
-Periodically upload every metric to Librato using the [Librato client](https://github.com/mihasya/go-metrics-librato):
-
-**Note**: the client included with this repository under the `librato` package
-has been deprecated and moved to the repository linked above.
-
-```go
-import "github.com/mihasya/go-metrics-librato"
-
-go librato.Librato(metrics.DefaultRegistry,
- 10e9, // interval
- "example@example.com", // account owner email address
- "token", // Librato API token
- "hostname", // source
- []float64{0.95}, // percentiles to send
- time.Millisecond, // time unit
-)
-```
-
Periodically emit every metric to StatHat:
```go
@@ -128,7 +110,7 @@ go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com")
Maintain all metrics along with expvars at `/debug/metrics`:
-This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/)
+This uses the same mechanism as [the official expvar](https://golang.org/pkg/expvar/)
but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars
as well as all your go-metrics.
@@ -157,7 +139,6 @@ Publishing Metrics
Clients are available for the following destinations:
-* Librato - https://github.com/mihasya/go-metrics-librato
* Graphite - https://github.com/cyberdelia/go-metrics-graphite
* InfluxDB - https://github.com/vrischmann/go-metrics-influxdb
* Ganglia - https://github.com/appscode/metlia
diff --git a/metrics/config.go b/metrics/config.go
index 169c683a97..6acb985c16 100644
--- a/metrics/config.go
+++ b/metrics/config.go
@@ -19,9 +19,20 @@ package metrics
// Config contains the configuration for the metric collection.
type Config struct {
Enabled bool `toml:",omitempty"`
- EnabledExpensive bool `toml:",omitempty"`
+ EnabledExpensive bool `toml:"-"`
HTTP string `toml:",omitempty"`
Port int `toml:",omitempty"`
+ EnableInfluxDB bool `toml:",omitempty"`
+ InfluxDBEndpoint string `toml:",omitempty"`
+ InfluxDBDatabase string `toml:",omitempty"`
+ InfluxDBUsername string `toml:",omitempty"`
+ InfluxDBPassword string `toml:",omitempty"`
+ InfluxDBTags string `toml:",omitempty"`
+
+ EnableInfluxDBV2 bool `toml:",omitempty"`
+ InfluxDBToken string `toml:",omitempty"`
+ InfluxDBBucket string `toml:",omitempty"`
+ InfluxDBOrganization string `toml:",omitempty"`
}
// DefaultConfig is the default config for metrics used in go-ethereum.
@@ -30,4 +41,16 @@ var DefaultConfig = Config{
EnabledExpensive: false,
HTTP: "127.0.0.1",
Port: 6060,
+ EnableInfluxDB: false,
+ InfluxDBEndpoint: "http://localhost:8086",
+ InfluxDBDatabase: "xdc",
+ InfluxDBUsername: "test",
+ InfluxDBPassword: "test",
+ InfluxDBTags: "host=localhost",
+
+ // influxdbv2-specific flags
+ EnableInfluxDBV2: false,
+ InfluxDBToken: "test",
+ InfluxDBBucket: "xdc",
+ InfluxDBOrganization: "xdc",
}
diff --git a/metrics/counter.go b/metrics/counter.go
index c7f2b4bd3a..0f373b0d92 100644
--- a/metrics/counter.go
+++ b/metrics/counter.go
@@ -1,112 +1,58 @@
package metrics
-import "sync/atomic"
-
-// Counters hold an int64 value that can be incremented and decremented.
-type Counter interface {
- Clear()
- Count() int64
- Dec(int64)
- Inc(int64)
- Snapshot() Counter
-}
+import (
+ "sync/atomic"
+)
// GetOrRegisterCounter returns an existing Counter or constructs and registers
-// a new StandardCounter.
-func GetOrRegisterCounter(name string, r Registry) Counter {
- if nil == r {
+// a new Counter.
+func GetOrRegisterCounter(name string, r Registry) *Counter {
+ if r == nil {
r = DefaultRegistry
}
- return r.GetOrRegister(name, NewCounter).(Counter)
+ return r.GetOrRegister(name, NewCounter).(*Counter)
}
-// NewCounter constructs a new StandardCounter.
-func NewCounter() Counter {
- if !Enabled {
- return NilCounter{}
- }
- return &StandardCounter{0}
+// NewCounter constructs a new Counter.
+func NewCounter() *Counter {
+ return new(Counter)
}
-// NewRegisteredCounter constructs and registers a new StandardCounter.
-func NewRegisteredCounter(name string, r Registry) Counter {
+// NewRegisteredCounter constructs and registers a new Counter.
+func NewRegisteredCounter(name string, r Registry) *Counter {
c := NewCounter()
- if nil == r {
+ if r == nil {
r = DefaultRegistry
}
r.Register(name, c)
return c
}
-// CounterSnapshot is a read-only copy of another Counter.
+// CounterSnapshot is a read-only copy of a Counter.
type CounterSnapshot int64
-// Clear panics.
-func (CounterSnapshot) Clear() {
- panic("Clear called on a CounterSnapshot")
-}
-
// Count returns the count at the time the snapshot was taken.
func (c CounterSnapshot) Count() int64 { return int64(c) }
-// Dec panics.
-func (CounterSnapshot) Dec(int64) {
- panic("Dec called on a CounterSnapshot")
-}
-
-// Inc panics.
-func (CounterSnapshot) Inc(int64) {
- panic("Inc called on a CounterSnapshot")
-}
-
-// Snapshot returns the snapshot.
-func (c CounterSnapshot) Snapshot() Counter { return c }
-
-// NilCounter is a no-op Counter.
-type NilCounter struct{}
-
-// Clear is a no-op.
-func (NilCounter) Clear() {}
-
-// Count is a no-op.
-func (NilCounter) Count() int64 { return 0 }
-
-// Dec is a no-op.
-func (NilCounter) Dec(i int64) {}
-
-// Inc is a no-op.
-func (NilCounter) Inc(i int64) {}
-
-// Snapshot is a no-op.
-func (NilCounter) Snapshot() Counter { return NilCounter{} }
-
-// StandardCounter is the standard implementation of a Counter and uses the
-// sync/atomic package to manage a single int64 value.
-type StandardCounter struct {
- count int64
-}
+// Counter hold an int64 value that can be incremented and decremented.
+type Counter atomic.Int64
// Clear sets the counter to zero.
-func (c *StandardCounter) Clear() {
- atomic.StoreInt64(&c.count, 0)
-}
-
-// Count returns the current count.
-func (c *StandardCounter) Count() int64 {
- return atomic.LoadInt64(&c.count)
+func (c *Counter) Clear() {
+ (*atomic.Int64)(c).Store(0)
}
// Dec decrements the counter by the given amount.
-func (c *StandardCounter) Dec(i int64) {
- atomic.AddInt64(&c.count, -i)
+func (c *Counter) Dec(i int64) {
+ (*atomic.Int64)(c).Add(-i)
}
// Inc increments the counter by the given amount.
-func (c *StandardCounter) Inc(i int64) {
- atomic.AddInt64(&c.count, i)
+func (c *Counter) Inc(i int64) {
+ (*atomic.Int64)(c).Add(i)
}
// Snapshot returns a read-only copy of the counter.
-func (c *StandardCounter) Snapshot() Counter {
- return CounterSnapshot(c.Count())
+func (c *Counter) Snapshot() CounterSnapshot {
+ return CounterSnapshot((*atomic.Int64)(c).Load())
}
diff --git a/metrics/counter_float64.go b/metrics/counter_float64.go
new file mode 100644
index 0000000000..91c4215c4d
--- /dev/null
+++ b/metrics/counter_float64.go
@@ -0,0 +1,69 @@
+package metrics
+
+import (
+ "math"
+ "sync/atomic"
+)
+
+// GetOrRegisterCounterFloat64 returns an existing *CounterFloat64 or constructs and registers
+// a new CounterFloat64.
+func GetOrRegisterCounterFloat64(name string, r Registry) *CounterFloat64 {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewCounterFloat64).(*CounterFloat64)
+}
+
+// NewCounterFloat64 constructs a new CounterFloat64.
+func NewCounterFloat64() *CounterFloat64 {
+ return new(CounterFloat64)
+}
+
+// NewRegisteredCounterFloat64 constructs and registers a new CounterFloat64.
+func NewRegisteredCounterFloat64(name string, r Registry) *CounterFloat64 {
+ c := NewCounterFloat64()
+ if r == nil {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// CounterFloat64Snapshot is a read-only copy of a float64 counter.
+type CounterFloat64Snapshot float64
+
+// Count returns the value at the time the snapshot was taken.
+func (c CounterFloat64Snapshot) Count() float64 { return float64(c) }
+
+// CounterFloat64 holds a float64 value that can be incremented and decremented.
+type CounterFloat64 atomic.Uint64
+
+// Clear sets the counter to zero.
+func (c *CounterFloat64) Clear() {
+ (*atomic.Uint64)(c).Store(0)
+}
+
+// Dec decrements the counter by the given amount.
+func (c *CounterFloat64) Dec(v float64) {
+ atomicAddFloat((*atomic.Uint64)(c), -v)
+}
+
+// Inc increments the counter by the given amount.
+func (c *CounterFloat64) Inc(v float64) {
+ atomicAddFloat((*atomic.Uint64)(c), v)
+}
+
+// Snapshot returns a read-only copy of the counter.
+func (c *CounterFloat64) Snapshot() CounterFloat64Snapshot {
+ return CounterFloat64Snapshot(math.Float64frombits((*atomic.Uint64)(c).Load()))
+}
+
+func atomicAddFloat(fbits *atomic.Uint64, v float64) {
+ for {
+ loadedBits := fbits.Load()
+ newBits := math.Float64bits(math.Float64frombits(loadedBits) + v)
+ if fbits.CompareAndSwap(loadedBits, newBits) {
+ break
+ }
+ }
+}
diff --git a/metrics/counter_float_64_test.go b/metrics/counter_float_64_test.go
new file mode 100644
index 0000000000..618cbbbc2b
--- /dev/null
+++ b/metrics/counter_float_64_test.go
@@ -0,0 +1,73 @@
+package metrics
+
+import (
+ "sync"
+ "testing"
+)
+
+func BenchmarkCounterFloat64(b *testing.B) {
+ c := NewCounterFloat64()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ c.Inc(1.0)
+ }
+}
+
+func BenchmarkCounterFloat64Parallel(b *testing.B) {
+ c := NewCounterFloat64()
+ b.ResetTimer()
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ for i := 0; i < b.N; i++ {
+ c.Inc(1.0)
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ if have, want := c.Snapshot().Count(), 10.0*float64(b.N); have != want {
+ b.Fatalf("have %f want %f", have, want)
+ }
+}
+
+func TestCounterFloat64(t *testing.T) {
+ c := NewCounterFloat64()
+ if count := c.Snapshot().Count(); count != 0 {
+ t.Errorf("wrong count: %v", count)
+ }
+ c.Dec(1.0)
+ if count := c.Snapshot().Count(); count != -1.0 {
+ t.Errorf("wrong count: %v", count)
+ }
+ snapshot := c.Snapshot()
+ c.Dec(2.0)
+ if count := c.Snapshot().Count(); count != -3.0 {
+ t.Errorf("wrong count: %v", count)
+ }
+ c.Inc(1.0)
+ if count := c.Snapshot().Count(); count != -2.0 {
+ t.Errorf("wrong count: %v", count)
+ }
+ c.Inc(2.0)
+ if count := c.Snapshot().Count(); count != 0.0 {
+ t.Errorf("wrong count: %v", count)
+ }
+ if count := snapshot.Count(); count != -1.0 {
+ t.Errorf("snapshot count wrong: %v", count)
+ }
+ c.Inc(1.0)
+ c.Clear()
+ if count := c.Snapshot().Count(); count != 0.0 {
+ t.Errorf("wrong count: %v", count)
+ }
+}
+
+func TestGetOrRegisterCounterFloat64(t *testing.T) {
+ r := NewRegistry()
+ NewRegisteredCounterFloat64("foo", r).Inc(47.0)
+ if c := GetOrRegisterCounterFloat64("foo", r).Snapshot(); c.Count() != 47.0 {
+ t.Fatal(c)
+ }
+}
diff --git a/metrics/counter_test.go b/metrics/counter_test.go
index dfb03b4e88..bf0ca6bae4 100644
--- a/metrics/counter_test.go
+++ b/metrics/counter_test.go
@@ -14,40 +14,31 @@ func TestCounterClear(t *testing.T) {
c := NewCounter()
c.Inc(1)
c.Clear()
- if count := c.Count(); 0 != count {
+ if count := c.Snapshot().Count(); count != 0 {
t.Errorf("c.Count(): 0 != %v\n", count)
}
}
-func TestCounterDec1(t *testing.T) {
+func TestCounter(t *testing.T) {
c := NewCounter()
+ if count := c.Snapshot().Count(); count != 0 {
+ t.Errorf("wrong count: %v", count)
+ }
c.Dec(1)
- if count := c.Count(); -1 != count {
- t.Errorf("c.Count(): -1 != %v\n", count)
+ if count := c.Snapshot().Count(); count != -1 {
+ t.Errorf("wrong count: %v", count)
}
-}
-
-func TestCounterDec2(t *testing.T) {
- c := NewCounter()
c.Dec(2)
- if count := c.Count(); -2 != count {
- t.Errorf("c.Count(): -2 != %v\n", count)
+ if count := c.Snapshot().Count(); count != -3 {
+ t.Errorf("wrong count: %v", count)
}
-}
-
-func TestCounterInc1(t *testing.T) {
- c := NewCounter()
c.Inc(1)
- if count := c.Count(); 1 != count {
- t.Errorf("c.Count(): 1 != %v\n", count)
+ if count := c.Snapshot().Count(); count != -2 {
+ t.Errorf("wrong count: %v", count)
}
-}
-
-func TestCounterInc2(t *testing.T) {
- c := NewCounter()
c.Inc(2)
- if count := c.Count(); 2 != count {
- t.Errorf("c.Count(): 2 != %v\n", count)
+ if count := c.Snapshot().Count(); count != 0 {
+ t.Errorf("wrong count: %v", count)
}
}
@@ -56,22 +47,15 @@ func TestCounterSnapshot(t *testing.T) {
c.Inc(1)
snapshot := c.Snapshot()
c.Inc(1)
- if count := snapshot.Count(); 1 != count {
+ if count := snapshot.Count(); count != 1 {
t.Errorf("c.Count(): 1 != %v\n", count)
}
}
-func TestCounterZero(t *testing.T) {
- c := NewCounter()
- if count := c.Count(); 0 != count {
- t.Errorf("c.Count(): 0 != %v\n", count)
- }
-}
-
func TestGetOrRegisterCounter(t *testing.T) {
r := NewRegistry()
NewRegisteredCounter("foo", r).Inc(47)
- if c := GetOrRegisterCounter("foo", r); 47 != c.Count() {
+ if c := GetOrRegisterCounter("foo", r).Snapshot(); c.Count() != 47 {
t.Fatal(c)
}
}
diff --git a/internal/debug/trace_fallback.go b/metrics/cpu.go
similarity index 67%
rename from internal/debug/trace_fallback.go
rename to metrics/cpu.go
index 4118ff4087..3a49cd4249 100644
--- a/internal/debug/trace_fallback.go
+++ b/metrics/cpu.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,18 +14,12 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-//+build !go1.5
+package metrics
-// no-op implementation of tracing methods for Go < 1.5.
-
-package debug
-
-import "errors"
-
-func (*HandlerT) StartGoTrace(string) error {
- return errors.New("tracing is not supported on Go < 1.5")
-}
-
-func (*HandlerT) StopGoTrace() error {
- return errors.New("tracing is not supported on Go < 1.5")
+// CPUStats is the system and process CPU stats.
+// All values are in seconds.
+type CPUStats struct {
+ GlobalTime float64 // Time spent by the CPU working on all processes
+ GlobalWait float64 // Time spent by waiting on disk for all processes
+ LocalTime float64 // Time spent by the CPU working on this process
}
diff --git a/mobile/geth_other.go b/metrics/cpu_disabled.go
similarity index 73%
rename from mobile/geth_other.go
rename to metrics/cpu_disabled.go
index 6f0c5dda68..025d97aeb3 100644
--- a/mobile/geth_other.go
+++ b/metrics/cpu_disabled.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,9 +14,11 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build !android,!ios
+//go:build ios || js
+// +build ios js
-package geth
+package metrics
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "GethMobile"
+// ReadCPUStats retrieves the current CPU stats. Internally this uses `gosigar`,
+// which is not supported on the platforms in this file.
+func ReadCPUStats(stats *CPUStats) {}
diff --git a/core/vm/runtime/fuzz.go b/metrics/cpu_enabled.go
similarity index 50%
rename from core/vm/runtime/fuzz.go
rename to metrics/cpu_enabled.go
index cb9ff08b5b..efb2234c99 100644
--- a/core/vm/runtime/fuzz.go
+++ b/metrics/cpu_enabled.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,23 +14,31 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build gofuzz
+//go:build !ios && !js
+// +build !ios,!js
-package runtime
+package metrics
-// Fuzz is the basic entry point for the go-fuzz tool
-//
-// This returns 1 for valid parsable/runable code, 0
-// for invalid opcode.
-func Fuzz(input []byte) int {
- _, _, err := Execute(input, input, &Config{
- GasLimit: 3000000,
- })
+import (
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/shirou/gopsutil/cpu"
+)
- // invalid opcode
- if err != nil && len(err.Error()) > 6 && string(err.Error()[:7]) == "invalid" {
- return 0
+// ReadCPUStats retrieves the current CPU stats.
+func ReadCPUStats(stats *CPUStats) {
+ // passing false to request all cpu times
+ timeStats, err := cpu.Times(false)
+ if err != nil {
+ log.Error("Could not read cpu stats", "err", err)
+ return
}
-
- return 1
+ if len(timeStats) == 0 {
+ log.Error("Empty cpu stats")
+ return
+ }
+ // requesting all cpu times will always return an array with only one time stats entry
+ timeStat := timeStats[0]
+ stats.GlobalTime = timeStat.User + timeStat.Nice + timeStat.System
+ stats.GlobalWait = timeStat.Iowait
+ stats.LocalTime = getProcessCPUTime()
}
diff --git a/internal/debug/loudpanic_fallback.go b/metrics/cputime_nop.go
similarity index 74%
rename from internal/debug/loudpanic_fallback.go
rename to metrics/cputime_nop.go
index 4ce4985da7..465d88c4d2 100644
--- a/internal/debug/loudpanic_fallback.go
+++ b/metrics/cputime_nop.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,11 +14,13 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build !go1.6
+//go:build windows || js
+// +build windows js
-package debug
+package metrics
-// LoudPanic panics in a way that gets all goroutine stacks printed on stderr.
-func LoudPanic(x interface{}) {
- panic(x)
+// getProcessCPUTime returns 0 on Windows as there is no system call to resolve
+// the actual process' CPU time.
+func getProcessCPUTime() float64 {
+ return 0
}
diff --git a/mobile/init.go b/metrics/cputime_unix.go
similarity index 59%
rename from mobile/init.go
rename to metrics/cputime_unix.go
index ff1463ca9b..a5b50ebaa2 100644
--- a/mobile/init.go
+++ b/metrics/cputime_unix.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,21 +14,23 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// Contains initialization code for the mbile library.
+//go:build !windows && !js
+// +build !windows,!js
-package geth
+package metrics
import (
- "os"
- "runtime"
+ syscall "golang.org/x/sys/unix"
"github.com/XinFinOrg/XDPoSChain/log"
)
-func init() {
- // Initialize the logger
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
-
- // Initialize the goroutine count
- runtime.GOMAXPROCS(runtime.NumCPU())
+// getProcessCPUTime retrieves the process' CPU time since program startup.
+func getProcessCPUTime() float64 {
+ var usage syscall.Rusage
+ if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil {
+ log.Warn("Failed to retrieve CPU time", "err", err)
+ return 0
+ }
+ return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1000000 //nolint:unconvert
}
diff --git a/metrics/debug.go b/metrics/debug.go
index de4a2739fe..5d0d3992f1 100644
--- a/metrics/debug.go
+++ b/metrics/debug.go
@@ -8,29 +8,29 @@ import (
var (
debugMetrics struct {
GCStats struct {
- LastGC Gauge
- NumGC Gauge
+ LastGC *Gauge
+ NumGC *Gauge
Pause Histogram
//PauseQuantiles Histogram
- PauseTotal Gauge
+ PauseTotal *Gauge
}
- ReadGCStats Timer
+ ReadGCStats *Timer
}
gcStats debug.GCStats
)
-// Capture new values for the Go garbage collector statistics exported in
-// debug.GCStats. This is designed to be called as a goroutine.
+// CaptureDebugGCStats captures new values for the Go garbage collector statistics
+// exported in debug.GCStats. This is designed to be called as a goroutine.
func CaptureDebugGCStats(r Registry, d time.Duration) {
for range time.Tick(d) {
CaptureDebugGCStatsOnce(r)
}
}
-// Capture new values for the Go garbage collector statistics exported in
-// debug.GCStats. This is designed to be called in a background goroutine.
-// Giving a registry which has not been given to RegisterDebugGCStats will
-// panic.
+// CaptureDebugGCStatsOnce captures new values for the Go garbage collector
+// statistics exported in debug.GCStats. This is designed to be called in
+// a background goroutine. Giving a registry which has not been given to
+// RegisterDebugGCStats will panic.
//
// Be careful (but much less so) with this because debug.ReadGCStats calls
// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world
@@ -50,9 +50,9 @@ func CaptureDebugGCStatsOnce(r Registry) {
debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal))
}
-// Register metrics for the Go garbage collector statistics exported in
-// debug.GCStats. The metrics are named by their fully-qualified Go symbols,
-// i.e. debug.GCStats.PauseTotal.
+// RegisterDebugGCStats registers metrics for the Go garbage collector statistics
+// exported in debug.GCStats. The metrics are named by their fully-qualified Go
+// symbols, i.e. debug.GCStats.PauseTotal.
func RegisterDebugGCStats(r Registry) {
debugMetrics.GCStats.LastGC = NewGauge()
debugMetrics.GCStats.NumGC = NewGauge()
diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go
index 4319f8b277..41bbe9adb2 100644
--- a/metrics/disk_nop.go
+++ b/metrics/disk_nop.go
@@ -14,6 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build !linux
// +build !linux
package metrics
@@ -22,5 +23,5 @@ import "errors"
// ReadDiskStats retrieves the disk IO stats belonging to the current process.
func ReadDiskStats(stats *DiskStats) error {
- return errors.New("Not implemented")
+ return errors.New("not implemented")
}
diff --git a/metrics/ewma.go b/metrics/ewma.go
index 3aecd4fa35..194527a798 100644
--- a/metrics/ewma.go
+++ b/metrics/ewma.go
@@ -4,115 +4,88 @@ import (
"math"
"sync"
"sync/atomic"
+ "time"
)
-// EWMAs continuously calculate an exponentially-weighted moving average
-// based on an outside source of clock ticks.
-type EWMA interface {
- Rate() float64
- Snapshot() EWMA
- Tick()
- Update(int64)
-}
-
-// NewEWMA constructs a new EWMA with the given alpha.
-func NewEWMA(alpha float64) EWMA {
- if !Enabled {
- return NilEWMA{}
- }
- return &StandardEWMA{alpha: alpha}
-}
-
-// NewEWMA1 constructs a new EWMA for a one-minute moving average.
-func NewEWMA1() EWMA {
- return NewEWMA(1 - math.Exp(-5.0/60.0/1))
-}
-
-// NewEWMA5 constructs a new EWMA for a five-minute moving average.
-func NewEWMA5() EWMA {
- return NewEWMA(1 - math.Exp(-5.0/60.0/5))
-}
-
-// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
-func NewEWMA15() EWMA {
- return NewEWMA(1 - math.Exp(-5.0/60.0/15))
-}
-
-// EWMASnapshot is a read-only copy of another EWMA.
+// EWMASnapshot is a read-only copy of an EWMA.
type EWMASnapshot float64
// Rate returns the rate of events per second at the time the snapshot was
// taken.
func (a EWMASnapshot) Rate() float64 { return float64(a) }
-// Snapshot returns the snapshot.
-func (a EWMASnapshot) Snapshot() EWMA { return a }
-
-// Tick panics.
-func (EWMASnapshot) Tick() {
- panic("Tick called on an EWMASnapshot")
+// NewEWMA constructs a new EWMA with the given alpha.
+func NewEWMA(alpha float64) *EWMA {
+ return &EWMA{alpha: alpha}
}
-// Update panics.
-func (EWMASnapshot) Update(int64) {
- panic("Update called on an EWMASnapshot")
+// NewEWMA1 constructs a new EWMA for a one-minute moving average.
+func NewEWMA1() *EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/1))
}
-// NilEWMA is a no-op EWMA.
-type NilEWMA struct{}
+// NewEWMA5 constructs a new EWMA for a five-minute moving average.
+func NewEWMA5() *EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/5))
+}
-// Rate is a no-op.
-func (NilEWMA) Rate() float64 { return 0.0 }
+// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
+func NewEWMA15() *EWMA {
+ return NewEWMA(1 - math.Exp(-5.0/60.0/15))
+}
-// Snapshot is a no-op.
-func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
-
-// Tick is a no-op.
-func (NilEWMA) Tick() {}
-
-// Update is a no-op.
-func (NilEWMA) Update(n int64) {}
-
-// StandardEWMA is the standard implementation of an EWMA and tracks the number
-// of uncounted events and processes them on each tick. It uses the
-// sync/atomic package to manage uncounted events.
-type StandardEWMA struct {
- uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment
+// EWMA continuously calculate an exponentially-weighted moving average
+// based on an outside source of clock ticks.
+type EWMA struct {
+ uncounted atomic.Int64
alpha float64
- rate float64
- init bool
+ rate atomic.Uint64
+ init atomic.Bool
mutex sync.Mutex
}
-// Rate returns the moving average rate of events per second.
-func (a *StandardEWMA) Rate() float64 {
- a.mutex.Lock()
- defer a.mutex.Unlock()
- return a.rate * float64(1e9)
-}
-
// Snapshot returns a read-only copy of the EWMA.
-func (a *StandardEWMA) Snapshot() EWMA {
- return EWMASnapshot(a.Rate())
+func (a *EWMA) Snapshot() EWMASnapshot {
+ r := math.Float64frombits(a.rate.Load()) * float64(time.Second)
+ return EWMASnapshot(r)
}
-// Tick ticks the clock to update the moving average. It assumes it is called
+// tick ticks the clock to update the moving average. It assumes it is called
// every five seconds.
-func (a *StandardEWMA) Tick() {
- count := atomic.LoadInt64(&a.uncounted)
- atomic.AddInt64(&a.uncounted, -count)
- instantRate := float64(count) / float64(5e9)
- a.mutex.Lock()
- defer a.mutex.Unlock()
- if a.init {
- a.rate += a.alpha * (instantRate - a.rate)
- } else {
- a.init = true
- a.rate = instantRate
+func (a *EWMA) tick() {
+ // Optimization to avoid mutex locking in the hot-path.
+ if a.init.Load() {
+ a.updateRate(a.fetchInstantRate())
+ return
}
+ // Slow-path: this is only needed on the first tick() and preserves transactional updating
+ // of init and rate in the else block. The first conditional is needed below because
+ // a different thread could have set a.init = 1 between the time of the first atomic load and when
+ // the lock was acquired.
+ a.mutex.Lock()
+ if a.init.Load() {
+ // The fetchInstantRate() uses atomic loading, which is unnecessary in this critical section
+ // but again, this section is only invoked on the first successful tick() operation.
+ a.updateRate(a.fetchInstantRate())
+ } else {
+ a.init.Store(true)
+ a.rate.Store(math.Float64bits(a.fetchInstantRate()))
+ }
+ a.mutex.Unlock()
+}
+
+func (a *EWMA) fetchInstantRate() float64 {
+ count := a.uncounted.Swap(0)
+ return float64(count) / float64(5*time.Second)
+}
+
+func (a *EWMA) updateRate(instantRate float64) {
+ currentRate := math.Float64frombits(a.rate.Load())
+ currentRate += a.alpha * (instantRate - currentRate)
+ a.rate.Store(math.Float64bits(currentRate))
}
// Update adds n uncounted events.
-func (a *StandardEWMA) Update(n int64) {
- atomic.AddInt64(&a.uncounted, n)
+func (a *EWMA) Update(n int64) {
+ a.uncounted.Add(n)
}
diff --git a/metrics/ewma_test.go b/metrics/ewma_test.go
index 0430fbd247..4b9bde3a4b 100644
--- a/metrics/ewma_test.go
+++ b/metrics/ewma_test.go
@@ -1,225 +1,89 @@
package metrics
-import "testing"
+import (
+ "math"
+ "testing"
+)
+
+const epsilon = 0.0000000000000001
func BenchmarkEWMA(b *testing.B) {
a := NewEWMA1()
b.ResetTimer()
for i := 0; i < b.N; i++ {
a.Update(1)
- a.Tick()
+ a.tick()
}
}
+func BenchmarkEWMAParallel(b *testing.B) {
+ a := NewEWMA1()
+ b.ResetTimer()
+
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ a.Update(1)
+ a.tick()
+ }
+ })
+}
+
func TestEWMA1(t *testing.T) {
a := NewEWMA1()
a.Update(3)
- a.Tick()
- if rate := a.Rate(); 0.6 != rate {
- t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.22072766470286553 != rate {
- t.Errorf("1 minute a.Rate(): 0.22072766470286553 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.08120116994196772 != rate {
- t.Errorf("2 minute a.Rate(): 0.08120116994196772 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.029872241020718428 != rate {
- t.Errorf("3 minute a.Rate(): 0.029872241020718428 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.01098938333324054 != rate {
- t.Errorf("4 minute a.Rate(): 0.01098938333324054 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.004042768199451294 != rate {
- t.Errorf("5 minute a.Rate(): 0.004042768199451294 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.0014872513059998212 != rate {
- t.Errorf("6 minute a.Rate(): 0.0014872513059998212 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.0005471291793327122 != rate {
- t.Errorf("7 minute a.Rate(): 0.0005471291793327122 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.00020127757674150815 != rate {
- t.Errorf("8 minute a.Rate(): 0.00020127757674150815 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 7.404588245200814e-05 != rate {
- t.Errorf("9 minute a.Rate(): 7.404588245200814e-05 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 2.7239957857491083e-05 != rate {
- t.Errorf("10 minute a.Rate(): 2.7239957857491083e-05 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 1.0021020474147462e-05 != rate {
- t.Errorf("11 minute a.Rate(): 1.0021020474147462e-05 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 3.6865274119969525e-06 != rate {
- t.Errorf("12 minute a.Rate(): 3.6865274119969525e-06 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 1.3561976441886433e-06 != rate {
- t.Errorf("13 minute a.Rate(): 1.3561976441886433e-06 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 4.989172314621449e-07 != rate {
- t.Errorf("14 minute a.Rate(): 4.989172314621449e-07 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 1.8354139230109722e-07 != rate {
- t.Errorf("15 minute a.Rate(): 1.8354139230109722e-07 != %v\n", rate)
+ a.tick()
+ for i, want := range []float64{0.6,
+ 0.22072766470286553, 0.08120116994196772, 0.029872241020718428,
+ 0.01098938333324054, 0.004042768199451294, 0.0014872513059998212,
+ 0.0005471291793327122, 0.00020127757674150815, 7.404588245200814e-05,
+ 2.7239957857491083e-05, 1.0021020474147462e-05, 3.6865274119969525e-06,
+ 1.3561976441886433e-06, 4.989172314621449e-07, 1.8354139230109722e-07,
+ } {
+ if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon {
+ t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate)
+ }
+ elapseMinute(a)
}
}
func TestEWMA5(t *testing.T) {
a := NewEWMA5()
a.Update(3)
- a.Tick()
- if rate := a.Rate(); 0.6 != rate {
- t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.49123845184678905 != rate {
- t.Errorf("1 minute a.Rate(): 0.49123845184678905 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.4021920276213837 != rate {
- t.Errorf("2 minute a.Rate(): 0.4021920276213837 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.32928698165641596 != rate {
- t.Errorf("3 minute a.Rate(): 0.32928698165641596 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.269597378470333 != rate {
- t.Errorf("4 minute a.Rate(): 0.269597378470333 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.2207276647028654 != rate {
- t.Errorf("5 minute a.Rate(): 0.2207276647028654 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.18071652714732128 != rate {
- t.Errorf("6 minute a.Rate(): 0.18071652714732128 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.14795817836496392 != rate {
- t.Errorf("7 minute a.Rate(): 0.14795817836496392 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.12113791079679326 != rate {
- t.Errorf("8 minute a.Rate(): 0.12113791079679326 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.09917933293295193 != rate {
- t.Errorf("9 minute a.Rate(): 0.09917933293295193 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.08120116994196763 != rate {
- t.Errorf("10 minute a.Rate(): 0.08120116994196763 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.06648189501740036 != rate {
- t.Errorf("11 minute a.Rate(): 0.06648189501740036 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.05443077197364752 != rate {
- t.Errorf("12 minute a.Rate(): 0.05443077197364752 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.04456414692860035 != rate {
- t.Errorf("13 minute a.Rate(): 0.04456414692860035 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.03648603757513079 != rate {
- t.Errorf("14 minute a.Rate(): 0.03648603757513079 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.0298722410207183831020718428 != rate {
- t.Errorf("15 minute a.Rate(): 0.0298722410207183831020718428 != %v\n", rate)
+ a.tick()
+ for i, want := range []float64{
+ 0.6, 0.49123845184678905, 0.4021920276213837, 0.32928698165641596,
+ 0.269597378470333, 0.2207276647028654, 0.18071652714732128,
+ 0.14795817836496392, 0.12113791079679326, 0.09917933293295193,
+ 0.08120116994196763, 0.06648189501740036, 0.05443077197364752,
+ 0.04456414692860035, 0.03648603757513079, 0.0298722410207183831020718428,
+ } {
+ if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon {
+ t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate)
+ }
+ elapseMinute(a)
}
}
func TestEWMA15(t *testing.T) {
a := NewEWMA15()
a.Update(3)
- a.Tick()
- if rate := a.Rate(); 0.6 != rate {
- t.Errorf("initial a.Rate(): 0.6 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.5613041910189706 != rate {
- t.Errorf("1 minute a.Rate(): 0.5613041910189706 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.5251039914257684 != rate {
- t.Errorf("2 minute a.Rate(): 0.5251039914257684 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.4912384518467888184678905 != rate {
- t.Errorf("3 minute a.Rate(): 0.4912384518467888184678905 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.459557003018789 != rate {
- t.Errorf("4 minute a.Rate(): 0.459557003018789 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.4299187863442732 != rate {
- t.Errorf("5 minute a.Rate(): 0.4299187863442732 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.4021920276213831 != rate {
- t.Errorf("6 minute a.Rate(): 0.4021920276213831 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.37625345116383313 != rate {
- t.Errorf("7 minute a.Rate(): 0.37625345116383313 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.3519877317060185 != rate {
- t.Errorf("8 minute a.Rate(): 0.3519877317060185 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.3292869816564153165641596 != rate {
- t.Errorf("9 minute a.Rate(): 0.3292869816564153165641596 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.3080502714195546 != rate {
- t.Errorf("10 minute a.Rate(): 0.3080502714195546 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.2881831806538789 != rate {
- t.Errorf("11 minute a.Rate(): 0.2881831806538789 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.26959737847033216 != rate {
- t.Errorf("12 minute a.Rate(): 0.26959737847033216 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.2522102307052083 != rate {
- t.Errorf("13 minute a.Rate(): 0.2522102307052083 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.23594443252115815 != rate {
- t.Errorf("14 minute a.Rate(): 0.23594443252115815 != %v\n", rate)
- }
- elapseMinute(a)
- if rate := a.Rate(); 0.2207276647028646247028654470286553 != rate {
- t.Errorf("15 minute a.Rate(): 0.2207276647028646247028654470286553 != %v\n", rate)
+ a.tick()
+ for i, want := range []float64{
+ 0.6, 0.5613041910189706, 0.5251039914257684, 0.4912384518467888184678905,
+ 0.459557003018789, 0.4299187863442732, 0.4021920276213831,
+ 0.37625345116383313, 0.3519877317060185, 0.3292869816564153165641596,
+ 0.3080502714195546, 0.2881831806538789, 0.26959737847033216,
+ 0.2522102307052083, 0.23594443252115815, 0.2207276647028646247028654470286553,
+ } {
+ if rate := a.Snapshot().Rate(); math.Abs(want-rate) > epsilon {
+ t.Errorf("%d minute a.Snapshot().Rate(): %f != %v\n", i, want, rate)
+ }
+ elapseMinute(a)
}
}
-func elapseMinute(a EWMA) {
+func elapseMinute(a *EWMA) {
for i := 0; i < 12; i++ {
- a.Tick()
+ a.tick()
}
}
diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go
index 253b4e9490..149176989d 100644
--- a/metrics/exp/exp.go
+++ b/metrics/exp/exp.go
@@ -10,6 +10,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/metrics"
+ "github.com/XinFinOrg/XDPoSChain/metrics/prometheus"
)
type exp struct {
@@ -43,6 +44,7 @@ func Exp(r metrics.Registry) {
// http.HandleFunc("/debug/vars", e.expHandler)
// haven't found an elegant way, so just use a different endpoint
http.Handle("/debug/metrics", h)
+ http.Handle("/debug/metrics/prometheus", prometheus.Handler(r))
}
// ExpHandler will return an expvar powered metrics handler.
@@ -56,6 +58,7 @@ func ExpHandler(r metrics.Registry) http.Handler {
func Setup(address string) {
m := http.NewServeMux()
m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry))
+ m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry))
log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address))
go func() {
if err := http.ListenAndServe(address, m); err != nil {
@@ -92,19 +95,42 @@ func (exp *exp) getFloat(name string) *expvar.Float {
return v
}
-func (exp *exp) publishCounter(name string, metric metrics.Counter) {
+func (exp *exp) getInfo(name string) *expvar.String {
+ var v *expvar.String
+ exp.expvarLock.Lock()
+ p := expvar.Get(name)
+ if p != nil {
+ v = p.(*expvar.String)
+ } else {
+ v = new(expvar.String)
+ expvar.Publish(name, v)
+ }
+ exp.expvarLock.Unlock()
+ return v
+}
+
+func (exp *exp) publishCounter(name string, metric metrics.CounterSnapshot) {
v := exp.getInt(name)
v.Set(metric.Count())
}
-func (exp *exp) publishGauge(name string, metric metrics.Gauge) {
+func (exp *exp) publishCounterFloat64(name string, metric metrics.CounterFloat64Snapshot) {
+ v := exp.getFloat(name)
+ v.Set(metric.Count())
+}
+
+func (exp *exp) publishGauge(name string, metric metrics.GaugeSnapshot) {
v := exp.getInt(name)
v.Set(metric.Value())
}
-func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64) {
+func (exp *exp) publishGaugeFloat64(name string, metric metrics.GaugeFloat64Snapshot) {
exp.getFloat(name).Set(metric.Value())
}
+func (exp *exp) publishGaugeInfo(name string, metric metrics.GaugeInfoSnapshot) {
+ exp.getInfo(name).Set(metric.Value().String())
+}
+
func (exp *exp) publishHistogram(name string, metric metrics.Histogram) {
h := metric.Snapshot()
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
@@ -120,7 +146,7 @@ func (exp *exp) publishHistogram(name string, metric metrics.Histogram) {
exp.getFloat(name + ".999-percentile").Set(ps[4])
}
-func (exp *exp) publishMeter(name string, metric metrics.Meter) {
+func (exp *exp) publishMeter(name string, metric *metrics.Meter) {
m := metric.Snapshot()
exp.getInt(name + ".count").Set(m.Count())
exp.getFloat(name + ".one-minute").Set(m.Rate1())
@@ -129,7 +155,7 @@ func (exp *exp) publishMeter(name string, metric metrics.Meter) {
exp.getFloat(name + ".mean").Set(m.RateMean())
}
-func (exp *exp) publishTimer(name string, metric metrics.Timer) {
+func (exp *exp) publishTimer(name string, metric *metrics.Timer) {
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
exp.getInt(name + ".count").Set(t.Count())
@@ -148,33 +174,37 @@ func (exp *exp) publishTimer(name string, metric metrics.Timer) {
exp.getFloat(name + ".mean-rate").Set(t.RateMean())
}
-func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) {
+func (exp *exp) publishResettingTimer(name string, metric *metrics.ResettingTimer) {
t := metric.Snapshot()
- ps := t.Percentiles([]float64{50, 75, 95, 99})
- exp.getInt(name + ".count").Set(int64(len(t.Values())))
+ ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99})
+ exp.getInt(name + ".count").Set(int64(t.Count()))
exp.getFloat(name + ".mean").Set(t.Mean())
- exp.getInt(name + ".50-percentile").Set(ps[0])
- exp.getInt(name + ".75-percentile").Set(ps[1])
- exp.getInt(name + ".95-percentile").Set(ps[2])
- exp.getInt(name + ".99-percentile").Set(ps[3])
+ exp.getFloat(name + ".50-percentile").Set(ps[0])
+ exp.getFloat(name + ".75-percentile").Set(ps[1])
+ exp.getFloat(name + ".95-percentile").Set(ps[2])
+ exp.getFloat(name + ".99-percentile").Set(ps[3])
}
func (exp *exp) syncToExpvar() {
exp.registry.Each(func(name string, i interface{}) {
switch i := i.(type) {
- case metrics.Counter:
- exp.publishCounter(name, i)
- case metrics.Gauge:
- exp.publishGauge(name, i)
- case metrics.GaugeFloat64:
- exp.publishGaugeFloat64(name, i)
+ case *metrics.Counter:
+ exp.publishCounter(name, i.Snapshot())
+ case *metrics.CounterFloat64:
+ exp.publishCounterFloat64(name, i.Snapshot())
+ case *metrics.Gauge:
+ exp.publishGauge(name, i.Snapshot())
+ case *metrics.GaugeFloat64:
+ exp.publishGaugeFloat64(name, i.Snapshot())
+ case *metrics.GaugeInfo:
+ exp.publishGaugeInfo(name, i.Snapshot())
case metrics.Histogram:
exp.publishHistogram(name, i)
- case metrics.Meter:
+ case *metrics.Meter:
exp.publishMeter(name, i)
- case metrics.Timer:
+ case *metrics.Timer:
exp.publishTimer(name, i)
- case metrics.ResettingTimer:
+ case *metrics.ResettingTimer:
exp.publishResettingTimer(name, i)
default:
panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))
diff --git a/metrics/gauge.go b/metrics/gauge.go
index b6b2758b0d..ba7843e03b 100644
--- a/metrics/gauge.go
+++ b/metrics/gauge.go
@@ -2,157 +2,69 @@ package metrics
import "sync/atomic"
-// Gauges hold an int64 value that can be set arbitrarily.
-type Gauge interface {
- Snapshot() Gauge
- Update(int64)
- Dec(int64)
- Inc(int64)
- Value() int64
-}
-
-// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
-// new StandardGauge.
-func GetOrRegisterGauge(name string, r Registry) Gauge {
- if nil == r {
- r = DefaultRegistry
- }
- return r.GetOrRegister(name, NewGauge).(Gauge)
-}
-
-// NewGauge constructs a new StandardGauge.
-func NewGauge() Gauge {
- if !Enabled {
- return NilGauge{}
- }
- return &StandardGauge{0}
-}
-
-// NewRegisteredGauge constructs and registers a new StandardGauge.
-func NewRegisteredGauge(name string, r Registry) Gauge {
- c := NewGauge()
- if nil == r {
- r = DefaultRegistry
- }
- r.Register(name, c)
- return c
-}
-
-// NewFunctionalGauge constructs a new FunctionalGauge.
-func NewFunctionalGauge(f func() int64) Gauge {
- if !Enabled {
- return NilGauge{}
- }
- return &FunctionalGauge{value: f}
-}
-
-// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge.
-func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge {
- c := NewFunctionalGauge(f)
- if nil == r {
- r = DefaultRegistry
- }
- r.Register(name, c)
- return c
-}
-
-// GaugeSnapshot is a read-only copy of another Gauge.
+// GaugeSnapshot is a read-only copy of a Gauge.
type GaugeSnapshot int64
-// Snapshot returns the snapshot.
-func (g GaugeSnapshot) Snapshot() Gauge { return g }
-
-// Update panics.
-func (GaugeSnapshot) Update(int64) {
- panic("Update called on a GaugeSnapshot")
-}
-
-// Dec panics.
-func (GaugeSnapshot) Dec(int64) {
- panic("Dec called on a GaugeSnapshot")
-}
-
-// Inc panics.
-func (GaugeSnapshot) Inc(int64) {
- panic("Inc called on a GaugeSnapshot")
-}
-
// Value returns the value at the time the snapshot was taken.
func (g GaugeSnapshot) Value() int64 { return int64(g) }
-// NilGauge is a no-op Gauge.
-type NilGauge struct{}
-
-// Snapshot is a no-op.
-func (NilGauge) Snapshot() Gauge { return NilGauge{} }
-
-// Update is a no-op.
-func (NilGauge) Update(v int64) {}
-
-// Dec is a no-op.
-func (NilGauge) Dec(i int64) {}
-
-// Inc is a no-op.
-func (NilGauge) Inc(i int64) {}
-
-// Value is a no-op.
-func (NilGauge) Value() int64 { return 0 }
-
-// StandardGauge is the standard implementation of a Gauge and uses the
-// sync/atomic package to manage a single int64 value.
-type StandardGauge struct {
- value int64
+// GetOrRegisterGauge returns an existing Gauge or constructs and registers a
+// new Gauge.
+func GetOrRegisterGauge(name string, r Registry) *Gauge {
+ if r == nil {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewGauge).(*Gauge)
}
+// NewGauge constructs a new Gauge.
+func NewGauge() *Gauge {
+ return &Gauge{}
+}
+
+// NewRegisteredGauge constructs and registers a new Gauge.
+func NewRegisteredGauge(name string, r Registry) *Gauge {
+ c := NewGauge()
+ if r == nil {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// Gauge holds an int64 value that can be set arbitrarily.
+type Gauge atomic.Int64
+
// Snapshot returns a read-only copy of the gauge.
-func (g *StandardGauge) Snapshot() Gauge {
- return GaugeSnapshot(g.Value())
+func (g *Gauge) Snapshot() GaugeSnapshot {
+ return GaugeSnapshot((*atomic.Int64)(g).Load())
}
// Update updates the gauge's value.
-func (g *StandardGauge) Update(v int64) {
- atomic.StoreInt64(&g.value, v)
+func (g *Gauge) Update(v int64) {
+ (*atomic.Int64)(g).Store(v)
}
-// Value returns the gauge's current value.
-func (g *StandardGauge) Value() int64 {
- return atomic.LoadInt64(&g.value)
+// UpdateIfGt updates the gauge's value if v is larger then the current value.
+func (g *Gauge) UpdateIfGt(v int64) {
+ value := (*atomic.Int64)(g)
+ for {
+ exist := value.Load()
+ if exist >= v {
+ break
+ }
+ if value.CompareAndSwap(exist, v) {
+ break
+ }
+ }
}
// Dec decrements the gauge's current value by the given amount.
-func (g *StandardGauge) Dec(i int64) {
- atomic.AddInt64(&g.value, -i)
+func (g *Gauge) Dec(i int64) {
+ (*atomic.Int64)(g).Add(-i)
}
// Inc increments the gauge's current value by the given amount.
-func (g *StandardGauge) Inc(i int64) {
- atomic.AddInt64(&g.value, i)
-}
-
-// FunctionalGauge returns value from given function
-type FunctionalGauge struct {
- value func() int64
-}
-
-// Value returns the gauge's current value.
-func (g FunctionalGauge) Value() int64 {
- return g.value()
-}
-
-// Snapshot returns the snapshot.
-func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) }
-
-// Update panics.
-func (FunctionalGauge) Update(int64) {
- panic("Update called on a FunctionalGauge")
-}
-
-// Dec panics.
-func (FunctionalGauge) Dec(int64) {
- panic("Dec called on a FunctionalGauge")
-}
-
-// Inc panics.
-func (FunctionalGauge) Inc(int64) {
- panic("Inc called on a FunctionalGauge")
+func (g *Gauge) Inc(i int64) {
+ (*atomic.Int64)(g).Add(i)
}
diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go
index 66819c9577..05b401ef9c 100644
--- a/metrics/gauge_float64.go
+++ b/metrics/gauge_float64.go
@@ -1,35 +1,32 @@
package metrics
-import "sync"
-
-// GaugeFloat64s hold a float64 value that can be set arbitrarily.
-type GaugeFloat64 interface {
- Snapshot() GaugeFloat64
- Update(float64)
- Value() float64
-}
+import (
+ "math"
+ "sync/atomic"
+)
// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a
-// new StandardGaugeFloat64.
-func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 {
+// new GaugeFloat64.
+func GetOrRegisterGaugeFloat64(name string, r Registry) *GaugeFloat64 {
if nil == r {
r = DefaultRegistry
}
- return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64)
+ return r.GetOrRegister(name, NewGaugeFloat64()).(*GaugeFloat64)
}
-// NewGaugeFloat64 constructs a new StandardGaugeFloat64.
-func NewGaugeFloat64() GaugeFloat64 {
- if !Enabled {
- return NilGaugeFloat64{}
- }
- return &StandardGaugeFloat64{
- value: 0.0,
- }
+// GaugeFloat64Snapshot is a read-only copy of a GaugeFloat64.
+type GaugeFloat64Snapshot float64
+
+// Value returns the value at the time the snapshot was taken.
+func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) }
+
+// NewGaugeFloat64 constructs a new GaugeFloat64.
+func NewGaugeFloat64() *GaugeFloat64 {
+ return new(GaugeFloat64)
}
-// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64.
-func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 {
+// NewRegisteredGaugeFloat64 constructs and registers a new GaugeFloat64.
+func NewRegisteredGaugeFloat64(name string, r Registry) *GaugeFloat64 {
c := NewGaugeFloat64()
if nil == r {
r = DefaultRegistry
@@ -38,90 +35,16 @@ func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 {
return c
}
-// NewFunctionalGauge constructs a new FunctionalGauge.
-func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 {
- if !Enabled {
- return NilGaugeFloat64{}
- }
- return &FunctionalGaugeFloat64{value: f}
-}
-
-// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge.
-func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 {
- c := NewFunctionalGaugeFloat64(f)
- if nil == r {
- r = DefaultRegistry
- }
- r.Register(name, c)
- return c
-}
-
-// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64.
-type GaugeFloat64Snapshot float64
-
-// Snapshot returns the snapshot.
-func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g }
-
-// Update panics.
-func (GaugeFloat64Snapshot) Update(float64) {
- panic("Update called on a GaugeFloat64Snapshot")
-}
-
-// Value returns the value at the time the snapshot was taken.
-func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) }
-
-// NilGauge is a no-op Gauge.
-type NilGaugeFloat64 struct{}
-
-// Snapshot is a no-op.
-func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} }
-
-// Update is a no-op.
-func (NilGaugeFloat64) Update(v float64) {}
-
-// Value is a no-op.
-func (NilGaugeFloat64) Value() float64 { return 0.0 }
-
-// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses
-// sync.Mutex to manage a single float64 value.
-type StandardGaugeFloat64 struct {
- mutex sync.Mutex
- value float64
-}
+// GaugeFloat64 hold a float64 value that can be set arbitrarily.
+type GaugeFloat64 atomic.Uint64
// Snapshot returns a read-only copy of the gauge.
-func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 {
- return GaugeFloat64Snapshot(g.Value())
+func (g *GaugeFloat64) Snapshot() GaugeFloat64Snapshot {
+ v := math.Float64frombits((*atomic.Uint64)(g).Load())
+ return GaugeFloat64Snapshot(v)
}
// Update updates the gauge's value.
-func (g *StandardGaugeFloat64) Update(v float64) {
- g.mutex.Lock()
- defer g.mutex.Unlock()
- g.value = v
-}
-
-// Value returns the gauge's current value.
-func (g *StandardGaugeFloat64) Value() float64 {
- g.mutex.Lock()
- defer g.mutex.Unlock()
- return g.value
-}
-
-// FunctionalGaugeFloat64 returns value from given function
-type FunctionalGaugeFloat64 struct {
- value func() float64
-}
-
-// Value returns the gauge's current value.
-func (g FunctionalGaugeFloat64) Value() float64 {
- return g.value()
-}
-
-// Snapshot returns the snapshot.
-func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) }
-
-// Update panics.
-func (FunctionalGaugeFloat64) Update(float64) {
- panic("Update called on a FunctionalGaugeFloat64")
+func (g *GaugeFloat64) Update(v float64) {
+ (*atomic.Uint64)(g).Store(math.Float64bits(v))
}
diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go
index 99e62a4030..194a18821f 100644
--- a/metrics/gauge_float64_test.go
+++ b/metrics/gauge_float64_test.go
@@ -1,8 +1,11 @@
package metrics
-import "testing"
+import (
+ "sync"
+ "testing"
+)
-func BenchmarkGuageFloat64(b *testing.B) {
+func BenchmarkGaugeFloat64(b *testing.B) {
g := NewGaugeFloat64()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -10,50 +13,39 @@ func BenchmarkGuageFloat64(b *testing.B) {
}
}
-func TestGaugeFloat64(t *testing.T) {
- g := NewGaugeFloat64()
- g.Update(float64(47.0))
- if v := g.Value(); float64(47.0) != v {
- t.Errorf("g.Value(): 47.0 != %v\n", v)
+func BenchmarkGaugeFloat64Parallel(b *testing.B) {
+ c := NewGaugeFloat64()
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ for i := 0; i < b.N; i++ {
+ c.Update(float64(i))
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ if have, want := c.Snapshot().Value(), float64(b.N-1); have != want {
+ b.Fatalf("have %f want %f", have, want)
}
}
func TestGaugeFloat64Snapshot(t *testing.T) {
g := NewGaugeFloat64()
- g.Update(float64(47.0))
+ g.Update(47.0)
snapshot := g.Snapshot()
g.Update(float64(0))
- if v := snapshot.Value(); float64(47.0) != v {
+ if v := snapshot.Value(); v != 47.0 {
t.Errorf("g.Value(): 47.0 != %v\n", v)
}
}
func TestGetOrRegisterGaugeFloat64(t *testing.T) {
r := NewRegistry()
- NewRegisteredGaugeFloat64("foo", r).Update(float64(47.0))
+ NewRegisteredGaugeFloat64("foo", r).Update(47.0)
t.Logf("registry: %v", r)
- if g := GetOrRegisterGaugeFloat64("foo", r); float64(47.0) != g.Value() {
- t.Fatal(g)
- }
-}
-
-func TestFunctionalGaugeFloat64(t *testing.T) {
- var counter float64
- fg := NewFunctionalGaugeFloat64(func() float64 {
- counter++
- return counter
- })
- fg.Value()
- fg.Value()
- if counter != 2 {
- t.Error("counter != 2")
- }
-}
-
-func TestGetOrRegisterFunctionalGaugeFloat64(t *testing.T) {
- r := NewRegistry()
- NewRegisteredFunctionalGaugeFloat64("foo", r, func() float64 { return 47 })
- if g := GetOrRegisterGaugeFloat64("foo", r); 47 != g.Value() {
+ if g := GetOrRegisterGaugeFloat64("foo", r).Snapshot(); g.Value() != 47.0 {
t.Fatal(g)
}
}
diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go
new file mode 100644
index 0000000000..2f78455649
--- /dev/null
+++ b/metrics/gauge_info.go
@@ -0,0 +1,64 @@
+package metrics
+
+import (
+ "encoding/json"
+ "sync"
+)
+
+// GaugeInfoValue is a mapping of keys to values
+type GaugeInfoValue map[string]string
+
+func (val GaugeInfoValue) String() string {
+ data, _ := json.Marshal(val)
+ return string(data)
+}
+
+// GetOrRegisterGaugeInfo returns an existing GaugeInfo or constructs and registers a
+// new GaugeInfo.
+func GetOrRegisterGaugeInfo(name string, r Registry) *GaugeInfo {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, NewGaugeInfo()).(*GaugeInfo)
+}
+
+// NewGaugeInfo constructs a new GaugeInfo.
+func NewGaugeInfo() *GaugeInfo {
+ return &GaugeInfo{
+ value: GaugeInfoValue{},
+ }
+}
+
+// NewRegisteredGaugeInfo constructs and registers a new GaugeInfo.
+func NewRegisteredGaugeInfo(name string, r Registry) *GaugeInfo {
+ c := NewGaugeInfo()
+ if nil == r {
+ r = DefaultRegistry
+ }
+ r.Register(name, c)
+ return c
+}
+
+// gaugeInfoSnapshot is a read-only copy of another GaugeInfo.
+type GaugeInfoSnapshot GaugeInfoValue
+
+// Value returns the value at the time the snapshot was taken.
+func (g GaugeInfoSnapshot) Value() GaugeInfoValue { return GaugeInfoValue(g) }
+
+// GaugeInfo maintains a set of key/value mappings.
+type GaugeInfo struct {
+ mutex sync.Mutex
+ value GaugeInfoValue
+}
+
+// Snapshot returns a read-only copy of the gauge.
+func (g *GaugeInfo) Snapshot() GaugeInfoSnapshot {
+ return GaugeInfoSnapshot(g.value)
+}
+
+// Update updates the gauge's value.
+func (g *GaugeInfo) Update(v GaugeInfoValue) {
+ g.mutex.Lock()
+ defer g.mutex.Unlock()
+ g.value = v
+}
diff --git a/metrics/gauge_info_test.go b/metrics/gauge_info_test.go
new file mode 100644
index 0000000000..319afbf92e
--- /dev/null
+++ b/metrics/gauge_info_test.go
@@ -0,0 +1,36 @@
+package metrics
+
+import (
+ "testing"
+)
+
+func TestGaugeInfoJsonString(t *testing.T) {
+ g := NewGaugeInfo()
+ g.Update(GaugeInfoValue{
+ "chain_id": "5",
+ "anotherKey": "any_string_value",
+ "third_key": "anything",
+ },
+ )
+ want := `{"anotherKey":"any_string_value","chain_id":"5","third_key":"anything"}`
+
+ original := g.Snapshot()
+ g.Update(GaugeInfoValue{"value": "updated"})
+
+ if have := original.Value().String(); have != want {
+ t.Errorf("\nhave: %v\nwant: %v\n", have, want)
+ }
+ if have, want := g.Snapshot().Value().String(), `{"value":"updated"}`; have != want {
+ t.Errorf("\nhave: %v\nwant: %v\n", have, want)
+ }
+}
+
+func TestGetOrRegisterGaugeInfo(t *testing.T) {
+ r := NewRegistry()
+ NewRegisteredGaugeInfo("foo", r).Update(
+ GaugeInfoValue{"chain_id": "5"})
+ g := GetOrRegisterGaugeInfo("foo", r).Snapshot()
+ if have, want := g.Value().String(), `{"chain_id":"5"}`; have != want {
+ t.Errorf("have\n%v\nwant\n%v\n", have, want)
+ }
+}
diff --git a/metrics/gauge_test.go b/metrics/gauge_test.go
index 1f2603d339..f2ba930bc4 100644
--- a/metrics/gauge_test.go
+++ b/metrics/gauge_test.go
@@ -1,11 +1,10 @@
package metrics
import (
- "fmt"
"testing"
)
-func BenchmarkGuage(b *testing.B) {
+func BenchmarkGauge(b *testing.B) {
g := NewGauge()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -13,20 +12,12 @@ func BenchmarkGuage(b *testing.B) {
}
}
-func TestGauge(t *testing.T) {
- g := NewGauge()
- g.Update(int64(47))
- if v := g.Value(); 47 != v {
- t.Errorf("g.Value(): 47 != %v\n", v)
- }
-}
-
func TestGaugeSnapshot(t *testing.T) {
g := NewGauge()
g.Update(int64(47))
snapshot := g.Snapshot()
g.Update(int64(0))
- if v := snapshot.Value(); 47 != v {
+ if v := snapshot.Value(); v != 47 {
t.Errorf("g.Value(): 47 != %v\n", v)
}
}
@@ -34,35 +25,7 @@ func TestGaugeSnapshot(t *testing.T) {
func TestGetOrRegisterGauge(t *testing.T) {
r := NewRegistry()
NewRegisteredGauge("foo", r).Update(47)
- if g := GetOrRegisterGauge("foo", r); 47 != g.Value() {
+ if g := GetOrRegisterGauge("foo", r); g.Snapshot().Value() != 47 {
t.Fatal(g)
}
}
-
-func TestFunctionalGauge(t *testing.T) {
- var counter int64
- fg := NewFunctionalGauge(func() int64 {
- counter++
- return counter
- })
- fg.Value()
- fg.Value()
- if counter != 2 {
- t.Error("counter != 2")
- }
-}
-
-func TestGetOrRegisterFunctionalGauge(t *testing.T) {
- r := NewRegistry()
- NewRegisteredFunctionalGauge("foo", r, func() int64 { return 47 })
- if g := GetOrRegisterGauge("foo", r); 47 != g.Value() {
- t.Fatal(g)
- }
-}
-
-func ExampleGetOrRegisterGauge() {
- m := "server.bytes_sent"
- g := GetOrRegisterGauge(m, nil)
- g.Update(47)
- fmt.Println(g.Value()) // Output: 47
-}
diff --git a/metrics/graphite.go b/metrics/graphite.go
deleted file mode 100644
index 142eec86be..0000000000
--- a/metrics/graphite.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package metrics
-
-import (
- "bufio"
- "fmt"
- "log"
- "net"
- "strconv"
- "strings"
- "time"
-)
-
-// GraphiteConfig provides a container with configuration parameters for
-// the Graphite exporter
-type GraphiteConfig struct {
- Addr *net.TCPAddr // Network address to connect to
- Registry Registry // Registry to be exported
- FlushInterval time.Duration // Flush interval
- DurationUnit time.Duration // Time conversion unit for durations
- Prefix string // Prefix to be prepended to metric names
- Percentiles []float64 // Percentiles to export from timers and histograms
-}
-
-// Graphite is a blocking exporter function which reports metrics in r
-// to a graphite server located at addr, flushing them every d duration
-// and prepending metric names with prefix.
-func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) {
- GraphiteWithConfig(GraphiteConfig{
- Addr: addr,
- Registry: r,
- FlushInterval: d,
- DurationUnit: time.Nanosecond,
- Prefix: prefix,
- Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999},
- })
-}
-
-// GraphiteWithConfig is a blocking exporter function just like Graphite,
-// but it takes a GraphiteConfig instead.
-func GraphiteWithConfig(c GraphiteConfig) {
- log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
- for range time.Tick(c.FlushInterval) {
- if err := graphite(&c); nil != err {
- log.Println(err)
- }
- }
-}
-
-// GraphiteOnce performs a single submission to Graphite, returning a
-// non-nil error on failed connections. This can be used in a loop
-// similar to GraphiteWithConfig for custom error handling.
-func GraphiteOnce(c GraphiteConfig) error {
- log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015")
- return graphite(&c)
-}
-
-func graphite(c *GraphiteConfig) error {
- now := time.Now().Unix()
- du := float64(c.DurationUnit)
- conn, err := net.DialTCP("tcp", nil, c.Addr)
- if nil != err {
- return err
- }
- defer conn.Close()
- w := bufio.NewWriter(conn)
- c.Registry.Each(func(name string, i interface{}) {
- switch metric := i.(type) {
- case Counter:
- fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now)
- case Gauge:
- fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now)
- case GaugeFloat64:
- fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now)
- case Histogram:
- h := metric.Snapshot()
- ps := h.Percentiles(c.Percentiles)
- fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now)
- fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now)
- fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now)
- fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now)
- fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now)
- for psIdx, psKey := range c.Percentiles {
- key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
- fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
- }
- case Meter:
- m := metric.Snapshot()
- fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now)
- fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now)
- fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now)
- fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now)
- fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now)
- case Timer:
- t := metric.Snapshot()
- ps := t.Percentiles(c.Percentiles)
- fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now)
- fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now)
- fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now)
- fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now)
- fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now)
- for psIdx, psKey := range c.Percentiles {
- key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1)
- fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now)
- }
- fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now)
- fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now)
- fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now)
- fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now)
- }
- w.Flush()
- })
- return nil
-}
diff --git a/metrics/graphite_test.go b/metrics/graphite_test.go
deleted file mode 100644
index c797c781df..0000000000
--- a/metrics/graphite_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package metrics
-
-import (
- "net"
- "time"
-)
-
-func ExampleGraphite() {
- addr, _ := net.ResolveTCPAddr("net", ":2003")
- go Graphite(DefaultRegistry, 1*time.Second, "some.prefix", addr)
-}
-
-func ExampleGraphiteWithConfig() {
- addr, _ := net.ResolveTCPAddr("net", ":2003")
- go GraphiteWithConfig(GraphiteConfig{
- Addr: addr,
- Registry: DefaultRegistry,
- FlushInterval: 1 * time.Second,
- DurationUnit: time.Millisecond,
- Percentiles: []float64{0.5, 0.75, 0.99, 0.999},
- })
-}
diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go
index f1ae31e34a..435e5e0bf9 100644
--- a/metrics/healthcheck.go
+++ b/metrics/healthcheck.go
@@ -1,61 +1,35 @@
package metrics
-// Healthchecks hold an error value describing an arbitrary up/down status.
-type Healthcheck interface {
- Check()
- Error() error
- Healthy()
- Unhealthy(error)
-}
-
// NewHealthcheck constructs a new Healthcheck which will use the given
// function to update its status.
-func NewHealthcheck(f func(Healthcheck)) Healthcheck {
- if !Enabled {
- return NilHealthcheck{}
- }
- return &StandardHealthcheck{nil, f}
+func NewHealthcheck(f func(*Healthcheck)) *Healthcheck {
+ return &Healthcheck{nil, f}
}
-// NilHealthcheck is a no-op.
-type NilHealthcheck struct{}
-
-// Check is a no-op.
-func (NilHealthcheck) Check() {}
-
-// Error is a no-op.
-func (NilHealthcheck) Error() error { return nil }
-
-// Healthy is a no-op.
-func (NilHealthcheck) Healthy() {}
-
-// Unhealthy is a no-op.
-func (NilHealthcheck) Unhealthy(error) {}
-
-// StandardHealthcheck is the standard implementation of a Healthcheck and
+// Healthcheck is the standard implementation of a Healthcheck and
// stores the status and a function to call to update the status.
-type StandardHealthcheck struct {
+type Healthcheck struct {
err error
- f func(Healthcheck)
+ f func(*Healthcheck)
}
// Check runs the healthcheck function to update the healthcheck's status.
-func (h *StandardHealthcheck) Check() {
+func (h *Healthcheck) Check() {
h.f(h)
}
// Error returns the healthcheck's status, which will be nil if it is healthy.
-func (h *StandardHealthcheck) Error() error {
+func (h *Healthcheck) Error() error {
return h.err
}
// Healthy marks the healthcheck as healthy.
-func (h *StandardHealthcheck) Healthy() {
+func (h *Healthcheck) Healthy() {
h.err = nil
}
// Unhealthy marks the healthcheck as unhealthy. The error is stored and
// may be retrieved by the Error method.
-func (h *StandardHealthcheck) Unhealthy(err error) {
+func (h *Healthcheck) Unhealthy(err error) {
h.err = err
}
diff --git a/metrics/histogram.go b/metrics/histogram.go
index 46f3bbd2f1..7c27bcc928 100644
--- a/metrics/histogram.go
+++ b/metrics/histogram.go
@@ -1,22 +1,25 @@
package metrics
-// Histograms calculate distribution statistics from a series of int64 values.
-type Histogram interface {
- Clear()
+type HistogramSnapshot interface {
Count() int64
Max() int64
Mean() float64
Min() int64
Percentile(float64) float64
Percentiles([]float64) []float64
- Sample() Sample
- Snapshot() Histogram
+ Size() int
StdDev() float64
Sum() int64
- Update(int64)
Variance() float64
}
+// Histogram calculates distribution statistics from a series of int64 values.
+type Histogram interface {
+ Clear()
+ Update(int64)
+ Snapshot() HistogramSnapshot
+}
+
// GetOrRegisterHistogram returns an existing Histogram or constructs and
// registers a new StandardHistogram.
func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
@@ -26,12 +29,18 @@ func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram)
}
+// GetOrRegisterHistogramLazy returns an existing Histogram or constructs and
+// registers a new StandardHistogram.
+func GetOrRegisterHistogramLazy(name string, r Registry, s func() Sample) Histogram {
+ if nil == r {
+ r = DefaultRegistry
+ }
+ return r.GetOrRegister(name, func() Histogram { return NewHistogram(s()) }).(Histogram)
+}
+
// NewHistogram constructs a new StandardHistogram from a Sample.
func NewHistogram(s Sample) Histogram {
- if !Enabled {
- return NilHistogram{}
- }
- return &StandardHistogram{sample: s}
+ return &StandardHistogram{s}
}
// NewRegisteredHistogram constructs and registers a new StandardHistogram from
@@ -45,109 +54,6 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
return c
}
-// HistogramSnapshot is a read-only copy of another Histogram.
-type HistogramSnapshot struct {
- sample *SampleSnapshot
-}
-
-// Clear panics.
-func (*HistogramSnapshot) Clear() {
- panic("Clear called on a HistogramSnapshot")
-}
-
-// Count returns the number of samples recorded at the time the snapshot was
-// taken.
-func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() }
-
-// Max returns the maximum value in the sample at the time the snapshot was
-// taken.
-func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() }
-
-// Mean returns the mean of the values in the sample at the time the snapshot
-// was taken.
-func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() }
-
-// Min returns the minimum value in the sample at the time the snapshot was
-// taken.
-func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() }
-
-// Percentile returns an arbitrary percentile of values in the sample at the
-// time the snapshot was taken.
-func (h *HistogramSnapshot) Percentile(p float64) float64 {
- return h.sample.Percentile(p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of values in the sample
-// at the time the snapshot was taken.
-func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 {
- return h.sample.Percentiles(ps)
-}
-
-// Sample returns the Sample underlying the histogram.
-func (h *HistogramSnapshot) Sample() Sample { return h.sample }
-
-// Snapshot returns the snapshot.
-func (h *HistogramSnapshot) Snapshot() Histogram { return h }
-
-// StdDev returns the standard deviation of the values in the sample at the
-// time the snapshot was taken.
-func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() }
-
-// Sum returns the sum in the sample at the time the snapshot was taken.
-func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() }
-
-// Update panics.
-func (*HistogramSnapshot) Update(int64) {
- panic("Update called on a HistogramSnapshot")
-}
-
-// Variance returns the variance of inputs at the time the snapshot was taken.
-func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() }
-
-// NilHistogram is a no-op Histogram.
-type NilHistogram struct{}
-
-// Clear is a no-op.
-func (NilHistogram) Clear() {}
-
-// Count is a no-op.
-func (NilHistogram) Count() int64 { return 0 }
-
-// Max is a no-op.
-func (NilHistogram) Max() int64 { return 0 }
-
-// Mean is a no-op.
-func (NilHistogram) Mean() float64 { return 0.0 }
-
-// Min is a no-op.
-func (NilHistogram) Min() int64 { return 0 }
-
-// Percentile is a no-op.
-func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
-
-// Percentiles is a no-op.
-func (NilHistogram) Percentiles(ps []float64) []float64 {
- return make([]float64, len(ps))
-}
-
-// Sample is a no-op.
-func (NilHistogram) Sample() Sample { return NilSample{} }
-
-// Snapshot is a no-op.
-func (NilHistogram) Snapshot() Histogram { return NilHistogram{} }
-
-// StdDev is a no-op.
-func (NilHistogram) StdDev() float64 { return 0.0 }
-
-// Sum is a no-op.
-func (NilHistogram) Sum() int64 { return 0 }
-
-// Update is a no-op.
-func (NilHistogram) Update(v int64) {}
-
-// Variance is a no-op.
-func (NilHistogram) Variance() float64 { return 0.0 }
-
// StandardHistogram is the standard implementation of a Histogram and uses a
// Sample to bound its memory use.
type StandardHistogram struct {
@@ -157,46 +63,10 @@ type StandardHistogram struct {
// Clear clears the histogram and its sample.
func (h *StandardHistogram) Clear() { h.sample.Clear() }
-// Count returns the number of samples recorded since the histogram was last
-// cleared.
-func (h *StandardHistogram) Count() int64 { return h.sample.Count() }
-
-// Max returns the maximum value in the sample.
-func (h *StandardHistogram) Max() int64 { return h.sample.Max() }
-
-// Mean returns the mean of the values in the sample.
-func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() }
-
-// Min returns the minimum value in the sample.
-func (h *StandardHistogram) Min() int64 { return h.sample.Min() }
-
-// Percentile returns an arbitrary percentile of the values in the sample.
-func (h *StandardHistogram) Percentile(p float64) float64 {
- return h.sample.Percentile(p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of the values in the
-// sample.
-func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
- return h.sample.Percentiles(ps)
-}
-
-// Sample returns the Sample underlying the histogram.
-func (h *StandardHistogram) Sample() Sample { return h.sample }
-
// Snapshot returns a read-only copy of the histogram.
-func (h *StandardHistogram) Snapshot() Histogram {
- return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)}
+func (h *StandardHistogram) Snapshot() HistogramSnapshot {
+ return h.sample.Snapshot()
}
-// StdDev returns the standard deviation of the values in the sample.
-func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() }
-
-// Sum returns the sum in the sample.
-func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() }
-
// Update samples a new value.
func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) }
-
-// Variance returns the variance of the values in the sample.
-func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() }
diff --git a/metrics/histogram_test.go b/metrics/histogram_test.go
index d7f4f0171c..22fc5468b0 100644
--- a/metrics/histogram_test.go
+++ b/metrics/histogram_test.go
@@ -14,7 +14,7 @@ func TestGetOrRegisterHistogram(t *testing.T) {
r := NewRegistry()
s := NewUniformSample(100)
NewRegisteredHistogram("foo", r, s).Update(47)
- if h := GetOrRegisterHistogram("foo", r, s); 1 != h.Count() {
+ if h := GetOrRegisterHistogram("foo", r, s).Snapshot(); h.Count() != 1 {
t.Fatal(h)
}
}
@@ -24,34 +24,34 @@ func TestHistogram10000(t *testing.T) {
for i := 1; i <= 10000; i++ {
h.Update(int64(i))
}
- testHistogram10000(t, h)
+ testHistogram10000(t, h.Snapshot())
}
func TestHistogramEmpty(t *testing.T) {
- h := NewHistogram(NewUniformSample(100))
- if count := h.Count(); 0 != count {
+ h := NewHistogram(NewUniformSample(100)).Snapshot()
+ if count := h.Count(); count != 0 {
t.Errorf("h.Count(): 0 != %v\n", count)
}
- if min := h.Min(); 0 != min {
+ if min := h.Min(); min != 0 {
t.Errorf("h.Min(): 0 != %v\n", min)
}
- if max := h.Max(); 0 != max {
+ if max := h.Max(); max != 0 {
t.Errorf("h.Max(): 0 != %v\n", max)
}
- if mean := h.Mean(); 0.0 != mean {
+ if mean := h.Mean(); mean != 0.0 {
t.Errorf("h.Mean(): 0.0 != %v\n", mean)
}
- if stdDev := h.StdDev(); 0.0 != stdDev {
+ if stdDev := h.StdDev(); stdDev != 0.0 {
t.Errorf("h.StdDev(): 0.0 != %v\n", stdDev)
}
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
- if 0.0 != ps[0] {
+ if ps[0] != 0.0 {
t.Errorf("median: 0.0 != %v\n", ps[0])
}
- if 0.0 != ps[1] {
+ if ps[1] != 0.0 {
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
}
- if 0.0 != ps[2] {
+ if ps[2] != 0.0 {
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
}
}
@@ -66,30 +66,30 @@ func TestHistogramSnapshot(t *testing.T) {
testHistogram10000(t, snapshot)
}
-func testHistogram10000(t *testing.T, h Histogram) {
- if count := h.Count(); 10000 != count {
+func testHistogram10000(t *testing.T, h HistogramSnapshot) {
+ if count := h.Count(); count != 10000 {
t.Errorf("h.Count(): 10000 != %v\n", count)
}
- if min := h.Min(); 1 != min {
+ if min := h.Min(); min != 1 {
t.Errorf("h.Min(): 1 != %v\n", min)
}
- if max := h.Max(); 10000 != max {
+ if max := h.Max(); max != 10000 {
t.Errorf("h.Max(): 10000 != %v\n", max)
}
- if mean := h.Mean(); 5000.5 != mean {
+ if mean := h.Mean(); mean != 5000.5 {
t.Errorf("h.Mean(): 5000.5 != %v\n", mean)
}
- if stdDev := h.StdDev(); 2886.751331514372 != stdDev {
+ if stdDev := h.StdDev(); stdDev != 2886.751331514372 {
t.Errorf("h.StdDev(): 2886.751331514372 != %v\n", stdDev)
}
ps := h.Percentiles([]float64{0.5, 0.75, 0.99})
- if 5000.5 != ps[0] {
+ if ps[0] != 5000.5 {
t.Errorf("median: 5000.5 != %v\n", ps[0])
}
- if 7500.75 != ps[1] {
+ if ps[1] != 7500.75 {
t.Errorf("75th percentile: 7500.75 != %v\n", ps[1])
}
- if 9900.99 != ps[2] {
+ if ps[2] != 9900.99 {
t.Errorf("99th percentile: 9900.99 != %v\n", ps[2])
}
}
diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go
index 90df2e18a7..37a6a3808e 100644
--- a/metrics/influxdb/influxdb.go
+++ b/metrics/influxdb/influxdb.go
@@ -2,226 +2,121 @@ package influxdb
import (
"fmt"
- "log"
- uurl "net/url"
- "time"
"github.com/XinFinOrg/XDPoSChain/metrics"
- "github.com/influxdata/influxdb/client"
)
-type reporter struct {
- reg metrics.Registry
- interval time.Duration
-
- url uurl.URL
- database string
- username string
- password string
- namespace string
- tags map[string]string
-
- client *client.Client
-
- cache map[string]int64
-}
-
-// InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval.
-func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) {
- InfluxDBWithTags(r, d, url, database, username, password, namespace, nil)
-}
-
-// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags
-func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) {
- u, err := uurl.Parse(url)
- if err != nil {
- log.Printf("unable to parse InfluxDB url %s. err=%v", url, err)
- return
- }
-
- rep := &reporter{
- reg: r,
- interval: d,
- url: *u,
- database: database,
- username: username,
- password: password,
- namespace: namespace,
- tags: tags,
- cache: make(map[string]int64),
- }
- if err := rep.makeClient(); err != nil {
- log.Printf("unable to make InfluxDB client. err=%v", err)
- return
- }
-
- rep.run()
-}
-
-func (r *reporter) makeClient() (err error) {
- r.client, err = client.NewClient(client.Config{
- URL: r.url,
- Username: r.username,
- Password: r.password,
- })
-
- return
-}
-
-func (r *reporter) run() {
- intervalTicker := time.Tick(r.interval)
- pingTicker := time.Tick(time.Second * 5)
-
- for {
- select {
- case <-intervalTicker:
- if err := r.send(); err != nil {
- log.Printf("unable to send to InfluxDB. err=%v", err)
- }
- case <-pingTicker:
- _, _, err := r.client.Ping()
- if err != nil {
- log.Printf("got error while sending a ping to InfluxDB, trying to recreate client. err=%v", err)
-
- if err = r.makeClient(); err != nil {
- log.Printf("unable to make InfluxDB client. err=%v", err)
- }
- }
+func readMeter(namespace, name string, i interface{}) (string, map[string]interface{}) {
+ switch metric := i.(type) {
+ case *metrics.Counter:
+ measurement := fmt.Sprintf("%s%s.count", namespace, name)
+ fields := map[string]interface{}{
+ "value": metric.Snapshot().Count(),
}
- }
-}
-
-func (r *reporter) send() error {
- var pts []client.Point
-
- r.reg.Each(func(name string, i interface{}) {
- now := time.Now()
- namespace := r.namespace
-
- switch metric := i.(type) {
- case metrics.Counter:
- v := metric.Count()
- l := r.cache[name]
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.count", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "value": v - l,
- },
- Time: now,
- })
- r.cache[name] = v
- case metrics.Gauge:
- ms := metric.Snapshot()
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "value": ms.Value(),
- },
- Time: now,
- })
- case metrics.GaugeFloat64:
- ms := metric.Snapshot()
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.gauge", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "value": ms.Value(),
- },
- Time: now,
- })
- case metrics.Histogram:
- ms := metric.Snapshot()
- ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.histogram", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "count": ms.Count(),
- "max": ms.Max(),
- "mean": ms.Mean(),
- "min": ms.Min(),
- "stddev": ms.StdDev(),
- "variance": ms.Variance(),
- "p50": ps[0],
- "p75": ps[1],
- "p95": ps[2],
- "p99": ps[3],
- "p999": ps[4],
- "p9999": ps[5],
- },
- Time: now,
- })
- case metrics.Meter:
- ms := metric.Snapshot()
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.meter", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "count": ms.Count(),
- "m1": ms.Rate1(),
- "m5": ms.Rate5(),
- "m15": ms.Rate15(),
- "mean": ms.RateMean(),
- },
- Time: now,
- })
- case metrics.Timer:
- ms := metric.Snapshot()
- ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.timer", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "count": ms.Count(),
- "max": ms.Max(),
- "mean": ms.Mean(),
- "min": ms.Min(),
- "stddev": ms.StdDev(),
- "variance": ms.Variance(),
- "p50": ps[0],
- "p75": ps[1],
- "p95": ps[2],
- "p99": ps[3],
- "p999": ps[4],
- "p9999": ps[5],
- "m1": ms.Rate1(),
- "m5": ms.Rate5(),
- "m15": ms.Rate15(),
- "meanrate": ms.RateMean(),
- },
- Time: now,
- })
- case metrics.ResettingTimer:
- t := metric.Snapshot()
-
- if len(t.Values()) > 0 {
- ps := t.Percentiles([]float64{50, 95, 99})
- val := t.Values()
- pts = append(pts, client.Point{
- Measurement: fmt.Sprintf("%s%s.span", namespace, name),
- Tags: r.tags,
- Fields: map[string]interface{}{
- "count": len(val),
- "max": val[len(val)-1],
- "mean": t.Mean(),
- "min": val[0],
- "p50": ps[0],
- "p95": ps[1],
- "p99": ps[2],
- },
- Time: now,
- })
- }
+ return measurement, fields
+ case *metrics.CounterFloat64:
+ measurement := fmt.Sprintf("%s%s.count", namespace, name)
+ fields := map[string]interface{}{
+ "value": metric.Snapshot().Count(),
}
- })
+ return measurement, fields
+ case *metrics.Gauge:
+ measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
+ fields := map[string]interface{}{
+ "value": metric.Snapshot().Value(),
+ }
+ return measurement, fields
+ case *metrics.GaugeFloat64:
+ measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
+ fields := map[string]interface{}{
+ "value": metric.Snapshot().Value(),
+ }
+ return measurement, fields
+ case *metrics.GaugeInfo:
+ ms := metric.Snapshot()
+ measurement := fmt.Sprintf("%s%s.gauge", namespace, name)
+ fields := map[string]interface{}{
+ "value": ms.Value().String(),
+ }
+ return measurement, fields
+ case metrics.Histogram:
+ ms := metric.Snapshot()
+ if ms.Count() <= 0 {
+ break
+ }
+ ps := ms.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
+ measurement := fmt.Sprintf("%s%s.histogram", namespace, name)
+ fields := map[string]interface{}{
+ "count": ms.Count(),
+ "max": ms.Max(),
+ "mean": ms.Mean(),
+ "min": ms.Min(),
+ "stddev": ms.StdDev(),
+ "variance": ms.Variance(),
+ "p25": ps[0],
+ "p50": ps[1],
+ "p75": ps[2],
+ "p95": ps[3],
+ "p99": ps[4],
+ "p999": ps[5],
+ "p9999": ps[6],
+ }
+ return measurement, fields
+ case *metrics.Meter:
+ ms := metric.Snapshot()
+ measurement := fmt.Sprintf("%s%s.meter", namespace, name)
+ fields := map[string]interface{}{
+ "count": ms.Count(),
+ "m1": ms.Rate1(),
+ "m5": ms.Rate5(),
+ "m15": ms.Rate15(),
+ "mean": ms.RateMean(),
+ }
+ return measurement, fields
+ case *metrics.Timer:
+ ms := metric.Snapshot()
+ ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
- bps := client.BatchPoints{
- Points: pts,
- Database: r.database,
+ measurement := fmt.Sprintf("%s%s.timer", namespace, name)
+ fields := map[string]interface{}{
+ "count": ms.Count(),
+ "max": ms.Max(),
+ "mean": ms.Mean(),
+ "min": ms.Min(),
+ "stddev": ms.StdDev(),
+ "variance": ms.Variance(),
+ "p50": ps[0],
+ "p75": ps[1],
+ "p95": ps[2],
+ "p99": ps[3],
+ "p999": ps[4],
+ "p9999": ps[5],
+ "m1": ms.Rate1(),
+ "m5": ms.Rate5(),
+ "m15": ms.Rate15(),
+ "meanrate": ms.RateMean(),
+ }
+ return measurement, fields
+ case *metrics.ResettingTimer:
+ ms := metric.Snapshot()
+ if ms.Count() == 0 {
+ break
+ }
+ ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
+ measurement := fmt.Sprintf("%s%s.timer", namespace, name)
+ fields := map[string]interface{}{
+ "count": ms.Count(),
+ "max": ms.Max(),
+ "mean": ms.Mean(),
+ "min": ms.Min(),
+ "p50": ps[0],
+ "p75": ps[1],
+ "p95": ps[2],
+ "p99": ps[3],
+ "p999": ps[4],
+ "p9999": ps[5],
+ }
+ return measurement, fields
}
-
- _, err := r.client.Write(bps)
- return err
+ return "", nil
}
diff --git a/metrics/influxdb/influxdb_test.go b/metrics/influxdb/influxdb_test.go
new file mode 100644
index 0000000000..d7e47fda0e
--- /dev/null
+++ b/metrics/influxdb/influxdb_test.go
@@ -0,0 +1,123 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package influxdb
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "os"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+ "github.com/XinFinOrg/XDPoSChain/metrics/internal"
+ influxdb2 "github.com/influxdata/influxdb-client-go/v2"
+)
+
+func TestMain(m *testing.M) {
+ metrics.Enable()
+ os.Exit(m.Run())
+}
+
+func TestExampleV1(t *testing.T) {
+ if runtime.GOARCH == "arm64" {
+ t.Skip("test skipped on ARM64 due to floating point precision differences")
+ }
+
+ r := internal.ExampleMetrics()
+ var have, want string
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ haveB, _ := io.ReadAll(r.Body)
+ have = string(haveB)
+ r.Body.Close()
+ }))
+ defer ts.Close()
+ u, _ := url.Parse(ts.URL)
+ rep := &reporter{
+ reg: r,
+ url: *u,
+ namespace: "goth.",
+ }
+ if err := rep.makeClient(); err != nil {
+ t.Fatal(err)
+ }
+ if err := rep.send(978307200); err != nil {
+ t.Fatal(err)
+ }
+ if wantB, err := os.ReadFile("./testdata/influxdbv1.want"); err != nil {
+ t.Fatal(err)
+ } else {
+ want = string(wantB)
+ }
+ if have != want {
+ t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want)
+ t.Logf("have vs want:\n%v", findFirstDiffPos(have, want))
+ }
+}
+
+func TestExampleV2(t *testing.T) {
+ if runtime.GOARCH == "arm64" {
+ t.Skip("test skipped on ARM64 due to floating point precision differences")
+ }
+
+ r := internal.ExampleMetrics()
+ var have, want string
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ haveB, _ := io.ReadAll(r.Body)
+ have = string(haveB)
+ r.Body.Close()
+ }))
+ defer ts.Close()
+
+ rep := &v2Reporter{
+ reg: r,
+ endpoint: ts.URL,
+ namespace: "goth.",
+ }
+ rep.client = influxdb2.NewClient(rep.endpoint, rep.token)
+ defer rep.client.Close()
+ rep.write = rep.client.WriteAPI(rep.organization, rep.bucket)
+
+ rep.send(978307200)
+
+ if wantB, err := os.ReadFile("./testdata/influxdbv2.want"); err != nil {
+ t.Fatal(err)
+ } else {
+ want = string(wantB)
+ }
+ if have != want {
+ t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want)
+ t.Logf("have vs want:\n%v", findFirstDiffPos(have, want))
+ }
+}
+
+func findFirstDiffPos(a, b string) string {
+ yy := strings.Split(b, "\n")
+ for i, x := range strings.Split(a, "\n") {
+ if i >= len(yy) {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i)
+ }
+ if y := yy[i]; x != y {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y)
+ }
+ }
+ return ""
+}
diff --git a/metrics/influxdb/influxdbv1.go b/metrics/influxdb/influxdbv1.go
new file mode 100644
index 0000000000..7b55872fba
--- /dev/null
+++ b/metrics/influxdb/influxdbv1.go
@@ -0,0 +1,152 @@
+package influxdb
+
+import (
+ "fmt"
+ uurl "net/url"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+ client "github.com/influxdata/influxdb1-client/v2"
+)
+
+type reporter struct {
+ reg metrics.Registry
+ interval time.Duration
+
+ url uurl.URL
+ database string
+ username string
+ password string
+ namespace string
+ tags map[string]string
+
+ client client.Client
+
+ cache map[string]int64
+}
+
+// InfluxDB starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval.
+func InfluxDB(r metrics.Registry, d time.Duration, url, database, username, password, namespace string) {
+ InfluxDBWithTags(r, d, url, database, username, password, namespace, nil)
+}
+
+// InfluxDBWithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags
+func InfluxDBWithTags(r metrics.Registry, d time.Duration, url, database, username, password, namespace string, tags map[string]string) {
+ u, err := uurl.Parse(url)
+ if err != nil {
+ log.Warn("Unable to parse InfluxDB", "url", url, "err", err)
+ return
+ }
+
+ rep := &reporter{
+ reg: r,
+ interval: d,
+ url: *u,
+ database: database,
+ username: username,
+ password: password,
+ namespace: namespace,
+ tags: tags,
+ cache: make(map[string]int64),
+ }
+ if err := rep.makeClient(); err != nil {
+ log.Warn("Unable to make InfluxDB client", "err", err)
+ return
+ }
+
+ rep.run()
+}
+
+// InfluxDBWithTagsOnce runs once an InfluxDB reporter and post the given metrics.Registry with the specified tags
+func InfluxDBWithTagsOnce(r metrics.Registry, url, database, username, password, namespace string, tags map[string]string) error {
+ u, err := uurl.Parse(url)
+ if err != nil {
+ return fmt.Errorf("unable to parse InfluxDB. url: %s, err: %v", url, err)
+ }
+
+ rep := &reporter{
+ reg: r,
+ url: *u,
+ database: database,
+ username: username,
+ password: password,
+ namespace: namespace,
+ tags: tags,
+ cache: make(map[string]int64),
+ }
+ if err := rep.makeClient(); err != nil {
+ return fmt.Errorf("unable to make InfluxDB client. err: %v", err)
+ }
+
+ if err := rep.send(0); err != nil {
+ return fmt.Errorf("unable to send to InfluxDB. err: %v", err)
+ }
+
+ return nil
+}
+
+func (r *reporter) makeClient() (err error) {
+ r.client, err = client.NewHTTPClient(client.HTTPConfig{
+ Addr: r.url.String(),
+ Username: r.username,
+ Password: r.password,
+ Timeout: 10 * time.Second,
+ })
+
+ return
+}
+
+func (r *reporter) run() {
+ intervalTicker := time.NewTicker(r.interval)
+ pingTicker := time.NewTicker(time.Second * 5)
+
+ defer intervalTicker.Stop()
+ defer pingTicker.Stop()
+
+ for {
+ select {
+ case <-intervalTicker.C:
+ if err := r.send(0); err != nil {
+ log.Warn("Unable to send to InfluxDB", "err", err)
+ }
+ case <-pingTicker.C:
+ _, _, err := r.client.Ping(0)
+ if err != nil {
+ log.Warn("Got error while sending a ping to InfluxDB, trying to recreate client", "err", err)
+
+ if err = r.makeClient(); err != nil {
+ log.Warn("Unable to make InfluxDB client", "err", err)
+ }
+ }
+ }
+ }
+}
+
+// send sends the measurements. If provided tstamp is >0, it is used. Otherwise,
+// a 'fresh' timestamp is used.
+func (r *reporter) send(tstamp int64) error {
+ bps, err := client.NewBatchPoints(
+ client.BatchPointsConfig{
+ Database: r.database,
+ })
+ if err != nil {
+ return err
+ }
+ r.reg.Each(func(name string, i interface{}) {
+ var now time.Time
+ if tstamp <= 0 {
+ now = time.Now()
+ } else {
+ now = time.Unix(tstamp, 0)
+ }
+ measurement, fields := readMeter(r.namespace, name, i)
+ if fields == nil {
+ return
+ }
+ if p, err := client.NewPoint(measurement, r.tags, fields, now); err == nil {
+ bps.AddPoint(p)
+ }
+ })
+ return r.client.Write(bps)
+}
diff --git a/metrics/influxdb/influxdbv2.go b/metrics/influxdb/influxdbv2.go
new file mode 100644
index 0000000000..5d8ba1b012
--- /dev/null
+++ b/metrics/influxdb/influxdbv2.go
@@ -0,0 +1,96 @@
+package influxdb
+
+import (
+ "context"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+ influxdb2 "github.com/influxdata/influxdb-client-go/v2"
+ "github.com/influxdata/influxdb-client-go/v2/api"
+)
+
+type v2Reporter struct {
+ reg metrics.Registry
+ interval time.Duration
+
+ endpoint string
+ token string
+ bucket string
+ organization string
+ namespace string
+ tags map[string]string
+
+ client influxdb2.Client
+ write api.WriteAPI
+}
+
+// InfluxDBV2WithTags starts a InfluxDB reporter which will post the from the given metrics.Registry at each d interval with the specified tags
+func InfluxDBV2WithTags(r metrics.Registry, d time.Duration, endpoint string, token string, bucket string, organization string, namespace string, tags map[string]string) {
+ rep := &v2Reporter{
+ reg: r,
+ interval: d,
+ endpoint: endpoint,
+ token: token,
+ bucket: bucket,
+ organization: organization,
+ namespace: namespace,
+ tags: tags,
+ }
+
+ rep.client = influxdb2.NewClient(rep.endpoint, rep.token)
+ defer rep.client.Close()
+
+ // async write client
+ rep.write = rep.client.WriteAPI(rep.organization, rep.bucket)
+ errorsCh := rep.write.Errors()
+
+ // have to handle write errors in a separate goroutine like this b/c the channel is unbuffered and will block writes if not read
+ go func() {
+ for err := range errorsCh {
+ log.Warn("write error", "err", err.Error())
+ }
+ }()
+ rep.run()
+}
+
+func (r *v2Reporter) run() {
+ intervalTicker := time.NewTicker(r.interval)
+ pingTicker := time.NewTicker(time.Second * 5)
+
+ defer intervalTicker.Stop()
+ defer pingTicker.Stop()
+
+ for {
+ select {
+ case <-intervalTicker.C:
+ r.send(0)
+ case <-pingTicker.C:
+ _, err := r.client.Health(context.Background())
+ if err != nil {
+ log.Warn("Got error from influxdb client health check", "err", err.Error())
+ }
+ }
+ }
+}
+
+// send sends the measurements. If provided tstamp is >0, it is used. Otherwise,
+// a 'fresh' timestamp is used.
+func (r *v2Reporter) send(tstamp int64) {
+ r.reg.Each(func(name string, i interface{}) {
+ var now time.Time
+ if tstamp <= 0 {
+ now = time.Now()
+ } else {
+ now = time.Unix(tstamp, 0)
+ }
+ measurement, fields := readMeter(r.namespace, name, i)
+ if fields == nil {
+ return
+ }
+ pt := influxdb2.NewPoint(measurement, r.tags, fields, now)
+ r.write.WritePoint(pt)
+ })
+ // Force all unwritten data to be sent
+ r.write.Flush()
+}
diff --git a/metrics/influxdb/testdata/influxdbv1.want b/metrics/influxdb/testdata/influxdbv1.want
new file mode 100644
index 0000000000..ded9434c73
--- /dev/null
+++ b/metrics/influxdb/testdata/influxdbv1.want
@@ -0,0 +1,11 @@
+goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000
+goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000
+goth.test/counter.count value=12345 978307200000000000
+goth.test/counter_float64.count value=54321.98 978307200000000000
+goth.test/gauge.gauge value=23456i 978307200000000000
+goth.test/gauge_float64.gauge value=34567.89 978307200000000000
+goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000
+goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000
+goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000
+goth.test/resetting_timer.timer count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000,p75=40500000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000 978307200000000000
+goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000
diff --git a/metrics/influxdb/testdata/influxdbv2.want b/metrics/influxdb/testdata/influxdbv2.want
new file mode 100644
index 0000000000..ded9434c73
--- /dev/null
+++ b/metrics/influxdb/testdata/influxdbv2.want
@@ -0,0 +1,11 @@
+goth.system/cpu/schedlatency.histogram count=5645i,max=41943040i,mean=1819544.0410983171,min=0i,p25=0,p50=0,p75=7168,p95=16777216,p99=29360128,p999=33554432,p9999=33554432,stddev=6393570.217198883,variance=40877740122252.57 978307200000000000
+goth.system/memory/pauses.histogram count=14i,max=229376i,mean=50066.28571428572,min=5120i,p25=10240,p50=32768,p75=57344,p95=196608,p99=196608,p999=196608,p9999=196608,stddev=54726.062410783874,variance=2994941906.9890113 978307200000000000
+goth.test/counter.count value=12345 978307200000000000
+goth.test/counter_float64.count value=54321.98 978307200000000000
+goth.test/gauge.gauge value=23456i 978307200000000000
+goth.test/gauge_float64.gauge value=34567.89 978307200000000000
+goth.test/gauge_info.gauge value="{\"arch\":\"amd64\",\"commit\":\"7caa2d8163ae3132c1c2d6978c76610caee2d949\",\"os\":\"linux\",\"protocol_versions\":\"64 65 66\",\"version\":\"1.10.18-unstable\"}" 978307200000000000
+goth.test/histogram.histogram count=3i,max=3i,mean=2,min=1i,p25=1,p50=2,p75=3,p95=3,p99=3,p999=3,p9999=3,stddev=0.816496580927726,variance=0.6666666666666666 978307200000000000
+goth.test/meter.meter count=0i,m1=0,m15=0,m5=0,mean=0 978307200000000000
+goth.test/resetting_timer.timer count=6i,max=120000000i,mean=30000000,min=10000000i,p50=12500000,p75=40500000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000 978307200000000000
+goth.test/timer.timer count=6i,m1=0,m15=0,m5=0,max=120000000i,mean=38333333.333333336,meanrate=0,min=20000000i,p50=22500000,p75=48000000,p95=120000000,p99=120000000,p999=120000000,p9999=120000000,stddev=36545253.529775314,variance=1335555555555555.2 978307200000000000
diff --git a/metrics/init_test.go b/metrics/init_test.go
index 43401e833c..af75bee425 100644
--- a/metrics/init_test.go
+++ b/metrics/init_test.go
@@ -1,5 +1,5 @@
package metrics
func init() {
- Enabled = true
+ metricsEnabled = true
}
diff --git a/metrics/internal/sampledata.go b/metrics/internal/sampledata.go
new file mode 100644
index 0000000000..15b52066f1
--- /dev/null
+++ b/metrics/internal/sampledata.go
@@ -0,0 +1,95 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package internal
+
+import (
+ "bytes"
+ "encoding/gob"
+ metrics2 "runtime/metrics"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+)
+
+// ExampleMetrics returns an ordered registry populated with a sample of metrics.
+func ExampleMetrics() metrics.Registry {
+ var registry = metrics.NewOrderedRegistry()
+
+ metrics.NewRegisteredCounterFloat64("test/counter", registry).Inc(12345)
+ metrics.NewRegisteredCounterFloat64("test/counter_float64", registry).Inc(54321.98)
+ metrics.NewRegisteredGauge("test/gauge", registry).Update(23456)
+ metrics.NewRegisteredGaugeFloat64("test/gauge_float64", registry).Update(34567.89)
+ metrics.NewRegisteredGaugeInfo("test/gauge_info", registry).Update(
+ metrics.GaugeInfoValue{
+ "version": "1.10.18-unstable",
+ "arch": "amd64",
+ "os": "linux",
+ "commit": "7caa2d8163ae3132c1c2d6978c76610caee2d949",
+ "protocol_versions": "64 65 66",
+ })
+
+ {
+ s := metrics.NewUniformSample(3)
+ s.Update(1)
+ s.Update(2)
+ s.Update(3)
+ //metrics.NewRegisteredHistogram("test/histogram", registry, metrics.NewSampleSnapshot(3, []int64{1, 2, 3}))
+ metrics.NewRegisteredHistogram("test/histogram", registry, s)
+ }
+ registry.Register("test/meter", metrics.NewInactiveMeter())
+ {
+ timer := metrics.NewRegisteredResettingTimer("test/resetting_timer", registry)
+ timer.Update(10 * time.Millisecond)
+ timer.Update(11 * time.Millisecond)
+ timer.Update(12 * time.Millisecond)
+ timer.Update(120 * time.Millisecond)
+ timer.Update(13 * time.Millisecond)
+ timer.Update(14 * time.Millisecond)
+ }
+ {
+ timer := metrics.NewRegisteredTimer("test/timer", registry)
+ timer.Update(20 * time.Millisecond)
+ timer.Update(21 * time.Millisecond)
+ timer.Update(22 * time.Millisecond)
+ timer.Update(120 * time.Millisecond)
+ timer.Update(23 * time.Millisecond)
+ timer.Update(24 * time.Millisecond)
+ timer.Stop()
+ }
+ registry.Register("test/empty_resetting_timer", metrics.NewResettingTimer().Snapshot())
+
+ { // go runtime metrics
+ var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00"
+ var gcPauses = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06R\xff\x82\x01\xff\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00"
+
+ var secondsToNs = float64(time.Second)
+
+ dserialize := func(data string) *metrics2.Float64Histogram {
+ var res metrics2.Float64Histogram
+ if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil {
+ panic(err)
+ }
+ return &res
+ }
+ cpuSchedLatency := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(sLatency))
+ registry.Register("system/cpu/schedlatency", cpuSchedLatency)
+
+ memPauses := metrics.RuntimeHistogramFromData(secondsToNs, dserialize(gcPauses))
+ registry.Register("system/memory/pauses", memPauses)
+ }
+ return registry
+}
diff --git a/metrics/internal/sampledata_test.go b/metrics/internal/sampledata_test.go
new file mode 100644
index 0000000000..f0c0ce39b2
--- /dev/null
+++ b/metrics/internal/sampledata_test.go
@@ -0,0 +1,27 @@
+package internal
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ metrics2 "runtime/metrics"
+ "testing"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+)
+
+func TestCollectRuntimeMetrics(t *testing.T) {
+ t.Skip("Only used for generating testdata")
+ serialize := func(path string, histogram *metrics2.Float64Histogram) {
+ var f = new(bytes.Buffer)
+ if err := gob.NewEncoder(f).Encode(histogram); err != nil {
+ panic(err)
+ }
+ fmt.Printf("var %v = %q\n", path, f.Bytes())
+ }
+ time.Sleep(2 * time.Second)
+ stats := metrics.ReadRuntimeStats()
+ serialize("schedlatency", stats.SchedLatency)
+ serialize("gcpauses", stats.GCPauses)
+}
diff --git a/metrics/json.go b/metrics/json.go
index 2087d8211e..6b134d477b 100644
--- a/metrics/json.go
+++ b/metrics/json.go
@@ -26,6 +26,6 @@ func WriteJSONOnce(r Registry, w io.Writer) {
json.NewEncoder(w).Encode(r)
}
-func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) {
- return json.Marshal(p.GetAll())
+func (r *PrefixedRegistry) MarshalJSON() ([]byte, error) {
+ return json.Marshal(r.GetAll())
}
diff --git a/metrics/json_test.go b/metrics/json_test.go
index cf70051f7a..811bc29f11 100644
--- a/metrics/json_test.go
+++ b/metrics/json_test.go
@@ -12,8 +12,8 @@ func TestRegistryMarshallJSON(t *testing.T) {
r := NewRegistry()
r.Register("counter", NewCounter())
enc.Encode(r)
- if s := b.String(); "{\"counter\":{\"count\":0}}\n" != s {
- t.Fatalf(s)
+ if s := b.String(); s != "{\"counter\":{\"count\":0}}\n" {
+ t.Fatal(s)
}
}
diff --git a/metrics/librato/client.go b/metrics/librato/client.go
deleted file mode 100644
index b74fedfec6..0000000000
--- a/metrics/librato/client.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package librato
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
-)
-
-const Operations = "operations"
-const OperationsShort = "ops"
-
-type LibratoClient struct {
- Email, Token string
-}
-
-// property strings
-const (
- // display attributes
- Color = "color"
- DisplayMax = "display_max"
- DisplayMin = "display_min"
- DisplayUnitsLong = "display_units_long"
- DisplayUnitsShort = "display_units_short"
- DisplayStacked = "display_stacked"
- DisplayTransform = "display_transform"
- // special gauge display attributes
- SummarizeFunction = "summarize_function"
- Aggregate = "aggregate"
-
- // metric keys
- Name = "name"
- Period = "period"
- Description = "description"
- DisplayName = "display_name"
- Attributes = "attributes"
-
- // measurement keys
- MeasureTime = "measure_time"
- Source = "source"
- Value = "value"
-
- // special gauge keys
- Count = "count"
- Sum = "sum"
- Max = "max"
- Min = "min"
- SumSquares = "sum_squares"
-
- // batch keys
- Counters = "counters"
- Gauges = "gauges"
-
- MetricsPostUrl = "https://metrics-api.librato.com/v1/metrics"
-)
-
-type Measurement map[string]interface{}
-type Metric map[string]interface{}
-
-type Batch struct {
- Gauges []Measurement `json:"gauges,omitempty"`
- Counters []Measurement `json:"counters,omitempty"`
- MeasureTime int64 `json:"measure_time"`
- Source string `json:"source"`
-}
-
-func (self *LibratoClient) PostMetrics(batch Batch) (err error) {
- var (
- js []byte
- req *http.Request
- resp *http.Response
- )
-
- if len(batch.Counters) == 0 && len(batch.Gauges) == 0 {
- return nil
- }
-
- if js, err = json.Marshal(batch); err != nil {
- return
- }
-
- if req, err = http.NewRequest("POST", MetricsPostUrl, bytes.NewBuffer(js)); err != nil {
- return
- }
-
- req.Header.Set("Content-Type", "application/json")
- req.SetBasicAuth(self.Email, self.Token)
-
- if resp, err = http.DefaultClient.Do(req); err != nil {
- return
- }
-
- if resp.StatusCode != http.StatusOK {
- var body []byte
- if body, err = io.ReadAll(resp.Body); err != nil {
- body = []byte(fmt.Sprintf("(could not fetch response body for error: %s)", err))
- }
- err = fmt.Errorf("Unable to post to Librato: %d %s %s", resp.StatusCode, resp.Status, string(body))
- }
- return
-}
diff --git a/metrics/librato/librato.go b/metrics/librato/librato.go
deleted file mode 100644
index 58ced3bf9a..0000000000
--- a/metrics/librato/librato.go
+++ /dev/null
@@ -1,235 +0,0 @@
-package librato
-
-import (
- "fmt"
- "log"
- "math"
- "regexp"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/metrics"
-)
-
-// a regexp for extracting the unit from time.Duration.String
-var unitRegexp = regexp.MustCompile(`[^\\d]+$`)
-
-// a helper that turns a time.Duration into librato display attributes for timer metrics
-func translateTimerAttributes(d time.Duration) (attrs map[string]interface{}) {
- attrs = make(map[string]interface{})
- attrs[DisplayTransform] = fmt.Sprintf("x/%d", int64(d))
- attrs[DisplayUnitsShort] = string(unitRegexp.Find([]byte(d.String())))
- return
-}
-
-type Reporter struct {
- Email, Token string
- Namespace string
- Source string
- Interval time.Duration
- Registry metrics.Registry
- Percentiles []float64 // percentiles to report on histogram metrics
- TimerAttributes map[string]interface{} // units in which timers will be displayed
- intervalSec int64
-}
-
-func NewReporter(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) *Reporter {
- return &Reporter{e, t, "", s, d, r, p, translateTimerAttributes(u), int64(d / time.Second)}
-}
-
-func Librato(r metrics.Registry, d time.Duration, e string, t string, s string, p []float64, u time.Duration) {
- NewReporter(r, d, e, t, s, p, u).Run()
-}
-
-func (self *Reporter) Run() {
- log.Printf("WARNING: This client has been DEPRECATED! It has been moved to https://github.com/mihasya/go-metrics-librato and will be removed from rcrowley/go-metrics on August 5th 2015")
- ticker := time.Tick(self.Interval)
- metricsApi := &LibratoClient{self.Email, self.Token}
- for now := range ticker {
- var metrics Batch
- var err error
- if metrics, err = self.BuildRequest(now, self.Registry); err != nil {
- log.Printf("ERROR constructing librato request body %s", err)
- continue
- }
- if err := metricsApi.PostMetrics(metrics); err != nil {
- log.Printf("ERROR sending metrics to librato %s", err)
- continue
- }
- }
-}
-
-// calculate sum of squares from data provided by metrics.Histogram
-// see http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods
-func sumSquares(s metrics.Sample) float64 {
- count := float64(s.Count())
- sumSquared := math.Pow(count*s.Mean(), 2)
- sumSquares := math.Pow(count*s.StdDev(), 2) + sumSquared/count
- if math.IsNaN(sumSquares) {
- return 0.0
- }
- return sumSquares
-}
-func sumSquaresTimer(t metrics.Timer) float64 {
- count := float64(t.Count())
- sumSquared := math.Pow(count*t.Mean(), 2)
- sumSquares := math.Pow(count*t.StdDev(), 2) + sumSquared/count
- if math.IsNaN(sumSquares) {
- return 0.0
- }
- return sumSquares
-}
-
-func (self *Reporter) BuildRequest(now time.Time, r metrics.Registry) (snapshot Batch, err error) {
- snapshot = Batch{
- // coerce timestamps to a stepping fn so that they line up in Librato graphs
- MeasureTime: (now.Unix() / self.intervalSec) * self.intervalSec,
- Source: self.Source,
- }
- snapshot.Gauges = make([]Measurement, 0)
- snapshot.Counters = make([]Measurement, 0)
- histogramGaugeCount := 1 + len(self.Percentiles)
- r.Each(func(name string, metric interface{}) {
- if self.Namespace != "" {
- name = fmt.Sprintf("%s.%s", self.Namespace, name)
- }
- measurement := Measurement{}
- measurement[Period] = self.Interval.Seconds()
- switch m := metric.(type) {
- case metrics.Counter:
- if m.Count() > 0 {
- measurement[Name] = fmt.Sprintf("%s.%s", name, "count")
- measurement[Value] = float64(m.Count())
- measurement[Attributes] = map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- }
- snapshot.Counters = append(snapshot.Counters, measurement)
- }
- case metrics.Gauge:
- measurement[Name] = name
- measurement[Value] = float64(m.Value())
- snapshot.Gauges = append(snapshot.Gauges, measurement)
- case metrics.GaugeFloat64:
- measurement[Name] = name
- measurement[Value] = m.Value()
- snapshot.Gauges = append(snapshot.Gauges, measurement)
- case metrics.Histogram:
- if m.Count() > 0 {
- gauges := make([]Measurement, histogramGaugeCount)
- s := m.Sample()
- measurement[Name] = fmt.Sprintf("%s.%s", name, "hist")
- measurement[Count] = uint64(s.Count())
- measurement[Max] = float64(s.Max())
- measurement[Min] = float64(s.Min())
- measurement[Sum] = float64(s.Sum())
- measurement[SumSquares] = sumSquares(s)
- gauges[0] = measurement
- for i, p := range self.Percentiles {
- gauges[i+1] = Measurement{
- Name: fmt.Sprintf("%s.%.2f", measurement[Name], p),
- Value: s.Percentile(p),
- Period: measurement[Period],
- }
- }
- snapshot.Gauges = append(snapshot.Gauges, gauges...)
- }
- case metrics.Meter:
- measurement[Name] = name
- measurement[Value] = float64(m.Count())
- snapshot.Counters = append(snapshot.Counters, measurement)
- snapshot.Gauges = append(snapshot.Gauges,
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "1min"),
- Value: m.Rate1(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "5min"),
- Value: m.Rate5(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "15min"),
- Value: m.Rate15(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- )
- case metrics.Timer:
- measurement[Name] = name
- measurement[Value] = float64(m.Count())
- snapshot.Counters = append(snapshot.Counters, measurement)
- if m.Count() > 0 {
- libratoName := fmt.Sprintf("%s.%s", name, "timer.mean")
- gauges := make([]Measurement, histogramGaugeCount)
- gauges[0] = Measurement{
- Name: libratoName,
- Count: uint64(m.Count()),
- Sum: m.Mean() * float64(m.Count()),
- Max: float64(m.Max()),
- Min: float64(m.Min()),
- SumSquares: sumSquaresTimer(m),
- Period: int64(self.Interval.Seconds()),
- Attributes: self.TimerAttributes,
- }
- for i, p := range self.Percentiles {
- gauges[i+1] = Measurement{
- Name: fmt.Sprintf("%s.timer.%2.0f", name, p*100),
- Value: m.Percentile(p),
- Period: int64(self.Interval.Seconds()),
- Attributes: self.TimerAttributes,
- }
- }
- snapshot.Gauges = append(snapshot.Gauges, gauges...)
- snapshot.Gauges = append(snapshot.Gauges,
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.1min"),
- Value: m.Rate1(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.5min"),
- Value: m.Rate5(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- Measurement{
- Name: fmt.Sprintf("%s.%s", name, "rate.15min"),
- Value: m.Rate15(),
- Period: int64(self.Interval.Seconds()),
- Attributes: map[string]interface{}{
- DisplayUnitsLong: Operations,
- DisplayUnitsShort: OperationsShort,
- DisplayMin: "0",
- },
- },
- )
- }
- }
- })
- return
-}
diff --git a/metrics/log.go b/metrics/log.go
index 0c8ea7c971..3380bbf9c4 100644
--- a/metrics/log.go
+++ b/metrics/log.go
@@ -21,19 +21,21 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) {
for range time.Tick(freq) {
r.Each(func(name string, i interface{}) {
switch metric := i.(type) {
- case Counter:
+ case *Counter:
l.Printf("counter %s\n", name)
- l.Printf(" count: %9d\n", metric.Count())
- case Gauge:
+ l.Printf(" count: %9d\n", metric.Snapshot().Count())
+ case *CounterFloat64:
+ l.Printf("counter %s\n", name)
+ l.Printf(" count: %f\n", metric.Snapshot().Count())
+ case *Gauge:
l.Printf("gauge %s\n", name)
- l.Printf(" value: %9d\n", metric.Value())
- case GaugeFloat64:
+ l.Printf(" value: %9d\n", metric.Snapshot().Value())
+ case *GaugeFloat64:
l.Printf("gauge %s\n", name)
- l.Printf(" value: %f\n", metric.Value())
- case Healthcheck:
- metric.Check()
- l.Printf("healthcheck %s\n", name)
- l.Printf(" error: %v\n", metric.Error())
+ l.Printf(" value: %f\n", metric.Snapshot().Value())
+ case *GaugeInfo:
+ l.Printf("gauge %s\n", name)
+ l.Printf(" value: %s\n", metric.Snapshot().Value())
case Histogram:
h := metric.Snapshot()
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
@@ -48,7 +50,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) {
l.Printf(" 95%%: %12.2f\n", ps[2])
l.Printf(" 99%%: %12.2f\n", ps[3])
l.Printf(" 99.9%%: %12.2f\n", ps[4])
- case Meter:
+ case *Meter:
m := metric.Snapshot()
l.Printf("meter %s\n", name)
l.Printf(" count: %9d\n", m.Count())
@@ -56,7 +58,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) {
l.Printf(" 5-min rate: %12.2f\n", m.Rate5())
l.Printf(" 15-min rate: %12.2f\n", m.Rate15())
l.Printf(" mean rate: %12.2f\n", m.RateMean())
- case Timer:
+ case *Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
l.Printf("timer %s\n", name)
diff --git a/metrics/meter.go b/metrics/meter.go
index 82b2141a62..194bd1f304 100644
--- a/metrics/meter.go
+++ b/metrics/meter.go
@@ -1,65 +1,46 @@
package metrics
import (
+ "math"
"sync"
+ "sync/atomic"
"time"
)
-// Meters count events to produce exponentially-weighted moving average rates
-// at one-, five-, and fifteen-minutes and a mean rate.
-type Meter interface {
- Count() int64
- Mark(int64)
- Rate1() float64
- Rate5() float64
- Rate15() float64
- RateMean() float64
- Snapshot() Meter
- Stop()
-}
-
// GetOrRegisterMeter returns an existing Meter or constructs and registers a
-// new StandardMeter.
+// new Meter.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
-func GetOrRegisterMeter(name string, r Registry) Meter {
- if nil == r {
+func GetOrRegisterMeter(name string, r Registry) *Meter {
+ if r == nil {
r = DefaultRegistry
}
- return r.GetOrRegister(name, NewMeter).(Meter)
+ return r.GetOrRegister(name, NewMeter).(*Meter)
}
-// NewMeter constructs a new StandardMeter and launches a goroutine.
+// NewMeter constructs a new Meter and launches a goroutine.
// Be sure to call Stop() once the meter is of no use to allow for garbage collection.
-func NewMeter() Meter {
- if !Enabled {
- return NilMeter{}
- }
- m := newStandardMeter()
- arbiter.Lock()
- defer arbiter.Unlock()
- arbiter.meters[m] = struct{}{}
- if !arbiter.started {
- arbiter.started = true
- go arbiter.tick()
- }
+func NewMeter() *Meter {
+ m := newMeter()
+ arbiter.add(m)
return m
}
-// NewMeter constructs and registers a new StandardMeter and launches a
-// goroutine.
-// Be sure to unregister the meter from the registry once it is of no use to
-// allow for garbage collection.
-func NewRegisteredMeter(name string, r Registry) Meter {
- c := NewMeter()
- if nil == r {
- r = DefaultRegistry
- }
- r.Register(name, c)
- return c
+// NewInactiveMeter returns a meter but does not start any goroutines. This
+// method is mainly intended for testing.
+func NewInactiveMeter() *Meter {
+ return newMeter()
}
-// MeterSnapshot is a read-only copy of another Meter.
+// NewRegisteredMeter constructs and registers a new Meter
+// and launches a goroutine.
+// Be sure to unregister the meter from the registry once it is of no use to
+// allow for garbage collection.
+func NewRegisteredMeter(name string, r Registry) *Meter {
+ return GetOrRegisterMeter(name, r)
+}
+
+// MeterSnapshot is a read-only copy of the meter's internal values.
type MeterSnapshot struct {
count int64
rate1, rate5, rate15, rateMean float64
@@ -68,11 +49,6 @@ type MeterSnapshot struct {
// Count returns the count of events at the time the snapshot was taken.
func (m *MeterSnapshot) Count() int64 { return m.count }
-// Mark panics.
-func (*MeterSnapshot) Mark(n int64) {
- panic("Mark called on a MeterSnapshot")
-}
-
// Rate1 returns the one-minute moving average rate of events per second at the
// time the snapshot was taken.
func (m *MeterSnapshot) Rate1() float64 { return m.rate1 }
@@ -89,51 +65,20 @@ func (m *MeterSnapshot) Rate15() float64 { return m.rate15 }
// snapshot was taken.
func (m *MeterSnapshot) RateMean() float64 { return m.rateMean }
-// Snapshot returns the snapshot.
-func (m *MeterSnapshot) Snapshot() Meter { return m }
+// Meter count events to produce exponentially-weighted moving average rates
+// at one-, five-, and fifteen-minutes and a mean rate.
+type Meter struct {
+ count atomic.Int64
+ uncounted atomic.Int64 // not yet added to the EWMAs
+ rateMean atomic.Uint64
-// Stop is a no-op.
-func (m *MeterSnapshot) Stop() {}
-
-// NilMeter is a no-op Meter.
-type NilMeter struct{}
-
-// Count is a no-op.
-func (NilMeter) Count() int64 { return 0 }
-
-// Mark is a no-op.
-func (NilMeter) Mark(n int64) {}
-
-// Rate1 is a no-op.
-func (NilMeter) Rate1() float64 { return 0.0 }
-
-// Rate5 is a no-op.
-func (NilMeter) Rate5() float64 { return 0.0 }
-
-// Rate15is a no-op.
-func (NilMeter) Rate15() float64 { return 0.0 }
-
-// RateMean is a no-op.
-func (NilMeter) RateMean() float64 { return 0.0 }
-
-// Snapshot is a no-op.
-func (NilMeter) Snapshot() Meter { return NilMeter{} }
-
-// Stop is a no-op.
-func (NilMeter) Stop() {}
-
-// StandardMeter is the standard implementation of a Meter.
-type StandardMeter struct {
- lock sync.RWMutex
- snapshot *MeterSnapshot
- a1, a5, a15 EWMA
+ a1, a5, a15 *EWMA
startTime time.Time
- stopped bool
+ stopped atomic.Bool
}
-func newStandardMeter() *StandardMeter {
- return &StandardMeter{
- snapshot: &MeterSnapshot{},
+func newMeter() *Meter {
+ return &Meter{
a1: NewEWMA1(),
a5: NewEWMA5(),
a15: NewEWMA15(),
@@ -142,120 +87,83 @@ func newStandardMeter() *StandardMeter {
}
// Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
-func (m *StandardMeter) Stop() {
- m.lock.Lock()
- stopped := m.stopped
- m.stopped = true
- m.lock.Unlock()
- if !stopped {
- arbiter.Lock()
- delete(arbiter.meters, m)
- arbiter.Unlock()
+func (m *Meter) Stop() {
+ if stopped := m.stopped.Swap(true); !stopped {
+ arbiter.remove(m)
}
}
-// Count returns the number of events recorded.
-func (m *StandardMeter) Count() int64 {
- m.lock.RLock()
- count := m.snapshot.count
- m.lock.RUnlock()
- return count
-}
-
// Mark records the occurrence of n events.
-func (m *StandardMeter) Mark(n int64) {
- m.lock.Lock()
- defer m.lock.Unlock()
- if m.stopped {
- return
- }
- m.snapshot.count += n
- m.a1.Update(n)
- m.a5.Update(n)
- m.a15.Update(n)
- m.updateSnapshot()
-}
-
-// Rate1 returns the one-minute moving average rate of events per second.
-func (m *StandardMeter) Rate1() float64 {
- m.lock.RLock()
- rate1 := m.snapshot.rate1
- m.lock.RUnlock()
- return rate1
-}
-
-// Rate5 returns the five-minute moving average rate of events per second.
-func (m *StandardMeter) Rate5() float64 {
- m.lock.RLock()
- rate5 := m.snapshot.rate5
- m.lock.RUnlock()
- return rate5
-}
-
-// Rate15 returns the fifteen-minute moving average rate of events per second.
-func (m *StandardMeter) Rate15() float64 {
- m.lock.RLock()
- rate15 := m.snapshot.rate15
- m.lock.RUnlock()
- return rate15
-}
-
-// RateMean returns the meter's mean rate of events per second.
-func (m *StandardMeter) RateMean() float64 {
- m.lock.RLock()
- rateMean := m.snapshot.rateMean
- m.lock.RUnlock()
- return rateMean
+func (m *Meter) Mark(n int64) {
+ m.uncounted.Add(n)
}
// Snapshot returns a read-only copy of the meter.
-func (m *StandardMeter) Snapshot() Meter {
- m.lock.RLock()
- snapshot := *m.snapshot
- m.lock.RUnlock()
- return &snapshot
+func (m *Meter) Snapshot() *MeterSnapshot {
+ return &MeterSnapshot{
+ count: m.count.Load() + m.uncounted.Load(),
+ rate1: m.a1.Snapshot().Rate(),
+ rate5: m.a5.Snapshot().Rate(),
+ rate15: m.a15.Snapshot().Rate(),
+ rateMean: math.Float64frombits(m.rateMean.Load()),
+ }
}
-func (m *StandardMeter) updateSnapshot() {
- // should run with write lock held on m.lock
- snapshot := m.snapshot
- snapshot.rate1 = m.a1.Rate()
- snapshot.rate5 = m.a5.Rate()
- snapshot.rate15 = m.a15.Rate()
- snapshot.rateMean = float64(snapshot.count) / time.Since(m.startTime).Seconds()
+func (m *Meter) tick() {
+ // Take the uncounted values, add to count
+ n := m.uncounted.Swap(0)
+ count := m.count.Add(n)
+ m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds()))
+ // Update the EWMA's internal state
+ m.a1.Update(n)
+ m.a5.Update(n)
+ m.a15.Update(n)
+ // And trigger them to calculate the rates
+ m.a1.tick()
+ m.a5.tick()
+ m.a15.tick()
}
-func (m *StandardMeter) tick() {
- m.lock.Lock()
- defer m.lock.Unlock()
- m.a1.Tick()
- m.a5.Tick()
- m.a15.Tick()
- m.updateSnapshot()
-}
+var arbiter = meterTicker{meters: make(map[*Meter]struct{})}
-// meterArbiter ticks meters every 5s from a single goroutine.
+// meterTicker ticks meters every 5s from a single goroutine.
// meters are references in a set for future stopping.
-type meterArbiter struct {
- sync.RWMutex
+type meterTicker struct {
+ mu sync.RWMutex
+
started bool
- meters map[*StandardMeter]struct{}
- ticker *time.Ticker
+ meters map[*Meter]struct{}
}
-var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})}
-
-// Ticks meters on the scheduled interval
-func (ma *meterArbiter) tick() {
- for range ma.ticker.C {
- ma.tickMeters()
+// add adds another *Meter ot the arbiter, and starts the arbiter ticker.
+func (ma *meterTicker) add(m *Meter) {
+ ma.mu.Lock()
+ defer ma.mu.Unlock()
+ ma.meters[m] = struct{}{}
+ if !ma.started {
+ ma.started = true
+ go ma.loop()
}
}
-func (ma *meterArbiter) tickMeters() {
- ma.RLock()
- defer ma.RUnlock()
- for meter := range ma.meters {
- meter.tick()
+// remove removes a meter from the set of ticked meters.
+func (ma *meterTicker) remove(m *Meter) {
+ ma.mu.Lock()
+ delete(ma.meters, m)
+ ma.mu.Unlock()
+}
+
+// loop ticks meters on a 5 second interval.
+func (ma *meterTicker) loop() {
+ ticker := time.NewTicker(5 * time.Second)
+ for range ticker.C {
+ if !metricsEnabled {
+ continue
+ }
+ ma.mu.RLock()
+ for meter := range ma.meters {
+ meter.tick()
+ }
+ ma.mu.RUnlock()
}
}
diff --git a/metrics/meter_test.go b/metrics/meter_test.go
index e889222601..d62cf38640 100644
--- a/metrics/meter_test.go
+++ b/metrics/meter_test.go
@@ -12,27 +12,29 @@ func BenchmarkMeter(b *testing.B) {
m.Mark(1)
}
}
-
+func TestMeter(t *testing.T) {
+ m := NewMeter()
+ m.Mark(47)
+ if v := m.Snapshot().Count(); v != 47 {
+ t.Fatalf("have %d want %d", v, 47)
+ }
+}
func TestGetOrRegisterMeter(t *testing.T) {
r := NewRegistry()
NewRegisteredMeter("foo", r).Mark(47)
- if m := GetOrRegisterMeter("foo", r); 47 != m.Count() {
- t.Fatal(m)
+ if m := GetOrRegisterMeter("foo", r).Snapshot(); m.Count() != 47 {
+ t.Fatal(m.Count())
}
}
func TestMeterDecay(t *testing.T) {
- ma := meterArbiter{
- ticker: time.NewTicker(time.Millisecond),
- meters: make(map[*StandardMeter]struct{}),
- }
- m := newStandardMeter()
- ma.meters[m] = struct{}{}
- go ma.tick()
+ m := newMeter()
m.Mark(1)
- rateMean := m.RateMean()
+ m.tick()
+ rateMean := m.Snapshot().RateMean()
time.Sleep(100 * time.Millisecond)
- if m.RateMean() >= rateMean {
+ m.tick()
+ if m.Snapshot().RateMean() >= rateMean {
t.Error("m.RateMean() didn't decrease")
}
}
@@ -40,7 +42,7 @@ func TestMeterDecay(t *testing.T) {
func TestMeterNonzero(t *testing.T) {
m := NewMeter()
m.Mark(3)
- if count := m.Count(); 3 != count {
+ if count := m.Snapshot().Count(); count != 3 {
t.Errorf("m.Count(): 3 != %v\n", count)
}
}
@@ -57,17 +59,25 @@ func TestMeterStop(t *testing.T) {
}
}
-func TestMeterSnapshot(t *testing.T) {
- m := NewMeter()
- m.Mark(1)
- if snapshot := m.Snapshot(); m.RateMean() != snapshot.RateMean() {
- t.Fatal(snapshot)
- }
-}
-
func TestMeterZero(t *testing.T) {
- m := NewMeter()
- if count := m.Count(); 0 != count {
+ m := NewMeter().Snapshot()
+ if count := m.Count(); count != 0 {
t.Errorf("m.Count(): 0 != %v\n", count)
}
}
+
+func TestMeterRepeat(t *testing.T) {
+ m := NewMeter()
+ for i := 0; i < 101; i++ {
+ m.Mark(int64(i))
+ }
+ if count := m.Snapshot().Count(); count != 5050 {
+ t.Errorf("m.Count(): 5050 != %v\n", count)
+ }
+ for i := 0; i < 101; i++ {
+ m.Mark(int64(i))
+ }
+ if count := m.Snapshot().Count(); count != 10100 {
+ t.Errorf("m.Count(): 10100 != %v\n", count)
+ }
+}
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 2d9d652a02..a9d6623173 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -6,79 +6,197 @@
package metrics
import (
- "os"
- "runtime"
- "strings"
+ "runtime/metrics"
+ "runtime/pprof"
"time"
-
- "github.com/XinFinOrg/XDPoSChain/log"
)
-// Enabled is checked by the constructor functions for all of the
-// standard metrics. If it is true, the metric returned is a stub.
+var (
+ metricsEnabled = false
+)
+
+// Enabled is checked by functions that are deemed 'expensive', e.g. if a
+// meter-type does locking and/or non-trivial math operations during update.
+func Enabled() bool {
+ return metricsEnabled
+}
+
+// Enable enables the metrics system.
+// The Enabled-flag is expected to be set, once, during startup, but toggling off and on
+// is not supported.
//
-// This global kill-switch helps quantify the observer effect and makes
-// for less cluttered pprof profiles.
-var Enabled bool = false
+// Enable is not safe to call concurrently. You need to call this as early as possible in
+// the program, before any metrics collection will happen.
+func Enable() {
+ metricsEnabled = true
+}
-// MetricsEnabledFlag is the CLI flag name to use to enable metrics collections.
-const MetricsEnabledFlag = "metrics"
+var threadCreateProfile = pprof.Lookup("threadcreate")
-// Init enables or disables the metrics system. Since we need this to run before
-// any other code gets to create meters and timers, we'll actually do an ugly hack
-// and peek into the command line args for the metrics flag.
-func init() {
- for _, arg := range os.Args {
- if flag := strings.TrimLeft(arg, "-"); flag == MetricsEnabledFlag {
- log.Info("Enabling metrics collection")
- Enabled = true
+type runtimeStats struct {
+ GCPauses *metrics.Float64Histogram
+ GCAllocBytes uint64
+ GCFreedBytes uint64
+
+ MemTotal uint64
+ HeapObjects uint64
+ HeapFree uint64
+ HeapReleased uint64
+ HeapUnused uint64
+
+ Goroutines uint64
+ SchedLatency *metrics.Float64Histogram
+}
+
+var runtimeSamples = []metrics.Sample{
+ {Name: "/gc/pauses:seconds"}, // histogram
+ {Name: "/gc/heap/allocs:bytes"},
+ {Name: "/gc/heap/frees:bytes"},
+ {Name: "/memory/classes/total:bytes"},
+ {Name: "/memory/classes/heap/objects:bytes"},
+ {Name: "/memory/classes/heap/free:bytes"},
+ {Name: "/memory/classes/heap/released:bytes"},
+ {Name: "/memory/classes/heap/unused:bytes"},
+ {Name: "/sched/goroutines:goroutines"},
+ {Name: "/sched/latencies:seconds"}, // histogram
+}
+
+func ReadRuntimeStats() *runtimeStats {
+ r := new(runtimeStats)
+ readRuntimeStats(r)
+ return r
+}
+
+func readRuntimeStats(v *runtimeStats) {
+ metrics.Read(runtimeSamples)
+ for _, s := range runtimeSamples {
+ // Skip invalid/unknown metrics. This is needed because some metrics
+ // are unavailable in older Go versions, and attempting to read a 'bad'
+ // metric panics.
+ if s.Value.Kind() == metrics.KindBad {
+ continue
+ }
+
+ switch s.Name {
+ case "/gc/pauses:seconds":
+ v.GCPauses = s.Value.Float64Histogram()
+ case "/gc/heap/allocs:bytes":
+ v.GCAllocBytes = s.Value.Uint64()
+ case "/gc/heap/frees:bytes":
+ v.GCFreedBytes = s.Value.Uint64()
+ case "/memory/classes/total:bytes":
+ v.MemTotal = s.Value.Uint64()
+ case "/memory/classes/heap/objects:bytes":
+ v.HeapObjects = s.Value.Uint64()
+ case "/memory/classes/heap/free:bytes":
+ v.HeapFree = s.Value.Uint64()
+ case "/memory/classes/heap/released:bytes":
+ v.HeapReleased = s.Value.Uint64()
+ case "/memory/classes/heap/unused:bytes":
+ v.HeapUnused = s.Value.Uint64()
+ case "/sched/goroutines:goroutines":
+ v.Goroutines = s.Value.Uint64()
+ case "/sched/latencies:seconds":
+ v.SchedLatency = s.Value.Float64Histogram()
}
}
}
-// CollectProcessMetrics periodically collects various metrics about the running
-// process.
+// CollectProcessMetrics periodically collects various metrics about the running process.
func CollectProcessMetrics(refresh time.Duration) {
// Short circuit if the metrics system is disabled
- if !Enabled {
+ if !metricsEnabled {
return
}
+
// Create the various data collectors
- memstats := make([]*runtime.MemStats, 2)
- diskstats := make([]*DiskStats, 2)
- for i := 0; i < len(memstats); i++ {
- memstats[i] = new(runtime.MemStats)
- diskstats[i] = new(DiskStats)
- }
+ var (
+ cpustats = make([]CPUStats, 2)
+ diskstats = make([]DiskStats, 2)
+ rstats = make([]runtimeStats, 2)
+ )
+
+ // This scale factor is used for the runtime's time metrics. It's useful to convert to
+ // ns here because the runtime gives times in float seconds, but runtimeHistogram can
+ // only provide integers for the minimum and maximum values.
+ const secondsToNs = float64(time.Second)
+
// Define the various metrics to collect
- memAllocs := GetOrRegisterMeter("system/memory/allocs", DefaultRegistry)
- memFrees := GetOrRegisterMeter("system/memory/frees", DefaultRegistry)
- memInuse := GetOrRegisterMeter("system/memory/inuse", DefaultRegistry)
- memPauses := GetOrRegisterMeter("system/memory/pauses", DefaultRegistry)
+ var (
+ cpuSysLoad = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry)
+ cpuSysWait = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry)
+ cpuProcLoad = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry)
+ cpuSysLoadTotal = GetOrRegisterCounterFloat64("system/cpu/sysload/total", DefaultRegistry)
+ cpuSysWaitTotal = GetOrRegisterCounterFloat64("system/cpu/syswait/total", DefaultRegistry)
+ cpuProcLoadTotal = GetOrRegisterCounterFloat64("system/cpu/procload/total", DefaultRegistry)
+ cpuThreads = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry)
+ cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry)
+ cpuSchedLatency = getOrRegisterRuntimeHistogram("system/cpu/schedlatency", secondsToNs, nil)
+ memPauses = getOrRegisterRuntimeHistogram("system/memory/pauses", secondsToNs, nil)
+ memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry)
+ memFrees = GetOrRegisterMeter("system/memory/frees", DefaultRegistry)
+ memTotal = GetOrRegisterGauge("system/memory/held", DefaultRegistry)
+ heapUsed = GetOrRegisterGauge("system/memory/used", DefaultRegistry)
+ heapObjects = GetOrRegisterGauge("system/memory/objects", DefaultRegistry)
+ diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry)
+ diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry)
+ diskReadBytesCounter = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry)
+ diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry)
+ diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry)
+ diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry)
+ )
- var diskReads, diskReadBytes, diskWrites, diskWriteBytes Meter
- if err := ReadDiskStats(diskstats[0]); err == nil {
- diskReads = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry)
- diskReadBytes = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry)
- diskWrites = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry)
- diskWriteBytes = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry)
- } else {
- log.Debug("Failed to read disk metrics", "err", err)
- }
- // Iterate loading the different stats and updating the meters
- for i := 1; ; i++ {
- runtime.ReadMemStats(memstats[i%2])
- memAllocs.Mark(int64(memstats[i%2].Mallocs - memstats[(i-1)%2].Mallocs))
- memFrees.Mark(int64(memstats[i%2].Frees - memstats[(i-1)%2].Frees))
- memInuse.Mark(int64(memstats[i%2].Alloc - memstats[(i-1)%2].Alloc))
- memPauses.Mark(int64(memstats[i%2].PauseTotalNs - memstats[(i-1)%2].PauseTotalNs))
+ var lastCollectTime time.Time
- if ReadDiskStats(diskstats[i%2]) == nil {
- diskReads.Mark(diskstats[i%2].ReadCount - diskstats[(i-1)%2].ReadCount)
- diskReadBytes.Mark(diskstats[i%2].ReadBytes - diskstats[(i-1)%2].ReadBytes)
- diskWrites.Mark(diskstats[i%2].WriteCount - diskstats[(i-1)%2].WriteCount)
- diskWriteBytes.Mark(diskstats[i%2].WriteBytes - diskstats[(i-1)%2].WriteBytes)
+ // Iterate loading the different stats and updating the meters.
+ now, prev := 0, 1
+ for ; ; now, prev = prev, now {
+ // Gather CPU times.
+ ReadCPUStats(&cpustats[now])
+ collectTime := time.Now()
+ secondsSinceLastCollect := collectTime.Sub(lastCollectTime).Seconds()
+ lastCollectTime = collectTime
+ if secondsSinceLastCollect > 0 {
+ sysLoad := cpustats[now].GlobalTime - cpustats[prev].GlobalTime
+ sysWait := cpustats[now].GlobalWait - cpustats[prev].GlobalWait
+ procLoad := cpustats[now].LocalTime - cpustats[prev].LocalTime
+ // Convert to integer percentage.
+ cpuSysLoad.Update(int64(sysLoad / secondsSinceLastCollect * 100))
+ cpuSysWait.Update(int64(sysWait / secondsSinceLastCollect * 100))
+ cpuProcLoad.Update(int64(procLoad / secondsSinceLastCollect * 100))
+ // increment counters (ms)
+ cpuSysLoadTotal.Inc(sysLoad)
+ cpuSysWaitTotal.Inc(sysWait)
+ cpuProcLoadTotal.Inc(procLoad)
}
+
+ // Threads
+ cpuThreads.Update(int64(threadCreateProfile.Count()))
+
+ // Go runtime metrics
+ readRuntimeStats(&rstats[now])
+
+ cpuGoroutines.Update(int64(rstats[now].Goroutines))
+ cpuSchedLatency.update(rstats[now].SchedLatency)
+ memPauses.update(rstats[now].GCPauses)
+
+ memAllocs.Mark(int64(rstats[now].GCAllocBytes - rstats[prev].GCAllocBytes))
+ memFrees.Mark(int64(rstats[now].GCFreedBytes - rstats[prev].GCFreedBytes))
+
+ memTotal.Update(int64(rstats[now].MemTotal))
+ heapUsed.Update(int64(rstats[now].MemTotal - rstats[now].HeapUnused - rstats[now].HeapFree - rstats[now].HeapReleased))
+ heapObjects.Update(int64(rstats[now].HeapObjects))
+
+ // Disk
+ if ReadDiskStats(&diskstats[now]) == nil {
+ diskReads.Mark(diskstats[now].ReadCount - diskstats[prev].ReadCount)
+ diskReadBytes.Mark(diskstats[now].ReadBytes - diskstats[prev].ReadBytes)
+ diskWrites.Mark(diskstats[now].WriteCount - diskstats[prev].WriteCount)
+ diskWriteBytes.Mark(diskstats[now].WriteBytes - diskstats[prev].WriteBytes)
+ diskReadBytesCounter.Inc(diskstats[now].ReadBytes - diskstats[prev].ReadBytes)
+ diskWriteBytesCounter.Inc(diskstats[now].WriteBytes - diskstats[prev].WriteBytes)
+ }
+
time.Sleep(refresh)
}
}
diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go
index 029c99870e..dc144f2425 100644
--- a/metrics/metrics_test.go
+++ b/metrics/metrics_test.go
@@ -2,110 +2,47 @@ package metrics
import (
"fmt"
- "io"
- "log"
"sync"
"testing"
"time"
)
-const FANOUT = 128
-
-// Stop the compiler from complaining during debugging.
-var (
- _ = io.Discard
- _ = log.LstdFlags
-)
+func TestReadRuntimeValues(t *testing.T) {
+ var v runtimeStats
+ readRuntimeStats(&v)
+ t.Logf("%+v", v)
+}
func BenchmarkMetrics(b *testing.B) {
- r := NewRegistry()
- c := NewRegisteredCounter("counter", r)
- g := NewRegisteredGauge("gauge", r)
- gf := NewRegisteredGaugeFloat64("gaugefloat64", r)
- h := NewRegisteredHistogram("histogram", r, NewUniformSample(100))
- m := NewRegisteredMeter("meter", r)
- t := NewRegisteredTimer("timer", r)
+ var (
+ r = NewRegistry()
+ c = NewRegisteredCounter("counter", r)
+ cf = NewRegisteredCounterFloat64("counterfloat64", r)
+ g = NewRegisteredGauge("gauge", r)
+ gf = NewRegisteredGaugeFloat64("gaugefloat64", r)
+ h = NewRegisteredHistogram("histogram", r, NewUniformSample(100))
+ m = NewRegisteredMeter("meter", r)
+ t = NewRegisteredTimer("timer", r)
+ )
RegisterDebugGCStats(r)
- RegisterRuntimeMemStats(r)
b.ResetTimer()
- ch := make(chan bool)
-
- wgD := &sync.WaitGroup{}
- /*
- wgD.Add(1)
+ var wg sync.WaitGroup
+ wg.Add(128)
+ for i := 0; i < 128; i++ {
go func() {
- defer wgD.Done()
- //log.Println("go CaptureDebugGCStats")
- for {
- select {
- case <-ch:
- //log.Println("done CaptureDebugGCStats")
- return
- default:
- CaptureDebugGCStatsOnce(r)
- }
- }
- }()
- //*/
-
- wgR := &sync.WaitGroup{}
- //*
- wgR.Add(1)
- go func() {
- defer wgR.Done()
- //log.Println("go CaptureRuntimeMemStats")
- for {
- select {
- case <-ch:
- //log.Println("done CaptureRuntimeMemStats")
- return
- default:
- CaptureRuntimeMemStatsOnce(r)
- }
- }
- }()
- //*/
-
- wgW := &sync.WaitGroup{}
- /*
- wgW.Add(1)
- go func() {
- defer wgW.Done()
- //log.Println("go Write")
- for {
- select {
- case <-ch:
- //log.Println("done Write")
- return
- default:
- WriteOnce(r, io.Discard)
- }
- }
- }()
- //*/
-
- wg := &sync.WaitGroup{}
- wg.Add(FANOUT)
- for i := 0; i < FANOUT; i++ {
- go func(i int) {
defer wg.Done()
- //log.Println("go", i)
for i := 0; i < b.N; i++ {
c.Inc(1)
+ cf.Inc(1.0)
g.Update(int64(i))
gf.Update(float64(i))
h.Update(int64(i))
m.Mark(1)
t.Update(1)
}
- //log.Println("done", i)
- }(i)
+ }()
}
wg.Wait()
- close(ch)
- wgD.Wait()
- wgR.Wait()
- wgW.Wait()
}
func Example() {
@@ -118,8 +55,8 @@ func Example() {
t.Time(func() { time.Sleep(10 * time.Millisecond) })
t.Update(1)
- fmt.Println(c.Count())
- fmt.Println(t.Min())
+ fmt.Println(c.Snapshot().Count())
+ fmt.Println(t.Snapshot().Min())
// Output: 17
// 1
}
diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go
index df7f152ed2..57af3d025e 100644
--- a/metrics/opentsdb.go
+++ b/metrics/opentsdb.go
@@ -3,6 +3,7 @@ package metrics
import (
"bufio"
"fmt"
+ "io"
"log"
"net"
"os"
@@ -10,7 +11,7 @@ import (
"time"
)
-var shortHostName string = ""
+var shortHostName = ""
// OpenTSDBConfig provides a container with configuration parameters for
// the OpenTSDB exporter
@@ -57,24 +58,22 @@ func getShortHostname() string {
return shortHostName
}
-func openTSDB(c *OpenTSDBConfig) error {
- shortHostname := getShortHostname()
- now := time.Now().Unix()
+// writeRegistry writes the registry-metrics on the opentsb format.
+func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname string) {
du := float64(c.DurationUnit)
- conn, err := net.DialTCP("tcp", nil, c.Addr)
- if nil != err {
- return err
- }
- defer conn.Close()
- w := bufio.NewWriter(conn)
+
c.Registry.Each(func(name string, i interface{}) {
switch metric := i.(type) {
- case Counter:
- fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname)
- case Gauge:
- fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
- case GaugeFloat64:
- fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname)
+ case *Counter:
+ fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname)
+ case *CounterFloat64:
+ fmt.Fprintf(w, "put %s.%s.count %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Count(), shortHostname)
+ case *Gauge:
+ fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname)
+ case *GaugeFloat64:
+ fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Snapshot().Value(), shortHostname)
+ case *GaugeInfo:
+ fmt.Fprintf(w, "put %s.%s.value %d %s host=%s\n", c.Prefix, name, now, metric.Snapshot().Value().String(), shortHostname)
case Histogram:
h := metric.Snapshot()
ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
@@ -88,14 +87,14 @@ func openTSDB(c *OpenTSDBConfig) error {
fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname)
fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname)
fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname)
- case Meter:
+ case *Meter:
m := metric.Snapshot()
fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname)
fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname)
fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname)
fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname)
fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname)
- case Timer:
+ case *Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname)
@@ -113,7 +112,17 @@ func openTSDB(c *OpenTSDBConfig) error {
fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname)
fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname)
}
- w.Flush()
})
+}
+
+func openTSDB(c *OpenTSDBConfig) error {
+ conn, err := net.DialTCP("tcp", nil, c.Addr)
+ if nil != err {
+ return err
+ }
+ defer conn.Close()
+ w := bufio.NewWriter(conn)
+ c.writeRegistry(w, time.Now().Unix(), getShortHostname())
+ w.Flush()
return nil
}
diff --git a/metrics/opentsdb_test.go b/metrics/opentsdb_test.go
index c43728960e..4548309f9c 100644
--- a/metrics/opentsdb_test.go
+++ b/metrics/opentsdb_test.go
@@ -1,7 +1,11 @@
package metrics
import (
+ "fmt"
"net"
+ "os"
+ "strings"
+ "testing"
"time"
)
@@ -19,3 +23,44 @@ func ExampleOpenTSDBWithConfig() {
DurationUnit: time.Millisecond,
})
}
+
+func TestExampleOpenTSB(t *testing.T) {
+ r := NewOrderedRegistry()
+ NewRegisteredGaugeInfo("foo", r).Update(GaugeInfoValue{"chain_id": "5"})
+ NewRegisteredGaugeFloat64("pi", r).Update(3.14)
+ NewRegisteredCounter("months", r).Inc(12)
+ NewRegisteredCounterFloat64("tau", r).Inc(1.57)
+ NewRegisteredMeter("elite", r).Mark(1337)
+ NewRegisteredTimer("second", r).Update(time.Second)
+ NewRegisteredCounterFloat64("tau", r).Inc(1.57)
+ NewRegisteredCounterFloat64("tau", r).Inc(1.57)
+
+ w := new(strings.Builder)
+ (&OpenTSDBConfig{
+ Registry: r,
+ DurationUnit: time.Millisecond,
+ Prefix: "pre",
+ }).writeRegistry(w, 978307200, "hal9000")
+
+ wantB, err := os.ReadFile("./testdata/opentsb.want")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have, want := w.String(), string(wantB); have != want {
+ t.Errorf("\nhave:\n%v\nwant:\n%v\n", have, want)
+ t.Logf("have vs want:\n%v", findFirstDiffPos(have, want))
+ }
+}
+
+func findFirstDiffPos(a, b string) string {
+ yy := strings.Split(b, "\n")
+ for i, x := range strings.Split(a, "\n") {
+ if i >= len(yy) {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i)
+ }
+ if y := yy[i]; x != y {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y)
+ }
+ }
+ return ""
+}
diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go
new file mode 100644
index 0000000000..1d49f51dc2
--- /dev/null
+++ b/metrics/prometheus/collector.go
@@ -0,0 +1,170 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package prometheus
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+)
+
+var (
+ typeGaugeTpl = "# TYPE %s gauge\n"
+ typeCounterTpl = "# TYPE %s counter\n"
+ typeSummaryTpl = "# TYPE %s summary\n"
+ keyValueTpl = "%s %v\n\n"
+ keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n"
+)
+
+// collector is a collection of byte buffers that aggregate Prometheus reports
+// for different metric types.
+type collector struct {
+ buff *bytes.Buffer
+}
+
+// newCollector creates a new Prometheus metric aggregator.
+func newCollector() *collector {
+ return &collector{
+ buff: &bytes.Buffer{},
+ }
+}
+
+// Add adds the metric i to the collector. This method returns an error if the
+// metric type is not supported/known.
+func (c *collector) Add(name string, i any) error {
+ switch m := i.(type) {
+ case *metrics.Counter:
+ c.addCounter(name, m.Snapshot())
+ case *metrics.CounterFloat64:
+ c.addCounterFloat64(name, m.Snapshot())
+ case *metrics.Gauge:
+ c.addGauge(name, m.Snapshot())
+ case *metrics.GaugeFloat64:
+ c.addGaugeFloat64(name, m.Snapshot())
+ case *metrics.GaugeInfo:
+ c.addGaugeInfo(name, m.Snapshot())
+ case metrics.Histogram:
+ c.addHistogram(name, m.Snapshot())
+ case *metrics.Meter:
+ c.addMeter(name, m.Snapshot())
+ case *metrics.Timer:
+ c.addTimer(name, m.Snapshot())
+ case *metrics.ResettingTimer:
+ c.addResettingTimer(name, m.Snapshot())
+ default:
+ return fmt.Errorf("unknown prometheus metric type %T", i)
+ }
+ return nil
+}
+
+func (c *collector) addCounter(name string, m metrics.CounterSnapshot) {
+ c.writeGaugeCounter(name, m.Count())
+}
+
+func (c *collector) addCounterFloat64(name string, m metrics.CounterFloat64Snapshot) {
+ c.writeGaugeCounter(name, m.Count())
+}
+
+func (c *collector) addGauge(name string, m metrics.GaugeSnapshot) {
+ c.writeGaugeCounter(name, m.Value())
+}
+
+func (c *collector) addGaugeFloat64(name string, m metrics.GaugeFloat64Snapshot) {
+ c.writeGaugeCounter(name, m.Value())
+}
+
+func (c *collector) addGaugeInfo(name string, m metrics.GaugeInfoSnapshot) {
+ c.writeGaugeInfo(name, m.Value())
+}
+
+func (c *collector) addHistogram(name string, m metrics.HistogramSnapshot) {
+ pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
+ ps := m.Percentiles(pv)
+ c.writeSummaryCounter(name, m.Count())
+ c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
+ for i := range pv {
+ c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
+ }
+ c.buff.WriteRune('\n')
+}
+
+func (c *collector) addMeter(name string, m *metrics.MeterSnapshot) {
+ c.writeGaugeCounter(name, m.Count())
+}
+
+func (c *collector) addTimer(name string, m *metrics.TimerSnapshot) {
+ pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
+ ps := m.Percentiles(pv)
+ c.writeSummaryCounter(name, m.Count())
+ c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
+ for i := range pv {
+ c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
+ }
+ c.buff.WriteRune('\n')
+}
+
+func (c *collector) addResettingTimer(name string, m *metrics.ResettingTimerSnapshot) {
+ if m.Count() <= 0 {
+ return
+ }
+ pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
+ ps := m.Percentiles(pv)
+ c.writeSummaryCounter(name, m.Count())
+ c.buff.WriteString(fmt.Sprintf(typeSummaryTpl, mutateKey(name)))
+ for i := range pv {
+ c.writeSummaryPercentile(name, strconv.FormatFloat(pv[i], 'f', -1, 64), ps[i])
+ }
+ c.buff.WriteRune('\n')
+}
+
+func (c *collector) writeGaugeInfo(name string, value metrics.GaugeInfoValue) {
+ name = mutateKey(name)
+ c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name))
+ c.buff.WriteString(name)
+ c.buff.WriteString(" ")
+ var kvs []string
+ for k, v := range value {
+ kvs = append(kvs, fmt.Sprintf("%v=%q", k, v))
+ }
+ sort.Strings(kvs)
+ c.buff.WriteString(fmt.Sprintf("{%v} 1\n\n", strings.Join(kvs, ", ")))
+}
+
+func (c *collector) writeGaugeCounter(name string, value interface{}) {
+ name = mutateKey(name)
+ c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name))
+ c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
+}
+
+func (c *collector) writeSummaryCounter(name string, value interface{}) {
+ name = mutateKey(name + "_count")
+ c.buff.WriteString(fmt.Sprintf(typeCounterTpl, name))
+ c.buff.WriteString(fmt.Sprintf(keyValueTpl, name, value))
+}
+
+func (c *collector) writeSummaryPercentile(name, p string, value interface{}) {
+ name = mutateKey(name)
+ c.buff.WriteString(fmt.Sprintf(keyQuantileTagValueTpl, name, p, value))
+}
+
+func mutateKey(key string) string {
+ return strings.ReplaceAll(key, "/", "_")
+}
diff --git a/metrics/prometheus/collector_test.go b/metrics/prometheus/collector_test.go
new file mode 100644
index 0000000000..49b979432f
--- /dev/null
+++ b/metrics/prometheus/collector_test.go
@@ -0,0 +1,65 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package prometheus
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+ "github.com/XinFinOrg/XDPoSChain/metrics/internal"
+)
+
+func TestMain(m *testing.M) {
+ metrics.Enable()
+ os.Exit(m.Run())
+}
+
+func TestCollector(t *testing.T) {
+ var (
+ c = newCollector()
+ want string
+ )
+ internal.ExampleMetrics().Each(func(name string, i interface{}) {
+ c.Add(name, i)
+ })
+ if wantB, err := os.ReadFile("./testdata/prometheus.want"); err != nil {
+ t.Fatal(err)
+ } else {
+ want = string(wantB)
+ }
+ if have := c.buff.String(); have != want {
+ t.Logf("have\n%v", have)
+ t.Logf("have vs want:\n%v", findFirstDiffPos(have, want))
+ t.Fatalf("unexpected collector output")
+ }
+}
+
+func findFirstDiffPos(a, b string) string {
+ yy := strings.Split(b, "\n")
+ for i, x := range strings.Split(a, "\n") {
+ if i >= len(yy) {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: ", i, x, i)
+ }
+ if y := yy[i]; x != y {
+ return fmt.Sprintf("have:%d: %s\nwant:%d: %s", i, x, i, y)
+ }
+ }
+ return ""
+}
diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go
new file mode 100644
index 0000000000..d11db3bae3
--- /dev/null
+++ b/metrics/prometheus/prometheus.go
@@ -0,0 +1,52 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package prometheus exposes go-metrics into a Prometheus format.
+package prometheus
+
+import (
+ "fmt"
+ "net/http"
+ "sort"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/metrics"
+)
+
+// Handler returns an HTTP handler which dump metrics in Prometheus format.
+func Handler(reg metrics.Registry) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Gather and pre-sort the metrics to avoid random listings
+ var names []string
+ reg.Each(func(name string, i interface{}) {
+ names = append(names, name)
+ })
+ sort.Strings(names)
+
+ // Aggregate all the metrics into a Prometheus collector
+ c := newCollector()
+
+ for _, name := range names {
+ i := reg.Get(name)
+ if err := c.Add(name, i); err != nil {
+ log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i))
+ }
+ }
+ w.Header().Add("Content-Type", "text/plain")
+ w.Header().Add("Content-Length", fmt.Sprint(c.buff.Len()))
+ w.Write(c.buff.Bytes())
+ })
+}
diff --git a/metrics/prometheus/testdata/prometheus.want b/metrics/prometheus/testdata/prometheus.want
new file mode 100644
index 0000000000..a999d83801
--- /dev/null
+++ b/metrics/prometheus/testdata/prometheus.want
@@ -0,0 +1,73 @@
+# TYPE system_cpu_schedlatency_count counter
+system_cpu_schedlatency_count 5645
+
+# TYPE system_cpu_schedlatency summary
+system_cpu_schedlatency {quantile="0.5"} 0
+system_cpu_schedlatency {quantile="0.75"} 7168
+system_cpu_schedlatency {quantile="0.95"} 1.6777216e+07
+system_cpu_schedlatency {quantile="0.99"} 2.9360128e+07
+system_cpu_schedlatency {quantile="0.999"} 3.3554432e+07
+system_cpu_schedlatency {quantile="0.9999"} 3.3554432e+07
+
+# TYPE system_memory_pauses_count counter
+system_memory_pauses_count 14
+
+# TYPE system_memory_pauses summary
+system_memory_pauses {quantile="0.5"} 32768
+system_memory_pauses {quantile="0.75"} 57344
+system_memory_pauses {quantile="0.95"} 196608
+system_memory_pauses {quantile="0.99"} 196608
+system_memory_pauses {quantile="0.999"} 196608
+system_memory_pauses {quantile="0.9999"} 196608
+
+# TYPE test_counter gauge
+test_counter 12345
+
+# TYPE test_counter_float64 gauge
+test_counter_float64 54321.98
+
+# TYPE test_gauge gauge
+test_gauge 23456
+
+# TYPE test_gauge_float64 gauge
+test_gauge_float64 34567.89
+
+# TYPE test_gauge_info gauge
+test_gauge_info {arch="amd64", commit="7caa2d8163ae3132c1c2d6978c76610caee2d949", os="linux", protocol_versions="64 65 66", version="1.10.18-unstable"} 1
+
+# TYPE test_histogram_count counter
+test_histogram_count 3
+
+# TYPE test_histogram summary
+test_histogram {quantile="0.5"} 2
+test_histogram {quantile="0.75"} 3
+test_histogram {quantile="0.95"} 3
+test_histogram {quantile="0.99"} 3
+test_histogram {quantile="0.999"} 3
+test_histogram {quantile="0.9999"} 3
+
+# TYPE test_meter gauge
+test_meter 0
+
+# TYPE test_resetting_timer_count counter
+test_resetting_timer_count 6
+
+# TYPE test_resetting_timer summary
+test_resetting_timer {quantile="0.5"} 1.25e+07
+test_resetting_timer {quantile="0.75"} 4.05e+07
+test_resetting_timer {quantile="0.95"} 1.2e+08
+test_resetting_timer {quantile="0.99"} 1.2e+08
+test_resetting_timer {quantile="0.999"} 1.2e+08
+test_resetting_timer {quantile="0.9999"} 1.2e+08
+
+# TYPE test_timer_count counter
+test_timer_count 6
+
+# TYPE test_timer summary
+test_timer {quantile="0.5"} 2.25e+07
+test_timer {quantile="0.75"} 4.8e+07
+test_timer {quantile="0.95"} 1.2e+08
+test_timer {quantile="0.99"} 1.2e+08
+test_timer {quantile="0.999"} 1.2e+08
+test_timer {quantile="0.9999"} 1.2e+08
+
diff --git a/metrics/registry.go b/metrics/registry.go
index cc34c9dfd2..527da6238d 100644
--- a/metrics/registry.go
+++ b/metrics/registry.go
@@ -1,29 +1,27 @@
package metrics
import (
+ "errors"
"fmt"
"reflect"
+ "sort"
"strings"
"sync"
)
-// DuplicateMetric is the error returned by Registry.Register when a metric
-// already exists. If you mean to Register that metric you must first
+// ErrDuplicateMetric is the error returned by Registry.Register when a metric
+// already exists. If you mean to Register that metric you must first
// Unregister the existing metric.
-type DuplicateMetric string
-
-func (err DuplicateMetric) Error() string {
- return fmt.Sprintf("duplicate metric: %s", string(err))
-}
+var ErrDuplicateMetric = errors.New("duplicate metric")
// A Registry holds references to a set of metrics by name and can iterate
// over them, calling callback functions provided by the user.
//
-// This is an interface so as to encourage other structs to implement
+// This is an interface to encourage other structs to implement
// the Registry API as appropriate.
type Registry interface {
- // Call the given function for each registered metric.
+ // Each call the given function for each registered metric.
Each(func(string, interface{}))
// Get the metric by the given name or nil if none is registered.
@@ -32,7 +30,7 @@ type Registry interface {
// GetAll metrics in the Registry.
GetAll() map[string]map[string]interface{}
- // Gets an existing metric or registers the given one.
+ // GetOrRegister gets an existing metric or registers the given one.
// The interface can be the metric to register if not found in registry,
// or a function returning the metric for lazy instantiation.
GetOrRegister(string, interface{}) interface{}
@@ -40,29 +38,47 @@ type Registry interface {
// Register the given metric under the given name.
Register(string, interface{}) error
- // Run all registered healthchecks.
+ // RunHealthchecks run all registered healthchecks.
RunHealthchecks()
// Unregister the metric with the given name.
Unregister(string)
-
- // Unregister all metrics. (Mostly for testing.)
- UnregisterAll()
}
-// The standard implementation of a Registry is a mutex-protected map
+type orderedRegistry struct {
+ StandardRegistry
+}
+
+// Each call the given function for each registered metric.
+func (r *orderedRegistry) Each(f func(string, interface{})) {
+ var names []string
+ reg := r.registered()
+ for name := range reg {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, name := range names {
+ f(name, reg[name])
+ }
+}
+
+// NewRegistry creates a new registry.
+func NewRegistry() Registry {
+ return new(StandardRegistry)
+}
+
+// NewOrderedRegistry creates a new ordered registry (for testing).
+func NewOrderedRegistry() Registry {
+ return new(orderedRegistry)
+}
+
+// StandardRegistry the standard implementation of a Registry uses sync.map
// of names to metrics.
type StandardRegistry struct {
- metrics map[string]interface{}
- mutex sync.Mutex
+ metrics sync.Map
}
-// Create a new registry.
-func NewRegistry() Registry {
- return &StandardRegistry{metrics: make(map[string]interface{})}
-}
-
-// Call the given function for each registered metric.
+// Each call the given function for each registered metric.
func (r *StandardRegistry) Each(f func(string, interface{})) {
for name, i := range r.registered() {
f(name, i)
@@ -71,45 +87,57 @@ func (r *StandardRegistry) Each(f func(string, interface{})) {
// Get the metric by the given name or nil if none is registered.
func (r *StandardRegistry) Get(name string) interface{} {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- return r.metrics[name]
+ item, _ := r.metrics.Load(name)
+ return item
}
-// Gets an existing metric or creates and registers a new one. Threadsafe
+// GetOrRegister gets an existing metric or creates and registers a new one. Threadsafe
// alternative to calling Get and Register on failure.
// The interface can be the metric to register if not found in registry,
// or a function returning the metric for lazy instantiation.
func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- if metric, ok := r.metrics[name]; ok {
- return metric
+ // fast path
+ cached, ok := r.metrics.Load(name)
+ if ok {
+ return cached
}
if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
i = v.Call(nil)[0].Interface()
}
- r.register(name, i)
- return i
+ item, _, ok := r.loadOrRegister(name, i)
+ if !ok {
+ return i
+ }
+ return item
}
-// Register the given metric under the given name. Returns a DuplicateMetric
+// Register the given metric under the given name. Returns a ErrDuplicateMetric
// if a metric by the given name is already registered.
func (r *StandardRegistry) Register(name string, i interface{}) error {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- return r.register(name, i)
+ // fast path
+ _, ok := r.metrics.Load(name)
+ if ok {
+ return fmt.Errorf("%w: %v", ErrDuplicateMetric, name)
+ }
+
+ if v := reflect.ValueOf(i); v.Kind() == reflect.Func {
+ i = v.Call(nil)[0].Interface()
+ }
+ _, loaded, _ := r.loadOrRegister(name, i)
+ if loaded {
+ return fmt.Errorf("%w: %v", ErrDuplicateMetric, name)
+ }
+ return nil
}
-// Run all registered healthchecks.
+// RunHealthchecks run all registered healthchecks.
func (r *StandardRegistry) RunHealthchecks() {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- for _, i := range r.metrics {
- if h, ok := i.(Healthcheck); ok {
+ r.metrics.Range(func(key, value any) bool {
+ if h, ok := value.(*Healthcheck); ok {
h.Check()
}
- }
+ return true
+ })
}
// GetAll metrics in the Registry
@@ -118,13 +146,15 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
r.Each(func(name string, i interface{}) {
values := make(map[string]interface{})
switch metric := i.(type) {
- case Counter:
- values["count"] = metric.Count()
- case Gauge:
- values["value"] = metric.Value()
- case GaugeFloat64:
- values["value"] = metric.Value()
- case Healthcheck:
+ case *Counter:
+ values["count"] = metric.Snapshot().Count()
+ case *CounterFloat64:
+ values["count"] = metric.Snapshot().Count()
+ case *Gauge:
+ values["value"] = metric.Snapshot().Value()
+ case *GaugeFloat64:
+ values["value"] = metric.Snapshot().Value()
+ case *Healthcheck:
values["error"] = nil
metric.Check()
if err := metric.Error(); nil != err {
@@ -143,14 +173,14 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
values["95%"] = ps[2]
values["99%"] = ps[3]
values["99.9%"] = ps[4]
- case Meter:
+ case *Meter:
m := metric.Snapshot()
values["count"] = m.Count()
values["1m.rate"] = m.Rate1()
values["5m.rate"] = m.Rate5()
values["15m.rate"] = m.Rate15()
values["mean.rate"] = m.RateMean()
- case Timer:
+ case *Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
values["count"] = t.Count()
@@ -175,45 +205,31 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} {
// Unregister the metric with the given name.
func (r *StandardRegistry) Unregister(name string) {
- r.mutex.Lock()
- defer r.mutex.Unlock()
r.stop(name)
- delete(r.metrics, name)
+ r.metrics.LoadAndDelete(name)
}
-// Unregister all metrics. (Mostly for testing.)
-func (r *StandardRegistry) UnregisterAll() {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- for name := range r.metrics {
- r.stop(name)
- delete(r.metrics, name)
- }
-}
-
-func (r *StandardRegistry) register(name string, i interface{}) error {
- if _, ok := r.metrics[name]; ok {
- return DuplicateMetric(name)
- }
+func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) {
switch i.(type) {
- case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer:
- r.metrics[name] = i
+ case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, *ResettingTimer:
+ default:
+ return nil, false, false
}
- return nil
+ item, loaded := r.metrics.LoadOrStore(name, i)
+ return item, loaded, true
}
func (r *StandardRegistry) registered() map[string]interface{} {
- r.mutex.Lock()
- defer r.mutex.Unlock()
- metrics := make(map[string]interface{}, len(r.metrics))
- for name, i := range r.metrics {
- metrics[name] = i
- }
+ metrics := make(map[string]interface{})
+ r.metrics.Range(func(key, value any) bool {
+ metrics[key.(string)] = value
+ return true
+ })
return metrics
}
func (r *StandardRegistry) stop(name string) {
- if i, ok := r.metrics[name]; ok {
+ if i, ok := r.metrics.Load(name); ok {
if s, ok := i.(Stoppable); ok {
s.Stop()
}
@@ -244,7 +260,7 @@ func NewPrefixedChildRegistry(parent Registry, prefix string) Registry {
}
}
-// Call the given function for each registered metric.
+// Each call the given function for each registered metric.
func (r *PrefixedRegistry) Each(fn func(string, interface{})) {
wrappedFn := func(prefix string) func(string, interface{}) {
return func(name string, iface interface{}) {
@@ -276,7 +292,7 @@ func (r *PrefixedRegistry) Get(name string) interface{} {
return r.underlying.Get(realName)
}
-// Gets an existing metric or registers the given one.
+// GetOrRegister gets an existing metric or registers the given one.
// The interface can be the metric to register if not found in registry,
// or a function returning the metric for lazy instantiation.
func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} {
@@ -290,7 +306,7 @@ func (r *PrefixedRegistry) Register(name string, metric interface{}) error {
return r.underlying.Register(realName, metric)
}
-// Run all registered healthchecks.
+// RunHealthchecks run all registered healthchecks.
func (r *PrefixedRegistry) RunHealthchecks() {
r.underlying.RunHealthchecks()
}
@@ -306,14 +322,11 @@ func (r *PrefixedRegistry) Unregister(name string) {
r.underlying.Unregister(realName)
}
-// Unregister all metrics. (Mostly for testing.)
-func (r *PrefixedRegistry) UnregisterAll() {
- r.underlying.UnregisterAll()
-}
+var (
+ DefaultRegistry = NewRegistry()
+)
-var DefaultRegistry Registry = NewRegistry()
-
-// Call the given function for each registered metric.
+// Each call the given function for each registered metric.
func Each(f func(string, interface{})) {
DefaultRegistry.Each(f)
}
@@ -323,19 +336,19 @@ func Get(name string) interface{} {
return DefaultRegistry.Get(name)
}
-// Gets an existing metric or creates and registers a new one. Threadsafe
+// GetOrRegister gets an existing metric or creates and registers a new one. Threadsafe
// alternative to calling Get and Register on failure.
func GetOrRegister(name string, i interface{}) interface{} {
return DefaultRegistry.GetOrRegister(name, i)
}
-// Register the given metric under the given name. Returns a DuplicateMetric
+// Register the given metric under the given name. Returns a ErrDuplicateMetric
// if a metric by the given name is already registered.
func Register(name string, i interface{}) error {
return DefaultRegistry.Register(name, i)
}
-// Register the given metric under the given name. Panics if a metric by the
+// MustRegister register the given metric under the given name. Panics if a metric by the
// given name is already registered.
func MustRegister(name string, i interface{}) {
if err := Register(name, i); err != nil {
@@ -343,7 +356,7 @@ func MustRegister(name string, i interface{}) {
}
}
-// Run all registered healthchecks.
+// RunHealthchecks run all registered healthchecks.
func RunHealthchecks() {
DefaultRegistry.RunHealthchecks()
}
diff --git a/metrics/registry_test.go b/metrics/registry_test.go
index a63e485fe9..bdc58fee6c 100644
--- a/metrics/registry_test.go
+++ b/metrics/registry_test.go
@@ -1,6 +1,7 @@
package metrics
import (
+ "sync"
"testing"
)
@@ -13,26 +14,50 @@ func BenchmarkRegistry(b *testing.B) {
}
}
+func BenchmarkRegistryGetOrRegisterParallel_8(b *testing.B) {
+ benchmarkRegistryGetOrRegisterParallel(b, 8)
+}
+
+func BenchmarkRegistryGetOrRegisterParallel_32(b *testing.B) {
+ benchmarkRegistryGetOrRegisterParallel(b, 32)
+}
+
+func benchmarkRegistryGetOrRegisterParallel(b *testing.B, amount int) {
+ r := NewRegistry()
+ b.ResetTimer()
+ var wg sync.WaitGroup
+ for i := 0; i < amount; i++ {
+ wg.Add(1)
+ go func() {
+ for i := 0; i < b.N; i++ {
+ r.GetOrRegister("foo", NewMeter)
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
+
func TestRegistry(t *testing.T) {
r := NewRegistry()
r.Register("foo", NewCounter())
i := 0
r.Each(func(name string, iface interface{}) {
i++
- if "foo" != name {
+ if name != "foo" {
t.Fatal(name)
}
- if _, ok := iface.(Counter); !ok {
+ if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
- if 1 != i {
+ if i != 1 {
t.Fatal(i)
}
r.Unregister("foo")
i = 0
r.Each(func(string, interface{}) { i++ })
- if 0 != i {
+ if i != 0 {
t.Fatal(i)
}
}
@@ -48,11 +73,11 @@ func TestRegistryDuplicate(t *testing.T) {
i := 0
r.Each(func(name string, iface interface{}) {
i++
- if _, ok := iface.(Counter); !ok {
+ if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
- if 1 != i {
+ if i != 1 {
t.Fatal(i)
}
}
@@ -60,11 +85,11 @@ func TestRegistryDuplicate(t *testing.T) {
func TestRegistryGet(t *testing.T) {
r := NewRegistry()
r.Register("foo", NewCounter())
- if count := r.Get("foo").(Counter).Count(); 0 != count {
+ if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 0 {
t.Fatal(count)
}
- r.Get("foo").(Counter).Inc(1)
- if count := r.Get("foo").(Counter).Count(); 1 != count {
+ r.Get("foo").(*Counter).Inc(1)
+ if count := r.Get("foo").(*Counter).Snapshot().Count(); count != 1 {
t.Fatal(count)
}
}
@@ -75,7 +100,7 @@ func TestRegistryGetOrRegister(t *testing.T) {
// First metric wins with GetOrRegister
_ = r.GetOrRegister("foo", NewCounter())
m := r.GetOrRegister("foo", NewGauge())
- if _, ok := m.(Counter); !ok {
+ if _, ok := m.(*Counter); !ok {
t.Fatal(m)
}
@@ -85,7 +110,7 @@ func TestRegistryGetOrRegister(t *testing.T) {
if name != "foo" {
t.Fatal(name)
}
- if _, ok := iface.(Counter); !ok {
+ if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
@@ -100,7 +125,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
// First metric wins with GetOrRegister
_ = r.GetOrRegister("foo", NewCounter)
m := r.GetOrRegister("foo", NewGauge)
- if _, ok := m.(Counter); !ok {
+ if _, ok := m.(*Counter); !ok {
t.Fatal(m)
}
@@ -110,7 +135,7 @@ func TestRegistryGetOrRegisterWithLazyInstantiation(t *testing.T) {
if name != "foo" {
t.Fatal(name)
}
- if _, ok := iface.(Counter); !ok {
+ if _, ok := iface.(*Counter); !ok {
t.Fatal(iface)
}
})
@@ -271,6 +296,9 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) {
t.Fatal(err.Error())
}
err = r2.Register("baz", NewCounter())
+ if err != nil {
+ t.Fatal(err.Error())
+ }
c := NewCounter()
Register("bars", c)
@@ -278,7 +306,7 @@ func TestChildPrefixedRegistryOfChildRegister(t *testing.T) {
r2.Each(func(name string, m interface{}) {
i++
if name != "prefix.prefix2.baz" {
- //t.Fatal(name)
+ t.Fatal(name)
}
})
if i != 1 {
@@ -294,12 +322,14 @@ func TestWalkRegistries(t *testing.T) {
t.Fatal(err.Error())
}
err = r2.Register("baz", NewCounter())
+ if err != nil {
+ t.Fatal(err.Error())
+ }
c := NewCounter()
Register("bars", c)
_, prefix := findPrefix(r2, "")
- if "prefix.prefix2." != prefix {
+ if prefix != "prefix.prefix2." {
t.Fatal(prefix)
}
-
}
diff --git a/metrics/resetting_sample.go b/metrics/resetting_sample.go
new file mode 100644
index 0000000000..730ef93416
--- /dev/null
+++ b/metrics/resetting_sample.go
@@ -0,0 +1,24 @@
+package metrics
+
+// ResettingSample converts an ordinary sample into one that resets whenever its
+// snapshot is retrieved. This will break for multi-monitor systems, but when only
+// a single metric is being pushed out, this ensure that low-frequency events don't
+// skew th charts indefinitely.
+func ResettingSample(sample Sample) Sample {
+ return &resettingSample{
+ Sample: sample,
+ }
+}
+
+// resettingSample is a simple wrapper around a sample that resets it upon the
+// snapshot retrieval.
+type resettingSample struct {
+ Sample
+}
+
+// Snapshot returns a read-only copy of the sample with the original reset.
+func (rs *resettingSample) Snapshot() *sampleSnapshot {
+ s := rs.Sample.Snapshot()
+ rs.Sample.Clear()
+ return s
+}
diff --git a/metrics/resetting_timer.go b/metrics/resetting_timer.go
index 57bcb31343..1b3e87bc3d 100644
--- a/metrics/resetting_timer.go
+++ b/metrics/resetting_timer.go
@@ -1,37 +1,21 @@
package metrics
import (
- "math"
- "sort"
"sync"
"time"
)
-// Initial slice capacity for the values stored in a ResettingTimer
-const InitialResettingTimerSliceCap = 10
-
-// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
-type ResettingTimer interface {
- Values() []int64
- Snapshot() ResettingTimer
- Percentiles([]float64) []int64
- Mean() float64
- Time(func())
- Update(time.Duration)
- UpdateSince(time.Time)
-}
-
// GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
-// new StandardResettingTimer.
-func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer {
+// new ResettingTimer.
+func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer {
if nil == r {
r = DefaultRegistry
}
- return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer)
+ return r.GetOrRegister(name, NewResettingTimer).(*ResettingTimer)
}
-// NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer.
-func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
+// NewRegisteredResettingTimer constructs and registers a new ResettingTimer.
+func NewRegisteredResettingTimer(name string, r Registry) *ResettingTimer {
c := NewResettingTimer()
if nil == r {
r = DefaultRegistry
@@ -40,198 +24,114 @@ func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
return c
}
-// NewResettingTimer constructs a new StandardResettingTimer
-func NewResettingTimer() ResettingTimer {
- if !Enabled {
- return NilResettingTimer{}
- }
- return &StandardResettingTimer{
- values: make([]int64, 0, InitialResettingTimerSliceCap),
+// NewResettingTimer constructs a new ResettingTimer
+func NewResettingTimer() *ResettingTimer {
+ return &ResettingTimer{
+ values: make([]int64, 0, 10),
}
}
-// NilResettingTimer is a no-op ResettingTimer.
-type NilResettingTimer struct {
-}
-
-// Values is a no-op.
-func (NilResettingTimer) Values() []int64 { return nil }
-
-// Snapshot is a no-op.
-func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} }
-
-// Time is a no-op.
-func (NilResettingTimer) Time(func()) {}
-
-// Update is a no-op.
-func (NilResettingTimer) Update(time.Duration) {}
-
-// Percentiles panics.
-func (NilResettingTimer) Percentiles([]float64) []int64 {
- panic("Percentiles called on a NilResettingTimer")
-}
-
-// Mean panics.
-func (NilResettingTimer) Mean() float64 {
- panic("Mean called on a NilResettingTimer")
-}
-
-// UpdateSince is a no-op.
-func (NilResettingTimer) UpdateSince(time.Time) {}
-
-// StandardResettingTimer is the standard implementation of a ResettingTimer.
-// and Meter.
-type StandardResettingTimer struct {
+// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
+type ResettingTimer struct {
values []int64
- mutex sync.Mutex
-}
+ sum int64 // sum is a running count of the total sum, used later to calculate mean
-// Values returns a slice with all measurements.
-func (t *StandardResettingTimer) Values() []int64 {
- return t.values
+ mutex sync.Mutex
}
// Snapshot resets the timer and returns a read-only copy of its contents.
-func (t *StandardResettingTimer) Snapshot() ResettingTimer {
+func (t *ResettingTimer) Snapshot() *ResettingTimerSnapshot {
t.mutex.Lock()
defer t.mutex.Unlock()
- currentValues := t.values
- t.values = make([]int64, 0, InitialResettingTimerSliceCap)
-
- return &ResettingTimerSnapshot{
- values: currentValues,
+ snapshot := &ResettingTimerSnapshot{}
+ if len(t.values) > 0 {
+ snapshot.mean = float64(t.sum) / float64(len(t.values))
+ snapshot.values = t.values
+ t.values = make([]int64, 0, 10)
}
-}
-
-// Percentiles panics.
-func (t *StandardResettingTimer) Percentiles([]float64) []int64 {
- panic("Percentiles called on a StandardResettingTimer")
-}
-
-// Mean panics.
-func (t *StandardResettingTimer) Mean() float64 {
- panic("Mean called on a StandardResettingTimer")
+ t.sum = 0
+ return snapshot
}
// Record the duration of the execution of the given function.
-func (t *StandardResettingTimer) Time(f func()) {
+func (t *ResettingTimer) Time(f func()) {
ts := time.Now()
f()
t.Update(time.Since(ts))
}
// Record the duration of an event.
-func (t *StandardResettingTimer) Update(d time.Duration) {
+func (t *ResettingTimer) Update(d time.Duration) {
+ if !metricsEnabled {
+ return
+ }
t.mutex.Lock()
defer t.mutex.Unlock()
t.values = append(t.values, int64(d))
+ t.sum += int64(d)
}
// Record the duration of an event that started at a time and ends now.
-func (t *StandardResettingTimer) UpdateSince(ts time.Time) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- t.values = append(t.values, int64(time.Since(ts)))
+func (t *ResettingTimer) UpdateSince(ts time.Time) {
+ t.Update(time.Since(ts))
}
// ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
type ResettingTimerSnapshot struct {
values []int64
mean float64
- thresholdBoundaries []int64
+ max int64
+ min int64
+ thresholdBoundaries []float64
calculated bool
}
-// Snapshot returns the snapshot.
-func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t }
-
-// Time panics.
-func (*ResettingTimerSnapshot) Time(func()) {
- panic("Time called on a ResettingTimerSnapshot")
-}
-
-// Update panics.
-func (*ResettingTimerSnapshot) Update(time.Duration) {
- panic("Update called on a ResettingTimerSnapshot")
-}
-
-// UpdateSince panics.
-func (*ResettingTimerSnapshot) UpdateSince(time.Time) {
- panic("UpdateSince called on a ResettingTimerSnapshot")
-}
-
-// Values returns all values from snapshot.
-func (t *ResettingTimerSnapshot) Values() []int64 {
- return t.values
+// Count return the length of the values from snapshot.
+func (t *ResettingTimerSnapshot) Count() int {
+ return len(t.values)
}
// Percentiles returns the boundaries for the input percentiles.
-func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 {
+// note: this method is not thread safe
+func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []float64 {
t.calc(percentiles)
-
return t.thresholdBoundaries
}
// Mean returns the mean of the snapshotted values
+// note: this method is not thread safe
func (t *ResettingTimerSnapshot) Mean() float64 {
if !t.calculated {
- t.calc([]float64{})
+ t.calc(nil)
}
return t.mean
}
-func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
- sort.Sort(Int64Slice(t.values))
-
- count := len(t.values)
- if count > 0 {
- min := t.values[0]
- max := t.values[count-1]
-
- cumulativeValues := make([]int64, count)
- cumulativeValues[0] = min
- for i := 1; i < count; i++ {
- cumulativeValues[i] = t.values[i] + cumulativeValues[i-1]
- }
-
- t.thresholdBoundaries = make([]int64, len(percentiles))
-
- thresholdBoundary := max
-
- for i, pct := range percentiles {
- if count > 1 {
- var abs float64
- if pct >= 0 {
- abs = pct
- } else {
- abs = 100 + pct
- }
- // poor man's math.Round(x):
- // math.Floor(x + 0.5)
- indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5))
- if pct >= 0 {
- indexOfPerc -= 1 // index offset=0
- }
- thresholdBoundary = t.values[indexOfPerc]
- }
-
- t.thresholdBoundaries[i] = thresholdBoundary
- }
-
- sum := cumulativeValues[count-1]
- t.mean = float64(sum) / float64(count)
- } else {
- t.thresholdBoundaries = make([]int64, len(percentiles))
- t.mean = 0
+// Max returns the max of the snapshotted values
+// note: this method is not thread safe
+func (t *ResettingTimerSnapshot) Max() int64 {
+ if !t.calculated {
+ t.calc(nil)
}
-
- t.calculated = true
+ return t.max
}
-// Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order.
-type Int64Slice []int64
+// Min returns the min of the snapshotted values
+// note: this method is not thread safe
+func (t *ResettingTimerSnapshot) Min() int64 {
+ if !t.calculated {
+ t.calc(nil)
+ }
+ return t.min
+}
-func (s Int64Slice) Len() int { return len(s) }
-func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] }
-func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
+ scores := CalculatePercentiles(t.values, percentiles)
+ t.thresholdBoundaries = scores
+ if len(t.values) == 0 {
+ return
+ }
+ t.min = t.values[0]
+ t.max = t.values[len(t.values)-1]
+}
diff --git a/metrics/resetting_timer_test.go b/metrics/resetting_timer_test.go
index 58fd47f352..4571fc8eb0 100644
--- a/metrics/resetting_timer_test.go
+++ b/metrics/resetting_timer_test.go
@@ -10,9 +10,9 @@ func TestResettingTimer(t *testing.T) {
values []int64
start int
end int
- wantP50 int64
- wantP95 int64
- wantP99 int64
+ wantP50 float64
+ wantP95 float64
+ wantP99 float64
wantMean float64
wantMin int64
wantMax int64
@@ -21,14 +21,14 @@ func TestResettingTimer(t *testing.T) {
values: []int64{},
start: 1,
end: 11,
- wantP50: 5, wantP95: 10, wantP99: 10,
+ wantP50: 5.5, wantP95: 10, wantP99: 10,
wantMin: 1, wantMax: 10, wantMean: 5.5,
},
{
values: []int64{},
start: 1,
end: 101,
- wantP50: 50, wantP95: 95, wantP99: 99,
+ wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99,
wantMin: 1, wantMax: 100, wantMean: 50.5,
},
{
@@ -56,7 +56,99 @@ func TestResettingTimer(t *testing.T) {
values: []int64{1, 10},
start: 0,
end: 0,
- wantP50: 1, wantP95: 10, wantP99: 10,
+ wantP50: 5.5, wantP95: 10, wantP99: 10,
+ wantMin: 1, wantMax: 10, wantMean: 5.5,
+ },
+ }
+ for i, tt := range tests {
+ timer := NewResettingTimer()
+
+ for i := tt.start; i < tt.end; i++ {
+ tt.values = append(tt.values, int64(i))
+ }
+
+ for _, v := range tt.values {
+ timer.Update(time.Duration(v))
+ }
+ snap := timer.Snapshot()
+
+ ps := snap.Percentiles([]float64{0.50, 0.95, 0.99})
+
+ if have, want := snap.Min(), tt.wantMin; have != want {
+ t.Fatalf("%d: min: have %d, want %d", i, have, want)
+ }
+ if have, want := snap.Max(), tt.wantMax; have != want {
+ t.Fatalf("%d: max: have %d, want %d", i, have, want)
+ }
+ if have, want := snap.Mean(), tt.wantMean; have != want {
+ t.Fatalf("%d: mean: have %v, want %v", i, have, want)
+ }
+ if have, want := ps[0], tt.wantP50; have != want {
+ t.Errorf("%d: p50: have %v, want %v", i, have, want)
+ }
+ if have, want := ps[1], tt.wantP95; have != want {
+ t.Errorf("%d: p95: have %v, want %v", i, have, want)
+ }
+ if have, want := ps[2], tt.wantP99; have != want {
+ t.Errorf("%d: p99: have %v, want %v", i, have, want)
+ }
+ }
+}
+
+func TestResettingTimerWithFivePercentiles(t *testing.T) {
+ tests := []struct {
+ values []int64
+ start int
+ end int
+ wantP05 float64
+ wantP20 float64
+ wantP50 float64
+ wantP95 float64
+ wantP99 float64
+ wantMean float64
+ wantMin int64
+ wantMax int64
+ }{
+ {
+ values: []int64{},
+ start: 1,
+ end: 11,
+ wantP05: 1, wantP20: 2.2, wantP50: 5.5, wantP95: 10, wantP99: 10,
+ wantMin: 1, wantMax: 10, wantMean: 5.5,
+ },
+ {
+ values: []int64{},
+ start: 1,
+ end: 101,
+ wantP05: 5.050000000000001, wantP20: 20.200000000000003, wantP50: 50.5, wantP95: 95.94999999999999, wantP99: 99.99,
+ wantMin: 1, wantMax: 100, wantMean: 50.5,
+ },
+ {
+ values: []int64{1},
+ start: 0,
+ end: 0,
+ wantP05: 1, wantP20: 1, wantP50: 1, wantP95: 1, wantP99: 1,
+ wantMin: 1, wantMax: 1, wantMean: 1,
+ },
+ {
+ values: []int64{0},
+ start: 0,
+ end: 0,
+ wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0,
+ wantMin: 0, wantMax: 0, wantMean: 0,
+ },
+ {
+ values: []int64{},
+ start: 0,
+ end: 0,
+ wantP05: 0, wantP20: 0, wantP50: 0, wantP95: 0, wantP99: 0,
+ wantMin: 0, wantMax: 0, wantMean: 0,
+ },
+ {
+ values: []int64{1, 10},
+ start: 0,
+ end: 0,
+ wantP05: 1, wantP20: 1, wantP50: 5.5, wantP95: 10, wantP99: 10,
wantMin: 1, wantMax: 10, wantMean: 5.5,
},
}
@@ -73,34 +165,33 @@ func TestResettingTimer(t *testing.T) {
snap := timer.Snapshot()
- ps := snap.Percentiles([]float64{50, 95, 99})
+ ps := snap.Percentiles([]float64{0.05, 0.20, 0.50, 0.95, 0.99})
- val := snap.Values()
+ if tt.wantMin != snap.Min() {
+ t.Errorf("%d: min: got %d, want %d", ind, snap.Min(), tt.wantMin)
+ }
- if len(val) > 0 {
- if tt.wantMin != val[0] {
- t.Fatalf("%d: min: got %d, want %d", ind, val[0], tt.wantMin)
- }
-
- if tt.wantMax != val[len(val)-1] {
- t.Fatalf("%d: max: got %d, want %d", ind, val[len(val)-1], tt.wantMax)
- }
+ if tt.wantMax != snap.Max() {
+ t.Errorf("%d: max: got %d, want %d", ind, snap.Max(), tt.wantMax)
}
if tt.wantMean != snap.Mean() {
- t.Fatalf("%d: mean: got %.2f, want %.2f", ind, snap.Mean(), tt.wantMean)
+ t.Errorf("%d: mean: got %.2f, want %.2f", ind, snap.Mean(), tt.wantMean)
}
-
- if tt.wantP50 != ps[0] {
- t.Fatalf("%d: p50: got %d, want %d", ind, ps[0], tt.wantP50)
+ if tt.wantP05 != ps[0] {
+ t.Errorf("%d: p05: got %v, want %v", ind, ps[0], tt.wantP05)
}
-
- if tt.wantP95 != ps[1] {
- t.Fatalf("%d: p95: got %d, want %d", ind, ps[1], tt.wantP95)
+ if tt.wantP20 != ps[1] {
+ t.Errorf("%d: p20: got %v, want %v", ind, ps[1], tt.wantP20)
}
-
- if tt.wantP99 != ps[2] {
- t.Fatalf("%d: p99: got %d, want %d", ind, ps[2], tt.wantP99)
+ if tt.wantP50 != ps[2] {
+ t.Errorf("%d: p50: got %v, want %v", ind, ps[2], tt.wantP50)
+ }
+ if tt.wantP95 != ps[3] {
+ t.Errorf("%d: p95: got %v, want %v", ind, ps[3], tt.wantP95)
+ }
+ if tt.wantP99 != ps[4] {
+ t.Errorf("%d: p99: got %v, want %v", ind, ps[4], tt.wantP99)
}
}
}
diff --git a/metrics/runtime.go b/metrics/runtime.go
deleted file mode 100644
index 9450c479ba..0000000000
--- a/metrics/runtime.go
+++ /dev/null
@@ -1,212 +0,0 @@
-package metrics
-
-import (
- "runtime"
- "runtime/pprof"
- "time"
-)
-
-var (
- memStats runtime.MemStats
- runtimeMetrics struct {
- MemStats struct {
- Alloc Gauge
- BuckHashSys Gauge
- DebugGC Gauge
- EnableGC Gauge
- Frees Gauge
- HeapAlloc Gauge
- HeapIdle Gauge
- HeapInuse Gauge
- HeapObjects Gauge
- HeapReleased Gauge
- HeapSys Gauge
- LastGC Gauge
- Lookups Gauge
- Mallocs Gauge
- MCacheInuse Gauge
- MCacheSys Gauge
- MSpanInuse Gauge
- MSpanSys Gauge
- NextGC Gauge
- NumGC Gauge
- GCCPUFraction GaugeFloat64
- PauseNs Histogram
- PauseTotalNs Gauge
- StackInuse Gauge
- StackSys Gauge
- Sys Gauge
- TotalAlloc Gauge
- }
- NumCgoCall Gauge
- NumGoroutine Gauge
- NumThread Gauge
- ReadMemStats Timer
- }
- frees uint64
- lookups uint64
- mallocs uint64
- numGC uint32
- numCgoCalls int64
-
- threadCreateProfile = pprof.Lookup("threadcreate")
-)
-
-// Capture new values for the Go runtime statistics exported in
-// runtime.MemStats. This is designed to be called as a goroutine.
-func CaptureRuntimeMemStats(r Registry, d time.Duration) {
- for range time.Tick(d) {
- CaptureRuntimeMemStatsOnce(r)
- }
-}
-
-// Capture new values for the Go runtime statistics exported in
-// runtime.MemStats. This is designed to be called in a background
-// goroutine. Giving a registry which has not been given to
-// RegisterRuntimeMemStats will panic.
-//
-// Be very careful with this because runtime.ReadMemStats calls the C
-// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld()
-// and that last one does what it says on the tin.
-func CaptureRuntimeMemStatsOnce(r Registry) {
- t := time.Now()
- runtime.ReadMemStats(&memStats) // This takes 50-200us.
- runtimeMetrics.ReadMemStats.UpdateSince(t)
-
- runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc))
- runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys))
- if memStats.DebugGC {
- runtimeMetrics.MemStats.DebugGC.Update(1)
- } else {
- runtimeMetrics.MemStats.DebugGC.Update(0)
- }
- if memStats.EnableGC {
- runtimeMetrics.MemStats.EnableGC.Update(1)
- } else {
- runtimeMetrics.MemStats.EnableGC.Update(0)
- }
-
- runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees))
- runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc))
- runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle))
- runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse))
- runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects))
- runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased))
- runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys))
- runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC))
- runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups))
- runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs))
- runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse))
- runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys))
- runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse))
- runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys))
- runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC))
- runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC))
- runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats))
-
- //
- i := numGC % uint32(len(memStats.PauseNs))
- ii := memStats.NumGC % uint32(len(memStats.PauseNs))
- if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) {
- for i = 0; i < uint32(len(memStats.PauseNs)); i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- } else {
- if i > ii {
- for ; i < uint32(len(memStats.PauseNs)); i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- i = 0
- }
- for ; i < ii; i++ {
- runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i]))
- }
- }
- frees = memStats.Frees
- lookups = memStats.Lookups
- mallocs = memStats.Mallocs
- numGC = memStats.NumGC
-
- runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs))
- runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse))
- runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys))
- runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys))
- runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc))
-
- currentNumCgoCalls := numCgoCall()
- runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls)
- numCgoCalls = currentNumCgoCalls
-
- runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine()))
-
- runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count()))
-}
-
-// Register runtimeMetrics for the Go runtime statistics exported in runtime and
-// specifically runtime.MemStats. The runtimeMetrics are named by their
-// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc.
-func RegisterRuntimeMemStats(r Registry) {
- runtimeMetrics.MemStats.Alloc = NewGauge()
- runtimeMetrics.MemStats.BuckHashSys = NewGauge()
- runtimeMetrics.MemStats.DebugGC = NewGauge()
- runtimeMetrics.MemStats.EnableGC = NewGauge()
- runtimeMetrics.MemStats.Frees = NewGauge()
- runtimeMetrics.MemStats.HeapAlloc = NewGauge()
- runtimeMetrics.MemStats.HeapIdle = NewGauge()
- runtimeMetrics.MemStats.HeapInuse = NewGauge()
- runtimeMetrics.MemStats.HeapObjects = NewGauge()
- runtimeMetrics.MemStats.HeapReleased = NewGauge()
- runtimeMetrics.MemStats.HeapSys = NewGauge()
- runtimeMetrics.MemStats.LastGC = NewGauge()
- runtimeMetrics.MemStats.Lookups = NewGauge()
- runtimeMetrics.MemStats.Mallocs = NewGauge()
- runtimeMetrics.MemStats.MCacheInuse = NewGauge()
- runtimeMetrics.MemStats.MCacheSys = NewGauge()
- runtimeMetrics.MemStats.MSpanInuse = NewGauge()
- runtimeMetrics.MemStats.MSpanSys = NewGauge()
- runtimeMetrics.MemStats.NextGC = NewGauge()
- runtimeMetrics.MemStats.NumGC = NewGauge()
- runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64()
- runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015))
- runtimeMetrics.MemStats.PauseTotalNs = NewGauge()
- runtimeMetrics.MemStats.StackInuse = NewGauge()
- runtimeMetrics.MemStats.StackSys = NewGauge()
- runtimeMetrics.MemStats.Sys = NewGauge()
- runtimeMetrics.MemStats.TotalAlloc = NewGauge()
- runtimeMetrics.NumCgoCall = NewGauge()
- runtimeMetrics.NumGoroutine = NewGauge()
- runtimeMetrics.NumThread = NewGauge()
- runtimeMetrics.ReadMemStats = NewTimer()
-
- r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc)
- r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys)
- r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC)
- r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC)
- r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees)
- r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc)
- r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle)
- r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse)
- r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects)
- r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased)
- r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys)
- r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC)
- r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups)
- r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs)
- r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse)
- r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys)
- r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse)
- r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys)
- r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC)
- r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC)
- r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction)
- r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs)
- r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs)
- r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse)
- r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys)
- r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys)
- r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc)
- r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall)
- r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine)
- r.Register("runtime.NumThread", runtimeMetrics.NumThread)
- r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats)
-}
diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go
deleted file mode 100644
index e3391f4e89..0000000000
--- a/metrics/runtime_cgo.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// +build cgo
-// +build !appengine
-
-package metrics
-
-import "runtime"
-
-func numCgoCall() int64 {
- return runtime.NumCgoCall()
-}
diff --git a/metrics/runtime_gccpufraction.go b/metrics/runtime_gccpufraction.go
deleted file mode 100644
index ca12c05bac..0000000000
--- a/metrics/runtime_gccpufraction.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build go1.5
-
-package metrics
-
-import "runtime"
-
-func gcCPUFraction(memStats *runtime.MemStats) float64 {
- return memStats.GCCPUFraction
-}
diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go
deleted file mode 100644
index 616a3b4751..0000000000
--- a/metrics/runtime_no_cgo.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// +build !cgo appengine
-
-package metrics
-
-func numCgoCall() int64 {
- return 0
-}
diff --git a/metrics/runtime_no_gccpufraction.go b/metrics/runtime_no_gccpufraction.go
deleted file mode 100644
index be96aa6f1b..0000000000
--- a/metrics/runtime_no_gccpufraction.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build !go1.5
-
-package metrics
-
-import "runtime"
-
-func gcCPUFraction(memStats *runtime.MemStats) float64 {
- return 0
-}
diff --git a/metrics/runtime_test.go b/metrics/runtime_test.go
deleted file mode 100644
index ebbfd501a2..0000000000
--- a/metrics/runtime_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package metrics
-
-import (
- "runtime"
- "testing"
- "time"
-)
-
-func BenchmarkRuntimeMemStats(b *testing.B) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- CaptureRuntimeMemStatsOnce(r)
- }
-}
-
-func TestRuntimeMemStats(t *testing.T) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- CaptureRuntimeMemStatsOnce(r)
- zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests.
- runtime.GC()
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero {
- t.Fatal(count - zero)
- }
- runtime.GC()
- runtime.GC()
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero {
- t.Fatal(count - zero)
- }
- for i := 0; i < 256; i++ {
- runtime.GC()
- }
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero {
- t.Fatal(count - zero)
- }
- for i := 0; i < 257; i++ {
- runtime.GC()
- }
- CaptureRuntimeMemStatsOnce(r)
- if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures.
- t.Fatal(count - zero)
- }
-}
-
-func TestRuntimeMemStatsNumThread(t *testing.T) {
- r := NewRegistry()
- RegisterRuntimeMemStats(r)
- CaptureRuntimeMemStatsOnce(r)
-
- if value := runtimeMetrics.NumThread.Value(); value < 1 {
- t.Fatalf("got NumThread: %d, wanted at least 1", value)
- }
-}
-
-func TestRuntimeMemStatsBlocking(t *testing.T) {
- if g := runtime.GOMAXPROCS(0); g < 2 {
- t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g)
- }
- ch := make(chan int)
- go testRuntimeMemStatsBlocking(ch)
- var memStats runtime.MemStats
- t0 := time.Now()
- runtime.ReadMemStats(&memStats)
- t1 := time.Now()
- t.Log("i++ during runtime.ReadMemStats:", <-ch)
- go testRuntimeMemStatsBlocking(ch)
- d := t1.Sub(t0)
- t.Log(d)
- time.Sleep(d)
- t.Log("i++ during time.Sleep:", <-ch)
-}
-
-func testRuntimeMemStatsBlocking(ch chan int) {
- i := 0
- for {
- select {
- case ch <- i:
- return
- default:
- i++
- }
- }
-}
diff --git a/metrics/runtimehistogram.go b/metrics/runtimehistogram.go
new file mode 100644
index 0000000000..92fcbcc281
--- /dev/null
+++ b/metrics/runtimehistogram.go
@@ -0,0 +1,301 @@
+package metrics
+
+import (
+ "math"
+ "runtime/metrics"
+ "sort"
+ "sync/atomic"
+)
+
+func getOrRegisterRuntimeHistogram(name string, scale float64, r Registry) *runtimeHistogram {
+ if r == nil {
+ r = DefaultRegistry
+ }
+ constructor := func() Histogram { return newRuntimeHistogram(scale) }
+ return r.GetOrRegister(name, constructor).(*runtimeHistogram)
+}
+
+// runtimeHistogram wraps a runtime/metrics histogram.
+type runtimeHistogram struct {
+ v atomic.Value // v is a pointer to a metrics.Float64Histogram
+ scaleFactor float64
+}
+
+func newRuntimeHistogram(scale float64) *runtimeHistogram {
+ h := &runtimeHistogram{scaleFactor: scale}
+ h.update(new(metrics.Float64Histogram))
+ return h
+}
+
+func RuntimeHistogramFromData(scale float64, hist *metrics.Float64Histogram) *runtimeHistogram {
+ h := &runtimeHistogram{scaleFactor: scale}
+ h.update(hist)
+ return h
+}
+
+func (h *runtimeHistogram) update(mh *metrics.Float64Histogram) {
+ if mh == nil {
+ // The update value can be nil if the current Go version doesn't support a
+ // requested metric. It's just easier to handle nil here than putting
+ // conditionals everywhere.
+ return
+ }
+
+ s := metrics.Float64Histogram{
+ Counts: make([]uint64, len(mh.Counts)),
+ Buckets: make([]float64, len(mh.Buckets)),
+ }
+ copy(s.Counts, mh.Counts)
+ for i, b := range mh.Buckets {
+ s.Buckets[i] = b * h.scaleFactor
+ }
+ h.v.Store(&s)
+}
+
+func (h *runtimeHistogram) Clear() {
+ panic("runtimeHistogram does not support Clear")
+}
+func (h *runtimeHistogram) Update(int64) {
+ panic("runtimeHistogram does not support Update")
+}
+
+// Snapshot returns a non-changing copy of the histogram.
+func (h *runtimeHistogram) Snapshot() HistogramSnapshot {
+ hist := h.v.Load().(*metrics.Float64Histogram)
+ return newRuntimeHistogramSnapshot(hist)
+}
+
+type runtimeHistogramSnapshot struct {
+ internal *metrics.Float64Histogram
+ calculated bool
+ // The following fields are (lazily) calculated based on 'internal'
+ mean float64
+ count int64
+ min int64 // min is the lowest sample value.
+ max int64 // max is the highest sample value.
+ variance float64
+}
+
+func newRuntimeHistogramSnapshot(h *metrics.Float64Histogram) *runtimeHistogramSnapshot {
+ return &runtimeHistogramSnapshot{
+ internal: h,
+ }
+}
+
+// calc calculates the values for the snapshot. This method is not threadsafe.
+func (h *runtimeHistogramSnapshot) calc() {
+ h.calculated = true
+ var (
+ count int64 // number of samples
+ sum float64 // approx sum of all sample values
+ min int64
+ max float64
+ )
+ if len(h.internal.Counts) == 0 {
+ return
+ }
+ for i, c := range h.internal.Counts {
+ if c == 0 {
+ continue
+ }
+ if count == 0 { // Set min only first loop iteration
+ min = int64(math.Floor(h.internal.Buckets[i]))
+ }
+ count += int64(c)
+ sum += h.midpoint(i) * float64(c)
+ // Set max on every iteration
+ edge := h.internal.Buckets[i+1]
+ if math.IsInf(edge, 1) {
+ edge = h.internal.Buckets[i]
+ }
+ if edge > max {
+ max = edge
+ }
+ }
+ h.min = min
+ h.max = int64(max)
+ h.mean = sum / float64(count)
+ h.count = count
+}
+
+// Count returns the sample count.
+func (h *runtimeHistogramSnapshot) Count() int64 {
+ if !h.calculated {
+ h.calc()
+ }
+ return h.count
+}
+
+// Size returns the size of the sample at the time the snapshot was taken.
+func (h *runtimeHistogramSnapshot) Size() int {
+ return len(h.internal.Counts)
+}
+
+// Mean returns an approximation of the mean.
+func (h *runtimeHistogramSnapshot) Mean() float64 {
+ if !h.calculated {
+ h.calc()
+ }
+ return h.mean
+}
+
+func (h *runtimeHistogramSnapshot) midpoint(bucket int) float64 {
+ high := h.internal.Buckets[bucket+1]
+ low := h.internal.Buckets[bucket]
+ if math.IsInf(high, 1) {
+ // The edge of the highest bucket can be +Inf, and it's supposed to mean that this
+ // bucket contains all remaining samples > low. We can't get the middle of an
+ // infinite range, so just return the lower bound of this bucket instead.
+ return low
+ }
+ if math.IsInf(low, -1) {
+ // Similarly, we can get -Inf in the left edge of the lowest bucket,
+ // and it means the bucket contains all remaining values < high.
+ return high
+ }
+ return (low + high) / 2
+}
+
+// StdDev approximates the standard deviation of the histogram.
+func (h *runtimeHistogramSnapshot) StdDev() float64 {
+ return math.Sqrt(h.Variance())
+}
+
+// Variance approximates the variance of the histogram.
+func (h *runtimeHistogramSnapshot) Variance() float64 {
+ if len(h.internal.Counts) == 0 {
+ return 0
+ }
+ if !h.calculated {
+ h.calc()
+ }
+ if h.count <= 1 {
+ // There is no variance when there are zero or one items.
+ return 0
+ }
+ // Variance is not calculated in 'calc', because it requires a second iteration.
+ // Therefore we calculate it lazily in this method, triggered either by
+ // a direct call to Variance or via StdDev.
+ if h.variance != 0.0 {
+ return h.variance
+ }
+ var sum float64
+
+ for i, c := range h.internal.Counts {
+ midpoint := h.midpoint(i)
+ d := midpoint - h.mean
+ sum += float64(c) * (d * d)
+ }
+ h.variance = sum / float64(h.count-1)
+ return h.variance
+}
+
+// Percentile computes the p'th percentile value.
+func (h *runtimeHistogramSnapshot) Percentile(p float64) float64 {
+ threshold := float64(h.Count()) * p
+ values := [1]float64{threshold}
+ h.computePercentiles(values[:])
+ return values[0]
+}
+
+// Percentiles computes all requested percentile values.
+func (h *runtimeHistogramSnapshot) Percentiles(ps []float64) []float64 {
+ // Compute threshold values. We need these to be sorted
+ // for the percentile computation, but restore the original
+ // order later, so keep the indexes as well.
+ count := float64(h.Count())
+ thresholds := make([]float64, len(ps))
+ indexes := make([]int, len(ps))
+ for i, percentile := range ps {
+ thresholds[i] = count * math.Max(0, math.Min(1.0, percentile))
+ indexes[i] = i
+ }
+ sort.Sort(floatsAscendingKeepingIndex{thresholds, indexes})
+
+ // Now compute. The result is stored back into the thresholds slice.
+ h.computePercentiles(thresholds)
+
+ // Put the result back into the requested order.
+ sort.Sort(floatsByIndex{thresholds, indexes})
+ return thresholds
+}
+
+func (h *runtimeHistogramSnapshot) computePercentiles(thresh []float64) {
+ var totalCount float64
+ for i, count := range h.internal.Counts {
+ totalCount += float64(count)
+
+ for len(thresh) > 0 && thresh[0] < totalCount {
+ thresh[0] = h.internal.Buckets[i]
+ thresh = thresh[1:]
+ }
+ if len(thresh) == 0 {
+ return
+ }
+ }
+}
+
+// Note: runtime/metrics.Float64Histogram is a collection of float64s, but the methods
+// below need to return int64 to satisfy the interface. The histogram provided by runtime
+// also doesn't keep track of individual samples, so results are approximated.
+
+// Max returns the highest sample value.
+func (h *runtimeHistogramSnapshot) Max() int64 {
+ if !h.calculated {
+ h.calc()
+ }
+ return h.max
+}
+
+// Min returns the lowest sample value.
+func (h *runtimeHistogramSnapshot) Min() int64 {
+ if !h.calculated {
+ h.calc()
+ }
+ return h.min
+}
+
+// Sum returns the sum of all sample values.
+func (h *runtimeHistogramSnapshot) Sum() int64 {
+ var sum float64
+ for i := range h.internal.Counts {
+ sum += h.internal.Buckets[i] * float64(h.internal.Counts[i])
+ }
+ return int64(math.Ceil(sum))
+}
+
+type floatsAscendingKeepingIndex struct {
+ values []float64
+ indexes []int
+}
+
+func (s floatsAscendingKeepingIndex) Len() int {
+ return len(s.values)
+}
+
+func (s floatsAscendingKeepingIndex) Less(i, j int) bool {
+ return s.values[i] < s.values[j]
+}
+
+func (s floatsAscendingKeepingIndex) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i]
+}
+
+type floatsByIndex struct {
+ values []float64
+ indexes []int
+}
+
+func (s floatsByIndex) Len() int {
+ return len(s.values)
+}
+
+func (s floatsByIndex) Less(i, j int) bool {
+ return s.indexes[i] < s.indexes[j]
+}
+
+func (s floatsByIndex) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ s.indexes[i], s.indexes[j] = s.indexes[j], s.indexes[i]
+}
diff --git a/metrics/runtimehistogram_test.go b/metrics/runtimehistogram_test.go
new file mode 100644
index 0000000000..cf7e36420a
--- /dev/null
+++ b/metrics/runtimehistogram_test.go
@@ -0,0 +1,162 @@
+package metrics
+
+import (
+ "bytes"
+ "encoding/gob"
+ "fmt"
+ "math"
+ "reflect"
+ "runtime/metrics"
+ "testing"
+ "time"
+)
+
+var _ Histogram = (*runtimeHistogram)(nil)
+
+type runtimeHistogramTest struct {
+ h metrics.Float64Histogram
+
+ Count int64
+ Min int64
+ Max int64
+ Sum int64
+ Mean float64
+ Variance float64
+ StdDev float64
+ Percentiles []float64 // .5 .8 .9 .99 .995
+}
+
+// This test checks the results of statistical functions implemented
+// by runtimeHistogramSnapshot.
+func TestRuntimeHistogramStats(t *testing.T) {
+ tests := []runtimeHistogramTest{
+ 0: {
+ h: metrics.Float64Histogram{
+ Counts: []uint64{},
+ Buckets: []float64{},
+ },
+ Count: 0,
+ Max: 0,
+ Min: 0,
+ Sum: 0,
+ Mean: 0,
+ Variance: 0,
+ StdDev: 0,
+ Percentiles: []float64{0, 0, 0, 0, 0},
+ },
+ 1: {
+ // This checks the case where the highest bucket is +Inf.
+ h: metrics.Float64Histogram{
+ Counts: []uint64{0, 1, 2},
+ Buckets: []float64{0, 0.5, 1, math.Inf(1)},
+ },
+ Count: 3,
+ Max: 1,
+ Min: 0,
+ Sum: 3,
+ Mean: 0.9166666,
+ Percentiles: []float64{1, 1, 1, 1, 1},
+ Variance: 0.020833,
+ StdDev: 0.144433,
+ },
+ 2: {
+ h: metrics.Float64Histogram{
+ Counts: []uint64{8, 6, 3, 1},
+ Buckets: []float64{12, 16, 18, 24, 25},
+ },
+ Count: 18,
+ Max: 25,
+ Min: 12,
+ Sum: 270,
+ Mean: 16.75,
+ Variance: 10.3015,
+ StdDev: 3.2096,
+ Percentiles: []float64{16, 18, 18, 24, 24},
+ },
+ }
+
+ for i, test := range tests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ s := RuntimeHistogramFromData(1.0, &test.h).Snapshot()
+
+ if v := s.Count(); v != test.Count {
+ t.Errorf("Count() = %v, want %v", v, test.Count)
+ }
+ if v := s.Min(); v != test.Min {
+ t.Errorf("Min() = %v, want %v", v, test.Min)
+ }
+ if v := s.Max(); v != test.Max {
+ t.Errorf("Max() = %v, want %v", v, test.Max)
+ }
+ if v := s.Sum(); v != test.Sum {
+ t.Errorf("Sum() = %v, want %v", v, test.Sum)
+ }
+ if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) {
+ t.Errorf("Mean() = %v, want %v", v, test.Mean)
+ }
+ if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) {
+ t.Errorf("Variance() = %v, want %v", v, test.Variance)
+ }
+ if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) {
+ t.Errorf("StdDev() = %v, want %v", v, test.StdDev)
+ }
+ ps := []float64{.5, .8, .9, .99, .995}
+ if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) {
+ t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles)
+ }
+ })
+ }
+}
+
+func approxEqual(x, y, ε float64) bool {
+ if math.IsInf(x, -1) && math.IsInf(y, -1) {
+ return true
+ }
+ if math.IsInf(x, 1) && math.IsInf(y, 1) {
+ return true
+ }
+ if math.IsNaN(x) && math.IsNaN(y) {
+ return true
+ }
+ return math.Abs(x-y) < ε
+}
+
+// This test verifies that requesting Percentiles in unsorted order
+// returns them in the requested order.
+func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) {
+ s := RuntimeHistogramFromData(1.0, &metrics.Float64Histogram{
+ Counts: []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
+ }).Snapshot()
+ result := s.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2})
+ expected := []float64{10, 2, 5, 1, 2}
+ if !reflect.DeepEqual(result, expected) {
+ t.Fatal("wrong result:", result)
+ }
+}
+
+func BenchmarkRuntimeHistogramSnapshotRead(b *testing.B) {
+ var sLatency = "7\xff\x81\x03\x01\x01\x10Float64Histogram\x01\xff\x82\x00\x01\x02\x01\x06Counts\x01\xff\x84\x00\x01\aBuckets\x01\xff\x86\x00\x00\x00\x16\xff\x83\x02\x01\x01\b[]uint64\x01\xff\x84\x00\x01\x06\x00\x00\x17\xff\x85\x02\x01\x01\t[]float64\x01\xff\x86\x00\x01\b\x00\x00\xfe\x06T\xff\x82\x01\xff\xa2\x00\xfe\r\xef\x00\x01\x02\x02\x04\x05\x04\b\x15\x17 B?6.L;$!2) \x1a? \x190aH7FY6#\x190\x1d\x14\x10\x1b\r\t\x04\x03\x01\x01\x00\x03\x02\x00\x03\x05\x05\x02\x02\x06\x04\v\x06\n\x15\x18\x13'&.\x12=H/L&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xa3\xfe\xf0\xff\x00\xf8\x95\xd6&\xe8\v.q>\xf8\x95\xd6&\xe8\v.\x81>\xf8\xdfA:\xdc\x11ʼn>\xf8\x95\xd6&\xe8\v.\x91>\xf8:\x8c0\xe2\x8ey\x95>\xf8\xdfA:\xdc\x11ř>\xf8\x84\xf7C֔\x10\x9e>\xf8\x95\xd6&\xe8\v.\xa1>\xf8:\x8c0\xe2\x8ey\xa5>\xf8\xdfA:\xdc\x11ũ>\xf8\x84\xf7C֔\x10\xae>\xf8\x95\xd6&\xe8\v.\xb1>\xf8:\x8c0\xe2\x8ey\xb5>\xf8\xdfA:\xdc\x11Ź>\xf8\x84\xf7C֔\x10\xbe>\xf8\x95\xd6&\xe8\v.\xc1>\xf8:\x8c0\xe2\x8ey\xc5>\xf8\xdfA:\xdc\x11\xc5\xc9>\xf8\x84\xf7C֔\x10\xce>\xf8\x95\xd6&\xe8\v.\xd1>\xf8:\x8c0\xe2\x8ey\xd5>\xf8\xdfA:\xdc\x11\xc5\xd9>\xf8\x84\xf7C֔\x10\xde>\xf8\x95\xd6&\xe8\v.\xe1>\xf8:\x8c0\xe2\x8ey\xe5>\xf8\xdfA:\xdc\x11\xc5\xe9>\xf8\x84\xf7C֔\x10\xee>\xf8\x95\xd6&\xe8\v.\xf1>\xf8:\x8c0\xe2\x8ey\xf5>\xf8\xdfA:\xdc\x11\xc5\xf9>\xf8\x84\xf7C֔\x10\xfe>\xf8\x95\xd6&\xe8\v.\x01?\xf8:\x8c0\xe2\x8ey\x05?\xf8\xdfA:\xdc\x11\xc5\t?\xf8\x84\xf7C֔\x10\x0e?\xf8\x95\xd6&\xe8\v.\x11?\xf8:\x8c0\xe2\x8ey\x15?\xf8\xdfA:\xdc\x11\xc5\x19?\xf8\x84\xf7C֔\x10\x1e?\xf8\x95\xd6&\xe8\v.!?\xf8:\x8c0\xe2\x8ey%?\xf8\xdfA:\xdc\x11\xc5)?\xf8\x84\xf7C֔\x10.?\xf8\x95\xd6&\xe8\v.1?\xf8:\x8c0\xe2\x8ey5?\xf8\xdfA:\xdc\x11\xc59?\xf8\x84\xf7C֔\x10>?\xf8\x95\xd6&\xe8\v.A?\xf8:\x8c0\xe2\x8eyE?\xf8\xdfA:\xdc\x11\xc5I?\xf8\x84\xf7C֔\x10N?\xf8\x95\xd6&\xe8\v.Q?\xf8:\x8c0\xe2\x8eyU?\xf8\xdfA:\xdc\x11\xc5Y?\xf8\x84\xf7C֔\x10^?\xf8\x95\xd6&\xe8\v.a?\xf8:\x8c0\xe2\x8eye?\xf8\xdfA:\xdc\x11\xc5i?\xf8\x84\xf7C֔\x10n?\xf8\x95\xd6&\xe8\v.q?\xf8:\x8c0\xe2\x8eyu?\xf8\xdfA:\xdc\x11\xc5y?\xf8\x84\xf7C֔\x10~?\xf8\x95\xd6&\xe8\v.\x81?\xf8:\x8c0\xe2\x8ey\x85?\xf8\xdfA:\xdc\x11ʼn?\xf8\x84\xf7C֔\x10\x8e?\xf8\x95\xd6&\xe8\v.\x91?\xf8:\x8c0\xe2\x8ey\x95?\xf8\xdfA:\xdc\x11ř?\xf8\x84\xf7C֔\x10\x9e?\xf8\x95\xd6&\xe8\v.\xa1?\xf8:\x8c0\xe2\x8ey\xa5?\xf8\xdfA:\xdc\x11ũ?\xf8\x84\xf7C֔\x10\xae?\xf8\x95\xd6&\xe8\v.\xb1?\xf8:\x8c0\xe2\x8ey\xb5?\xf8\xdfA:\xdc\x11Ź?\xf8\x84\xf7C֔\x10\xbe?\xf8\x95\xd6&\xe8\v.\xc1?\xf8:\x8c0\xe2\x8ey\xc5?\xf8\xdfA:\xdc\x11\xc5\xc9?\xf8\x84\xf7C֔\x10\xce?\xf8\x95\xd6&\xe8\v.\xd1?\xf8:\x8c0\xe2\x8ey\xd5?\xf8\xdfA:\xdc\x11\xc5\xd9?\xf8\x84\xf7C֔\x10\xde?\xf8\x95\xd6&\xe8\v.\xe1?\xf8:\x8c0\xe2\x8ey\xe5?\xf8\xdfA:\xdc\x11\xc5\xe9?\xf8\x84\xf7C֔\x10\xee?\xf8\x95\xd6&\xe8\v.\xf1?\xf8:\x8c0\xe2\x8ey\xf5?\xf8\xdfA:\xdc\x11\xc5\xf9?\xf8\x84\xf7C֔\x10\xfe?\xf8\x95\xd6&\xe8\v.\x01@\xf8:\x8c0\xe2\x8ey\x05@\xf8\xdfA:\xdc\x11\xc5\t@\xf8\x84\xf7C֔\x10\x0e@\xf8\x95\xd6&\xe8\v.\x11@\xf8:\x8c0\xe2\x8ey\x15@\xf8\xdfA:\xdc\x11\xc5\x19@\xf8\x84\xf7C֔\x10\x1e@\xf8\x95\xd6&\xe8\v.!@\xf8:\x8c0\xe2\x8ey%@\xf8\xdfA:\xdc\x11\xc5)@\xf8\x84\xf7C֔\x10.@\xf8\x95\xd6&\xe8\v.1@\xf8:\x8c0\xe2\x8ey5@\xf8\xdfA:\xdc\x11\xc59@\xf8\x84\xf7C֔\x10>@\xf8\x95\xd6&\xe8\v.A@\xf8:\x8c0\xe2\x8eyE@\xf8\xdfA:\xdc\x11\xc5I@\xf8\x84\xf7C֔\x10N@\xf8\x95\xd6&\xe8\v.Q@\xf8:\x8c0\xe2\x8eyU@\xf8\xdfA:\xdc\x11\xc5Y@\xf8\x84\xf7C֔\x10^@\xf8\x95\xd6&\xe8\v.a@\xf8:\x8c0\xe2\x8eye@\xf8\xdfA:\xdc\x11\xc5i@\xf8\x84\xf7C֔\x10n@\xf8\x95\xd6&\xe8\v.q@\xf8:\x8c0\xe2\x8eyu@\xf8\xdfA:\xdc\x11\xc5y@\xf8\x84\xf7C֔\x10~@\xf8\x95\xd6&\xe8\v.\x81@\xf8:\x8c0\xe2\x8ey\x85@\xf8\xdfA:\xdc\x11ʼn@\xf8\x84\xf7C֔\x10\x8e@\xf8\x95\xd6&\xe8\v.\x91@\xf8:\x8c0\xe2\x8ey\x95@\xf8\xdfA:\xdc\x11ř@\xf8\x84\xf7C֔\x10\x9e@\xf8\x95\xd6&\xe8\v.\xa1@\xf8:\x8c0\xe2\x8ey\xa5@\xf8\xdfA:\xdc\x11ũ@\xf8\x84\xf7C֔\x10\xae@\xf8\x95\xd6&\xe8\v.\xb1@\xf8:\x8c0\xe2\x8ey\xb5@\xf8\xdfA:\xdc\x11Ź@\xf8\x84\xf7C֔\x10\xbe@\xf8\x95\xd6&\xe8\v.\xc1@\xf8:\x8c0\xe2\x8ey\xc5@\xf8\xdfA:\xdc\x11\xc5\xc9@\xf8\x84\xf7C֔\x10\xce@\xf8\x95\xd6&\xe8\v.\xd1@\xf8:\x8c0\xe2\x8ey\xd5@\xf8\xdfA:\xdc\x11\xc5\xd9@\xf8\x84\xf7C֔\x10\xde@\xf8\x95\xd6&\xe8\v.\xe1@\xf8:\x8c0\xe2\x8ey\xe5@\xf8\xdfA:\xdc\x11\xc5\xe9@\xf8\x84\xf7C֔\x10\xee@\xf8\x95\xd6&\xe8\v.\xf1@\xf8:\x8c0\xe2\x8ey\xf5@\xf8\xdfA:\xdc\x11\xc5\xf9@\xf8\x84\xf7C֔\x10\xfe@\xf8\x95\xd6&\xe8\v.\x01A\xfe\xf0\x7f\x00"
+
+ dserialize := func(data string) *metrics.Float64Histogram {
+ var res metrics.Float64Histogram
+ if err := gob.NewDecoder(bytes.NewReader([]byte(data))).Decode(&res); err != nil {
+ panic(err)
+ }
+ return &res
+ }
+ latency := RuntimeHistogramFromData(float64(time.Second), dserialize(sLatency))
+ b.ResetTimer()
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ snap := latency.Snapshot()
+ // These are the fields that influxdb accesses
+ _ = snap.Count()
+ _ = snap.Max()
+ _ = snap.Mean()
+ _ = snap.Min()
+ _ = snap.StdDev()
+ _ = snap.Variance()
+ _ = snap.Percentiles([]float64{0.25, 0.5, 0.75, 0.95, 0.99, 0.999, 0.9999})
+ }
+}
diff --git a/metrics/sample.go b/metrics/sample.go
index 5c4845a4f8..dc8167809f 100644
--- a/metrics/sample.go
+++ b/metrics/sample.go
@@ -3,30 +3,128 @@ package metrics
import (
"math"
"math/rand"
- "sort"
+ "slices"
"sync"
"time"
)
const rescaleThreshold = time.Hour
-// Samples maintain a statistically-significant selection of values from
+// Sample maintains a statistically-significant selection of values from
// a stream.
type Sample interface {
+ Snapshot() *sampleSnapshot
Clear()
- Count() int64
- Max() int64
- Mean() float64
- Min() int64
- Percentile(float64) float64
- Percentiles([]float64) []float64
- Size() int
- Snapshot() Sample
- StdDev() float64
- Sum() int64
Update(int64)
- Values() []int64
- Variance() float64
+}
+
+var (
+ _ Sample = (*ExpDecaySample)(nil)
+ _ Sample = (*UniformSample)(nil)
+ _ Sample = (*resettingSample)(nil)
+)
+
+// sampleSnapshot is a read-only copy of a Sample.
+type sampleSnapshot struct {
+ count int64
+ values []int64
+
+ max int64
+ min int64
+ mean float64
+ sum int64
+ variance float64
+}
+
+// newSampleSnapshotPrecalculated creates a read-only sampleSnapShot, using
+// precalculated sums to avoid iterating the values
+func newSampleSnapshotPrecalculated(count int64, values []int64, min, max, sum int64) *sampleSnapshot {
+ if len(values) == 0 {
+ return &sampleSnapshot{
+ count: count,
+ values: values,
+ }
+ }
+ return &sampleSnapshot{
+ count: count,
+ values: values,
+ max: max,
+ min: min,
+ mean: float64(sum) / float64(len(values)),
+ sum: sum,
+ }
+}
+
+// newSampleSnapshot creates a read-only sampleSnapShot, and calculates some
+// numbers.
+func newSampleSnapshot(count int64, values []int64) *sampleSnapshot {
+ var (
+ max int64 = math.MinInt64
+ min int64 = math.MaxInt64
+ sum int64
+ )
+ for _, v := range values {
+ sum += v
+ if v > max {
+ max = v
+ }
+ if v < min {
+ min = v
+ }
+ }
+ return newSampleSnapshotPrecalculated(count, values, min, max, sum)
+}
+
+// Count returns the count of inputs at the time the snapshot was taken.
+func (s *sampleSnapshot) Count() int64 { return s.count }
+
+// Max returns the maximal value at the time the snapshot was taken.
+func (s *sampleSnapshot) Max() int64 { return s.max }
+
+// Mean returns the mean value at the time the snapshot was taken.
+func (s *sampleSnapshot) Mean() float64 { return s.mean }
+
+// Min returns the minimal value at the time the snapshot was taken.
+func (s *sampleSnapshot) Min() int64 { return s.min }
+
+// Percentile returns an arbitrary percentile of values at the time the
+// snapshot was taken.
+func (s *sampleSnapshot) Percentile(p float64) float64 {
+ return SamplePercentile(s.values, p)
+}
+
+// Percentiles returns a slice of arbitrary percentiles of values at the time
+// the snapshot was taken.
+func (s *sampleSnapshot) Percentiles(ps []float64) []float64 {
+ return CalculatePercentiles(s.values, ps)
+}
+
+// Size returns the size of the sample at the time the snapshot was taken.
+func (s *sampleSnapshot) Size() int { return len(s.values) }
+
+// StdDev returns the standard deviation of values at the time the snapshot was
+// taken.
+func (s *sampleSnapshot) StdDev() float64 {
+ if s.variance == 0.0 {
+ s.variance = SampleVariance(s.mean, s.values)
+ }
+ return math.Sqrt(s.variance)
+}
+
+// Sum returns the sum of values at the time the snapshot was taken.
+func (s *sampleSnapshot) Sum() int64 { return s.sum }
+
+// Values returns a copy of the values in the sample.
+func (s *sampleSnapshot) Values() []int64 {
+ return slices.Clone(s.values)
+}
+
+// Variance returns the variance of values at the time the snapshot was taken.
+func (s *sampleSnapshot) Variance() float64 {
+ if s.variance == 0.0 {
+ s.variance = SampleVariance(s.mean, s.values)
+ }
+ return s.variance
}
// ExpDecaySample is an exponentially-decaying sample using a forward-decaying
@@ -41,14 +139,12 @@ type ExpDecaySample struct {
reservoirSize int
t0, t1 time.Time
values *expDecaySampleHeap
+ rand *rand.Rand
}
// NewExpDecaySample constructs a new exponentially-decaying sample with the
// given reservoir size and alpha.
func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
- if !Enabled {
- return NilSample{}
- }
s := &ExpDecaySample{
alpha: alpha,
reservoirSize: reservoirSize,
@@ -59,6 +155,12 @@ func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
return s
}
+// SetRand sets the random source (useful in tests)
+func (s *ExpDecaySample) SetRand(prng *rand.Rand) Sample {
+ s.rand = prng
+ return s
+}
+
// Clear clears all samples.
func (s *ExpDecaySample) Clear() {
s.mutex.Lock()
@@ -69,94 +171,37 @@ func (s *ExpDecaySample) Clear() {
s.values.Clear()
}
-// Count returns the number of samples recorded, which may exceed the
-// reservoir size.
-func (s *ExpDecaySample) Count() int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return s.count
-}
-
-// Max returns the maximum value in the sample, which may not be the maximum
-// value ever to be part of the sample.
-func (s *ExpDecaySample) Max() int64 {
- return SampleMax(s.Values())
-}
-
-// Mean returns the mean of the values in the sample.
-func (s *ExpDecaySample) Mean() float64 {
- return SampleMean(s.Values())
-}
-
-// Min returns the minimum value in the sample, which may not be the minimum
-// value ever to be part of the sample.
-func (s *ExpDecaySample) Min() int64 {
- return SampleMin(s.Values())
-}
-
-// Percentile returns an arbitrary percentile of values in the sample.
-func (s *ExpDecaySample) Percentile(p float64) float64 {
- return SamplePercentile(s.Values(), p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of values in the
-// sample.
-func (s *ExpDecaySample) Percentiles(ps []float64) []float64 {
- return SamplePercentiles(s.Values(), ps)
-}
-
-// Size returns the size of the sample, which is at most the reservoir size.
-func (s *ExpDecaySample) Size() int {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return s.values.Size()
-}
-
// Snapshot returns a read-only copy of the sample.
-func (s *ExpDecaySample) Snapshot() Sample {
+func (s *ExpDecaySample) Snapshot() *sampleSnapshot {
s.mutex.Lock()
defer s.mutex.Unlock()
- vals := s.values.Values()
- values := make([]int64, len(vals))
- for i, v := range vals {
- values[i] = v.v
+ var (
+ samples = s.values.Values()
+ values = make([]int64, len(samples))
+ max int64 = math.MinInt64
+ min int64 = math.MaxInt64
+ sum int64
+ )
+ for i, item := range samples {
+ v := item.v
+ values[i] = v
+ sum += v
+ if v > max {
+ max = v
+ }
+ if v < min {
+ min = v
+ }
}
- return &SampleSnapshot{
- count: s.count,
- values: values,
- }
-}
-
-// StdDev returns the standard deviation of the values in the sample.
-func (s *ExpDecaySample) StdDev() float64 {
- return SampleStdDev(s.Values())
-}
-
-// Sum returns the sum of the values in the sample.
-func (s *ExpDecaySample) Sum() int64 {
- return SampleSum(s.Values())
+ return newSampleSnapshotPrecalculated(s.count, values, min, max, sum)
}
// Update samples a new value.
func (s *ExpDecaySample) Update(v int64) {
- s.update(time.Now(), v)
-}
-
-// Values returns a copy of the values in the sample.
-func (s *ExpDecaySample) Values() []int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- vals := s.values.Values()
- values := make([]int64, len(vals))
- for i, v := range vals {
- values[i] = v.v
+ if !metricsEnabled {
+ return
}
- return values
-}
-
-// Variance returns the variance of the values in the sample.
-func (s *ExpDecaySample) Variance() float64 {
- return SampleVariance(s.Values())
+ s.update(time.Now(), v)
}
// update samples a new value at a particular timestamp. This is a method all
@@ -168,8 +213,14 @@ func (s *ExpDecaySample) update(t time.Time, v int64) {
if s.values.Size() == s.reservoirSize {
s.values.Pop()
}
+ var f64 float64
+ if s.rand != nil {
+ f64 = s.rand.Float64()
+ } else {
+ f64 = rand.Float64()
+ }
s.values.Push(expDecaySample{
- k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(),
+ k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / f64,
v: v,
})
if t.After(s.t1) {
@@ -185,216 +236,54 @@ func (s *ExpDecaySample) update(t time.Time, v int64) {
}
}
-// NilSample is a no-op Sample.
-type NilSample struct{}
-
-// Clear is a no-op.
-func (NilSample) Clear() {}
-
-// Count is a no-op.
-func (NilSample) Count() int64 { return 0 }
-
-// Max is a no-op.
-func (NilSample) Max() int64 { return 0 }
-
-// Mean is a no-op.
-func (NilSample) Mean() float64 { return 0.0 }
-
-// Min is a no-op.
-func (NilSample) Min() int64 { return 0 }
-
-// Percentile is a no-op.
-func (NilSample) Percentile(p float64) float64 { return 0.0 }
-
-// Percentiles is a no-op.
-func (NilSample) Percentiles(ps []float64) []float64 {
- return make([]float64, len(ps))
+// SamplePercentile returns an arbitrary percentile of the slice of int64.
+func SamplePercentile(values []int64, p float64) float64 {
+ return CalculatePercentiles(values, []float64{p})[0]
}
-// Size is a no-op.
-func (NilSample) Size() int { return 0 }
-
-// Sample is a no-op.
-func (NilSample) Snapshot() Sample { return NilSample{} }
-
-// StdDev is a no-op.
-func (NilSample) StdDev() float64 { return 0.0 }
-
-// Sum is a no-op.
-func (NilSample) Sum() int64 { return 0 }
-
-// Update is a no-op.
-func (NilSample) Update(v int64) {}
-
-// Values is a no-op.
-func (NilSample) Values() []int64 { return []int64{} }
-
-// Variance is a no-op.
-func (NilSample) Variance() float64 { return 0.0 }
-
-// SampleMax returns the maximum value of the slice of int64.
-func SampleMax(values []int64) int64 {
- if 0 == len(values) {
- return 0
- }
- var max int64 = math.MinInt64
- for _, v := range values {
- if max < v {
- max = v
- }
- }
- return max
-}
-
-// SampleMean returns the mean value of the slice of int64.
-func SampleMean(values []int64) float64 {
- if 0 == len(values) {
- return 0.0
- }
- return float64(SampleSum(values)) / float64(len(values))
-}
-
-// SampleMin returns the minimum value of the slice of int64.
-func SampleMin(values []int64) int64 {
- if 0 == len(values) {
- return 0
- }
- var min int64 = math.MaxInt64
- for _, v := range values {
- if min > v {
- min = v
- }
- }
- return min
-}
-
-// SamplePercentiles returns an arbitrary percentile of the slice of int64.
-func SamplePercentile(values int64Slice, p float64) float64 {
- return SamplePercentiles(values, []float64{p})[0]
-}
-
-// SamplePercentiles returns a slice of arbitrary percentiles of the slice of
-// int64.
-func SamplePercentiles(values int64Slice, ps []float64) []float64 {
+// CalculatePercentiles returns a slice of arbitrary percentiles of the slice of
+// int64. This method returns interpolated results, so e.g. if there are only two
+// values, [0, 10], a 50% percentile will land between them.
+//
+// Note: As a side-effect, this method will also sort the slice of values.
+// Note2: The input format for percentiles is NOT percent! To express 50%, use 0.5, not 50.
+func CalculatePercentiles(values []int64, ps []float64) []float64 {
scores := make([]float64, len(ps))
size := len(values)
- if size > 0 {
- sort.Sort(values)
- for i, p := range ps {
- pos := p * float64(size+1)
- if pos < 1.0 {
- scores[i] = float64(values[0])
- } else if pos >= float64(size) {
- scores[i] = float64(values[size-1])
- } else {
- lower := float64(values[int(pos)-1])
- upper := float64(values[int(pos)])
- scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
- }
+ if size == 0 {
+ return scores
+ }
+ slices.Sort(values)
+ for i, p := range ps {
+ pos := p * float64(size+1)
+
+ if pos < 1.0 {
+ scores[i] = float64(values[0])
+ } else if pos >= float64(size) {
+ scores[i] = float64(values[size-1])
+ } else {
+ lower := float64(values[int(pos)-1])
+ upper := float64(values[int(pos)])
+ scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
}
}
return scores
}
-// SampleSnapshot is a read-only copy of another Sample.
-type SampleSnapshot struct {
- count int64
- values []int64
-}
-
-func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot {
- return &SampleSnapshot{
- count: count,
- values: values,
- }
-}
-
-// Clear panics.
-func (*SampleSnapshot) Clear() {
- panic("Clear called on a SampleSnapshot")
-}
-
-// Count returns the count of inputs at the time the snapshot was taken.
-func (s *SampleSnapshot) Count() int64 { return s.count }
-
-// Max returns the maximal value at the time the snapshot was taken.
-func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) }
-
-// Mean returns the mean value at the time the snapshot was taken.
-func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) }
-
-// Min returns the minimal value at the time the snapshot was taken.
-func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) }
-
-// Percentile returns an arbitrary percentile of values at the time the
-// snapshot was taken.
-func (s *SampleSnapshot) Percentile(p float64) float64 {
- return SamplePercentile(s.values, p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of values at the time
-// the snapshot was taken.
-func (s *SampleSnapshot) Percentiles(ps []float64) []float64 {
- return SamplePercentiles(s.values, ps)
-}
-
-// Size returns the size of the sample at the time the snapshot was taken.
-func (s *SampleSnapshot) Size() int { return len(s.values) }
-
-// Snapshot returns the snapshot.
-func (s *SampleSnapshot) Snapshot() Sample { return s }
-
-// StdDev returns the standard deviation of values at the time the snapshot was
-// taken.
-func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) }
-
-// Sum returns the sum of values at the time the snapshot was taken.
-func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) }
-
-// Update panics.
-func (*SampleSnapshot) Update(int64) {
- panic("Update called on a SampleSnapshot")
-}
-
-// Values returns a copy of the values in the sample.
-func (s *SampleSnapshot) Values() []int64 {
- values := make([]int64, len(s.values))
- copy(values, s.values)
- return values
-}
-
-// Variance returns the variance of values at the time the snapshot was taken.
-func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) }
-
-// SampleStdDev returns the standard deviation of the slice of int64.
-func SampleStdDev(values []int64) float64 {
- return math.Sqrt(SampleVariance(values))
-}
-
-// SampleSum returns the sum of the slice of int64.
-func SampleSum(values []int64) int64 {
- var sum int64
- for _, v := range values {
- sum += v
- }
- return sum
-}
-
// SampleVariance returns the variance of the slice of int64.
-func SampleVariance(values []int64) float64 {
- if 0 == len(values) {
+func SampleVariance(mean float64, values []int64) float64 {
+ if len(values) == 0 {
return 0.0
}
- m := SampleMean(values)
var sum float64
for _, v := range values {
- d := float64(v) - m
+ d := float64(v) - mean
sum += d * d
}
return sum / float64(len(values))
}
-// A uniform sample using Vitter's Algorithm R.
+// UniformSample implements a uniform sample using Vitter's Algorithm R.
//
//
type UniformSample struct {
@@ -402,136 +291,62 @@ type UniformSample struct {
mutex sync.Mutex
reservoirSize int
values []int64
+ rand *rand.Rand
}
// NewUniformSample constructs a new uniform sample with the given reservoir
// size.
func NewUniformSample(reservoirSize int) Sample {
- if !Enabled {
- return NilSample{}
- }
return &UniformSample{
reservoirSize: reservoirSize,
values: make([]int64, 0, reservoirSize),
}
}
+// SetRand sets the random source (useful in tests)
+func (s *UniformSample) SetRand(prng *rand.Rand) Sample {
+ s.rand = prng
+ return s
+}
+
// Clear clears all samples.
func (s *UniformSample) Clear() {
s.mutex.Lock()
defer s.mutex.Unlock()
s.count = 0
- s.values = make([]int64, 0, s.reservoirSize)
-}
-
-// Count returns the number of samples recorded, which may exceed the
-// reservoir size.
-func (s *UniformSample) Count() int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return s.count
-}
-
-// Max returns the maximum value in the sample, which may not be the maximum
-// value ever to be part of the sample.
-func (s *UniformSample) Max() int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleMax(s.values)
-}
-
-// Mean returns the mean of the values in the sample.
-func (s *UniformSample) Mean() float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleMean(s.values)
-}
-
-// Min returns the minimum value in the sample, which may not be the minimum
-// value ever to be part of the sample.
-func (s *UniformSample) Min() int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleMin(s.values)
-}
-
-// Percentile returns an arbitrary percentile of values in the sample.
-func (s *UniformSample) Percentile(p float64) float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SamplePercentile(s.values, p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of values in the
-// sample.
-func (s *UniformSample) Percentiles(ps []float64) []float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SamplePercentiles(s.values, ps)
-}
-
-// Size returns the size of the sample, which is at most the reservoir size.
-func (s *UniformSample) Size() int {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return len(s.values)
+ clear(s.values)
}
// Snapshot returns a read-only copy of the sample.
-func (s *UniformSample) Snapshot() Sample {
+func (s *UniformSample) Snapshot() *sampleSnapshot {
s.mutex.Lock()
- defer s.mutex.Unlock()
- values := make([]int64, len(s.values))
- copy(values, s.values)
- return &SampleSnapshot{
- count: s.count,
- values: values,
- }
-}
-
-// StdDev returns the standard deviation of the values in the sample.
-func (s *UniformSample) StdDev() float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleStdDev(s.values)
-}
-
-// Sum returns the sum of the values in the sample.
-func (s *UniformSample) Sum() int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleSum(s.values)
+ values := slices.Clone(s.values)
+ count := s.count
+ s.mutex.Unlock()
+ return newSampleSnapshot(count, values)
}
// Update samples a new value.
func (s *UniformSample) Update(v int64) {
+ if !metricsEnabled {
+ return
+ }
s.mutex.Lock()
defer s.mutex.Unlock()
s.count++
if len(s.values) < s.reservoirSize {
s.values = append(s.values, v)
- } else {
- r := rand.Int63n(s.count)
- if r < int64(len(s.values)) {
- s.values[int(r)] = v
- }
+ return
+ }
+ var r int64
+ if s.rand != nil {
+ r = s.rand.Int63n(s.count)
+ } else {
+ r = rand.Int63n(s.count)
+ }
+ if r < int64(len(s.values)) {
+ s.values[int(r)] = v
}
-}
-
-// Values returns a copy of the values in the sample.
-func (s *UniformSample) Values() []int64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- values := make([]int64, len(s.values))
- copy(values, s.values)
- return values
-}
-
-// Variance returns the variance of the values in the sample.
-func (s *UniformSample) Variance() float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- return SampleVariance(s.values)
}
// expDecaySample represents an individual sample in a heap.
@@ -608,9 +423,3 @@ func (h *expDecaySampleHeap) down(i, n int) {
i = j
}
}
-
-type int64Slice []int64
-
-func (p int64Slice) Len() int { return len(p) }
-func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
-func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/metrics/sample_test.go b/metrics/sample_test.go
index d60e99c5bb..6619eb1e9e 100644
--- a/metrics/sample_test.go
+++ b/metrics/sample_test.go
@@ -1,56 +1,43 @@
package metrics
import (
+ "math"
"math/rand"
- "runtime"
"testing"
"time"
)
+const epsilonPercentile = .00000000001
+
// Benchmark{Compute,Copy}{1000,1000000} demonstrate that, even for relatively
// expensive computations like Variance, the cost of copying the Sample, as
// approximated by a make and copy, is much greater than the cost of the
// computation for small samples and only slightly less for large samples.
func BenchmarkCompute1000(b *testing.B) {
s := make([]int64, 1000)
+ var sum int64
for i := 0; i < len(s); i++ {
s[i] = int64(i)
+ sum += int64(i)
}
+ mean := float64(sum) / float64(len(s))
b.ResetTimer()
for i := 0; i < b.N; i++ {
- SampleVariance(s)
+ SampleVariance(mean, s)
}
}
+
func BenchmarkCompute1000000(b *testing.B) {
s := make([]int64, 1000000)
+ var sum int64
for i := 0; i < len(s); i++ {
s[i] = int64(i)
+ sum += int64(i)
}
+ mean := float64(sum) / float64(len(s))
b.ResetTimer()
for i := 0; i < b.N; i++ {
- SampleVariance(s)
- }
-}
-func BenchmarkCopy1000(b *testing.B) {
- s := make([]int64, 1000)
- for i := 0; i < len(s); i++ {
- s[i] = int64(i)
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- sCopy := make([]int64, len(s))
- copy(sCopy, s)
- }
-}
-func BenchmarkCopy1000000(b *testing.B) {
- s := make([]int64, 1000000)
- for i := 0; i < len(s); i++ {
- s[i] = int64(i)
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- sCopy := make([]int64, len(s))
- copy(sCopy, s)
+ SampleVariance(mean, s)
}
}
@@ -78,68 +65,35 @@ func BenchmarkUniformSample1028(b *testing.B) {
benchmarkSample(b, NewUniformSample(1028))
}
-func TestExpDecaySample10(t *testing.T) {
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
- for i := 0; i < 10; i++ {
- s.Update(int64(i))
- }
- if size := s.Count(); 10 != size {
- t.Errorf("s.Count(): 10 != %v\n", size)
- }
- if size := s.Size(); 10 != size {
- t.Errorf("s.Size(): 10 != %v\n", size)
- }
- if l := len(s.Values()); 10 != l {
- t.Errorf("len(s.Values()): 10 != %v\n", l)
- }
- for _, v := range s.Values() {
- if v > 10 || v < 0 {
- t.Errorf("out of range [0, 10): %v\n", v)
+func TestExpDecaySample(t *testing.T) {
+ for _, tc := range []struct {
+ reservoirSize int
+ alpha float64
+ updates int
+ }{
+ {100, 0.99, 10},
+ {1000, 0.01, 100},
+ {100, 0.99, 1000},
+ } {
+ sample := NewExpDecaySample(tc.reservoirSize, tc.alpha)
+ for i := 0; i < tc.updates; i++ {
+ sample.Update(int64(i))
}
- }
-}
-
-func TestExpDecaySample100(t *testing.T) {
- rand.Seed(1)
- s := NewExpDecaySample(1000, 0.01)
- for i := 0; i < 100; i++ {
- s.Update(int64(i))
- }
- if size := s.Count(); 100 != size {
- t.Errorf("s.Count(): 100 != %v\n", size)
- }
- if size := s.Size(); 100 != size {
- t.Errorf("s.Size(): 100 != %v\n", size)
- }
- if l := len(s.Values()); 100 != l {
- t.Errorf("len(s.Values()): 100 != %v\n", l)
- }
- for _, v := range s.Values() {
- if v > 100 || v < 0 {
- t.Errorf("out of range [0, 100): %v\n", v)
+ snap := sample.Snapshot()
+ if have, want := int(snap.Count()), tc.updates; have != want {
+ t.Errorf("unexpected count: have %d want %d", have, want)
}
- }
-}
-
-func TestExpDecaySample1000(t *testing.T) {
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
- for i := 0; i < 1000; i++ {
- s.Update(int64(i))
- }
- if size := s.Count(); 1000 != size {
- t.Errorf("s.Count(): 1000 != %v\n", size)
- }
- if size := s.Size(); 100 != size {
- t.Errorf("s.Size(): 100 != %v\n", size)
- }
- if l := len(s.Values()); 100 != l {
- t.Errorf("len(s.Values()): 100 != %v\n", l)
- }
- for _, v := range s.Values() {
- if v > 1000 || v < 0 {
- t.Errorf("out of range [0, 1000): %v\n", v)
+ if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want {
+ t.Errorf("unexpected size: have %d want %d", have, want)
+ }
+ values := snap.values
+ if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want {
+ t.Errorf("unexpected values length: have %d want %d", have, want)
+ }
+ for _, v := range values {
+ if v > int64(tc.updates) || v < 0 {
+ t.Errorf("out of range [0, %d]: %v", tc.updates, v)
+ }
}
}
}
@@ -149,16 +103,15 @@ func TestExpDecaySample1000(t *testing.T) {
// The priority becomes +Inf quickly after starting if this is done,
// effectively freezing the set of samples until a rescale step happens.
func TestExpDecaySampleNanosecondRegression(t *testing.T) {
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
- for i := 0; i < 100; i++ {
- s.Update(10)
+ sw := NewExpDecaySample(1000, 0.99)
+ for i := 0; i < 1000; i++ {
+ sw.Update(10)
}
time.Sleep(1 * time.Millisecond)
- for i := 0; i < 100; i++ {
- s.Update(20)
+ for i := 0; i < 1000; i++ {
+ sw.Update(20)
}
- v := s.Values()
+ v := sw.Snapshot().values
avg := float64(0)
for i := 0; i < len(v); i++ {
avg += float64(v[i])
@@ -182,8 +135,7 @@ func TestExpDecaySampleRescale(t *testing.T) {
func TestExpDecaySampleSnapshot(t *testing.T) {
now := time.Now()
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
+ s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
}
@@ -194,44 +146,44 @@ func TestExpDecaySampleSnapshot(t *testing.T) {
func TestExpDecaySampleStatistics(t *testing.T) {
now := time.Now()
- rand.Seed(1)
- s := NewExpDecaySample(100, 0.99)
+ s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
}
- testExpDecaySampleStatistics(t, s)
+ testExpDecaySampleStatistics(t, s.Snapshot())
}
func TestUniformSample(t *testing.T) {
- rand.Seed(1)
- s := NewUniformSample(100)
+ sw := NewUniformSample(100)
for i := 0; i < 1000; i++ {
- s.Update(int64(i))
+ sw.Update(int64(i))
}
- if size := s.Count(); 1000 != size {
+ s := sw.Snapshot()
+ if size := s.Count(); size != 1000 {
t.Errorf("s.Count(): 1000 != %v\n", size)
}
- if size := s.Size(); 100 != size {
+ if size := s.Size(); size != 100 {
t.Errorf("s.Size(): 100 != %v\n", size)
}
- if l := len(s.Values()); 100 != l {
+ values := s.values
+
+ if l := len(values); l != 100 {
t.Errorf("len(s.Values()): 100 != %v\n", l)
}
- for _, v := range s.Values() {
+ for _, v := range values {
if v > 1000 || v < 0 {
- t.Errorf("out of range [0, 100): %v\n", v)
+ t.Errorf("out of range [0, 1000]: %v\n", v)
}
}
}
func TestUniformSampleIncludesTail(t *testing.T) {
- rand.Seed(1)
- s := NewUniformSample(100)
+ sw := NewUniformSample(100)
max := 100
for i := 0; i < max; i++ {
- s.Update(int64(i))
+ sw.Update(int64(i))
}
- v := s.Values()
+ v := sw.Snapshot().values
sum := 0
exp := (max - 1) * max / 2
for i := 0; i < len(v); i++ {
@@ -243,7 +195,7 @@ func TestUniformSampleIncludesTail(t *testing.T) {
}
func TestUniformSampleSnapshot(t *testing.T) {
- s := NewUniformSample(100)
+ s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.Update(int64(i))
}
@@ -253,80 +205,74 @@ func TestUniformSampleSnapshot(t *testing.T) {
}
func TestUniformSampleStatistics(t *testing.T) {
- rand.Seed(1)
- s := NewUniformSample(100)
+ s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
for i := 1; i <= 10000; i++ {
s.Update(int64(i))
}
- testUniformSampleStatistics(t, s)
+ testUniformSampleStatistics(t, s.Snapshot())
}
func benchmarkSample(b *testing.B, s Sample) {
- var memStats runtime.MemStats
- runtime.ReadMemStats(&memStats)
- pauseTotalNs := memStats.PauseTotalNs
- b.ResetTimer()
for i := 0; i < b.N; i++ {
s.Update(1)
}
- b.StopTimer()
- runtime.GC()
- runtime.ReadMemStats(&memStats)
- b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N)
}
-func testExpDecaySampleStatistics(t *testing.T, s Sample) {
- if count := s.Count(); 10000 != count {
+func testExpDecaySampleStatistics(t *testing.T, s *sampleSnapshot) {
+ if sum := s.Sum(); sum != 496598 {
+ t.Errorf("s.Sum(): 496598 != %v\n", sum)
+ }
+ if count := s.Count(); count != 10000 {
t.Errorf("s.Count(): 10000 != %v\n", count)
}
- if min := s.Min(); 107 != min {
+ if min := s.Min(); min != 107 {
t.Errorf("s.Min(): 107 != %v\n", min)
}
- if max := s.Max(); 10000 != max {
+ if max := s.Max(); max != 10000 {
t.Errorf("s.Max(): 10000 != %v\n", max)
}
- if mean := s.Mean(); 4965.98 != mean {
+ if mean := s.Mean(); mean != 4965.98 {
t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
}
- if stdDev := s.StdDev(); 2959.825156930727 != stdDev {
+ if stdDev := s.StdDev(); stdDev != 2959.825156930727 {
t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
}
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
- if 4615 != ps[0] {
+ if ps[0] != 4615 {
t.Errorf("median: 4615 != %v\n", ps[0])
}
- if 7672 != ps[1] {
+ if ps[1] != 7672 {
t.Errorf("75th percentile: 7672 != %v\n", ps[1])
}
- if 9998.99 != ps[2] {
+ if ps[2] != 9998.99 {
t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
}
}
-func testUniformSampleStatistics(t *testing.T, s Sample) {
- if count := s.Count(); 10000 != count {
+func testUniformSampleStatistics(t *testing.T, s *sampleSnapshot) {
+ if count := s.Count(); count != 10000 {
t.Errorf("s.Count(): 10000 != %v\n", count)
}
- if min := s.Min(); 37 != min {
+ if min := s.Min(); min != 37 {
t.Errorf("s.Min(): 37 != %v\n", min)
}
- if max := s.Max(); 9989 != max {
+ if max := s.Max(); max != 9989 {
t.Errorf("s.Max(): 9989 != %v\n", max)
}
- if mean := s.Mean(); 4748.14 != mean {
+ if mean := s.Mean(); mean != 4748.14 {
t.Errorf("s.Mean(): 4748.14 != %v\n", mean)
}
- if stdDev := s.StdDev(); 2826.684117548333 != stdDev {
+ if stdDev := s.StdDev(); stdDev != 2826.684117548333 {
t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev)
}
ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
- if 4599 != ps[0] {
+ if ps[0] != 4599 {
t.Errorf("median: 4599 != %v\n", ps[0])
}
- if 7380.5 != ps[1] {
+ if ps[1] != 7380.5 {
t.Errorf("75th percentile: 7380.5 != %v\n", ps[1])
}
- if 9986.429999999998 != ps[2] {
+ if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile {
t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2])
}
}
@@ -345,6 +291,7 @@ func TestUniformSampleConcurrentUpdateCount(t *testing.T) {
quit := make(chan struct{})
go func() {
t := time.NewTicker(10 * time.Millisecond)
+ defer t.Stop()
for {
select {
case <-t.C:
@@ -356,8 +303,22 @@ func TestUniformSampleConcurrentUpdateCount(t *testing.T) {
}
}()
for i := 0; i < 1000; i++ {
- s.Count()
+ s.Snapshot().Count()
time.Sleep(5 * time.Millisecond)
}
quit <- struct{}{}
}
+
+func BenchmarkCalculatePercentiles(b *testing.B) {
+ pss := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
+ var vals []int64
+ for i := 0; i < 1000; i++ {
+ vals = append(vals, int64(rand.Int31()))
+ }
+ v := make([]int64, len(vals))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ copy(v, vals)
+ _ = CalculatePercentiles(v, pss)
+ }
+}
diff --git a/metrics/syslog.go b/metrics/syslog.go
index a0ed4b1b23..0bc4ed0da5 100644
--- a/metrics/syslog.go
+++ b/metrics/syslog.go
@@ -1,3 +1,4 @@
+//go:build !windows
// +build !windows
package metrics
@@ -14,13 +15,17 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) {
for range time.Tick(d) {
r.Each(func(name string, i interface{}) {
switch metric := i.(type) {
- case Counter:
- w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count()))
- case Gauge:
- w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value()))
- case GaugeFloat64:
- w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value()))
- case Healthcheck:
+ case *Counter:
+ w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Snapshot().Count()))
+ case *CounterFloat64:
+ w.Info(fmt.Sprintf("counter %s: count: %f", name, metric.Snapshot().Count()))
+ case *Gauge:
+ w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Snapshot().Value()))
+ case *GaugeFloat64:
+ w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Snapshot().Value()))
+ case *GaugeInfo:
+ w.Info(fmt.Sprintf("gauge %s: value: %s", name, metric.Snapshot().Value()))
+ case *Healthcheck:
metric.Check()
w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error()))
case Histogram:
@@ -40,7 +45,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) {
ps[3],
ps[4],
))
- case Meter:
+ case *Meter:
m := metric.Snapshot()
w.Info(fmt.Sprintf(
"meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f",
@@ -51,7 +56,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) {
m.Rate15(),
m.RateMean(),
))
- case Timer:
+ case *Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
w.Info(fmt.Sprintf(
diff --git a/metrics/testdata/opentsb.want b/metrics/testdata/opentsb.want
new file mode 100644
index 0000000000..43fe1b2ac2
--- /dev/null
+++ b/metrics/testdata/opentsb.want
@@ -0,0 +1,23 @@
+put pre.elite.count 978307200 1337 host=hal9000
+put pre.elite.one-minute 978307200 0.00 host=hal9000
+put pre.elite.five-minute 978307200 0.00 host=hal9000
+put pre.elite.fifteen-minute 978307200 0.00 host=hal9000
+put pre.elite.mean 978307200 0.00 host=hal9000
+put pre.foo.value 978307200 {"chain_id":"5"} host=hal9000
+put pre.months.count 978307200 12 host=hal9000
+put pre.pi.value 978307200 3.140000 host=hal9000
+put pre.second.count 978307200 1 host=hal9000
+put pre.second.min 978307200 1000 host=hal9000
+put pre.second.max 978307200 1000 host=hal9000
+put pre.second.mean 978307200 1000.00 host=hal9000
+put pre.second.std-dev 978307200 0.00 host=hal9000
+put pre.second.50-percentile 978307200 1000.00 host=hal9000
+put pre.second.75-percentile 978307200 1000.00 host=hal9000
+put pre.second.95-percentile 978307200 1000.00 host=hal9000
+put pre.second.99-percentile 978307200 1000.00 host=hal9000
+put pre.second.999-percentile 978307200 1000.00 host=hal9000
+put pre.second.one-minute 978307200 0.00 host=hal9000
+put pre.second.five-minute 978307200 0.00 host=hal9000
+put pre.second.fifteen-minute 978307200 0.00 host=hal9000
+put pre.second.mean-rate 978307200 0.00 host=hal9000
+put pre.tau.count 978307200 1.570000 host=hal9000
diff --git a/metrics/timer.go b/metrics/timer.go
index 89e22208fd..9df15c967a 100644
--- a/metrics/timer.go
+++ b/metrics/timer.go
@@ -5,55 +5,30 @@ import (
"time"
)
-// Timers capture the duration and rate of events.
-type Timer interface {
- Count() int64
- Max() int64
- Mean() float64
- Min() int64
- Percentile(float64) float64
- Percentiles([]float64) []float64
- Rate1() float64
- Rate5() float64
- Rate15() float64
- RateMean() float64
- Snapshot() Timer
- StdDev() float64
- Stop()
- Sum() int64
- Time(func())
- Update(time.Duration)
- UpdateSince(time.Time)
- Variance() float64
-}
-
// GetOrRegisterTimer returns an existing Timer or constructs and registers a
-// new StandardTimer.
+// new Timer.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
-func GetOrRegisterTimer(name string, r Registry) Timer {
+func GetOrRegisterTimer(name string, r Registry) *Timer {
if nil == r {
r = DefaultRegistry
}
- return r.GetOrRegister(name, NewTimer).(Timer)
+ return r.GetOrRegister(name, NewTimer).(*Timer)
}
-// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter.
+// NewCustomTimer constructs a new Timer from a Histogram and a Meter.
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
-func NewCustomTimer(h Histogram, m Meter) Timer {
- if !Enabled {
- return NilTimer{}
- }
- return &StandardTimer{
+func NewCustomTimer(h Histogram, m *Meter) *Timer {
+ return &Timer{
histogram: h,
meter: m,
}
}
-// NewRegisteredTimer constructs and registers a new StandardTimer.
+// NewRegisteredTimer constructs and registers a new Timer.
// Be sure to unregister the meter from the registry once it is of no use to
// allow for garbage collection.
-func NewRegisteredTimer(name string, r Registry) Timer {
+func NewRegisteredTimer(name string, r Registry) *Timer {
c := NewTimer()
if nil == r {
r = DefaultRegistry
@@ -62,196 +37,62 @@ func NewRegisteredTimer(name string, r Registry) Timer {
return c
}
-// NewTimer constructs a new StandardTimer using an exponentially-decaying
+// NewTimer constructs a new Timer using an exponentially-decaying
// sample with the same reservoir size and alpha as UNIX load averages.
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
-func NewTimer() Timer {
- if !Enabled {
- return NilTimer{}
- }
- return &StandardTimer{
+func NewTimer() *Timer {
+ return &Timer{
histogram: NewHistogram(NewExpDecaySample(1028, 0.015)),
meter: NewMeter(),
}
}
-// NilTimer is a no-op Timer.
-type NilTimer struct {
- h Histogram
- m Meter
-}
-
-// Count is a no-op.
-func (NilTimer) Count() int64 { return 0 }
-
-// Max is a no-op.
-func (NilTimer) Max() int64 { return 0 }
-
-// Mean is a no-op.
-func (NilTimer) Mean() float64 { return 0.0 }
-
-// Min is a no-op.
-func (NilTimer) Min() int64 { return 0 }
-
-// Percentile is a no-op.
-func (NilTimer) Percentile(p float64) float64 { return 0.0 }
-
-// Percentiles is a no-op.
-func (NilTimer) Percentiles(ps []float64) []float64 {
- return make([]float64, len(ps))
-}
-
-// Rate1 is a no-op.
-func (NilTimer) Rate1() float64 { return 0.0 }
-
-// Rate5 is a no-op.
-func (NilTimer) Rate5() float64 { return 0.0 }
-
-// Rate15 is a no-op.
-func (NilTimer) Rate15() float64 { return 0.0 }
-
-// RateMean is a no-op.
-func (NilTimer) RateMean() float64 { return 0.0 }
-
-// Snapshot is a no-op.
-func (NilTimer) Snapshot() Timer { return NilTimer{} }
-
-// StdDev is a no-op.
-func (NilTimer) StdDev() float64 { return 0.0 }
-
-// Stop is a no-op.
-func (NilTimer) Stop() {}
-
-// Sum is a no-op.
-func (NilTimer) Sum() int64 { return 0 }
-
-// Time is a no-op.
-func (NilTimer) Time(func()) {}
-
-// Update is a no-op.
-func (NilTimer) Update(time.Duration) {}
-
-// UpdateSince is a no-op.
-func (NilTimer) UpdateSince(time.Time) {}
-
-// Variance is a no-op.
-func (NilTimer) Variance() float64 { return 0.0 }
-
-// StandardTimer is the standard implementation of a Timer and uses a Histogram
-// and Meter.
-type StandardTimer struct {
+// Timer captures the duration and rate of events, using a Histogram and a Meter.
+type Timer struct {
histogram Histogram
- meter Meter
+ meter *Meter
mutex sync.Mutex
}
-// Count returns the number of events recorded.
-func (t *StandardTimer) Count() int64 {
- return t.histogram.Count()
-}
-
-// Max returns the maximum value in the sample.
-func (t *StandardTimer) Max() int64 {
- return t.histogram.Max()
-}
-
-// Mean returns the mean of the values in the sample.
-func (t *StandardTimer) Mean() float64 {
- return t.histogram.Mean()
-}
-
-// Min returns the minimum value in the sample.
-func (t *StandardTimer) Min() int64 {
- return t.histogram.Min()
-}
-
-// Percentile returns an arbitrary percentile of the values in the sample.
-func (t *StandardTimer) Percentile(p float64) float64 {
- return t.histogram.Percentile(p)
-}
-
-// Percentiles returns a slice of arbitrary percentiles of the values in the
-// sample.
-func (t *StandardTimer) Percentiles(ps []float64) []float64 {
- return t.histogram.Percentiles(ps)
-}
-
-// Rate1 returns the one-minute moving average rate of events per second.
-func (t *StandardTimer) Rate1() float64 {
- return t.meter.Rate1()
-}
-
-// Rate5 returns the five-minute moving average rate of events per second.
-func (t *StandardTimer) Rate5() float64 {
- return t.meter.Rate5()
-}
-
-// Rate15 returns the fifteen-minute moving average rate of events per second.
-func (t *StandardTimer) Rate15() float64 {
- return t.meter.Rate15()
-}
-
-// RateMean returns the meter's mean rate of events per second.
-func (t *StandardTimer) RateMean() float64 {
- return t.meter.RateMean()
-}
-
// Snapshot returns a read-only copy of the timer.
-func (t *StandardTimer) Snapshot() Timer {
+func (t *Timer) Snapshot() *TimerSnapshot {
t.mutex.Lock()
defer t.mutex.Unlock()
return &TimerSnapshot{
- histogram: t.histogram.Snapshot().(*HistogramSnapshot),
- meter: t.meter.Snapshot().(*MeterSnapshot),
+ histogram: t.histogram.Snapshot(),
+ meter: t.meter.Snapshot(),
}
}
-// StdDev returns the standard deviation of the values in the sample.
-func (t *StandardTimer) StdDev() float64 {
- return t.histogram.StdDev()
-}
-
// Stop stops the meter.
-func (t *StandardTimer) Stop() {
+func (t *Timer) Stop() {
t.meter.Stop()
}
-// Sum returns the sum in the sample.
-func (t *StandardTimer) Sum() int64 {
- return t.histogram.Sum()
-}
-
-// Record the duration of the execution of the given function.
-func (t *StandardTimer) Time(f func()) {
+// Time record the duration of the execution of the given function.
+func (t *Timer) Time(f func()) {
ts := time.Now()
f()
t.Update(time.Since(ts))
}
-// Record the duration of an event.
-func (t *StandardTimer) Update(d time.Duration) {
+// Update the duration of an event, in nanoseconds.
+func (t *Timer) Update(d time.Duration) {
t.mutex.Lock()
defer t.mutex.Unlock()
- t.histogram.Update(int64(d))
+ t.histogram.Update(d.Nanoseconds())
t.meter.Mark(1)
}
-// Record the duration of an event that started at a time and ends now.
-func (t *StandardTimer) UpdateSince(ts time.Time) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- t.histogram.Update(int64(time.Since(ts)))
- t.meter.Mark(1)
-}
-
-// Variance returns the variance of the values in the sample.
-func (t *StandardTimer) Variance() float64 {
- return t.histogram.Variance()
+// UpdateSince update the duration of an event that started at a time and ends now.
+// The record uses nanoseconds.
+func (t *Timer) UpdateSince(ts time.Time) {
+ t.Update(time.Since(ts))
}
// TimerSnapshot is a read-only copy of another Timer.
type TimerSnapshot struct {
- histogram *HistogramSnapshot
+ histogram HistogramSnapshot
meter *MeterSnapshot
}
@@ -262,6 +103,9 @@ func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() }
// Max returns the maximum value at the time the snapshot was taken.
func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() }
+// Size returns the size of the sample at the time the snapshot was taken.
+func (t *TimerSnapshot) Size() int { return t.histogram.Size() }
+
// Mean returns the mean value at the time the snapshot was taken.
func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() }
@@ -296,34 +140,13 @@ func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() }
// snapshot was taken.
func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() }
-// Snapshot returns the snapshot.
-func (t *TimerSnapshot) Snapshot() Timer { return t }
-
// StdDev returns the standard deviation of the values at the time the snapshot
// was taken.
func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() }
-// Stop is a no-op.
-func (t *TimerSnapshot) Stop() {}
-
// Sum returns the sum at the time the snapshot was taken.
func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() }
-// Time panics.
-func (*TimerSnapshot) Time(func()) {
- panic("Time called on a TimerSnapshot")
-}
-
-// Update panics.
-func (*TimerSnapshot) Update(time.Duration) {
- panic("Update called on a TimerSnapshot")
-}
-
-// UpdateSince panics.
-func (*TimerSnapshot) UpdateSince(time.Time) {
- panic("UpdateSince called on a TimerSnapshot")
-}
-
// Variance returns the variance of the values at the time the snapshot was
// taken.
func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() }
diff --git a/metrics/timer_test.go b/metrics/timer_test.go
index c1f0ff9388..0afa63a230 100644
--- a/metrics/timer_test.go
+++ b/metrics/timer_test.go
@@ -18,7 +18,7 @@ func BenchmarkTimer(b *testing.B) {
func TestGetOrRegisterTimer(t *testing.T) {
r := NewRegistry()
NewRegisteredTimer("foo", r).Update(47)
- if tm := GetOrRegisterTimer("foo", r); 1 != tm.Count() {
+ if tm := GetOrRegisterTimer("foo", r).Snapshot(); tm.Count() != 1 {
t.Fatal(tm)
}
}
@@ -27,7 +27,7 @@ func TestTimerExtremes(t *testing.T) {
tm := NewTimer()
tm.Update(math.MaxInt64)
tm.Update(0)
- if stdDev := tm.StdDev(); 4.611686018427388e+18 != stdDev {
+ if stdDev := tm.Snapshot().StdDev(); stdDev != 4.611686018427388e+18 {
t.Errorf("tm.StdDev(): 4.611686018427388e+18 != %v\n", stdDev)
}
}
@@ -45,50 +45,63 @@ func TestTimerStop(t *testing.T) {
}
func TestTimerFunc(t *testing.T) {
- tm := NewTimer()
- tm.Time(func() { time.Sleep(50e6) })
- if max := tm.Max(); 35e6 > max || max > 95e6 {
- t.Errorf("tm.Max(): 35e6 > %v || %v > 95e6\n", max, max)
+ var (
+ tm = NewTimer()
+ testStart = time.Now()
+ actualTime time.Duration
+ )
+ tm.Time(func() {
+ time.Sleep(50 * time.Millisecond)
+ actualTime = time.Since(testStart)
+ })
+ var (
+ drift = time.Millisecond * 2
+ measured = time.Duration(tm.Snapshot().Max())
+ ceil = actualTime + drift
+ floor = actualTime - drift
+ )
+ if measured > ceil || measured < floor {
+ t.Errorf("tm.Max(): %v > %v || %v > %v\n", measured, ceil, measured, floor)
}
}
func TestTimerZero(t *testing.T) {
- tm := NewTimer()
- if count := tm.Count(); 0 != count {
+ tm := NewTimer().Snapshot()
+ if count := tm.Count(); count != 0 {
t.Errorf("tm.Count(): 0 != %v\n", count)
}
- if min := tm.Min(); 0 != min {
+ if min := tm.Min(); min != 0 {
t.Errorf("tm.Min(): 0 != %v\n", min)
}
- if max := tm.Max(); 0 != max {
+ if max := tm.Max(); max != 0 {
t.Errorf("tm.Max(): 0 != %v\n", max)
}
- if mean := tm.Mean(); 0.0 != mean {
+ if mean := tm.Mean(); mean != 0.0 {
t.Errorf("tm.Mean(): 0.0 != %v\n", mean)
}
- if stdDev := tm.StdDev(); 0.0 != stdDev {
+ if stdDev := tm.StdDev(); stdDev != 0.0 {
t.Errorf("tm.StdDev(): 0.0 != %v\n", stdDev)
}
ps := tm.Percentiles([]float64{0.5, 0.75, 0.99})
- if 0.0 != ps[0] {
+ if ps[0] != 0.0 {
t.Errorf("median: 0.0 != %v\n", ps[0])
}
- if 0.0 != ps[1] {
+ if ps[1] != 0.0 {
t.Errorf("75th percentile: 0.0 != %v\n", ps[1])
}
- if 0.0 != ps[2] {
+ if ps[2] != 0.0 {
t.Errorf("99th percentile: 0.0 != %v\n", ps[2])
}
- if rate1 := tm.Rate1(); 0.0 != rate1 {
+ if rate1 := tm.Rate1(); rate1 != 0.0 {
t.Errorf("tm.Rate1(): 0.0 != %v\n", rate1)
}
- if rate5 := tm.Rate5(); 0.0 != rate5 {
+ if rate5 := tm.Rate5(); rate5 != 0.0 {
t.Errorf("tm.Rate5(): 0.0 != %v\n", rate5)
}
- if rate15 := tm.Rate15(); 0.0 != rate15 {
+ if rate15 := tm.Rate15(); rate15 != 0.0 {
t.Errorf("tm.Rate15(): 0.0 != %v\n", rate15)
}
- if rateMean := tm.RateMean(); 0.0 != rateMean {
+ if rateMean := tm.RateMean(); rateMean != 0.0 {
t.Errorf("tm.RateMean(): 0.0 != %v\n", rateMean)
}
}
@@ -97,5 +110,5 @@ func ExampleGetOrRegisterTimer() {
m := "account.create.latency"
t := GetOrRegisterTimer(m, nil)
t.Update(47)
- fmt.Println(t.Max()) // Output: 47
+ fmt.Println(t.Snapshot().Max()) // Output: 47
}
diff --git a/metrics/writer.go b/metrics/writer.go
index 88521a80d9..2a41f8e1fe 100644
--- a/metrics/writer.go
+++ b/metrics/writer.go
@@ -3,7 +3,8 @@ package metrics
import (
"fmt"
"io"
- "sort"
+ "slices"
+ "strings"
"time"
)
@@ -18,24 +19,29 @@ func Write(r Registry, d time.Duration, w io.Writer) {
// WriteOnce sorts and writes metrics in the given registry to the given
// io.Writer.
func WriteOnce(r Registry, w io.Writer) {
- var namedMetrics namedMetricSlice
+ var namedMetrics []namedMetric
r.Each(func(name string, i interface{}) {
namedMetrics = append(namedMetrics, namedMetric{name, i})
})
-
- sort.Sort(namedMetrics)
+ slices.SortFunc(namedMetrics, namedMetric.cmp)
for _, namedMetric := range namedMetrics {
switch metric := namedMetric.m.(type) {
- case Counter:
+ case *Counter:
fmt.Fprintf(w, "counter %s\n", namedMetric.name)
- fmt.Fprintf(w, " count: %9d\n", metric.Count())
- case Gauge:
+ fmt.Fprintf(w, " count: %9d\n", metric.Snapshot().Count())
+ case *CounterFloat64:
+ fmt.Fprintf(w, "counter %s\n", namedMetric.name)
+ fmt.Fprintf(w, " count: %f\n", metric.Snapshot().Count())
+ case *Gauge:
fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
- fmt.Fprintf(w, " value: %9d\n", metric.Value())
- case GaugeFloat64:
+ fmt.Fprintf(w, " value: %9d\n", metric.Snapshot().Value())
+ case *GaugeFloat64:
fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
- fmt.Fprintf(w, " value: %f\n", metric.Value())
- case Healthcheck:
+ fmt.Fprintf(w, " value: %f\n", metric.Snapshot().Value())
+ case *GaugeInfo:
+ fmt.Fprintf(w, "gauge %s\n", namedMetric.name)
+ fmt.Fprintf(w, " value: %s\n", metric.Snapshot().Value().String())
+ case *Healthcheck:
metric.Check()
fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name)
fmt.Fprintf(w, " error: %v\n", metric.Error())
@@ -53,7 +59,7 @@ func WriteOnce(r Registry, w io.Writer) {
fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2])
fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3])
fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4])
- case Meter:
+ case *Meter:
m := metric.Snapshot()
fmt.Fprintf(w, "meter %s\n", namedMetric.name)
fmt.Fprintf(w, " count: %9d\n", m.Count())
@@ -61,7 +67,7 @@ func WriteOnce(r Registry, w io.Writer) {
fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5())
fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15())
fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean())
- case Timer:
+ case *Timer:
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999})
fmt.Fprintf(w, "timer %s\n", namedMetric.name)
@@ -88,13 +94,6 @@ type namedMetric struct {
m interface{}
}
-// namedMetricSlice is a slice of namedMetrics that implements sort.Interface.
-type namedMetricSlice []namedMetric
-
-func (nms namedMetricSlice) Len() int { return len(nms) }
-
-func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] }
-
-func (nms namedMetricSlice) Less(i, j int) bool {
- return nms[i].name < nms[j].name
+func (m namedMetric) cmp(other namedMetric) int {
+ return strings.Compare(m.name, other.name)
}
diff --git a/metrics/writer_test.go b/metrics/writer_test.go
index 1aacc28712..edcfe955ab 100644
--- a/metrics/writer_test.go
+++ b/metrics/writer_test.go
@@ -1,19 +1,19 @@
package metrics
import (
- "sort"
+ "slices"
"testing"
)
func TestMetricsSorting(t *testing.T) {
- var namedMetrics = namedMetricSlice{
+ var namedMetrics = []namedMetric{
{name: "zzz"},
{name: "bbb"},
{name: "fff"},
{name: "ggg"},
}
- sort.Sort(namedMetrics)
+ slices.SortFunc(namedMetrics, namedMetric.cmp)
for i, name := range []string{"bbb", "fff", "ggg", "zzz"} {
if namedMetrics[i].name != name {
t.Fail()
diff --git a/miner/agent.go b/miner/agent.go
index 5ea0a17ddd..4616c2c5bc 100644
--- a/miner/agent.go
+++ b/miner/agent.go
@@ -49,70 +49,70 @@ func NewCpuAgent(chain consensus.ChainReader, engine consensus.Engine) *CpuAgent
return miner
}
-func (self *CpuAgent) Work() chan<- *Work { return self.workCh }
-func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }
+func (ca *CpuAgent) Work() chan<- *Work { return ca.workCh }
+func (ca *CpuAgent) SetReturnCh(ch chan<- *Result) { ca.returnCh = ch }
-func (self *CpuAgent) Stop() {
- if !atomic.CompareAndSwapInt32(&self.isMining, 1, 0) {
+func (ca *CpuAgent) Stop() {
+ if !atomic.CompareAndSwapInt32(&ca.isMining, 1, 0) {
return // agent already stopped
}
- self.stop <- struct{}{}
+ ca.stop <- struct{}{}
done:
// Empty work channel
for {
select {
- case <-self.workCh:
+ case <-ca.workCh:
default:
break done
}
}
}
-func (self *CpuAgent) Start() {
- if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) {
+func (ca *CpuAgent) Start() {
+ if !atomic.CompareAndSwapInt32(&ca.isMining, 0, 1) {
return // agent already started
}
- go self.update()
+ go ca.update()
}
-func (self *CpuAgent) update() {
+func (ca *CpuAgent) update() {
out:
for {
select {
- case work := <-self.workCh:
- self.mu.Lock()
- if self.quitCurrentOp != nil {
- close(self.quitCurrentOp)
+ case work := <-ca.workCh:
+ ca.mu.Lock()
+ if ca.quitCurrentOp != nil {
+ close(ca.quitCurrentOp)
}
- self.quitCurrentOp = make(chan struct{})
- go self.mine(work, self.quitCurrentOp)
- self.mu.Unlock()
- case <-self.stop:
- self.mu.Lock()
- if self.quitCurrentOp != nil {
- close(self.quitCurrentOp)
- self.quitCurrentOp = nil
+ ca.quitCurrentOp = make(chan struct{})
+ go ca.mine(work, ca.quitCurrentOp)
+ ca.mu.Unlock()
+ case <-ca.stop:
+ ca.mu.Lock()
+ if ca.quitCurrentOp != nil {
+ close(ca.quitCurrentOp)
+ ca.quitCurrentOp = nil
}
- self.mu.Unlock()
+ ca.mu.Unlock()
break out
}
}
}
-func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
- if result, err := self.engine.Seal(self.chain, work.Block, stop); result != nil {
+func (ca *CpuAgent) mine(work *Work, stop <-chan struct{}) {
+ if result, err := ca.engine.Seal(ca.chain, work.Block, stop); result != nil {
log.Info("Successfully sealed new block", "number", result.Number(), "hash", result.Hash())
- self.returnCh <- &Result{work, result}
+ ca.returnCh <- &Result{work, result}
} else {
if err != nil {
log.Warn("Block sealing failed", "err", err)
}
- self.returnCh <- nil
+ ca.returnCh <- nil
}
}
-func (self *CpuAgent) GetHashRate() int64 {
- if pow, ok := self.engine.(consensus.PoW); ok {
+func (ca *CpuAgent) GetHashRate() int64 {
+ if pow, ok := ca.engine.(consensus.PoW); ok {
return int64(pow.Hashrate())
}
return 0
diff --git a/miner/miner.go b/miner/miner.go
index 835f0f014b..f48df9491d 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -21,14 +21,14 @@ import (
"fmt"
"sync/atomic"
- "github.com/XinFinOrg/XDPoSChain/XDCxlending"
-
"github.com/XinFinOrg/XDPoSChain/XDCx"
+ "github.com/XinFinOrg/XDPoSChain/XDCxlending"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
+ "github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
"github.com/XinFinOrg/XDPoSChain/ethdb"
@@ -41,11 +41,11 @@ import (
type Backend interface {
AccountManager() *accounts.Manager
BlockChain() *core.BlockChain
- TxPool() *core.TxPool
+ TxPool() *txpool.TxPool
ChainDb() ethdb.Database
GetXDCX() *XDCx.XDCX
- OrderPool() *core.OrderPool
- LendingPool() *core.LendingPool
+ OrderPool() *txpool.OrderPool
+ LendingPool() *txpool.LendingPool
GetXDCXLending() *XDCxlending.Lending
}
@@ -82,73 +82,73 @@ func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine con
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
// and halt your mining operation for as long as the DOS continues.
-func (self *Miner) update() {
- events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
+func (m *Miner) update() {
+ events := m.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
for ev := range events.Chan() {
switch ev.Data.(type) {
case downloader.StartEvent:
- atomic.StoreInt32(&self.canStart, 0)
- if self.Mining() {
- self.Stop()
- atomic.StoreInt32(&self.shouldStart, 1)
+ atomic.StoreInt32(&m.canStart, 0)
+ if m.Mining() {
+ m.Stop()
+ atomic.StoreInt32(&m.shouldStart, 1)
log.Info("Mining aborted due to sync")
}
case downloader.DoneEvent, downloader.FailedEvent:
- shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
+ shouldStart := atomic.LoadInt32(&m.shouldStart) == 1
- atomic.StoreInt32(&self.canStart, 1)
- atomic.StoreInt32(&self.shouldStart, 0)
+ atomic.StoreInt32(&m.canStart, 1)
+ atomic.StoreInt32(&m.shouldStart, 0)
if shouldStart {
- self.Start(self.coinbase)
+ m.Start(m.coinbase)
}
}
}
}
-func (self *Miner) Start(coinbase common.Address) {
- atomic.StoreInt32(&self.shouldStart, 1)
- self.SetEtherbase(coinbase)
+func (m *Miner) Start(coinbase common.Address) {
+ atomic.StoreInt32(&m.shouldStart, 1)
+ m.SetEtherbase(coinbase)
- if atomic.LoadInt32(&self.canStart) == 0 {
+ if atomic.LoadInt32(&m.canStart) == 0 {
log.Info("Network syncing, will start miner afterwards")
return
}
- atomic.StoreInt32(&self.mining, 1)
+ atomic.StoreInt32(&m.mining, 1)
log.Info("Starting mining operation")
- self.worker.start()
- self.worker.commitNewWork()
+ m.worker.start()
+ m.worker.commitNewWork()
}
-func (self *Miner) Stop() {
- self.worker.stop()
- atomic.StoreInt32(&self.mining, 0)
- atomic.StoreInt32(&self.shouldStart, 0)
+func (m *Miner) Stop() {
+ m.worker.stop()
+ atomic.StoreInt32(&m.mining, 0)
+ atomic.StoreInt32(&m.shouldStart, 0)
}
-func (self *Miner) Register(agent Agent) {
- if self.Mining() {
+func (m *Miner) Register(agent Agent) {
+ if m.Mining() {
agent.Start()
}
- self.worker.register(agent)
+ m.worker.register(agent)
}
-func (self *Miner) Unregister(agent Agent) {
- self.worker.unregister(agent)
+func (m *Miner) Unregister(agent Agent) {
+ m.worker.unregister(agent)
}
-func (self *Miner) Mining() bool {
- return atomic.LoadInt32(&self.mining) > 0
+func (m *Miner) Mining() bool {
+ return atomic.LoadInt32(&m.mining) > 0
}
-func (self *Miner) HashRate() (tot int64) {
- if pow, ok := self.engine.(consensus.PoW); ok {
+func (m *Miner) HashRate() (tot int64) {
+ if pow, ok := m.engine.(consensus.PoW); ok {
tot += int64(pow.Hashrate())
}
// do we care this might race? is it worth we're rewriting some
// aspects of the worker/locking up agents so we can get an accurate
// hashrate?
- for agent := range self.worker.agents {
+ for agent := range m.worker.agents {
if _, ok := agent.(*CpuAgent); !ok {
tot += agent.GetHashRate()
}
@@ -156,17 +156,17 @@ func (self *Miner) HashRate() (tot int64) {
return
}
-func (self *Miner) SetExtra(extra []byte) error {
+func (m *Miner) SetExtra(extra []byte) error {
if uint64(len(extra)) > params.MaximumExtraDataSize {
- return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
+ return fmt.Errorf("extra exceeds max length: %d > %v", len(extra), params.MaximumExtraDataSize)
}
- self.worker.setExtra(extra)
+ m.worker.setExtra(extra)
return nil
}
// Pending returns the currently pending block and associated state.
-func (self *Miner) Pending() (*types.Block, *state.StateDB) {
- return self.worker.pending()
+func (m *Miner) Pending() (*types.Block, *state.StateDB) {
+ return m.worker.pending()
}
// PendingBlock returns the currently pending block.
@@ -174,22 +174,22 @@ func (self *Miner) Pending() (*types.Block, *state.StateDB) {
// Note, to access both the pending block and the pending state
// simultaneously, please use Pending(), as the pending state can
// change between multiple method calls
-func (self *Miner) PendingBlock() *types.Block {
- return self.worker.pendingBlock()
+func (m *Miner) PendingBlock() *types.Block {
+ return m.worker.pendingBlock()
}
// PendingBlockAndReceipts returns the currently pending block and corresponding receipts.
-func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
- return miner.worker.pendingBlockAndReceipts()
+func (m *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
+ return m.worker.pendingBlockAndReceipts()
}
-func (self *Miner) SetEtherbase(addr common.Address) {
- self.coinbase = addr
- self.worker.setEtherbase(addr)
+func (m *Miner) SetEtherbase(addr common.Address) {
+ m.coinbase = addr
+ m.worker.setEtherbase(addr)
}
// SubscribePendingLogs starts delivering logs from pending transactions
// to the given channel.
-func (self *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription {
- return self.worker.pendingLogsFeed.Subscribe(ch)
+func (m *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription {
+ return m.worker.pendingLogsFeed.Subscribe(ch)
}
diff --git a/miner/remote_agent.go b/miner/remote_agent.go
index 8b4f9b9a8d..dd30d81822 100644
--- a/miner/remote_agent.go
+++ b/miner/remote_agent.go
@@ -128,7 +128,7 @@ func (a *RemoteAgent) GetWork() ([3]string, error) {
a.work[block.HashNoNonce()] = a.currentWork
return res, nil
}
- return res, errors.New("No work available yet, don't panic.")
+ return res, errors.New("no work available yet, don't panic")
}
// SubmitWork tries to inject a pow solution into the remote agent, returning
diff --git a/miner/unconfirmed_test.go b/miner/unconfirmed_test.go
index 71a0dde931..781ba06366 100644
--- a/miner/unconfirmed_test.go
+++ b/miner/unconfirmed_test.go
@@ -72,7 +72,7 @@ func TestUnconfirmedShifts(t *testing.T) {
if n := pool.blocks.Len(); n != int(limit)/2 {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2)
}
- // Try to shift all the remaining blocks out and verify emptyness
+ // Try to shift all the remaining blocks out and verify emptiness
pool.Shift(start + 2*uint64(limit))
if n := pool.blocks.Len(); n != 0 {
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0)
diff --git a/miner/worker.go b/miner/worker.go
index 31bb721744..6514b48811 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -33,6 +33,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
+ "github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
"github.com/XinFinOrg/XDPoSChain/contracts"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/state"
@@ -186,16 +187,16 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
return worker
}
-func (self *worker) setEtherbase(addr common.Address) {
- self.mu.Lock()
- defer self.mu.Unlock()
- self.coinbase = addr
+func (w *worker) setEtherbase(addr common.Address) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.coinbase = addr
}
-func (self *worker) setExtra(extra []byte) {
- self.mu.Lock()
- defer self.mu.Unlock()
- self.extra = extra
+func (w *worker) setExtra(extra []byte) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.extra = extra
}
// pending returns the pending state and corresponding block. The returned
@@ -225,58 +226,58 @@ func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
return w.snapshotBlock, w.snapshotReceipts
}
-func (self *worker) start() {
- self.mu.Lock()
- defer self.mu.Unlock()
+func (w *worker) start() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
- atomic.StoreInt32(&self.mining, 1)
+ atomic.StoreInt32(&w.mining, 1)
// spin up agents
- for agent := range self.agents {
+ for agent := range w.agents {
agent.Start()
}
}
-func (self *worker) stop() {
- self.wg.Wait()
+func (w *worker) stop() {
+ w.wg.Wait()
- self.mu.Lock()
- defer self.mu.Unlock()
- if atomic.LoadInt32(&self.mining) == 1 {
- for agent := range self.agents {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if atomic.LoadInt32(&w.mining) == 1 {
+ for agent := range w.agents {
agent.Stop()
}
}
- atomic.StoreInt32(&self.mining, 0)
- atomic.StoreInt32(&self.atWork, 0)
+ atomic.StoreInt32(&w.mining, 0)
+ atomic.StoreInt32(&w.atWork, 0)
}
-func (self *worker) register(agent Agent) {
- self.mu.Lock()
- defer self.mu.Unlock()
- self.agents[agent] = struct{}{}
- agent.SetReturnCh(self.recv)
+func (w *worker) register(agent Agent) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.agents[agent] = struct{}{}
+ agent.SetReturnCh(w.recv)
}
-func (self *worker) unregister(agent Agent) {
- self.mu.Lock()
- defer self.mu.Unlock()
- delete(self.agents, agent)
+func (w *worker) unregister(agent Agent) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ delete(w.agents, agent)
agent.Stop()
}
-func (self *worker) update() {
- if self.announceTxs {
- defer self.txsSub.Unsubscribe()
+func (w *worker) update() {
+ if w.announceTxs {
+ defer w.txsSub.Unsubscribe()
}
- defer self.chainHeadSub.Unsubscribe()
- defer self.chainSideSub.Unsubscribe()
+ defer w.chainHeadSub.Unsubscribe()
+ defer w.chainSideSub.Unsubscribe()
// timeout waiting for v1 inital value
minePeriod := 2
- MinePeriodCh := self.engine.(*XDPoS.XDPoS).MinePeriodCh
+ MinePeriodCh := w.engine.(*XDPoS.XDPoS).MinePeriodCh
defer close(MinePeriodCh)
- NewRoundCh := self.engine.(*XDPoS.XDPoS).NewRoundCh
+ NewRoundCh := w.engine.(*XDPoS.XDPoS).NewRoundCh
defer close(NewRoundCh)
timeout := time.NewTimer(time.Duration(minePeriod) * time.Second)
@@ -290,7 +291,7 @@ func (self *worker) update() {
for {
// A real event arrived, process interesting content
select {
- case d := <-self.resetCh:
+ case d := <-w.resetCh:
// Reset the timer to the new duration.
if !timeout.Stop() {
// Drain the timer channel if it had already expired.
@@ -313,67 +314,67 @@ func (self *worker) update() {
case v := <-MinePeriodCh:
log.Info("[worker] update wait period", "period", v)
minePeriod = v
- self.resetCh <- time.Duration(minePeriod) * time.Second
+ w.resetCh <- time.Duration(minePeriod) * time.Second
case <-c:
- if atomic.LoadInt32(&self.mining) == 1 {
- self.commitNewWork()
+ if atomic.LoadInt32(&w.mining) == 1 {
+ w.commitNewWork()
}
- resetTime := getResetTime(self.chain, minePeriod)
- self.resetCh <- resetTime
+ resetTime := getResetTime(w.chain, minePeriod)
+ w.resetCh <- resetTime
// Handle ChainHeadEvent
- case <-self.chainHeadCh:
- self.commitNewWork()
- resetTime := getResetTime(self.chain, minePeriod)
- self.resetCh <- resetTime
+ case <-w.chainHeadCh:
+ w.commitNewWork()
+ resetTime := getResetTime(w.chain, minePeriod)
+ w.resetCh <- resetTime
// Handle new round
case <-NewRoundCh:
- self.commitNewWork()
- resetTime := getResetTime(self.chain, minePeriod)
- self.resetCh <- resetTime
+ w.commitNewWork()
+ resetTime := getResetTime(w.chain, minePeriod)
+ w.resetCh <- resetTime
// Handle ChainSideEvent
- case <-self.chainSideCh:
+ case <-w.chainSideCh:
// Handle NewTxsEvent
- case ev := <-self.txsCh:
+ case ev := <-w.txsCh:
// Apply transactions to the pending state if we're not mining.
//
// Note all transactions received may not be continuous with transactions
// already included in the current mining block. These transactions will
// be automatically eliminated.
- if atomic.LoadInt32(&self.mining) == 0 {
- self.currentMu.Lock()
+ if atomic.LoadInt32(&w.mining) == 0 {
+ w.currentMu.Lock()
txs := make(map[common.Address]types.Transactions)
for _, tx := range ev.Txs {
- acc, _ := types.Sender(self.current.signer, tx)
+ acc, _ := types.Sender(w.current.signer, tx)
txs[acc] = append(txs[acc], tx)
}
- feeCapacity := state.GetTRC21FeeCapacityFromState(self.current.state)
- txset, specialTxs := types.NewTransactionsByPriceAndNonce(self.current.signer, txs, nil, feeCapacity)
+ feeCapacity := state.GetTRC21FeeCapacityFromState(w.current.state)
+ txset, specialTxs := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, nil, feeCapacity)
- tcount := self.current.tcount
- self.current.commitTransactions(self.mux, feeCapacity, txset, specialTxs, self.chain, self.coinbase, &self.pendingLogsFeed)
+ tcount := w.current.tcount
+ w.current.commitTransactions(w.mux, feeCapacity, txset, specialTxs, w.chain, w.coinbase, &w.pendingLogsFeed)
// Only update the snapshot if any new transactions were added
// to the pending block
- if tcount != self.current.tcount {
- self.updateSnapshot()
+ if tcount != w.current.tcount {
+ w.updateSnapshot()
}
- self.currentMu.Unlock()
+ w.currentMu.Unlock()
} else {
// If we're mining, but nothing is being processed, wake on new transactions
- if self.config.XDPoS != nil && self.config.XDPoS.Period == 0 {
- self.commitNewWork()
+ if w.config.XDPoS != nil && w.config.XDPoS.Period == 0 {
+ w.commitNewWork()
}
}
- case <-self.chainHeadSub.Err():
+ case <-w.chainHeadSub.Err():
return
- case <-self.chainSideSub.Err():
+ case <-w.chainSideSub.Err():
return
}
}
@@ -392,18 +393,18 @@ func getResetTime(chain *core.BlockChain, minePeriod int) time.Duration {
return resetTime
}
-func (self *worker) wait() {
+func (w *worker) wait() {
for {
mustCommitNewWork := true
- for result := range self.recv {
- atomic.AddInt32(&self.atWork, -1)
+ for result := range w.recv {
+ atomic.AddInt32(&w.atWork, -1)
if result == nil {
continue
}
block := result.Block
- if self.config.XDPoS != nil && block.NumberU64() >= self.config.XDPoS.Epoch && len(block.Validator()) == 0 {
- self.mux.Post(core.NewMinedBlockEvent{Block: block})
+ if w.config.XDPoS != nil && block.NumberU64() >= w.config.XDPoS.Epoch && len(block.Validator()) == 0 {
+ w.mux.Post(core.NewMinedBlockEvent{Block: block})
continue
}
work := result.Work
@@ -426,9 +427,9 @@ func (self *worker) wait() {
log.BlockHash = hash
}
// Commit block and state to database.
- self.currentMu.Lock()
- stat, err := self.chain.WriteBlockWithState(block, receipts, work.state, work.tradingState, work.lendingState)
- self.currentMu.Unlock()
+ w.currentMu.Lock()
+ stat, err := w.chain.WriteBlockWithState(block, receipts, work.state, work.tradingState, work.lendingState)
+ w.currentMu.Unlock()
if err != nil {
log.Error("Failed writing block to chain", "err", err)
continue
@@ -439,7 +440,7 @@ func (self *worker) wait() {
mustCommitNewWork = false
}
// Broadcast the block and announce chain insertion event
- self.mux.Post(core.NewMinedBlockEvent{Block: block})
+ w.mux.Post(core.NewMinedBlockEvent{Block: block})
var (
events []interface{}
logs = work.state.Logs()
@@ -450,7 +451,7 @@ func (self *worker) wait() {
}
if work.config.XDPoS != nil {
// epoch block
- isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header())
+ isEpochSwitchBlock, _, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header())
if err != nil {
log.Error("[wait] fail to check if block is epoch switch block when worker waiting", "BlockNum", block.Number(), "Hash", block.Hash())
}
@@ -458,29 +459,29 @@ func (self *worker) wait() {
core.CheckpointCh <- 1
}
}
- self.chain.UpdateBlocksHashCache(block)
- self.chain.PostChainEvents(events, logs)
+ w.chain.UpdateBlocksHashCache(block)
+ w.chain.PostChainEvents(events, logs)
// Insert the block into the set of pending ones to wait for confirmations
- self.unconfirmed.Insert(block.NumberU64(), block.Hash())
+ w.unconfirmed.Insert(block.NumberU64(), block.Hash())
if mustCommitNewWork {
- self.commitNewWork()
+ w.commitNewWork()
}
- if self.config.XDPoS != nil {
- c := self.engine.(*XDPoS.XDPoS)
- err = c.HandleProposedBlock(self.chain, block.Header())
+ if w.config.XDPoS != nil {
+ c := w.engine.(*XDPoS.XDPoS)
+ err = c.HandleProposedBlock(w.chain, block.Header())
if err != nil {
log.Warn("[wait] Unable to handle new proposed block", "err", err, "number", block.Number(), "hash", block.Hash())
}
- authorized := c.IsAuthorisedAddress(self.chain, block.Header(), self.coinbase)
+ authorized := c.IsAuthorisedAddress(w.chain, block.Header(), w.coinbase)
if !authorized {
valid := false
- masternodes := c.GetMasternodes(self.chain, block.Header())
+ masternodes := c.GetMasternodes(w.chain, block.Header())
for _, m := range masternodes {
- if m == self.coinbase {
+ if m == w.coinbase {
valid = true
break
}
@@ -491,8 +492,8 @@ func (self *worker) wait() {
}
}
// Send tx sign to smart contract blockSigners.
- if block.NumberU64()%common.MergeSignRange == 0 || !self.config.IsTIP2019(block.Number()) {
- if err := contracts.CreateTransactionSign(self.config, self.eth.TxPool(), self.eth.AccountManager(), block, self.chainDb, self.coinbase); err != nil {
+ if block.NumberU64()%common.MergeSignRange == 0 || !w.config.IsTIP2019(block.Number()) {
+ if err := contracts.CreateTransactionSign(w.config, w.eth.TxPool(), w.eth.AccountManager(), block, w.chainDb, w.coinbase); err != nil {
log.Error("Fail to create tx sign for signer", "error", err)
}
}
@@ -502,12 +503,12 @@ func (self *worker) wait() {
}
// push sends a new work task to currently live miner agents.
-func (self *worker) push(work *Work) {
- if atomic.LoadInt32(&self.mining) != 1 {
+func (w *worker) push(work *Work) {
+ if atomic.LoadInt32(&w.mining) != 1 {
return
}
- for agent := range self.agents {
- atomic.AddInt32(&self.atWork, 1)
+ for agent := range w.agents {
+ atomic.AddInt32(&w.atWork, 1)
if ch := agent.Work(); ch != nil {
ch <- work
}
@@ -541,25 +542,25 @@ func (w *worker) updateSnapshot() {
}
// makeCurrent creates a new environment for the current cycle.
-func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
+func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
// Retrieve the parent state to execute on top and start a prefetcher for
// the miner to speed block sealing up a bit
- state, err := self.chain.StateAt(parent.Root())
+ state, err := w.chain.StateAt(parent.Root())
if err != nil {
return err
}
- author, _ := self.chain.Engine().Author(parent.Header())
+ author, _ := w.chain.Engine().Author(parent.Header())
var XDCxState *tradingstate.TradingStateDB
var lendingState *lendingstate.LendingStateDB
- if self.config.XDPoS != nil {
- XDCX := self.eth.GetXDCX()
+ if w.config.XDPoS != nil {
+ XDCX := w.eth.GetXDCX()
XDCxState, err = XDCX.GetTradingState(parent, author)
if err != nil {
log.Error("Failed to get XDCx state ", "number", parent.Number(), "err", err)
return err
}
- lending := self.eth.GetXDCXLending()
+ lending := w.eth.GetXDCXLending()
lendingState, err = lending.GetLendingState(parent, author)
if err != nil {
log.Error("Failed to get lending state ", "number", parent.Number(), "err", err)
@@ -568,8 +569,8 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
}
work := &Work{
- config: self.config,
- signer: types.MakeSigner(self.config, header.Number),
+ config: w.config,
+ signer: types.MakeSigner(w.config, header.Number),
state: state,
parentState: state.Copy(),
tradingState: XDCxState,
@@ -583,7 +584,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
// Keep track of transactions which return errors so they can be removed
work.tcount = 0
- self.current = work
+ w.current = work
return nil
}
@@ -594,37 +595,37 @@ func abs(x int64) int64 {
return x
}
-func (self *worker) commitNewWork() {
- self.mu.Lock()
- defer self.mu.Unlock()
- self.uncleMu.Lock()
- defer self.uncleMu.Unlock()
- self.currentMu.Lock()
- defer self.currentMu.Unlock()
+func (w *worker) commitNewWork() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ w.uncleMu.Lock()
+ defer w.uncleMu.Unlock()
+ w.currentMu.Lock()
+ defer w.currentMu.Unlock()
tstart := time.Now()
- c := self.engine.(*XDPoS.XDPoS)
+ c := w.engine.(*XDPoS.XDPoS)
var parent *types.Block
if c != nil {
- parent = c.FindParentBlockToAssign(self.chain, self.chain.CurrentBlock())
+ parent = c.FindParentBlockToAssign(w.chain, w.chain.CurrentBlock())
} else {
- parent = self.chain.CurrentBlock()
+ parent = w.chain.CurrentBlock()
}
var signers map[common.Address]struct{}
- if parent.Hash().Hex() == self.lastParentBlockCommit {
+ if parent.Hash().Hex() == w.lastParentBlockCommit {
return
}
- if !self.announceTxs && atomic.LoadInt32(&self.mining) == 0 {
+ if !w.announceTxs && atomic.LoadInt32(&w.mining) == 0 {
return
}
// Only try to commit new work if we are mining
- if atomic.LoadInt32(&self.mining) == 1 {
+ if atomic.LoadInt32(&w.mining) == 1 {
// check if we are right after parent's coinbase in the list
- if self.config.XDPoS != nil {
- ok, err := c.YourTurn(self.chain, parent.Header(), self.coinbase)
+ if w.config.XDPoS != nil {
+ ok, err := c.YourTurn(w.chain, parent.Header(), w.coinbase)
if err != nil {
log.Warn("Failed when trying to commit new work", "err", err)
return
@@ -651,15 +652,18 @@ func (self *worker) commitNewWork() {
ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1),
GasLimit: params.TargetGasLimit,
- Extra: self.extra,
+ Extra: w.extra,
Time: big.NewInt(tstamp),
}
+ // Set baseFee if we are on an EIP-1559 chain
+ header.BaseFee = eip1559.CalcBaseFee(w.config, header)
+
// Only set the coinbase if we are mining (avoid spurious block rewards)
- if atomic.LoadInt32(&self.mining) == 1 {
- header.Coinbase = self.coinbase
+ if atomic.LoadInt32(&w.mining) == 1 {
+ header.Coinbase = w.coinbase
}
- if err := self.engine.Prepare(self.chain, header); err != nil {
+ if err := w.engine.Prepare(w.chain, header); err != nil {
if err == consensus.ErrNotReadyToPropose {
log.Info("Waiting...", "err", err)
return
@@ -668,12 +672,12 @@ func (self *worker) commitNewWork() {
return
}
// If we are care about TheDAO hard-fork check whether to override the extra-data or not
- if daoBlock := self.config.DAOForkBlock; daoBlock != nil {
+ if daoBlock := w.config.DAOForkBlock; daoBlock != nil {
// Check whether the block is among the fork extra-override range
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 {
// Depending whether we support or oppose the fork, override differently
- if self.config.DAOForkSupport {
+ if w.config.DAOForkSupport {
header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data
@@ -681,14 +685,14 @@ func (self *worker) commitNewWork() {
}
}
// Could potentially happen if starting to mine in an odd state.
- err := self.makeCurrent(parent, header)
+ err := w.makeCurrent(parent, header)
if err != nil {
log.Error("Failed to create mining context", "err", err)
return
}
// Create the current work task and check any fork transitions needed
- work := self.current
- if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
+ work := w.current
+ if w.config.DAOForkSupport && w.config.DAOForkBlock != nil && w.config.DAOForkBlock.Cmp(header.Number) == 0 {
misc.ApplyDAOHardFork(work.state)
}
if common.TIPSigning.Cmp(header.Number) == 0 {
@@ -709,31 +713,27 @@ func (self *worker) commitNewWork() {
lendingFinalizedTradeTransaction *types.Transaction
)
feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), work.state)
- if self.config.XDPoS != nil {
- isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
+ if w.config.XDPoS != nil {
+ isEpochSwitchBlock, _, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
if err != nil {
log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash())
}
if !isEpochSwitchBlock {
- pending, err := self.eth.TxPool().Pending()
- if err != nil {
- log.Error("Failed to fetch pending transactions", "err", err)
- return
- }
- txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity)
+ pending := w.eth.TxPool().Pending(true)
+ txs, specialTxs = types.NewTransactionsByPriceAndNonce(w.current.signer, pending, signers, feeCapacity)
}
}
- if atomic.LoadInt32(&self.mining) == 1 {
- wallet, err := self.eth.AccountManager().Find(accounts.Account{Address: self.coinbase})
+ if atomic.LoadInt32(&w.mining) == 1 {
+ wallet, err := w.eth.AccountManager().Find(accounts.Account{Address: w.coinbase})
if err != nil {
- log.Warn("Can't find coinbase account wallet", "coinbase", self.coinbase, "err", err)
+ log.Warn("Can't find coinbase account wallet", "coinbase", w.coinbase, "err", err)
return
}
- if self.config.XDPoS != nil && self.chain.Config().IsTIPXDCXMiner(header.Number) {
- XDCX := self.eth.GetXDCX()
- XDCXLending := self.eth.GetXDCXLending()
- if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch {
- isEpochSwitchBlock, epochNumber, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
+ if w.config.XDPoS != nil && w.chain.Config().IsTIPXDCXMiner(header.Number) {
+ XDCX := w.eth.GetXDCX()
+ XDCXLending := w.eth.GetXDCXLending()
+ if XDCX != nil && header.Number.Uint64() > w.config.XDPoS.Epoch {
+ isEpochSwitchBlock, epochNumber, err := w.engine.(*XDPoS.XDPoS).IsEpochSwitch(header)
if err != nil {
log.Error("[commitNewWork] fail to check if block is epoch switch block when performing XDCX and XDCXLending operations", "BlockNum", header.Number, "Hash", header.Hash())
}
@@ -748,16 +748,16 @@ func (self *worker) commitNewWork() {
// won't grasp tx at checkpoint
//https://github.com/XinFinOrg/XDPoSChain-v1/pull/416
log.Debug("Start processing order pending")
- tradingOrderPending, _ := self.eth.OrderPool().Pending()
+ tradingOrderPending, _ := w.eth.OrderPool().Pending()
log.Debug("Start processing order pending", "len", len(tradingOrderPending))
- tradingTxMatches, tradingMatchingResults = XDCX.ProcessOrderPending(header, self.coinbase, self.chain, tradingOrderPending, work.state, work.tradingState)
+ tradingTxMatches, tradingMatchingResults = XDCX.ProcessOrderPending(header, w.coinbase, w.chain, tradingOrderPending, work.state, work.tradingState)
log.Debug("trading transaction matches found", "tradingTxMatches", len(tradingTxMatches))
- lendingOrderPending, _ := self.eth.LendingPool().Pending()
- lendingInput, lendingMatchingResults = XDCXLending.ProcessOrderPending(header, self.coinbase, self.chain, lendingOrderPending, work.state, work.lendingState, work.tradingState)
+ lendingOrderPending, _ := w.eth.LendingPool().Pending()
+ lendingInput, lendingMatchingResults = XDCXLending.ProcessOrderPending(header, w.coinbase, w.chain, lendingOrderPending, work.state, work.lendingState, work.tradingState)
log.Debug("lending transaction matches found", "lendingInput", len(lendingInput), "lendingMatchingResults", len(lendingMatchingResults))
- if header.Number.Uint64()%self.config.XDPoS.Epoch == common.LiquidateLendingTradeBlock {
- updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err = XDCXLending.ProcessLiquidationData(header, self.chain, work.state, work.tradingState, work.lendingState)
+ if header.Number.Uint64()%w.config.XDPoS.Epoch == common.LiquidateLendingTradeBlock {
+ updatedTrades, liquidatedTrades, autoRepayTrades, autoTopUpTrades, autoRecallTrades, err = XDCXLending.ProcessLiquidationData(header, w.chain, work.state, work.tradingState, work.lendingState)
if err != nil {
log.Error("Fail when process lending liquidation data ", "error", err)
return
@@ -776,16 +776,16 @@ func (self *worker) commitNewWork() {
log.Error("Fail to marshal txMatch", "error", err)
return
}
- nonce := work.state.GetNonce(self.coinbase)
+ nonce := work.state.GetNonce(w.coinbase)
tx := types.NewTransaction(nonce, common.XDCXAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txMatchBytes)
- txM, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId)
+ txM, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, tx, w.config.ChainId)
if err != nil {
log.Error("Fail to create tx matches", "error", err)
return
} else {
tradingTransaction = txM
if XDCX.IsSDKNode() {
- self.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults)
+ w.chain.AddMatchingResult(tradingTransaction.Hash(), tradingMatchingResults)
}
// force adding trading, lending transaction to this block
if tradingTransaction != nil {
@@ -806,16 +806,16 @@ func (self *worker) commitNewWork() {
log.Error("Fail to marshal lendingData", "error", err)
return
}
- nonce := work.state.GetNonce(self.coinbase)
+ nonce := work.state.GetNonce(w.coinbase)
lendingTx := types.NewTransaction(nonce, common.XDCXLendingAddressBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), lendingDataBytes)
- signedLendingTx, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, lendingTx, self.config.ChainId)
+ signedLendingTx, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, lendingTx, w.config.ChainId)
if err != nil {
log.Error("Fail to create lending tx", "error", err)
return
} else {
lendingTransaction = signedLendingTx
if XDCX.IsSDKNode() {
- self.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults)
+ w.chain.AddLendingResult(lendingTransaction.Hash(), lendingMatchingResults)
}
if lendingTransaction != nil {
specialTxs = append(specialTxs, lendingTransaction)
@@ -830,16 +830,16 @@ func (self *worker) commitNewWork() {
log.Error("Fail to marshal lendingData", "error", err)
return
}
- nonce := work.state.GetNonce(self.coinbase)
+ nonce := work.state.GetNonce(w.coinbase)
finalizedTx := types.NewTransaction(nonce, common.XDCXLendingFinalizedTradeAddressBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), finalizedTradeData)
- signedFinalizedTx, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, finalizedTx, self.config.ChainId)
+ signedFinalizedTx, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, finalizedTx, w.config.ChainId)
if err != nil {
log.Error("Fail to create lending tx", "error", err)
return
} else {
lendingFinalizedTradeTransaction = signedFinalizedTx
if XDCX.IsSDKNode() {
- self.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades)
+ w.chain.AddFinalizedTrades(lendingFinalizedTradeTransaction.Hash(), updatedTrades)
}
if lendingFinalizedTradeTransaction != nil {
specialTxs = append(specialTxs, lendingFinalizedTradeTransaction)
@@ -850,8 +850,8 @@ func (self *worker) commitNewWork() {
XDCxStateRoot := work.tradingState.IntermediateRoot()
LendingStateRoot := work.lendingState.IntermediateRoot()
txData := append(XDCxStateRoot.Bytes(), LendingStateRoot.Bytes()...)
- tx := types.NewTransaction(work.state.GetNonce(self.coinbase), common.TradingStateAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData)
- txStateRoot, err := wallet.SignTx(accounts.Account{Address: self.coinbase}, tx, self.config.ChainId)
+ tx := types.NewTransaction(work.state.GetNonce(w.coinbase), common.TradingStateAddrBinary, big.NewInt(0), txMatchGasLimit, big.NewInt(0), txData)
+ txStateRoot, err := wallet.SignTx(accounts.Account{Address: w.coinbase}, tx, w.config.ChainId)
if err != nil {
log.Error("Fail to create tx state root", "error", err)
return
@@ -859,29 +859,29 @@ func (self *worker) commitNewWork() {
specialTxs = append(specialTxs, txStateRoot)
}
}
- work.commitTransactions(self.mux, feeCapacity, txs, specialTxs, self.chain, self.coinbase, &self.pendingLogsFeed)
+ work.commitTransactions(w.mux, feeCapacity, txs, specialTxs, w.chain, w.coinbase, &w.pendingLogsFeed)
// compute uncles for the new block.
var (
uncles []*types.Header
)
// Create the new block to seal with the consensus engine
- if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.parentState, work.txs, uncles, work.receipts); err != nil {
+ if work.Block, err = w.engine.Finalize(w.chain, header, work.state, work.parentState, work.txs, uncles, work.receipts); err != nil {
log.Error("Failed to finalize block for sealing", "err", err)
return
}
- if atomic.LoadInt32(&self.mining) == 1 {
+ if atomic.LoadInt32(&w.mining) == 1 {
log.Info("Committing new block", "number", work.Block.Number(), "txs", work.tcount, "special-txs", len(specialTxs), "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart)))
- self.unconfirmed.Shift(work.Block.NumberU64() - 1)
- self.lastParentBlockCommit = parent.Hash().Hex()
+ w.unconfirmed.Shift(work.Block.NumberU64() - 1)
+ w.lastParentBlockCommit = parent.Hash().Hex()
}
- self.push(work)
- self.updateSnapshot()
+ w.push(work)
+ w.updateSnapshot()
}
-func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Address]*big.Int, txs *types.TransactionsByPriceAndNonce, specialTxs types.Transactions, bc *core.BlockChain, coinbase common.Address, pendingLogsFeed *event.Feed) {
- gp := new(core.GasPool).AddGas(env.header.GasLimit)
+func (w *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Address]*big.Int, txs *types.TransactionsByPriceAndNonce, specialTxs types.Transactions, bc *core.BlockChain, coinbase common.Address, pendingLogsFeed *event.Feed) {
+ gp := new(core.GasPool).AddGas(w.header.GasLimit)
balanceUpdated := map[common.Address]*big.Int{}
totalFeeUsed := big.NewInt(0)
var coalescedLogs []*types.Log
@@ -889,7 +889,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
for _, tx := range specialTxs {
to := tx.To()
//HF number for black-list
- if (env.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
+ if (w.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
from := tx.From()
// check if sender is in black list
if from != nil && common.Blacklist[*from] {
@@ -930,12 +930,12 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
// during transaction acceptance is the transaction pool.
//
// We use the eip155 signer regardless of the current hf.
- from, _ := types.Sender(env.signer, tx)
+ from, _ := types.Sender(w.signer, tx)
// Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do.
hash := tx.Hash()
- if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
- log.Trace("Ignoring reply protected special transaction", "hash", hash, "eip155", env.config.EIP155Block)
+ if tx.Protected() && !w.config.IsEIP155(w.header.Number) {
+ log.Trace("Ignoring reply protected special transaction", "hash", hash, "eip155", w.config.EIP155Block)
continue
}
if *to == common.BlockSignersBinary {
@@ -944,20 +944,20 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
continue
}
blkNumber := binary.BigEndian.Uint64(data[8:40])
- if blkNumber >= env.header.Number.Uint64() || blkNumber <= env.header.Number.Uint64()-env.config.XDPoS.Epoch*2 {
- log.Trace("Data special transaction invalid number", "hash", hash, "blkNumber", blkNumber, "miner", env.header.Number)
+ if blkNumber >= w.header.Number.Uint64() || blkNumber <= w.header.Number.Uint64()-w.config.XDPoS.Epoch*2 {
+ log.Trace("Data special transaction invalid number", "hash", hash, "blkNumber", blkNumber, "miner", w.header.Number)
continue
}
}
// Start executing the transaction
- env.state.Prepare(hash, common.Hash{}, env.tcount)
+ w.state.SetTxContext(hash, w.tcount)
- nonce := env.state.GetNonce(from)
+ nonce := w.state.GetNonce(from)
if nonce != tx.Nonce() && !tx.IsSkipNonceTransaction() {
log.Trace("Skipping account with special transaction invalid nonce", "sender", from, "nonce", nonce, "tx nonce ", tx.Nonce(), "to", to)
continue
}
- err, logs, tokenFeeUsed, gas := env.commitTransaction(balanceFee, tx, bc, coinbase, gp)
+ logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp)
switch err {
case core.ErrNonceTooLow:
// New head notification data race between the transaction pool and miner, shift
@@ -969,7 +969,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
case nil:
// Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...)
- env.tcount++
+ w.tcount++
default:
// Strange error, discard the transaction and get the next in line (note, the
@@ -977,7 +977,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
log.Debug("Add Special Transaction failed, account skipped", "hash", hash, "sender", from, "nonce", tx.Nonce(), "to", to, "err", err)
}
if tokenFeeUsed {
- fee := common.GetGasFee(env.header.Number.Uint64(), gas)
+ fee := common.GetGasFee(w.header.Number.Uint64(), gas)
balanceFee[*to] = new(big.Int).Sub(balanceFee[*to], fee)
balanceUpdated[*to] = balanceFee[*to]
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
@@ -1002,7 +1002,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
//HF number for black-list
to := tx.To()
- if (env.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
+ if (w.header.Number.Uint64() >= common.BlackListHFNumber) && !common.IsTestnet {
from := tx.From()
// check if sender is in black list
if from != nil && common.Blacklist[*from] {
@@ -1041,18 +1041,18 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
// during transaction acceptance is the transaction pool.
//
// We use the eip155 signer regardless of the current hf.
- from, _ := types.Sender(env.signer, tx)
+ from, _ := types.Sender(w.signer, tx)
hash := tx.Hash()
// Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do.
- if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
- log.Trace("Ignoring reply protected transaction", "hash", hash, "eip155", env.config.EIP155Block)
+ if tx.Protected() && !w.config.IsEIP155(w.header.Number) {
+ log.Trace("Ignoring reply protected transaction", "hash", hash, "eip155", w.config.EIP155Block)
txs.Pop()
continue
}
// Start executing the transaction
- env.state.Prepare(hash, common.Hash{}, env.tcount)
- nonce := env.state.GetNonce(from)
+ w.state.SetTxContext(hash, w.tcount)
+ nonce := w.state.GetNonce(from)
if nonce > tx.Nonce() {
// New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
@@ -1065,7 +1065,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
txs.Pop()
continue
}
- err, logs, tokenFeeUsed, gas := env.commitTransaction(balanceFee, tx, bc, coinbase, gp)
+ logs, tokenFeeUsed, gas, err := w.commitTransaction(balanceFee, tx, bc, coinbase, gp)
switch {
case errors.Is(err, core.ErrGasLimitReached):
// Pop the current out-of-gas transaction without shifting in the next from the account
@@ -1085,10 +1085,10 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
case errors.Is(err, nil):
// Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...)
- env.tcount++
+ w.tcount++
txs.Shift()
- case errors.Is(err, core.ErrTxTypeNotSupported):
+ case errors.Is(err, types.ErrTxTypeNotSupported):
// Pop the unsupported transaction without shifting in the next from the account
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
txs.Pop()
@@ -1100,13 +1100,13 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
txs.Shift()
}
if tokenFeeUsed {
- fee := common.GetGasFee(env.header.Number.Uint64(), gas)
+ fee := common.GetGasFee(w.header.Number.Uint64(), gas)
balanceFee[*to] = new(big.Int).Sub(balanceFee[*to], fee)
balanceUpdated[*to] = balanceFee[*to]
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
}
}
- state.UpdateTRC21Fee(env.state, balanceUpdated, totalFeeUsed)
+ state.UpdateTRC21Fee(w.state, balanceUpdated, totalFeeUsed)
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
// logs by filling in the block hash when the block was mined by the local miner. This can
// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
@@ -1118,27 +1118,27 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad
}
pendingLogsFeed.Send(cpy)
}
- if env.tcount > 0 {
+ if w.tcount > 0 {
go func(tcount int) {
err := mux.Post(core.PendingStateEvent{})
if err != nil {
log.Warn("[commitTransactions] Error when sending PendingStateEvent", "tcount", tcount)
}
- }(env.tcount)
+ }(w.tcount)
}
}
-func (env *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log, bool, uint64) {
- snap := env.state.Snapshot()
+func (w *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) ([]*types.Log, bool, uint64, error) {
+ snap := w.state.Snapshot()
- receipt, gas, err, tokenFeeUsed := core.ApplyTransaction(env.config, balanceFee, bc, &coinbase, gp, env.state, env.tradingState, env.header, tx, &env.header.GasUsed, vm.Config{})
+ receipt, gas, err, tokenFeeUsed := core.ApplyTransaction(w.config, balanceFee, bc, &coinbase, gp, w.state, w.tradingState, w.header, tx, &w.header.GasUsed, vm.Config{})
if err != nil {
- env.state.RevertToSnapshot(snap)
- return err, nil, false, 0
+ w.state.RevertToSnapshot(snap)
+ return nil, false, 0, err
}
- env.txs = append(env.txs, tx)
- env.receipts = append(env.receipts, receipt)
+ w.txs = append(w.txs, tx)
+ w.receipts = append(w.receipts, receipt)
- return nil, receipt.Logs, tokenFeeUsed, gas
+ return receipt.Logs, tokenFeeUsed, gas, nil
}
diff --git a/mobile/accounts.go b/mobile/accounts.go
deleted file mode 100644
index 48b1225f62..0000000000
--- a/mobile/accounts.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the accounts package to support client side key
-// management on mobile platforms.
-
-package geth
-
-import (
- "errors"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/accounts"
- "github.com/XinFinOrg/XDPoSChain/accounts/keystore"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
-)
-
-const (
- // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
- // memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptN = int(keystore.StandardScryptN)
-
- // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
- // memory and taking approximately 1s CPU time on a modern processor.
- StandardScryptP = int(keystore.StandardScryptP)
-
- // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
- // memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptN = int(keystore.LightScryptN)
-
- // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
- // memory and taking approximately 100ms CPU time on a modern processor.
- LightScryptP = int(keystore.LightScryptP)
-)
-
-// Account represents a stored key.
-type Account struct{ account accounts.Account }
-
-// Accounts represents a slice of accounts.
-type Accounts struct{ accounts []accounts.Account }
-
-// Size returns the number of accounts in the slice.
-func (a *Accounts) Size() int {
- return len(a.accounts)
-}
-
-// Get returns the account at the given index from the slice.
-func (a *Accounts) Get(index int) (account *Account, _ error) {
- if index < 0 || index >= len(a.accounts) {
- return nil, errors.New("index out of bounds")
- }
- return &Account{a.accounts[index]}, nil
-}
-
-// Set sets the account at the given index in the slice.
-func (a *Accounts) Set(index int, account *Account) error {
- if index < 0 || index >= len(a.accounts) {
- return errors.New("index out of bounds")
- }
- a.accounts[index] = account.account
- return nil
-}
-
-// GetAddress retrieves the address associated with the account.
-func (a *Account) GetAddress() *Address {
- return &Address{a.account.Address}
-}
-
-// GetURL retrieves the canonical URL of the account.
-func (a *Account) GetURL() string {
- return a.account.URL.String()
-}
-
-// KeyStore manages a key storage directory on disk.
-type KeyStore struct{ keystore *keystore.KeyStore }
-
-// NewKeyStore creates a keystore for the given directory.
-func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
- return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)}
-}
-
-// HasAddress reports whether a key with the given address is present.
-func (ks *KeyStore) HasAddress(address *Address) bool {
- return ks.keystore.HasAddress(address.address)
-}
-
-// GetAccounts returns all key files present in the directory.
-func (ks *KeyStore) GetAccounts() *Accounts {
- return &Accounts{ks.keystore.Accounts()}
-}
-
-// DeleteAccount deletes the key matched by account if the passphrase is correct.
-// If a contains no filename, the address must match a unique key.
-func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error {
- return ks.keystore.Delete(account.account, passphrase)
-}
-
-// SignHash calculates a ECDSA signature for the given hash. The produced signature
-// is in the [R || S || V] format where V is 0 or 1.
-func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash))
-}
-
-// SignTx signs the given transaction with the requested account.
-func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
- if chainID == nil { // Null passed from mobile app
- chainID = new(BigInt)
- }
- signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
- if err != nil {
- return nil, err
- }
- return &Transaction{signed}, nil
-}
-
-// SignHashPassphrase signs hash if the private key matching the given address can
-// be decrypted with the given passphrase. The produced signature is in the
-// [R || S || V] format where V is 0 or 1.
-func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
- return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash))
-}
-
-// SignTxPassphrase signs the transaction if the private key matching the
-// given address can be decrypted with the given passphrase.
-func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) {
- if chainID == nil { // Null passed from mobile app
- chainID = new(BigInt)
- }
- signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint)
- if err != nil {
- return nil, err
- }
- return &Transaction{signed}, nil
-}
-
-// Unlock unlocks the given account indefinitely.
-func (ks *KeyStore) Unlock(account *Account, passphrase string) error {
- return ks.keystore.TimedUnlock(account.account, passphrase, 0)
-}
-
-// Lock removes the private key with the given address from memory.
-func (ks *KeyStore) Lock(address *Address) error {
- return ks.keystore.Lock(address.address)
-}
-
-// TimedUnlock unlocks the given account with the passphrase. The account stays
-// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the
-// account until the program exits. The account must match a unique key file.
-//
-// If the account address is already unlocked for a duration, TimedUnlock extends or
-// shortens the active unlock timeout. If the address was previously unlocked
-// indefinitely the timeout is not altered.
-func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error {
- return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout))
-}
-
-// NewAccount generates a new key and stores it into the key directory,
-// encrypting it with the passphrase.
-func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) {
- account, err := ks.keystore.NewAccount(passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{account}, nil
-}
-
-// UpdateAccount changes the passphrase of an existing account.
-func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
- return ks.keystore.Update(account.account, passphrase, newPassphrase)
-}
-
-// ExportKey exports as a JSON key, encrypted with newPassphrase.
-func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) {
- return ks.keystore.Export(account.account, passphrase, newPassphrase)
-}
-
-// ImportKey stores the given encrypted JSON key into the key directory.
-func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) {
- acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase)
- if err != nil {
- return nil, err
- }
- return &Account{acc}, nil
-}
-
-// ImportECDSAKey stores the given encrypted JSON key into the key directory.
-func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) {
- privkey, err := crypto.ToECDSA(common.CopyBytes(key))
- if err != nil {
- return nil, err
- }
- acc, err := ks.keystore.ImportECDSA(privkey, passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{acc}, nil
-}
-
-// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores
-// a key file in the key directory. The key file is encrypted with the same passphrase.
-func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) {
- account, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase)
- if err != nil {
- return nil, err
- }
- return &Account{account}, nil
-}
diff --git a/mobile/android_test.go b/mobile/android_test.go
deleted file mode 100644
index 8cc4daacb8..0000000000
--- a/mobile/android_test.go
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package geth
-
-import (
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/internal/build"
-)
-
-// androidTestClass is a Java class to do some lightweight tests against the Android
-// bindings. The goal is not to test each individual functionality, rather just to
-// catch breaking API and/or implementation changes.
-const androidTestClass = `
-package go;
-
-import android.test.InstrumentationTestCase;
-import android.test.MoreAsserts;
-
-import java.math.BigInteger;
-import java.util.Arrays;
-
-import org.ethereum.geth.*;
-
-public class AndroidTest extends InstrumentationTestCase {
- public AndroidTest() {}
-
- public void testAccountManagement() {
- // Create an encrypted keystore with light crypto parameters.
- KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
-
- try {
- // Create a new account with the specified encryption passphrase.
- Account newAcc = ks.newAccount("Creation password");
-
- // Export the newly created account with a different passphrase. The returned
- // data from this method invocation is a JSON encoded, encrypted key-file.
- byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password");
-
- // Update the passphrase on the account created above inside the local keystore.
- ks.updateAccount(newAcc, "Creation password", "Update password");
-
- // Delete the account updated above from the local keystore.
- ks.deleteAccount(newAcc, "Update password");
-
- // Import back the account we've exported (and then deleted) above with yet
- // again a fresh passphrase.
- Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password");
-
- // Create a new account to sign transactions with
- Account signer = ks.newAccount("Signer password");
-
- Transaction tx = new Transaction(
- 1, new Address("0x0000000000000000000000000000000000000000"),
- new BigInt(0), 0, new BigInt(1), null); // Random empty transaction
- BigInt chain = new BigInt(1); // Chain identifier of the main net
-
- // Sign a transaction with a single authorization
- Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain);
-
- // Sign a transaction with multiple manually cancelled authorizations
- ks.unlock(signer, "Signer password");
- signed = ks.signTx(signer, tx, chain);
- ks.lock(signer.getAddress());
-
- // Sign a transaction with multiple automatically cancelled authorizations
- ks.timedUnlock(signer, "Signer password", 1000000000);
- signed = ks.signTx(signer, tx, chain);
- } catch (Exception e) {
- fail(e.toString());
- }
- }
-
- public void testInprocNode() {
- Context ctx = new Context();
-
- try {
- // Start up a new inprocess node
- Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig());
- node.start();
-
- // Retrieve some data via function calls (we don't really care about the results)
- NodeInfo info = node.getNodeInfo();
- info.getName();
- info.getListenerAddress();
- info.getProtocols();
-
- // Retrieve some data via the APIs (we don't really care about the results)
- EthereumClient ec = node.getEthereumClient();
- ec.getBlockByNumber(ctx, -1).getNumber();
-
- NewHeadHandler handler = new NewHeadHandler() {
- @Override public void onError(String error) {}
- @Override public void onNewHead(final Header header) {}
- };
- ec.subscribeNewHead(ctx, handler, 16);
- } catch (Exception e) {
- fail(e.toString());
- }
- }
-
- // Tests that recovering transaction signers works for both Homestead and EIP155
- // signatures too. Regression test for go-ethereum issue #14599.
- public void testIssue14599() {
- try {
- byte[] preEIP155RLP = new BigInteger("f901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884", 16).toByteArray();
- preEIP155RLP = Arrays.copyOfRange(preEIP155RLP, 1, preEIP155RLP.length);
-
- byte[] postEIP155RLP = new BigInteger("f86b80847735940082520894ef5bbb9bba2e1ca69ef81b23a8727d889f3ef0a1880de0b6b3a7640000802ba06fef16c44726a102e6d55a651740636ef8aec6df3ebf009e7b0c1f29e4ac114aa057e7fbc69760b522a78bb568cfc37a58bfdcf6ea86cb8f9b550263f58074b9cc", 16).toByteArray();
- postEIP155RLP = Arrays.copyOfRange(postEIP155RLP, 1, postEIP155RLP.length);
-
- Transaction preEIP155 = new Transaction(preEIP155RLP);
- Transaction postEIP155 = new Transaction(postEIP155RLP);
-
- preEIP155.getFrom(null); // Homestead should accept homestead
- preEIP155.getFrom(new BigInt(4)); // EIP155 should accept homestead (missing chain ID)
- postEIP155.getFrom(new BigInt(4)); // EIP155 should accept EIP 155
-
- try {
- postEIP155.getFrom(null);
- fail("EIP155 transaction accepted by Homestead");
- } catch (Exception e) {}
- } catch (Exception e) {
- fail(e.toString());
- }
- }
-}
-`
-
-// TestAndroid runs the Android java test class specified above.
-//
-// This requires the gradle command in PATH and the Android SDK whose path is available
-// through ANDROID_HOME environment variable. To successfully run the tests, an Android
-// device must also be available with debugging enabled.
-//
-// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest
-func TestAndroid(t *testing.T) {
- t.Skip("skip this test since it's not being used")
- // Skip tests on Windows altogether
- if runtime.GOOS == "windows" {
- t.Skip("cannot test Android bindings on Windows, skipping")
- }
- // Make sure all the Android tools are installed
- if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil {
- t.Skip("command gradle not found, skipping")
- }
- if sdk := os.Getenv("ANDROID_HOME"); sdk == "" {
- // Android SDK not explicitly given, try to auto-resolve
- autopath := filepath.Join(os.Getenv("HOME"), "Android", "Sdk")
- if _, err := os.Stat(autopath); err != nil {
- t.Skip("ANDROID_HOME environment var not set, skipping")
- }
- os.Setenv("ANDROID_HOME", autopath)
- }
- if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil {
- t.Log("gomobile missing, installing it...")
- if out, err := exec.Command("go", "get", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
- t.Fatalf("install failed: %v\n%s", err, string(out))
- }
- t.Log("initializing gomobile...")
- start := time.Now()
- if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil {
- t.Fatalf("initialization failed: %v", err)
- }
- t.Logf("initialization took %v", time.Since(start))
- }
- // Create and switch to a temporary workspace
- workspace, err := os.MkdirTemp("", "geth-android-")
- if err != nil {
- t.Fatalf("failed to create temporary workspace: %v", err)
- }
- defer os.RemoveAll(workspace)
-
- pwd, err := os.Getwd()
- if err != nil {
- t.Fatalf("failed to get current working directory: %v", err)
- }
- if err := os.Chdir(workspace); err != nil {
- t.Fatalf("failed to switch to temporary workspace: %v", err)
- }
- defer os.Chdir(pwd)
-
- // Create the skeleton of the Android project
- for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} {
- err = os.MkdirAll(dir, os.ModePerm)
- if err != nil {
- t.Fatal(err)
- }
- }
- // Generate the mobile bindings for Geth and add the tester class
- gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/XinFinOrg/XDPoSChain/mobile")
- if output, err := gobind.CombinedOutput(); err != nil {
- t.Logf("%s", output)
- t.Fatalf("failed to run gomobile bind: %v", err)
- }
- build.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar", os.ModePerm)
-
- if err = os.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil {
- t.Fatalf("failed to write Android test class: %v", err)
- }
- // Finish creating the project and run the tests via gradle
- if err = os.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil {
- t.Fatalf("failed to write Android manifest: %v", err)
- }
- if err = os.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil {
- t.Fatalf("failed to write gradle build file: %v", err)
- }
- if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil {
- t.Logf("%s", output)
- t.Errorf("failed to run gradle test: %v", err)
- }
-}
-
-const androidManifest = `
-
-
-
-`
-
-const gradleConfig = `buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
- }
-}
-allprojects {
- repositories { jcenter() }
-}
-apply plugin: 'com.android.library'
-android {
- compileSdkVersion 'android-19'
- buildToolsVersion '21.1.2'
- defaultConfig { minSdkVersion 15 }
-}
-repositories {
- flatDir { dirs 'libs' }
-}
-dependencies {
- compile 'com.android.support:appcompat-v7:19.0.0'
- compile(name: "geth", ext: "aar")
-}
-`
diff --git a/mobile/big.go b/mobile/big.go
deleted file mode 100644
index 1f1537322a..0000000000
--- a/mobile/big.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the math/big package.
-
-package geth
-
-import (
- "errors"
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/common"
-)
-
-// A BigInt represents a signed multi-precision integer.
-type BigInt struct {
- bigint *big.Int
-}
-
-// NewBigInt allocates and returns a new BigInt set to x.
-func NewBigInt(x int64) *BigInt {
- return &BigInt{big.NewInt(x)}
-}
-
-// GetBytes returns the absolute value of x as a big-endian byte slice.
-func (bi *BigInt) GetBytes() []byte {
- return bi.bigint.Bytes()
-}
-
-// String returns the value of x as a formatted decimal string.
-func (bi *BigInt) String() string {
- return bi.bigint.String()
-}
-
-// GetInt64 returns the int64 representation of x. If x cannot be represented in
-// an int64, the result is undefined.
-func (bi *BigInt) GetInt64() int64 {
- return bi.bigint.Int64()
-}
-
-// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets
-// the big int to that value.
-func (bi *BigInt) SetBytes(buf []byte) {
- bi.bigint.SetBytes(common.CopyBytes(buf))
-}
-
-// SetInt64 sets the big int to x.
-func (bi *BigInt) SetInt64(x int64) {
- bi.bigint.SetInt64(x)
-}
-
-// Sign returns:
-//
-// -1 if x < 0
-// 0 if x == 0
-// +1 if x > 0
-//
-func (bi *BigInt) Sign() int {
- return bi.bigint.Sign()
-}
-
-// SetString sets the big int to x.
-//
-// The string prefix determines the actual conversion base. A prefix of "0x" or
-// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix
-// selects base 2. Otherwise the selected base is 10.
-func (bi *BigInt) SetString(x string, base int) {
- bi.bigint.SetString(x, base)
-}
-
-// BigInts represents a slice of big ints.
-type BigInts struct{ bigints []*big.Int }
-
-// Size returns the number of big ints in the slice.
-func (bi *BigInts) Size() int {
- return len(bi.bigints)
-}
-
-// Get returns the bigint at the given index from the slice.
-func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) {
- if index < 0 || index >= len(bi.bigints) {
- return nil, errors.New("index out of bounds")
- }
- return &BigInt{bi.bigints[index]}, nil
-}
-
-// Set sets the big int at the given index in the slice.
-func (bi *BigInts) Set(index int, bigint *BigInt) error {
- if index < 0 || index >= len(bi.bigints) {
- return errors.New("index out of bounds")
- }
- bi.bigints[index] = bigint.bigint
- return nil
-}
-
-// GetString returns the value of x as a formatted string in some number base.
-func (bi *BigInt) GetString(base int) string {
- return bi.bigint.Text(base)
-}
diff --git a/mobile/bind.go b/mobile/bind.go
deleted file mode 100644
index 23893e6d9b..0000000000
--- a/mobile/bind.go
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the bind package.
-
-package geth
-
-import (
- "math/big"
- "strings"
-
- "github.com/XinFinOrg/XDPoSChain/accounts/abi"
- "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core/types"
-)
-
-// Signer is an interaface defining the callback when a contract requires a
-// method to sign the transaction before submission.
-type Signer interface {
- Sign(*Address, *Transaction) (tx *Transaction, _ error)
-}
-
-type signer struct {
- sign bind.SignerFn
-}
-
-func (s *signer) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) {
- sig, err := s.sign(addr.address, unsignedTx.tx)
- if err != nil {
- return nil, err
- }
- return &Transaction{sig}, nil
-}
-
-// CallOpts is the collection of options to fine tune a contract call request.
-type CallOpts struct {
- opts bind.CallOpts
-}
-
-// NewCallOpts creates a new option set for contract calls.
-func NewCallOpts() *CallOpts {
- return new(CallOpts)
-}
-
-func (opts *CallOpts) IsPending() bool { return opts.opts.Pending }
-func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ }
-
-// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// Even then it's awkward to unpack the subtleties of a Go context out to Java.
-// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} }
-
-func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending }
-func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ }
-func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context }
-
-// TransactOpts is the collection of authorization data required to create a
-// valid Ethereum transaction.
-type TransactOpts struct {
- opts bind.TransactOpts
-}
-
-func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} }
-func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() }
-func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} }
-func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} }
-func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimit) }
-
-// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} }
-
-// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876)
-// Even then it's awkward to unpack the subtleties of a Go context out to Java.
-//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} }
-
-func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address }
-func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) }
-func (opts *TransactOpts) SetSigner(s Signer) {
- opts.opts.Signer = func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
- sig, err := s.Sign(&Address{addr}, &Transaction{tx})
- if err != nil {
- return nil, err
- }
- return sig.tx, nil
- }
-}
-func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint }
-func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint }
-func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = uint64(limit) }
-func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context }
-
-// BoundContract is the base wrapper object that reflects a contract on the
-// Ethereum network. It contains a collection of methods that are used by the
-// higher level contract bindings to operate.
-type BoundContract struct {
- contract *bind.BoundContract
- address common.Address
- deployer *types.Transaction
-}
-
-// DeployContract deploys a contract onto the Ethereum blockchain and binds the
-// deployment address with a wrapper.
-func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) {
- // Deploy the contract to the network
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- return nil, err
- }
- addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...)
- if err != nil {
- return nil, err
- }
- return &BoundContract{
- contract: bound,
- address: addr,
- deployer: tx,
- }, nil
-}
-
-// BindContract creates a low level contract interface through which calls and
-// transactions may be made through.
-func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) {
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- return nil, err
- }
- return &BoundContract{
- contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client),
- address: address.address,
- }, nil
-}
-
-func (c *BoundContract) GetAddress() *Address { return &Address{c.address} }
-func (c *BoundContract) GetDeployer() *Transaction {
- if c.deployer == nil {
- return nil
- }
- return &Transaction{c.deployer}
-}
-
-// Call invokes the (constant) contract method with params as input values and
-// sets the output to result.
-func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error {
- if len(out.objects) == 1 {
- result := out.objects[0]
- if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil {
- return err
- }
- out.objects[0] = result
- } else {
- results := make([]interface{}, len(out.objects))
- copy(results, out.objects)
- if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil {
- return err
- }
- copy(out.objects, results)
- }
- return nil
-}
-
-// Transact invokes the (paid) contract method with params as input values.
-func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) {
- rawTx, err := c.contract.Transact(&opts.opts, method, args.objects...)
- if err != nil {
- return nil, err
- }
- return &Transaction{rawTx}, nil
-}
-
-// Transfer initiates a plain transaction to move funds to the contract, calling
-// its default method if one is available.
-func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) {
- rawTx, err := c.contract.Transfer(&opts.opts)
- if err != nil {
- return nil, err
- }
- return &Transaction{rawTx}, nil
-}
diff --git a/mobile/common.go b/mobile/common.go
deleted file mode 100644
index 6e69110a91..0000000000
--- a/mobile/common.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the common package.
-
-package geth
-
-import (
- "encoding/hex"
- "errors"
- "fmt"
- "strings"
-
- "github.com/XinFinOrg/XDPoSChain/common"
-)
-
-// Hash represents the 32 byte Keccak256 hash of arbitrary data.
-type Hash struct {
- hash common.Hash
-}
-
-// NewHashFromBytes converts a slice of bytes to a hash value.
-func NewHashFromBytes(binary []byte) (hash *Hash, _ error) {
- h := new(Hash)
- if err := h.SetBytes(common.CopyBytes(binary)); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// NewHashFromHex converts a hex string to a hash value.
-func NewHashFromHex(hex string) (hash *Hash, _ error) {
- h := new(Hash)
- if err := h.SetHex(hex); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// SetBytes sets the specified slice of bytes as the hash value.
-func (h *Hash) SetBytes(hash []byte) error {
- if length := len(hash); length != common.HashLength {
- return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength)
- }
- copy(h.hash[:], hash)
- return nil
-}
-
-// GetBytes retrieves the byte representation of the hash.
-func (h *Hash) GetBytes() []byte {
- return h.hash[:]
-}
-
-// SetHex sets the specified hex string as the hash value.
-func (h *Hash) SetHex(hash string) error {
- hash = strings.ToLower(hash)
- if len(hash) >= 2 && hash[:2] == "0x" {
- hash = hash[2:]
- }
- if length := len(hash); length != 2*common.HashLength {
- return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength)
- }
- bin, err := hex.DecodeString(hash)
- if err != nil {
- return err
- }
- copy(h.hash[:], bin)
- return nil
-}
-
-// GetHex retrieves the hex string representation of the hash.
-func (h *Hash) GetHex() string {
- return h.hash.Hex()
-}
-
-// Hashes represents a slice of hashes.
-type Hashes struct{ hashes []common.Hash }
-
-// NewHashes creates a slice of uninitialized Hashes.
-func NewHashes(size int) *Hashes {
- return &Hashes{
- hashes: make([]common.Hash, size),
- }
-}
-
-// NewHashesEmpty creates an empty slice of Hashes values.
-func NewHashesEmpty() *Hashes {
- return NewHashes(0)
-}
-
-// Size returns the number of hashes in the slice.
-func (h *Hashes) Size() int {
- return len(h.hashes)
-}
-
-// Get returns the hash at the given index from the slice.
-func (h *Hashes) Get(index int) (hash *Hash, _ error) {
- if index < 0 || index >= len(h.hashes) {
- return nil, errors.New("index out of bounds")
- }
- return &Hash{h.hashes[index]}, nil
-}
-
-// Set sets the Hash at the given index in the slice.
-func (h *Hashes) Set(index int, hash *Hash) error {
- if index < 0 || index >= len(h.hashes) {
- return errors.New("index out of bounds")
- }
- h.hashes[index] = hash.hash
- return nil
-}
-
-// Append adds a new Hash element to the end of the slice.
-func (h *Hashes) Append(hash *Hash) {
- h.hashes = append(h.hashes, hash.hash)
-}
-
-// Address represents the 20 byte address of an Ethereum account.
-type Address struct {
- address common.Address
-}
-
-// NewAddressFromBytes converts a slice of bytes to a hash value.
-func NewAddressFromBytes(binary []byte) (address *Address, _ error) {
- a := new(Address)
- if err := a.SetBytes(common.CopyBytes(binary)); err != nil {
- return nil, err
- }
- return a, nil
-}
-
-// NewAddressFromHex converts a hex string to a address value.
-func NewAddressFromHex(hex string) (address *Address, _ error) {
- a := new(Address)
- if err := a.SetHex(hex); err != nil {
- return nil, err
- }
- return a, nil
-}
-
-// SetBytes sets the specified slice of bytes as the address value.
-func (a *Address) SetBytes(address []byte) error {
- if length := len(address); length != common.AddressLength {
- return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength)
- }
- copy(a.address[:], address)
- return nil
-}
-
-// GetBytes retrieves the byte representation of the address.
-func (a *Address) GetBytes() []byte {
- return a.address[:]
-}
-
-// SetHex sets the specified hex string as the address value.
-func (a *Address) SetHex(address string) error {
- address = strings.ToLower(address)
- if len(address) >= 2 && address[:2] == "0x" {
- address = address[2:]
- }
- if length := len(address); length != 2*common.AddressLength {
- return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength)
- }
- bin, err := hex.DecodeString(address)
- if err != nil {
- return err
- }
- copy(a.address[:], bin)
- return nil
-}
-
-// GetHex retrieves the hex string representation of the address.
-func (a *Address) GetHex() string {
- return a.address.Hex()
-}
-
-// Addresses represents a slice of addresses.
-type Addresses struct{ addresses []common.Address }
-
-// NewAddresses creates a slice of uninitialized addresses.
-func NewAddresses(size int) *Addresses {
- return &Addresses{
- addresses: make([]common.Address, size),
- }
-}
-
-// NewAddressesEmpty creates an empty slice of Addresses values.
-func NewAddressesEmpty() *Addresses {
- return NewAddresses(0)
-}
-
-// Size returns the number of addresses in the slice.
-func (a *Addresses) Size() int {
- return len(a.addresses)
-}
-
-// Get returns the address at the given index from the slice.
-func (a *Addresses) Get(index int) (address *Address, _ error) {
- if index < 0 || index >= len(a.addresses) {
- return nil, errors.New("index out of bounds")
- }
- return &Address{a.addresses[index]}, nil
-}
-
-// Set sets the address at the given index in the slice.
-func (a *Addresses) Set(index int, address *Address) error {
- if index < 0 || index >= len(a.addresses) {
- return errors.New("index out of bounds")
- }
- a.addresses[index] = address.address
- return nil
-}
-
-// Append adds a new address element to the end of the slice.
-func (a *Addresses) Append(address *Address) {
- a.addresses = append(a.addresses, address.address)
-}
diff --git a/mobile/context.go b/mobile/context.go
deleted file mode 100644
index f1fff90114..0000000000
--- a/mobile/context.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the golang.org/x/net/context package to support
-// client side context management on mobile platforms.
-
-package geth
-
-import (
- "context"
- "time"
-)
-
-// Context carries a deadline, a cancelation signal, and other values across API
-// boundaries.
-type Context struct {
- context context.Context
- cancel context.CancelFunc
-}
-
-// NewContext returns a non-nil, empty Context. It is never canceled, has no
-// values, and has no deadline. It is typically used by the main function,
-// initialization, and tests, and as the top-level Context for incoming requests.
-func NewContext() *Context {
- return &Context{
- context: context.Background(),
- }
-}
-
-// WithCancel returns a copy of the original context with cancellation mechanism
-// included.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithCancel() *Context {
- child, cancel := context.WithCancel(c.context)
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
-
-// WithDeadline returns a copy of the original context with the deadline adjusted
-// to be no later than the specified time.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithDeadline(sec int64, nsec int64) *Context {
- child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec))
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
-
-// WithTimeout returns a copy of the original context with the deadline adjusted
-// to be no later than now + the duration specified.
-//
-// Canceling this context releases resources associated with it, so code should
-// call cancel as soon as the operations running in this Context complete.
-func (c *Context) WithTimeout(nsec int64) *Context {
- child, cancel := context.WithTimeout(c.context, time.Duration(nsec))
- return &Context{
- context: child,
- cancel: cancel,
- }
-}
diff --git a/mobile/discover.go b/mobile/discover.go
deleted file mode 100644
index b46c1d6f25..0000000000
--- a/mobile/discover.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the accounts package to support client side enode
-// management on mobile platforms.
-
-package geth
-
-import (
- "errors"
-
- "github.com/XinFinOrg/XDPoSChain/p2p/discv5"
-)
-
-// Enode represents a host on the network.
-type Enode struct {
- node *discv5.Node
-}
-
-// NewEnode parses a node designator.
-//
-// There are two basic forms of node designators
-// - incomplete nodes, which only have the public key (node ID)
-// - complete nodes, which contain the public key and IP/Port information
-//
-// For incomplete nodes, the designator must look like one of these
-//
-// enode://
-//
-//
-// For complete nodes, the node ID is encoded in the username portion
-// of the URL, separated from the host by an @ sign. The hostname can
-// only be given as an IP address, DNS domain names are not allowed.
-// The port in the host name section is the TCP listening port. If the
-// TCP and UDP (discovery) ports differ, the UDP port is specified as
-// query parameter "discport".
-//
-// In the following example, the node URL describes
-// a node with IP address 10.3.58.6, TCP listening port 30303
-// and UDP discovery port 30301.
-//
-// enode://@10.3.58.6:30303?discport=30301
-func NewEnode(rawurl string) (enode *Enode, _ error) {
- node, err := discv5.ParseNode(rawurl)
- if err != nil {
- return nil, err
- }
- return &Enode{node}, nil
-}
-
-// Enodes represents a slice of accounts.
-type Enodes struct{ nodes []*discv5.Node }
-
-// NewEnodes creates a slice of uninitialized enodes.
-func NewEnodes(size int) *Enodes {
- return &Enodes{
- nodes: make([]*discv5.Node, size),
- }
-}
-
-// NewEnodesEmpty creates an empty slice of Enode values.
-func NewEnodesEmpty() *Enodes {
- return NewEnodes(0)
-}
-
-// Size returns the number of enodes in the slice.
-func (e *Enodes) Size() int {
- return len(e.nodes)
-}
-
-// Get returns the enode at the given index from the slice.
-func (e *Enodes) Get(index int) (enode *Enode, _ error) {
- if index < 0 || index >= len(e.nodes) {
- return nil, errors.New("index out of bounds")
- }
- return &Enode{e.nodes[index]}, nil
-}
-
-// Set sets the enode at the given index in the slice.
-func (e *Enodes) Set(index int, enode *Enode) error {
- if index < 0 || index >= len(e.nodes) {
- return errors.New("index out of bounds")
- }
- e.nodes[index] = enode.node
- return nil
-}
-
-// Append adds a new enode element to the end of the slice.
-func (e *Enodes) Append(enode *Enode) {
- e.nodes = append(e.nodes, enode.node)
-}
diff --git a/mobile/doc.go b/mobile/doc.go
deleted file mode 100644
index 64d47bec2a..0000000000
--- a/mobile/doc.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package geth contains the simplified mobile APIs to go-ethereum.
-//
-// The scope of this package is *not* to allow writing a custom Ethereum client
-// with pieces plucked from go-ethereum, rather to allow writing native dapps on
-// mobile platforms. Keep this in mind when using or extending this package!
-//
-// API limitations
-//
-// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the
-// exposed APIs need to be manually wrapped into simplified types, with custom
-// constructors and getters/setters to ensure that they can be meaninfully used
-// from Java/ObjC too.
-//
-// With this in mind, please try to limit the scope of this package and only add
-// essentials without which mobile support cannot work, especially since manually
-// syncing the code will be unwieldy otherwise. In the long term we might consider
-// writing custom library generators, but those are out of scope now.
-//
-// Content wise each file in this package corresponds to an entire Go package
-// from the go-ethereum repository. Please adhere to this scoping to prevent this
-// package getting unmaintainable.
-//
-// Wrapping guidelines:
-//
-// Every type that is to be exposed should be wrapped into its own plain struct,
-// which internally contains a single field: the original go-ethereum version.
-// This is needed because gomobile cannot expose named types for now.
-//
-// Whenever a method argument or a return type is a custom struct, the pointer
-// variant should always be used as value types crossing over between language
-// boundaries might have strange behaviors.
-//
-// Slices of types should be converted into a single multiplicative type wrapping
-// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations
-// should not be provided to limit the remote code complexity. Arrays should be
-// avoided as much as possible since they complicate bounds checking.
-//
-// If a method has multiple return values (e.g. some return + an error), those
-// are generated as output arguments in ObjC. To avoid weird generated names like
-// ret_0 for them, please always assign names to output variables if tuples.
-//
-// Note, a panic *cannot* cross over language boundaries, instead will result in
-// an undebuggable SEGFAULT in the process. For error handling only ever use error
-// returns, which may be the only or the second return.
-package geth
diff --git a/mobile/ethclient.go b/mobile/ethclient.go
deleted file mode 100644
index 8a06b1f38c..0000000000
--- a/mobile/ethclient.go
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains a wrapper for the Ethereum client.
-
-package geth
-
-import (
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethclient"
-)
-
-// EthereumClient provides access to the Ethereum APIs.
-type EthereumClient struct {
- client *ethclient.Client
-}
-
-// NewEthereumClient connects a client to the given URL.
-func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) {
- rawClient, err := ethclient.Dial(rawurl)
- return &EthereumClient{rawClient}, err
-}
-
-// GetBlockByHash returns the given full block.
-func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) {
- rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash)
- return &Block{rawBlock}, err
-}
-
-// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the
-// latest known block is returned.
-func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) {
- if number < 0 {
- rawBlock, err := ec.client.BlockByNumber(ctx.context, nil)
- return &Block{rawBlock}, err
- }
- rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number))
- return &Block{rawBlock}, err
-}
-
-// GetHeaderByHash returns the block header with the given hash.
-func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) {
- rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash)
- return &Header{rawHeader}, err
-}
-
-// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0,
-// the latest known header is returned.
-func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) {
- if number < 0 {
- rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil)
- return &Header{rawHeader}, err
- }
- rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number))
- return &Header{rawHeader}, err
-}
-
-// GetTransactionByHash returns the transaction with the given hash.
-func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) {
- // TODO(karalabe): handle isPending
- rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash)
- return &Transaction{rawTx}, err
-}
-
-// GetTransactionSender returns the sender address of a transaction. The transaction must
-// be included in blockchain at the given block and index.
-func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) {
- addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index))
- return &Address{addr}, err
-}
-
-// GetTransactionCount returns the total number of transactions in the given block.
-func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) {
- rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash)
- return int(rawCount), err
-}
-
-// GetTransactionInBlock returns a single transaction at index in the given block.
-func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) {
- rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index))
- return &Transaction{rawTx}, err
-
-}
-
-// GetTransactionReceipt returns the receipt of a transaction by transaction hash.
-// Note that the receipt is not available for pending transactions.
-func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) {
- rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash)
- return &Receipt{rawReceipt}, err
-}
-
-// SyncProgress retrieves the current progress of the sync algorithm. If there's
-// no sync currently running, it returns nil.
-func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) {
- rawProgress, err := ec.client.SyncProgress(ctx.context)
- if rawProgress == nil {
- return nil, err
- }
- return &SyncProgress{*rawProgress}, err
-}
-
-// NewHeadHandler is a client-side subscription callback to invoke on events and
-// subscription failure.
-type NewHeadHandler interface {
- OnNewHead(header *Header)
- OnError(failure string)
-}
-
-// SubscribeNewHead subscribes to notifications about the current blockchain head
-// on the given channel.
-func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) {
- // Subscribe to the event internally
- ch := make(chan *types.Header, buffer)
- rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch)
- if err != nil {
- return nil, err
- }
- // Start up a dispatcher to feed into the callback
- go func() {
- for {
- select {
- case header := <-ch:
- handler.OnNewHead(&Header{header})
-
- case err := <-rawSub.Err():
- handler.OnError(err.Error())
- return
- }
- }
- }()
- return &Subscription{rawSub}, nil
-}
-
-// State Access
-
-// GetBalanceAt returns the wei balance of the given account.
-// The block number can be <0, in which case the balance is taken from the latest known block.
-func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) {
- if number < 0 {
- rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil)
- return &BigInt{rawBalance}, err
- }
- rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number))
- return &BigInt{rawBalance}, err
-}
-
-// GetStorageAt returns the value of key in the contract storage of the given account.
-// The block number can be <0, in which case the value is taken from the latest known block.
-func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) {
- if number < 0 {
- return ec.client.StorageAt(ctx.context, account.address, key.hash, nil)
- }
- return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number))
-}
-
-// GetCodeAt returns the contract code of the given account.
-// The block number can be <0, in which case the code is taken from the latest known block.
-func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) {
- if number < 0 {
- return ec.client.CodeAt(ctx.context, account.address, nil)
- }
- return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number))
-}
-
-// GetNonceAt returns the account nonce of the given account.
-// The block number can be <0, in which case the nonce is taken from the latest known block.
-func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) {
- if number < 0 {
- rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil)
- return int64(rawNonce), err
- }
- rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number))
- return int64(rawNonce), err
-}
-
-// Filters
-
-// FilterLogs executes a filter query.
-func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) {
- rawLogs, err := ec.client.FilterLogs(ctx.context, query.query)
- if err != nil {
- return nil, err
- }
- // Temp hack due to vm.Logs being []*vm.Log
- res := make([]*types.Log, len(rawLogs))
- for i := range rawLogs {
- res[i] = &rawLogs[i]
- }
- return &Logs{res}, nil
-}
-
-// FilterLogsHandler is a client-side subscription callback to invoke on events and
-// subscription failure.
-type FilterLogsHandler interface {
- OnFilterLogs(log *Log)
- OnError(failure string)
-}
-
-// SubscribeFilterLogs subscribes to the results of a streaming filter query.
-func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) {
- // Subscribe to the event internally
- ch := make(chan types.Log, buffer)
- rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch)
- if err != nil {
- return nil, err
- }
- // Start up a dispatcher to feed into the callback
- go func() {
- for {
- select {
- case log := <-ch:
- handler.OnFilterLogs(&Log{&log})
-
- case err := <-rawSub.Err():
- handler.OnError(err.Error())
- return
- }
- }
- }()
- return &Subscription{rawSub}, nil
-}
-
-// Pending State
-
-// GetPendingBalanceAt returns the wei balance of the given account in the pending state.
-func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) {
- rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address)
- return &BigInt{rawBalance}, err
-}
-
-// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
-func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) {
- return ec.client.PendingStorageAt(ctx.context, account.address, key.hash)
-}
-
-// GetPendingCodeAt returns the contract code of the given account in the pending state.
-func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) {
- return ec.client.PendingCodeAt(ctx.context, account.address)
-}
-
-// GetPendingNonceAt returns the account nonce of the given account in the pending state.
-// This is the nonce that should be used for the next transaction.
-func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) {
- rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address)
- return int64(rawNonce), err
-}
-
-// GetPendingTransactionCount returns the total number of transactions in the pending state.
-func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) {
- rawCount, err := ec.client.PendingTransactionCount(ctx.context)
- return int(rawCount), err
-}
-
-// Contract Calling
-
-// CallContract executes a message call transaction, which is directly executed in the VM
-// of the node, but never mined into the blockchain.
-//
-// blockNumber selects the block height at which the call runs. It can be <0, in which
-// case the code is taken from the latest known block. Note that state from very old
-// blocks might not be available.
-func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) {
- if number < 0 {
- return ec.client.CallContract(ctx.context, msg.msg, nil)
- }
- return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number))
-}
-
-// PendingCallContract executes a message call transaction using the EVM.
-// The state seen by the contract call is the pending state.
-func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) {
- return ec.client.PendingCallContract(ctx.context, msg.msg)
-}
-
-// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
-// execution of a transaction.
-func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) {
- rawPrice, err := ec.client.SuggestGasPrice(ctx.context)
- return &BigInt{rawPrice}, err
-}
-
-// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
-// the current pending state of the backend blockchain. There is no guarantee that this is
-// the true gas limit requirement as other transactions may be added or removed by miners,
-// but it should provide a basis for setting a reasonable default.
-func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) {
- rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg)
- return int64(rawGas), err
-}
-
-// SendTransaction injects a signed transaction into the pending pool for execution.
-//
-// If the transaction was a contract creation use the TransactionReceipt method to get the
-// contract address after the transaction has been mined.
-func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error {
- return ec.client.SendTransaction(ctx.context, tx.tx)
-}
diff --git a/mobile/ethereum.go b/mobile/ethereum.go
deleted file mode 100644
index f7904b7c32..0000000000
--- a/mobile/ethereum.go
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the go-ethereum root package.
-
-package geth
-
-import (
- "errors"
-
- ethereum "github.com/XinFinOrg/XDPoSChain"
- "github.com/XinFinOrg/XDPoSChain/common"
-)
-
-// Subscription represents an event subscription where events are
-// delivered on a data channel.
-type Subscription struct {
- sub ethereum.Subscription
-}
-
-// Unsubscribe cancels the sending of events to the data channel
-// and closes the error channel.
-func (s *Subscription) Unsubscribe() {
- s.sub.Unsubscribe()
-}
-
-// CallMsg contains parameters for contract calls.
-type CallMsg struct {
- msg ethereum.CallMsg
-}
-
-// NewCallMsg creates an empty contract call parameter list.
-func NewCallMsg() *CallMsg {
- return new(CallMsg)
-}
-
-func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} }
-func (msg *CallMsg) GetGas() int64 { return int64(msg.msg.Gas) }
-func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} }
-func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} }
-func (msg *CallMsg) GetData() []byte { return msg.msg.Data }
-func (msg *CallMsg) GetTo() *Address {
- if to := msg.msg.To; to != nil {
- return &Address{*msg.msg.To}
- }
- return nil
-}
-
-func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address }
-func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) }
-func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint }
-func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint }
-func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) }
-func (msg *CallMsg) SetTo(address *Address) {
- if address == nil {
- msg.msg.To = nil
- }
- msg.msg.To = &address.address
-}
-
-// SyncProgress gives progress indications when the node is synchronising with
-// the Ethereum network.
-type SyncProgress struct {
- progress ethereum.SyncProgress
-}
-
-func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) }
-func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) }
-func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) }
-func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) }
-func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) }
-
-// Topics is a set of topic lists to filter events with.
-type Topics struct{ topics [][]common.Hash }
-
-// NewTopics creates a slice of uninitialized Topics.
-func NewTopics(size int) *Topics {
- return &Topics{
- topics: make([][]common.Hash, size),
- }
-}
-
-// NewTopicsEmpty creates an empty slice of Topics values.
-func NewTopicsEmpty() *Topics {
- return NewTopics(0)
-}
-
-// Size returns the number of topic lists inside the set
-func (t *Topics) Size() int {
- return len(t.topics)
-}
-
-// Get returns the topic list at the given index from the slice.
-func (t *Topics) Get(index int) (hashes *Hashes, _ error) {
- if index < 0 || index >= len(t.topics) {
- return nil, errors.New("index out of bounds")
- }
- return &Hashes{t.topics[index]}, nil
-}
-
-// Set sets the topic list at the given index in the slice.
-func (t *Topics) Set(index int, topics *Hashes) error {
- if index < 0 || index >= len(t.topics) {
- return errors.New("index out of bounds")
- }
- t.topics[index] = topics.hashes
- return nil
-}
-
-// Append adds a new topic list to the end of the slice.
-func (t *Topics) Append(topics *Hashes) {
- t.topics = append(t.topics, topics.hashes)
-}
-
-// FilterQuery contains options for contact log filtering.
-type FilterQuery struct {
- query ethereum.FilterQuery
-}
-
-// NewFilterQuery creates an empty filter query for contact log filtering.
-func NewFilterQuery() *FilterQuery {
- return new(FilterQuery)
-}
-
-func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} }
-func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} }
-func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} }
-func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} }
-
-func (fq *FilterQuery) SetFromBlock(fromBlock *BigInt) { fq.query.FromBlock = fromBlock.bigint }
-func (fq *FilterQuery) SetToBlock(toBlock *BigInt) { fq.query.ToBlock = toBlock.bigint }
-func (fq *FilterQuery) SetAddresses(addresses *Addresses) { fq.query.Addresses = addresses.addresses }
-func (fq *FilterQuery) SetTopics(topics *Topics) { fq.query.Topics = topics.topics }
diff --git a/mobile/geth.go b/mobile/geth.go
deleted file mode 100644
index fd62ea0049..0000000000
--- a/mobile/geth.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the node package to support client side node
-// management on mobile platforms.
-
-package geth
-
-import (
- "encoding/json"
- "fmt"
- "path/filepath"
-
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/eth/downloader"
- "github.com/XinFinOrg/XDPoSChain/eth/ethconfig"
- "github.com/XinFinOrg/XDPoSChain/ethclient"
- "github.com/XinFinOrg/XDPoSChain/ethstats"
- "github.com/XinFinOrg/XDPoSChain/les"
- "github.com/XinFinOrg/XDPoSChain/node"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/p2p/nat"
- "github.com/XinFinOrg/XDPoSChain/params"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
-)
-
-// NodeConfig represents the collection of configuration values to fine tune the Geth
-// node embedded into a mobile process. The available values are a subset of the
-// entire API provided by go-ethereum to reduce the maintenance surface and dev
-// complexity.
-type NodeConfig struct {
- // Bootstrap nodes used to establish connectivity with the rest of the network.
- BootstrapNodes *Enodes
-
- // MaxPeers is the maximum number of peers that can be connected. If this is
- // set to zero, then only the configured static and trusted peers can connect.
- MaxPeers int
-
- // EthereumEnabled specifies whether the node should run the Ethereum protocol.
- EthereumEnabled bool
-
- // EthereumNetworkID is the network identifier used by the Ethereum protocol to
- // decide if remote peers should be accepted or not.
- EthereumNetworkID int64 // uint64 in truth, but Java can't handle that...
-
- // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An
- // empty genesis state is equivalent to using the mainnet's state.
- EthereumGenesis string
-
- // EthereumDatabaseCache is the system memory in MB to allocate for database caching.
- // A minimum of 16MB is always reserved.
- EthereumDatabaseCache int
-
- // EthereumNetStats is a netstats connection string to use to report various
- // chain, transaction and node stats to a monitoring server.
- //
- // It has the form "nodename:secret@host:port"
- EthereumNetStats string
-
- // WhisperEnabled specifies whether the node should run the Whisper protocol.
- WhisperEnabled bool
-}
-
-// defaultNodeConfig contains the default node configuration values to use if all
-// or some fields are missing from the user's specified list.
-var defaultNodeConfig = &NodeConfig{
- BootstrapNodes: FoundationBootnodes(),
- MaxPeers: 25,
- EthereumEnabled: true,
- EthereumNetworkID: 1,
- EthereumDatabaseCache: 16,
-}
-
-// NewNodeConfig creates a new node option set, initialized to the default values.
-func NewNodeConfig() *NodeConfig {
- config := *defaultNodeConfig
- return &config
-}
-
-// Node represents a Geth Ethereum node instance.
-type Node struct {
- node *node.Node
-}
-
-// NewNode creates and configures a new Geth node.
-func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
- // If no or partial configurations were specified, use defaults
- if config == nil {
- config = NewNodeConfig()
- }
- if config.MaxPeers == 0 {
- config.MaxPeers = defaultNodeConfig.MaxPeers
- }
- if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 {
- config.BootstrapNodes = defaultNodeConfig.BootstrapNodes
- }
- // Create the empty networking stack
- nodeConf := &node.Config{
- Name: clientIdentifier,
- Version: params.Version,
- DataDir: datadir,
- KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
- P2P: p2p.Config{
- NoDiscovery: true,
- DiscoveryV5: true,
- BootstrapNodesV5: config.BootstrapNodes.nodes,
- ListenAddr: ":0",
- NAT: nat.Any(),
- MaxPeers: config.MaxPeers,
- },
- }
- rawStack, err := node.New(nodeConf)
- if err != nil {
- return nil, err
- }
-
- var genesis *core.Genesis
- if config.EthereumGenesis != "" {
- // Parse the user supplied genesis spec if not mainnet
- genesis = new(core.Genesis)
- if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil {
- return nil, fmt.Errorf("invalid genesis spec: %v", err)
- }
- // If we have the testnet, hard code the chain configs too
- if config.EthereumGenesis == TestnetGenesis() {
- genesis.Config = params.TestnetChainConfig
- if config.EthereumNetworkID == 1 {
- config.EthereumNetworkID = 3
- }
- }
- }
- // Register the Ethereum protocol if requested
- if config.EthereumEnabled {
- ethConf := ethconfig.Defaults
- ethConf.Genesis = genesis
- ethConf.SyncMode = downloader.LightSync
- ethConf.NetworkId = uint64(config.EthereumNetworkID)
- ethConf.DatabaseCache = config.EthereumDatabaseCache
- if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- return les.New(ctx, ðConf)
- }); err != nil {
- return nil, fmt.Errorf("ethereum init: %v", err)
- }
- // If netstats reporting is requested, do it
- if config.EthereumNetStats != "" {
- if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- var lesServ *les.LightEthereum
- ctx.Service(&lesServ)
-
- return ethstats.New(config.EthereumNetStats, nil, lesServ)
- }); err != nil {
- return nil, fmt.Errorf("netstats init: %v", err)
- }
- }
- }
- // Register the Whisper protocol if requested
- if config.WhisperEnabled {
- if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) {
- return whisper.New(&whisper.DefaultConfig), nil
- }); err != nil {
- return nil, fmt.Errorf("whisper init: %v", err)
- }
- }
- return &Node{rawStack}, nil
-}
-
-// Start creates a live P2P node and starts running it.
-func (n *Node) Start() error {
- return n.node.Start()
-}
-
-// Stop terminates a running node along with all it's services. In the node was
-// not started, an error is returned.
-func (n *Node) Stop() error {
- return n.node.Stop()
-}
-
-// GetEthereumClient retrieves a client to access the Ethereum subsystem.
-func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) {
- rpc, err := n.node.Attach()
- if err != nil {
- return nil, err
- }
- return &EthereumClient{ethclient.NewClient(rpc)}, nil
-}
-
-// GetNodeInfo gathers and returns a collection of metadata known about the host.
-func (n *Node) GetNodeInfo() *NodeInfo {
- return &NodeInfo{n.node.Server().NodeInfo()}
-}
-
-// GetPeersInfo returns an array of metadata objects describing connected peers.
-func (n *Node) GetPeersInfo() *PeerInfos {
- return &PeerInfos{n.node.Server().PeersInfo()}
-}
diff --git a/mobile/interface.go b/mobile/interface.go
deleted file mode 100644
index 42ec9bb50a..0000000000
--- a/mobile/interface.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains perverted wrappers to allow crossing over empty interfaces.
-
-package geth
-
-import (
- "errors"
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/common"
-)
-
-// Interface represents a wrapped version of Go's interface{}, with the capacity
-// to store arbitrary data types.
-//
-// Since it's impossible to get the arbitrary-ness converted between Go and mobile
-// platforms, we're using explicit getters and setters for the conversions. There
-// is of course no point in enumerating everything, just enough to support the
-// contract bindins requiring client side generated code.
-type Interface struct {
- object interface{}
-}
-
-// NewInterface creates a new empty interface that can be used to pass around
-// generic types.
-func NewInterface() *Interface {
- return new(Interface)
-}
-
-func (i *Interface) SetBool(b bool) { i.object = &b }
-func (i *Interface) SetBools(bs []bool) { i.object = &bs }
-func (i *Interface) SetString(str string) { i.object = &str }
-func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs }
-func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b }
-func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries }
-func (i *Interface) SetAddress(address *Address) { i.object = &address.address }
-func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses }
-func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash }
-func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes }
-func (i *Interface) SetInt8(n int8) { i.object = &n }
-func (i *Interface) SetInt16(n int16) { i.object = &n }
-func (i *Interface) SetInt32(n int32) { i.object = &n }
-func (i *Interface) SetInt64(n int64) { i.object = &n }
-func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n }
-func (i *Interface) SetUint64(bigint *BigInt) { n := bigint.bigint.Uint64(); i.object = &n }
-func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint }
-func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints }
-
-func (i *Interface) SetDefaultBool() { i.object = new(bool) }
-func (i *Interface) SetDefaultBools() { i.object = new([]bool) }
-func (i *Interface) SetDefaultString() { i.object = new(string) }
-func (i *Interface) SetDefaultStrings() { i.object = new([]string) }
-func (i *Interface) SetDefaultBinary() { i.object = new([]byte) }
-func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) }
-func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) }
-func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) }
-func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) }
-func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) }
-func (i *Interface) SetDefaultInt8() { i.object = new(int8) }
-func (i *Interface) SetDefaultInt16() { i.object = new(int16) }
-func (i *Interface) SetDefaultInt32() { i.object = new(int32) }
-func (i *Interface) SetDefaultInt64() { i.object = new(int64) }
-func (i *Interface) SetDefaultUint8() { i.object = new(uint8) }
-func (i *Interface) SetDefaultUint16() { i.object = new(uint16) }
-func (i *Interface) SetDefaultUint32() { i.object = new(uint32) }
-func (i *Interface) SetDefaultUint64() { i.object = new(uint64) }
-func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) }
-func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) }
-
-func (i *Interface) GetBool() bool { return *i.object.(*bool) }
-func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) }
-func (i *Interface) GetString() string { return *i.object.(*string) }
-func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} }
-func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) }
-func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) }
-func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} }
-func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} }
-func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} }
-func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} }
-func (i *Interface) GetInt8() int8 { return *i.object.(*int8) }
-func (i *Interface) GetInt16() int16 { return *i.object.(*int16) }
-func (i *Interface) GetInt32() int32 { return *i.object.(*int32) }
-func (i *Interface) GetInt64() int64 { return *i.object.(*int64) }
-func (i *Interface) GetUint8() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))}
-}
-func (i *Interface) GetUint16() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))}
-}
-func (i *Interface) GetUint32() *BigInt {
- return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))}
-}
-func (i *Interface) GetUint64() *BigInt {
- return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))}
-}
-func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} }
-func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} }
-
-// Interfaces is a slices of wrapped generic objects.
-type Interfaces struct {
- objects []interface{}
-}
-
-// NewInterfaces creates a slice of uninitialized interfaces.
-func NewInterfaces(size int) *Interfaces {
- return &Interfaces{
- objects: make([]interface{}, size),
- }
-}
-
-// Size returns the number of interfaces in the slice.
-func (i *Interfaces) Size() int {
- return len(i.objects)
-}
-
-// Get returns the bigint at the given index from the slice.
-func (i *Interfaces) Get(index int) (iface *Interface, _ error) {
- if index < 0 || index >= len(i.objects) {
- return nil, errors.New("index out of bounds")
- }
- return &Interface{i.objects[index]}, nil
-}
-
-// Set sets the big int at the given index in the slice.
-func (i *Interfaces) Set(index int, object *Interface) error {
- if index < 0 || index >= len(i.objects) {
- return errors.New("index out of bounds")
- }
- i.objects[index] = object.object
- return nil
-}
diff --git a/mobile/p2p.go b/mobile/p2p.go
deleted file mode 100644
index 87c6e341ed..0000000000
--- a/mobile/p2p.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains wrappers for the p2p package.
-
-package geth
-
-import (
- "errors"
-
- "github.com/XinFinOrg/XDPoSChain/p2p"
-)
-
-// NodeInfo represents pi short summary of the information known about the host.
-type NodeInfo struct {
- info *p2p.NodeInfo
-}
-
-func (ni *NodeInfo) GetID() string { return ni.info.ID }
-func (ni *NodeInfo) GetName() string { return ni.info.Name }
-func (ni *NodeInfo) GetEnode() string { return ni.info.Enode }
-func (ni *NodeInfo) GetIP() string { return ni.info.IP }
-func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery }
-func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener }
-func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr }
-func (ni *NodeInfo) GetProtocols() *Strings {
- protos := []string{}
- for proto := range ni.info.Protocols {
- protos = append(protos, proto)
- }
- return &Strings{protos}
-}
-
-// PeerInfo represents pi short summary of the information known about pi connected peer.
-type PeerInfo struct {
- info *p2p.PeerInfo
-}
-
-func (pi *PeerInfo) GetID() string { return pi.info.ID }
-func (pi *PeerInfo) GetName() string { return pi.info.Name }
-func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} }
-func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress }
-func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress }
-
-// PeerInfos represents a slice of infos about remote peers.
-type PeerInfos struct {
- infos []*p2p.PeerInfo
-}
-
-// Size returns the number of peer info entries in the slice.
-func (pi *PeerInfos) Size() int {
- return len(pi.infos)
-}
-
-// Get returns the peer info at the given index from the slice.
-func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) {
- if index < 0 || index >= len(pi.infos) {
- return nil, errors.New("index out of bounds")
- }
- return &PeerInfo{pi.infos[index]}, nil
-}
diff --git a/mobile/params.go b/mobile/params.go
deleted file mode 100644
index f57d837845..0000000000
--- a/mobile/params.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the params package.
-
-package geth
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/p2p/discv5"
- "github.com/XinFinOrg/XDPoSChain/params"
-)
-
-// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It
-// is actually empty since that defaults to the hard coded binary genesis block.
-func MainnetGenesis() string {
- return ""
-}
-
-// TestnetGenesis returns the JSON spec to use for the Ethereum test network.
-func TestnetGenesis() string {
- enc, err := json.Marshal(core.DefaultTestnetGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network
-func RinkebyGenesis() string {
- enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock())
- if err != nil {
- panic(err)
- }
- return string(enc)
-}
-
-// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated
-// by the foundation running the V5 discovery protocol.
-func FoundationBootnodes() *Enodes {
- nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))}
- for i, url := range params.DiscoveryV5Bootnodes {
- nodes.nodes[i] = discv5.MustParseNode(url)
- }
- return nodes
-}
diff --git a/mobile/primitives.go b/mobile/primitives.go
deleted file mode 100644
index 5c6617fa47..0000000000
--- a/mobile/primitives.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains various wrappers for primitive types.
-
-package geth
-
-import (
- "errors"
- "fmt"
-)
-
-// Strings represents s slice of strs.
-type Strings struct{ strs []string }
-
-// Size returns the number of strs in the slice.
-func (s *Strings) Size() int {
- return len(s.strs)
-}
-
-// Get returns the string at the given index from the slice.
-func (s *Strings) Get(index int) (str string, _ error) {
- if index < 0 || index >= len(s.strs) {
- return "", errors.New("index out of bounds")
- }
- return s.strs[index], nil
-}
-
-// Set sets the string at the given index in the slice.
-func (s *Strings) Set(index int, str string) error {
- if index < 0 || index >= len(s.strs) {
- return errors.New("index out of bounds")
- }
- s.strs[index] = str
- return nil
-}
-
-// String implements the Stringer interface.
-func (s *Strings) String() string {
- return fmt.Sprintf("%v", s.strs)
-}
diff --git a/mobile/types.go b/mobile/types.go
deleted file mode 100644
index f577606e97..0000000000
--- a/mobile/types.go
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the core/types package.
-
-package geth
-
-import (
- "encoding/json"
- "errors"
- "fmt"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that
-// a sufficient amount of computation has been carried out on a block.
-type Nonce struct {
- nonce types.BlockNonce
-}
-
-// GetBytes retrieves the byte representation of the block nonce.
-func (n *Nonce) GetBytes() []byte {
- return n.nonce[:]
-}
-
-// GetHex retrieves the hex string representation of the block nonce.
-func (n *Nonce) GetHex() string {
- return fmt.Sprintf("0x%x", n.nonce[:])
-}
-
-// Bloom represents a 256 bit bloom filter.
-type Bloom struct {
- bloom types.Bloom
-}
-
-// GetBytes retrieves the byte representation of the bloom filter.
-func (b *Bloom) GetBytes() []byte {
- return b.bloom[:]
-}
-
-// GetHex retrieves the hex string representation of the bloom filter.
-func (b *Bloom) GetHex() string {
- return fmt.Sprintf("0x%x", b.bloom[:])
-}
-
-// Header represents a block header in the Ethereum blockchain.
-type Header struct {
- header *types.Header
-}
-
-// NewHeaderFromRLP parses a header from an RLP data dump.
-func NewHeaderFromRLP(data []byte) (*Header, error) {
- h := &Header{
- header: new(types.Header),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// EncodeRLP encodes a header into an RLP data dump.
-func (h *Header) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(h.header)
-}
-
-// NewHeaderFromJSON parses a header from an JSON data dump.
-func NewHeaderFromJSON(data string) (*Header, error) {
- h := &Header{
- header: new(types.Header),
- }
- if err := json.Unmarshal([]byte(data), h.header); err != nil {
- return nil, err
- }
- return h, nil
-}
-
-// EncodeJSON encodes a header into an JSON data dump.
-func (h *Header) EncodeJSON() (string, error) {
- data, err := json.Marshal(h.header)
- return string(data), err
-}
-
-// String implements the fmt.Stringer interface to print some semi-meaningful
-// data dump of the header for debugging purposes.
-func (h *Header) String() string {
- return h.header.String()
-}
-
-func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} }
-func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} }
-func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} }
-func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} }
-func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} }
-func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} }
-func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} }
-func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} }
-func (h *Header) GetNumber() int64 { return h.header.Number.Int64() }
-func (h *Header) GetGasLimit() int64 { return int64(h.header.GasLimit) }
-func (h *Header) GetGasUsed() int64 { return int64(h.header.GasUsed) }
-func (h *Header) GetTime() int64 { return h.header.Time.Int64() }
-func (h *Header) GetExtra() []byte { return h.header.Extra }
-func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} }
-func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} }
-func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} }
-
-// Headers represents a slice of headers.
-type Headers struct{ headers []*types.Header }
-
-// Size returns the number of headers in the slice.
-func (h *Headers) Size() int {
- return len(h.headers)
-}
-
-// Get returns the header at the given index from the slice.
-func (h *Headers) Get(index int) (header *Header, _ error) {
- if index < 0 || index >= len(h.headers) {
- return nil, errors.New("index out of bounds")
- }
- return &Header{h.headers[index]}, nil
-}
-
-// Block represents an entire block in the Ethereum blockchain.
-type Block struct {
- block *types.Block
-}
-
-// NewBlockFromRLP parses a block from an RLP data dump.
-func NewBlockFromRLP(data []byte) (*Block, error) {
- b := &Block{
- block: new(types.Block),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil {
- return nil, err
- }
- return b, nil
-}
-
-// EncodeRLP encodes a block into an RLP data dump.
-func (b *Block) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(b.block)
-}
-
-// NewBlockFromJSON parses a block from an JSON data dump.
-func NewBlockFromJSON(data string) (*Block, error) {
- b := &Block{
- block: new(types.Block),
- }
- if err := json.Unmarshal([]byte(data), b.block); err != nil {
- return nil, err
- }
- return b, nil
-}
-
-// EncodeJSON encodes a block into an JSON data dump.
-func (b *Block) EncodeJSON() (string, error) {
- data, err := json.Marshal(b.block)
- return string(data), err
-}
-
-// String implements the fmt.Stringer interface to print some semi-meaningful
-// data dump of the block for debugging purposes.
-func (b *Block) String() string {
- return b.block.String()
-}
-
-func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} }
-func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} }
-func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} }
-func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} }
-func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} }
-func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} }
-func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} }
-func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} }
-func (b *Block) GetNumber() int64 { return b.block.Number().Int64() }
-func (b *Block) GetGasLimit() int64 { return int64(b.block.GasLimit()) }
-func (b *Block) GetGasUsed() int64 { return int64(b.block.GasUsed()) }
-func (b *Block) GetTime() int64 { return b.block.Time().Int64() }
-func (b *Block) GetExtra() []byte { return b.block.Extra() }
-func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} }
-func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) }
-
-func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} }
-func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} }
-
-func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} }
-func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} }
-func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} }
-func (b *Block) GetTransaction(hash *Hash) *Transaction {
- return &Transaction{b.block.Transaction(hash.hash)}
-}
-
-// Transaction represents a single Ethereum transaction.
-type Transaction struct {
- tx *types.Transaction
-}
-
-// NewTransaction creates a new transaction with the given properties.
-func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction {
- return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))}
-}
-
-// NewTransactionFromRLP parses a transaction from an RLP data dump.
-func NewTransactionFromRLP(data []byte) (*Transaction, error) {
- tx := &Transaction{
- tx: new(types.Transaction),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil {
- return nil, err
- }
- return tx, nil
-}
-
-// EncodeRLP encodes a transaction into an RLP data dump.
-func (tx *Transaction) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(tx.tx)
-}
-
-// NewTransactionFromJSON parses a transaction from an JSON data dump.
-func NewTransactionFromJSON(data string) (*Transaction, error) {
- tx := &Transaction{
- tx: new(types.Transaction),
- }
- if err := json.Unmarshal([]byte(data), tx.tx); err != nil {
- return nil, err
- }
- return tx, nil
-}
-
-// EncodeJSON encodes a transaction into an JSON data dump.
-func (tx *Transaction) EncodeJSON() (string, error) {
- data, err := json.Marshal(tx.tx)
- return string(data), err
-}
-
-// String implements the fmt.Stringer interface to print some semi-meaningful
-// data dump of the transaction for debugging purposes.
-func (tx *Transaction) String() string {
- return tx.tx.String()
-}
-
-func (tx *Transaction) GetData() []byte { return tx.tx.Data() }
-func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) }
-func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} }
-func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} }
-func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) }
-
-func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} }
-func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} }
-
-// Deprecated: GetSigHash cannot know which signer to use.
-func (tx *Transaction) GetSigHash() *Hash { return &Hash{types.HomesteadSigner{}.Hash(tx.tx)} }
-
-// Deprecated: use EthereumClient.TransactionSender
-func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) {
- var signer types.Signer = types.HomesteadSigner{}
- if chainID != nil {
- signer = types.NewEIP155Signer(chainID.bigint)
- }
- from, err := types.Sender(signer, tx.tx)
- return &Address{from}, err
-}
-
-func (tx *Transaction) GetTo() *Address {
- if to := tx.tx.To(); to != nil {
- return &Address{*to}
- }
- return nil
-}
-
-func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) {
- var signer types.Signer = types.HomesteadSigner{}
- if chainID != nil {
- signer = types.NewEIP155Signer(chainID.bigint)
- }
- rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig))
- return &Transaction{rawTx}, err
-}
-
-// Transactions represents a slice of transactions.
-type Transactions struct{ txs types.Transactions }
-
-// Size returns the number of transactions in the slice.
-func (txs *Transactions) Size() int {
- return len(txs.txs)
-}
-
-// Get returns the transaction at the given index from the slice.
-func (txs *Transactions) Get(index int) (tx *Transaction, _ error) {
- if index < 0 || index >= len(txs.txs) {
- return nil, errors.New("index out of bounds")
- }
- return &Transaction{txs.txs[index]}, nil
-}
-
-// Receipt represents the results of a transaction.
-type Receipt struct {
- receipt *types.Receipt
-}
-
-// NewReceiptFromRLP parses a transaction receipt from an RLP data dump.
-func NewReceiptFromRLP(data []byte) (*Receipt, error) {
- r := &Receipt{
- receipt: new(types.Receipt),
- }
- if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil {
- return nil, err
- }
- return r, nil
-}
-
-// EncodeRLP encodes a transaction receipt into an RLP data dump.
-func (r *Receipt) EncodeRLP() ([]byte, error) {
- return rlp.EncodeToBytes(r.receipt)
-}
-
-// NewReceiptFromJSON parses a transaction receipt from an JSON data dump.
-func NewReceiptFromJSON(data string) (*Receipt, error) {
- r := &Receipt{
- receipt: new(types.Receipt),
- }
- if err := json.Unmarshal([]byte(data), r.receipt); err != nil {
- return nil, err
- }
- return r, nil
-}
-
-// EncodeJSON encodes a transaction receipt into an JSON data dump.
-func (r *Receipt) EncodeJSON() (string, error) {
- data, err := rlp.EncodeToBytes(r.receipt)
- return string(data), err
-}
-
-// String implements the fmt.Stringer interface to print some semi-meaningful
-// data dump of the transaction receipt for debugging purposes.
-func (r *Receipt) String() string {
- return r.receipt.String()
-}
-
-func (r *Receipt) GetPostState() []byte { return r.receipt.PostState }
-func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) }
-func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} }
-func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} }
-func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} }
-func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} }
-func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) }
diff --git a/mobile/vm.go b/mobile/vm.go
deleted file mode 100644
index daca706c31..0000000000
--- a/mobile/vm.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains all the wrappers from the core/types package.
-
-package geth
-
-import (
- "errors"
-
- "github.com/XinFinOrg/XDPoSChain/core/types"
-)
-
-// Log represents a contract log event. These events are generated by the LOG
-// opcode and stored/indexed by the node.
-type Log struct {
- log *types.Log
-}
-
-func (l *Log) GetAddress() *Address { return &Address{l.log.Address} }
-func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} }
-func (l *Log) GetData() []byte { return l.log.Data }
-func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) }
-func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} }
-func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) }
-func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} }
-func (l *Log) GetIndex() int { return int(l.log.Index) }
-
-// Logs represents a slice of VM logs.
-type Logs struct{ logs []*types.Log }
-
-// Size returns the number of logs in the slice.
-func (l *Logs) Size() int {
- return len(l.logs)
-}
-
-// Get returns the log at the given index from the slice.
-func (l *Logs) Get(index int) (log *Log, _ error) {
- if index < 0 || index >= len(l.logs) {
- return nil, errors.New("index out of bounds")
- }
- return &Log{l.logs[index]}, nil
-}
diff --git a/node/api.go b/node/api.go
index 8a36115244..362880ceaa 100644
--- a/node/api.go
+++ b/node/api.go
@@ -21,11 +21,9 @@ import (
"errors"
"fmt"
"strings"
- "time"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/metrics"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
"github.com/XinFinOrg/XDPoSChain/rpc"
@@ -60,7 +58,7 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
return true, nil
}
-// RemovePeer disconnects from a a remote node if the connection exists
+// RemovePeer disconnects from a remote node if the connection exists
func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
// Make sure the server is running, fail otherwise
server := api.node.Server()
@@ -76,6 +74,37 @@ func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
return true, nil
}
+// AddTrustedPeer allows a remote node to always connect, even if slots are full
+func (api *PrivateAdminAPI) AddTrustedPeer(url string) (bool, error) {
+ // Make sure the server is running, fail otherwise
+ server := api.node.Server()
+ if server == nil {
+ return false, ErrNodeStopped
+ }
+ node, err := discover.ParseNode(url)
+ if err != nil {
+ return false, fmt.Errorf("invalid enode: %v", err)
+ }
+ server.AddTrustedPeer(node)
+ return true, nil
+}
+
+// RemoveTrustedPeer removes a remote node from the trusted peer set, but it
+// does not disconnect it automatically.
+func (api *PrivateAdminAPI) RemoveTrustedPeer(url string) (bool, error) {
+ // Make sure the server is running, fail otherwise
+ server := api.node.Server()
+ if server == nil {
+ return false, ErrNodeStopped
+ }
+ node, err := discover.ParseNode(url)
+ if err != nil {
+ return false, fmt.Errorf("invalid enode: %v", err)
+ }
+ server.RemoveTrustedPeer(node)
+ return true, nil
+}
+
// PeerEvents creates an RPC subscription which receives peer events from the
// node's p2p.Server
func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
@@ -158,7 +187,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
}
}
- if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts); err != nil {
+ if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts, api.node.config.WSOrigins); err != nil {
return false, err
}
return true, nil
@@ -218,7 +247,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
return true, nil
}
-// StopRPC terminates an already running websocket RPC API endpoint.
+// StopWS terminates an already running websocket RPC API endpoint.
func (api *PrivateAdminAPI) StopWS() (bool, error) {
api.node.lock.Lock()
defer api.node.lock.Unlock()
@@ -267,121 +296,6 @@ func (api *PublicAdminAPI) Datadir() string {
return api.node.DataDir()
}
-// PublicDebugAPI is the collection of debugging related API methods exposed over
-// both secure and unsecure RPC channels.
-type PublicDebugAPI struct {
- node *Node // Node interfaced by this API
-}
-
-// NewPublicDebugAPI creates a new API definition for the public debug methods
-// of the node itself.
-func NewPublicDebugAPI(node *Node) *PublicDebugAPI {
- return &PublicDebugAPI{node: node}
-}
-
-// Metrics retrieves all the known system metric collected by the node.
-func (api *PublicDebugAPI) Metrics(raw bool) (map[string]interface{}, error) {
- // Create a rate formatter
- units := []string{"", "K", "M", "G", "T", "E", "P"}
- round := func(value float64, prec int) string {
- unit := 0
- for value >= 1000 {
- unit, value, prec = unit+1, value/1000, 2
- }
- return fmt.Sprintf(fmt.Sprintf("%%.%df%s", prec, units[unit]), value)
- }
- format := func(total float64, rate float64) string {
- return fmt.Sprintf("%s (%s/s)", round(total, 0), round(rate, 2))
- }
- // Iterate over all the metrics, and just dump for now
- counters := make(map[string]interface{})
- metrics.DefaultRegistry.Each(func(name string, metric interface{}) {
- // Create or retrieve the counter hierarchy for this metric
- root, parts := counters, strings.Split(name, "/")
- for _, part := range parts[:len(parts)-1] {
- if _, ok := root[part]; !ok {
- root[part] = make(map[string]interface{})
- }
- root = root[part].(map[string]interface{})
- }
- name = parts[len(parts)-1]
-
- // Fill the counter with the metric details, formatting if requested
- if raw {
- switch metric := metric.(type) {
- case metrics.Counter:
- root[name] = map[string]interface{}{
- "Overall": float64(metric.Count()),
- }
-
- case metrics.Meter:
- root[name] = map[string]interface{}{
- "AvgRate01Min": metric.Rate1(),
- "AvgRate05Min": metric.Rate5(),
- "AvgRate15Min": metric.Rate15(),
- "MeanRate": metric.RateMean(),
- "Overall": float64(metric.Count()),
- }
-
- case metrics.Timer:
- root[name] = map[string]interface{}{
- "AvgRate01Min": metric.Rate1(),
- "AvgRate05Min": metric.Rate5(),
- "AvgRate15Min": metric.Rate15(),
- "MeanRate": metric.RateMean(),
- "Overall": float64(metric.Count()),
- "Percentiles": map[string]interface{}{
- "5": metric.Percentile(0.05),
- "20": metric.Percentile(0.2),
- "50": metric.Percentile(0.5),
- "80": metric.Percentile(0.8),
- "95": metric.Percentile(0.95),
- },
- }
-
- default:
- root[name] = "Unknown metric type"
- }
- } else {
- switch metric := metric.(type) {
- case metrics.Counter:
- root[name] = map[string]interface{}{
- "Overall": float64(metric.Count()),
- }
-
- case metrics.Meter:
- root[name] = map[string]interface{}{
- "Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
- "Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
- "Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
- "Overall": format(float64(metric.Count()), metric.RateMean()),
- }
-
- case metrics.Timer:
- root[name] = map[string]interface{}{
- "Avg01Min": format(metric.Rate1()*60, metric.Rate1()),
- "Avg05Min": format(metric.Rate5()*300, metric.Rate5()),
- "Avg15Min": format(metric.Rate15()*900, metric.Rate15()),
- "Overall": format(float64(metric.Count()), metric.RateMean()),
- "Maximum": time.Duration(metric.Max()).String(),
- "Minimum": time.Duration(metric.Min()).String(),
- "Percentiles": map[string]interface{}{
- "5": time.Duration(metric.Percentile(0.05)).String(),
- "20": time.Duration(metric.Percentile(0.2)).String(),
- "50": time.Duration(metric.Percentile(0.5)).String(),
- "80": time.Duration(metric.Percentile(0.8)).String(),
- "95": time.Duration(metric.Percentile(0.95)).String(),
- },
- }
-
- default:
- root[name] = "Unknown metric type"
- }
- }
- })
- return counters, nil
-}
-
// PublicWeb3API offers helper utils
type PublicWeb3API struct {
stack *Node
diff --git a/node/config.go b/node/config.go
index 848d563e9f..355f316a19 100644
--- a/node/config.go
+++ b/node/config.go
@@ -23,7 +23,6 @@ import (
"path/filepath"
"runtime"
"strings"
- "time"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
@@ -33,6 +32,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
)
const (
@@ -89,20 +89,17 @@ type Config struct {
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
- IPCPath string `toml:",omitempty"`
+ IPCPath string
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
// field is empty, no HTTP API endpoint will be started.
- HTTPHost string `toml:",omitempty"`
+ HTTPHost string
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful
// for ephemeral nodes).
HTTPPort int `toml:",omitempty"`
- // HTTPWriteTimeout is the write timeout for the HTTP RPC server.
- HTTPWriteTimeout time.Duration `toml:",omitempty"`
-
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
// clients. Please be aware that CORS is a browser enforced security, it's fully
// useless for custom HTTP clients.
@@ -120,11 +117,15 @@ type Config struct {
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- HTTPModules []string `toml:",omitempty"`
+ HTTPModules []string
+
+ // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC
+ // interface.
+ HTTPTimeouts rpc.HTTPTimeouts
// WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started.
- WSHost string `toml:",omitempty"`
+ WSHost string
// WSPort is the TCP port number on which to start the websocket RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful for
@@ -139,7 +140,7 @@ type Config struct {
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- WSModules []string `toml:",omitempty"`
+ WSModules []string
// WSExposeAll exposes all API modules via the WebSocket RPC interface rather
// than just the public ones.
@@ -214,7 +215,7 @@ func DefaultHTTPEndpoint() string {
return config.HTTPEndpoint()
}
-// WSEndpoint resolves an websocket endpoint based on the configured host interface
+// WSEndpoint resolves a websocket endpoint based on the configured host interface
// and port parameters.
func (c *Config) WSEndpoint() string {
if c.WSHost == "" {
diff --git a/node/config_test.go b/node/config_test.go
index 8c187323b3..321a7bad6e 100644
--- a/node/config_test.go
+++ b/node/config_test.go
@@ -37,14 +37,22 @@ func TestDatadirCreation(t *testing.T) {
}
defer os.RemoveAll(dir)
- if _, err := New(&Config{DataDir: dir}); err != nil {
+ node, err := New(&Config{DataDir: dir})
+ if err != nil {
t.Fatalf("failed to create stack with existing datadir: %v", err)
}
+ if err := node.Close(); err != nil {
+ t.Fatalf("failed to close node: %v", err)
+ }
// Generate a long non-existing datadir path and check that it gets created by a node
dir = filepath.Join(dir, "a", "b", "c", "d", "e", "f")
- if _, err := New(&Config{DataDir: dir}); err != nil {
+ node, err = New(&Config{DataDir: dir})
+ if err != nil {
t.Fatalf("failed to create stack with creatable datadir: %v", err)
}
+ if err := node.Close(); err != nil {
+ t.Fatalf("failed to close node: %v", err)
+ }
if _, err := os.Stat(dir); err != nil {
t.Fatalf("freshly created datadir not accessible: %v", err)
}
@@ -56,8 +64,12 @@ func TestDatadirCreation(t *testing.T) {
defer os.Remove(file.Name())
dir = filepath.Join(file.Name(), "invalid/path")
- if _, err := New(&Config{DataDir: dir}); err == nil {
+ node, err = New(&Config{DataDir: dir})
+ if err == nil {
t.Fatalf("protocol stack created with an invalid datadir")
+ if err := node.Close(); err != nil {
+ t.Fatalf("failed to close node: %v", err)
+ }
}
}
diff --git a/node/defaults.go b/node/defaults.go
index f9f4f8f268..8baa8b6e39 100644
--- a/node/defaults.go
+++ b/node/defaults.go
@@ -21,32 +21,31 @@ import (
"os/user"
"path/filepath"
"runtime"
- "time"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/p2p/nat"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
)
const (
- DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
- DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
- DefaultHTTPWriteTimeOut = 10 * time.Second // Default write timeout for the HTTP RPC server
- DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
- DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
+ DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
+ DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
+ DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
+ DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
)
// DefaultConfig contains reasonable default settings.
var DefaultConfig = Config{
DataDir: DefaultDataDir(),
HTTPPort: DefaultHTTPPort,
- HTTPWriteTimeout: DefaultHTTPWriteTimeOut,
HTTPModules: []string{"net", "web3"},
HTTPVirtualHosts: []string{"localhost"},
+ HTTPTimeouts: rpc.DefaultHTTPTimeouts,
WSPort: DefaultWSPort,
WSModules: []string{"net", "web3"},
P2P: p2p.Config{
ListenAddr: ":30303",
- MaxPeers: 25,
+ MaxPeers: 50,
NAT: nat.Any(),
},
}
@@ -57,11 +56,20 @@ func DefaultDataDir() string {
// Try to place the data folder in the user's home dir
home := homeDir()
if home != "" {
- if runtime.GOOS == "darwin" {
+ switch runtime.GOOS {
+ case "darwin":
return filepath.Join(home, "Library", "XDCchain")
- } else if runtime.GOOS == "windows" {
- return filepath.Join(home, "AppData", "Roaming", "XDCchain")
- } else {
+ case "windows":
+ // We used to put everything in %HOME%\AppData\Roaming, but this caused
+ // problems with non-typical setups. If this fallback location exists and
+ // is non-empty, use it, otherwise DTRT and check %LOCALAPPDATA%.
+ fallback := filepath.Join(home, "AppData", "Roaming", "XDCchain")
+ appdata := windowsAppData()
+ if appdata == "" || isNonEmptyDir(fallback) {
+ return fallback
+ }
+ return filepath.Join(appdata, "XDCchain")
+ default:
return filepath.Join(home, ".XDC")
}
}
@@ -69,6 +77,27 @@ func DefaultDataDir() string {
return ""
}
+func windowsAppData() string {
+ v := os.Getenv("LOCALAPPDATA")
+ if v == "" {
+ // Windows XP and below don't have LocalAppData. Crash here because
+ // we don't support Windows XP and undefining the variable will cause
+ // other issues.
+ panic("environment variable LocalAppData is undefined")
+ }
+ return v
+}
+
+func isNonEmptyDir(dir string) bool {
+ f, err := os.Open(dir)
+ if err != nil {
+ return false
+ }
+ names, _ := f.Readdir(1)
+ f.Close()
+ return len(names) > 0
+}
+
func homeDir() string {
if home := os.Getenv("HOME"); home != "" {
return home
diff --git a/node/doc.go b/node/doc.go
index d9688e0a12..e3cc58e5f4 100644
--- a/node/doc.go
+++ b/node/doc.go
@@ -59,7 +59,7 @@ using the same data directory will store this information in different subdirect
the data directory.
LevelDB databases are also stored within the instance subdirectory. If multiple node
-instances use the same data directory, openening the databases with identical names will
+instances use the same data directory, opening the databases with identical names will
create one database for each instance.
The account key store is shared among all node instances using the same data directory
@@ -69,7 +69,7 @@ unless its location is changed through the KeyStoreDir configuration option.
Data Directory Sharing Example
In this example, two node instances named A and B are started with the same data
-directory. Mode instance A opens the database "db", node instance B opens the databases
+directory. Node instance A opens the database "db", node instance B opens the databases
"db" and "db-2". The following files will be created in the data directory:
data-directory/
@@ -84,7 +84,7 @@ directory. Mode instance A opens the database "db", node instance B opens the da
static-nodes.json -- devp2p static node list of instance B
db/ -- LevelDB content for "db"
db-2/ -- LevelDB content for "db-2"
- B.ipc -- JSON-RPC UNIX domain socket endpoint of instance A
+ B.ipc -- JSON-RPC UNIX domain socket endpoint of instance B
keystore/ -- account key store, used by both instances
*/
package node
diff --git a/node/endpoints.go b/node/endpoints.go
new file mode 100644
index 0000000000..234b49e73d
--- /dev/null
+++ b/node/endpoints.go
@@ -0,0 +1,98 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package node
+
+import (
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+)
+
+// StartHTTPEndpoint starts the HTTP RPC endpoint.
+func StartHTTPEndpoint(endpoint string, timeouts rpc.HTTPTimeouts, handler http.Handler) (*http.Server, net.Addr, error) {
+ // start the HTTP listener
+ var (
+ listener net.Listener
+ err error
+ )
+ if listener, err = net.Listen("tcp", endpoint); err != nil {
+ return nil, nil, err
+ }
+ // Bundle and start the HTTP server
+ httpSrv := &http.Server{
+ Handler: handler,
+ ReadTimeout: timeouts.ReadTimeout,
+ WriteTimeout: timeouts.WriteTimeout,
+ IdleTimeout: timeouts.IdleTimeout,
+ }
+ log.Info("StartHTTPEndpoint", "ReadTimeout", timeouts.ReadTimeout, "WriteTimeout", timeouts.WriteTimeout, "IdleTimeout", timeouts.IdleTimeout)
+ go httpSrv.Serve(listener)
+ return httpSrv, listener.Addr(), err
+}
+
+// startWSEndpoint starts a websocket endpoint.
+func startWSEndpoint(endpoint string, handler http.Handler) (*http.Server, net.Addr, error) {
+ // start the HTTP listener
+ var (
+ listener net.Listener
+ err error
+ )
+ if listener, err = net.Listen("tcp", endpoint); err != nil {
+ return nil, nil, err
+ }
+ wsSrv := &http.Server{Handler: handler}
+ go wsSrv.Serve(listener)
+ return wsSrv, listener.Addr(), err
+}
+
+// checkModuleAvailability checks that all names given in modules are actually
+// available API services. It assumes that the MetadataApi module ("rpc") is always available;
+// the registration of this "rpc" module happens in NewServer() and is thus common to all endpoints.
+func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available []string) {
+ availableSet := make(map[string]struct{})
+ for _, api := range apis {
+ if _, ok := availableSet[api.Namespace]; !ok {
+ availableSet[api.Namespace] = struct{}{}
+ available = append(available, api.Namespace)
+ }
+ }
+ for _, name := range modules {
+ if _, ok := availableSet[name]; !ok && name != rpc.MetadataApi {
+ bad = append(bad, name)
+ }
+ }
+ return bad, available
+}
+
+// CheckTimeouts ensures that timeout values are meaningful
+func CheckTimeouts(timeouts *rpc.HTTPTimeouts) {
+ if timeouts.ReadTimeout < time.Second {
+ log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", rpc.DefaultHTTPTimeouts.ReadTimeout)
+ timeouts.ReadTimeout = rpc.DefaultHTTPTimeouts.ReadTimeout
+ }
+ if timeouts.WriteTimeout < time.Second {
+ log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", rpc.DefaultHTTPTimeouts.WriteTimeout)
+ timeouts.WriteTimeout = rpc.DefaultHTTPTimeouts.WriteTimeout
+ }
+ if timeouts.IdleTimeout < time.Second {
+ log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", rpc.DefaultHTTPTimeouts.IdleTimeout)
+ timeouts.IdleTimeout = rpc.DefaultHTTPTimeouts.IdleTimeout
+ }
+}
diff --git a/node/node.go b/node/node.go
index a9f767bf28..40cef4bab3 100644
--- a/node/node.go
+++ b/node/node.go
@@ -17,9 +17,11 @@
package node
import (
+ "context"
"errors"
"fmt"
"net"
+ "net/http"
"os"
"path/filepath"
"reflect"
@@ -60,14 +62,16 @@ type Node struct {
ipcListener net.Listener // IPC RPC listener socket to serve API requests
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
- httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
- httpWhitelist []string // HTTP RPC modules to allow through this endpoint
- httpListener net.Listener // HTTP RPC listener socket to server API requests
- httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
+ httpEndpoint string // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
+ httpWhitelist []string // HTTP RPC modules to allow through this endpoint
+ httpListenerAddr net.Addr // Address of HTTP RPC listener socket serving API requests
+ httpServer *http.Server // HTTP RPC HTTP server
+ httpHandler *rpc.Server // HTTP RPC request handler to process the API requests
- wsEndpoint string // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
- wsListener net.Listener // Websocket RPC listener socket to server API requests
- wsHandler *rpc.Server // Websocket RPC request handler to process the API requests
+ wsEndpoint string // WebSocket endpoint (interface + port) to listen at (empty = WebSocket disabled)
+ wsListenerAddr net.Addr // Address of WebSocket RPC listener socket serving API requests
+ wsHTTPServer *http.Server // WebSocket RPC HTTP server
+ wsHandler *rpc.Server // WebSocket RPC request handler to process the API requests
stop chan struct{} // Channel to wait for termination notifications
lock sync.RWMutex
@@ -127,6 +131,29 @@ func New(conf *Config) (*Node, error) {
}, nil
}
+// Close stops the Node and releases resources acquired in
+// Node constructor New.
+func (n *Node) Close() error {
+ var errs []error
+
+ // Terminate all subsystems and collect any errors
+ if err := n.Stop(); err != nil && err != ErrNodeStopped {
+ errs = append(errs, err)
+ }
+ if err := n.accman.Close(); err != nil {
+ errs = append(errs, err)
+ }
+ // Report any errors that might have occurred
+ switch len(errs) {
+ case 0:
+ return nil
+ case 1:
+ return errs[0]
+ default:
+ return fmt.Errorf("%v", errs)
+ }
+}
+
// Register injects a new service into the node's stack. The service created by
// the passed constructor must be unique in its type with regard to sibling ones.
func (n *Node) Register(constructor ServiceConstructor) error {
@@ -140,7 +167,7 @@ func (n *Node) Register(constructor ServiceConstructor) error {
return nil
}
-// Start create a live P2P node and starts running it.
+// Start creates a live P2P node and starts running it.
func (n *Node) Start() error {
n.lock.Lock()
defer n.lock.Unlock()
@@ -203,7 +230,7 @@ func (n *Node) Start() error {
return convertFileLockError(err)
}
// Start each of the services
- started := []reflect.Type{}
+ var started []reflect.Type
for kind, service := range services {
// Start the next service, stopping all previous upon failure
if err := service.Start(running); err != nil {
@@ -217,7 +244,7 @@ func (n *Node) Start() error {
// Mark the service started for potential cleanup
started = append(started, kind)
}
- // Lastly start the configured RPC interfaces
+ // Lastly, start the configured RPC interfaces
if err := n.startRPC(services); err != nil {
for _, service := range services {
service.Stop()
@@ -252,7 +279,7 @@ func (n *Node) openDataDir() error {
return nil
}
-// startRPC is a helper method to start all the various RPC endpoint during node
+// startRPC is a helper method to start all the various RPC endpoints during node
// startup. It's not meant to be called at any time afterwards as it makes certain
// assumptions about the state of the node.
func (n *Node) startRPC(services map[reflect.Type]Service) error {
@@ -269,17 +296,21 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
n.stopInProc()
return err
}
- if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil {
+ if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts, n.config.WSOrigins); err != nil {
n.stopIPC()
n.stopInProc()
return err
}
- if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
- n.stopHTTP()
- n.stopIPC()
- n.stopInProc()
- return err
+ // if endpoints are not the same, start separate servers
+ if n.httpEndpoint != n.wsEndpoint {
+ if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
+ n.stopHTTP()
+ n.stopIPC()
+ n.stopInProc()
+ return err
+ }
}
+
// All API endpoints started successfully
n.rpcAPIs = apis
return nil
@@ -293,7 +324,7 @@ func (n *Node) startInProc(apis []rpc.API) error {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return err
}
- n.log.Debug("InProc registered", "service", api.Service, "namespace", api.Namespace)
+ n.log.Debug("InProc registered", "namespace", api.Namespace)
}
n.inprocHandler = handler
return nil
@@ -320,51 +351,16 @@ func (n *Node) RegisterAPIs(apis []rpc.API) {
// startIPC initializes and starts the IPC RPC endpoint.
func (n *Node) startIPC(apis []rpc.API) error {
- // Short circuit if the IPC endpoint isn't being exposed
if n.ipcEndpoint == "" {
- return nil
+ return nil // IPC disabled.
}
- // Register all the APIs exposed by the services
- handler := rpc.NewServer()
- for _, api := range apis {
- if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
- return err
- }
- n.log.Debug("IPC registered", "service", api.Service, "namespace", api.Namespace)
- }
- // All APIs registered, start the IPC listener
- var (
- listener net.Listener
- err error
- )
- if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil {
+ listener, handler, err := rpc.StartIPCEndpoint(n.ipcEndpoint, apis)
+ if err != nil {
return err
}
- go func() {
- n.log.Info("IPC endpoint opened", "url", n.ipcEndpoint)
-
- for {
- conn, err := listener.Accept()
- if err != nil {
- // Terminate if the listener was closed
- n.lock.RLock()
- closed := n.ipcListener == nil
- n.lock.RUnlock()
- if closed {
- return
- }
- // Not closed, just some error; report and continue
- n.log.Error("IPC accept failed", "err", err)
- continue
- }
- log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr())
- go handler.ServeCodec(rpc.NewCodec(conn), 0)
- }
- }()
- // All listeners booted successfully
n.ipcListener = listener
n.ipcHandler = handler
-
+ log.Info("IPC endpoint opened", "url", n.ipcEndpoint)
return nil
}
@@ -374,7 +370,7 @@ func (n *Node) stopIPC() {
n.ipcListener.Close()
n.ipcListener = nil
- n.log.Info("IPC endpoint closed", "endpoint", n.ipcEndpoint)
+ n.log.Info("IPC endpoint closed", "url", n.ipcEndpoint)
}
if n.ipcHandler != nil {
n.ipcHandler.Stop()
@@ -383,51 +379,47 @@ func (n *Node) stopIPC() {
}
// startHTTP initializes and starts the HTTP RPC endpoint.
-func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error {
+func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts, wsOrigins []string) error {
// Short circuit if the HTTP endpoint isn't being exposed
if endpoint == "" {
return nil
}
- // Generate the whitelist based on the allowed modules
- whitelist := make(map[string]bool)
- for _, module := range modules {
- whitelist[module] = true
- }
- // Register all the APIs exposed by the services
- handler := rpc.NewServer()
- for _, api := range apis {
- if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
- if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
- return err
- }
- n.log.Debug("HTTP registered", "service", api.Service, "namespace", api.Namespace)
- }
- }
- // All APIs registered, start the HTTP listener
- var (
- listener net.Listener
- err error
- )
- if listener, err = net.Listen("tcp", endpoint); err != nil {
+ // register apis and create handler stack
+ srv := rpc.NewServer()
+ err := RegisterApisFromWhitelist(apis, modules, srv, false)
+ if err != nil {
return err
}
- go rpc.NewHTTPServer(cors, vhosts, handler, n.config.HTTPWriteTimeout).Serve(listener)
- n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ","))
+ handler := NewHTTPHandlerStack(srv, cors, vhosts, &timeouts)
+ // wrap handler in WebSocket handler only if WebSocket port is the same as http rpc
+ if n.httpEndpoint == n.wsEndpoint {
+ handler = NewWebsocketUpgradeHandler(handler, srv.WebsocketHandler(wsOrigins))
+ }
+ httpServer, addr, err := StartHTTPEndpoint(endpoint, timeouts, handler)
+ if err != nil {
+ return err
+ }
+ n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%v/", addr),
+ "cors", strings.Join(cors, ","),
+ "vhosts", strings.Join(vhosts, ","))
+ if n.httpEndpoint == n.wsEndpoint {
+ n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
+ }
// All listeners booted successfully
n.httpEndpoint = endpoint
- n.httpListener = listener
- n.httpHandler = handler
+ n.httpListenerAddr = addr
+ n.httpServer = httpServer
+ n.httpHandler = srv
return nil
}
// stopHTTP terminates the HTTP RPC endpoint.
func (n *Node) stopHTTP() {
- if n.httpListener != nil {
- n.httpListener.Close()
- n.httpListener = nil
-
- n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%s", n.httpEndpoint))
+ if n.httpServer != nil {
+ // Don't bother imposing a timeout here.
+ n.httpServer.Shutdown(context.Background())
+ n.log.Info("HTTP endpoint closed", "url", fmt.Sprintf("http://%v/", n.httpListenerAddr))
}
if n.httpHandler != nil {
n.httpHandler.Stop()
@@ -435,53 +427,39 @@ func (n *Node) stopHTTP() {
}
}
-// startWS initializes and starts the websocket RPC endpoint.
+// startWS initializes and starts the WebSocket RPC endpoint.
func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool) error {
// Short circuit if the WS endpoint isn't being exposed
if endpoint == "" {
return nil
}
- // Generate the whitelist based on the allowed modules
- whitelist := make(map[string]bool)
- for _, module := range modules {
- whitelist[module] = true
- }
- // Register all the APIs exposed by the services
- handler := rpc.NewServer()
- for _, api := range apis {
- if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
- if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
- return err
- }
- n.log.Debug("WebSocket registered", "service", api.Service, "namespace", api.Namespace)
- }
- }
- // All APIs registered, start the HTTP listener
- var (
- listener net.Listener
- err error
- )
- if listener, err = net.Listen("tcp", endpoint); err != nil {
+
+ srv := rpc.NewServer()
+ handler := srv.WebsocketHandler(wsOrigins)
+ err := RegisterApisFromWhitelist(apis, modules, srv, exposeAll)
+ if err != nil {
return err
}
- go rpc.NewWSServer(wsOrigins, handler).Serve(listener)
- n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%s", listener.Addr()))
-
+ httpServer, addr, err := startWSEndpoint(endpoint, handler)
+ if err != nil {
+ return err
+ }
+ n.log.Info("WebSocket endpoint opened", "url", fmt.Sprintf("ws://%v", addr))
// All listeners booted successfully
n.wsEndpoint = endpoint
- n.wsListener = listener
- n.wsHandler = handler
+ n.wsListenerAddr = addr
+ n.wsHTTPServer = httpServer
+ n.wsHandler = srv
return nil
}
-// stopWS terminates the websocket RPC endpoint.
+// stopWS terminates the WebSocket RPC endpoint.
func (n *Node) stopWS() {
- if n.wsListener != nil {
- n.wsListener.Close()
- n.wsListener = nil
-
- n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%s", n.wsEndpoint))
+ if n.wsHTTPServer != nil {
+ // Don't bother imposing a timeout here.
+ n.wsHTTPServer.Shutdown(context.Background())
+ n.log.Info("WebSocket endpoint closed", "url", fmt.Sprintf("ws://%v", n.wsListenerAddr))
}
if n.wsHandler != nil {
n.wsHandler.Stop()
@@ -645,11 +623,23 @@ func (n *Node) IPCEndpoint() string {
// HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack.
func (n *Node) HTTPEndpoint() string {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+
+ if n.httpListenerAddr != nil {
+ return n.httpListenerAddr.String()
+ }
return n.httpEndpoint
}
// WSEndpoint retrieves the current WS endpoint used by the protocol stack.
func (n *Node) WSEndpoint() string {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+
+ if n.wsListenerAddr != nil {
+ return n.wsListenerAddr.String()
+ }
return n.wsEndpoint
}
@@ -690,11 +680,6 @@ func (n *Node) apis() []rpc.API {
Namespace: "debug",
Version: "1.0",
Service: debug.Handler,
- }, {
- Namespace: "debug",
- Version: "1.0",
- Service: NewPublicDebugAPI(n),
- Public: true,
}, {
Namespace: "web3",
Version: "1.0",
@@ -703,3 +688,25 @@ func (n *Node) apis() []rpc.API {
},
}
}
+
+// RegisterApisFromWhitelist checks the given modules' availability, generates a whitelist based on the allowed modules,
+// and then registers all of the APIs exposed by the services.
+func RegisterApisFromWhitelist(apis []rpc.API, modules []string, srv *rpc.Server, exposeAll bool) error {
+ if bad, available := checkModuleAvailability(modules, apis); len(bad) > 0 {
+ log.Error("Unavailable modules in HTTP API list", "unavailable", bad, "available", available)
+ }
+ // Generate the whitelist based on the allowed modules
+ whitelist := make(map[string]bool)
+ for _, module := range modules {
+ whitelist[module] = true
+ }
+ // Register all the APIs exposed by the services
+ for _, api := range apis {
+ if exposeAll || whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
+ if err := srv.RegisterName(api.Namespace, api.Service); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/node/node_example_test.go b/node/node_example_test.go
index f882673718..b2ad1a8ed3 100644
--- a/node/node_example_test.go
+++ b/node/node_example_test.go
@@ -29,10 +29,10 @@ import (
// life cycle management.
//
// The following methods are needed to implement a node.Service:
-// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
-// - APIs() []rpc.API - api methods the service wants to expose on rpc channels
-// - Start() error - method invoked when the node is ready to start the service
-// - Stop() error - method invoked when the node terminates the service
+// - Protocols() []p2p.Protocol - devp2p protocols the service can communicate on
+// - APIs() []rpc.API - api methods the service wants to expose on rpc channels
+// - Start() error - method invoked when the node is ready to start the service
+// - Stop() error - method invoked when the node terminates the service
type SampleService struct{}
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
@@ -47,6 +47,8 @@ func ExampleService() {
if err != nil {
log.Fatalf("Failed to create network node: %v", err)
}
+ defer stack.Close()
+
// Create and register a simple network service. This is done through the definition
// of a node.ServiceConstructor that will instantiate a node.Service. The reason for
// the factory method approach is to support service restarts without relying on the
diff --git a/node/node_test.go b/node/node_test.go
index 2bff5a68cc..b733ea5174 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -18,6 +18,7 @@ package node
import (
"errors"
+ "net/http"
"os"
"reflect"
"testing"
@@ -26,6 +27,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/rpc"
+ "github.com/stretchr/testify/assert"
)
var (
@@ -45,6 +47,8 @@ func TestNodeLifeCycle(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Ensure that a stopped node can be stopped again
for i := 0; i < 3; i++ {
if err := stack.Stop(); err != ErrNodeStopped {
@@ -56,7 +60,7 @@ func TestNodeLifeCycle(t *testing.T) {
t.Fatalf("failed to start node: %v", err)
}
if err := stack.Start(); err != ErrNodeRunning {
- t.Fatalf("start failure mismatch: have %v, want %v ", err, ErrNodeRunning)
+ t.Fatalf("start failure mismatch: have %v, want %v", err, ErrNodeRunning)
}
// Ensure that a node can be restarted arbitrarily many times
for i := 0; i < 3; i++ {
@@ -69,7 +73,7 @@ func TestNodeLifeCycle(t *testing.T) {
t.Fatalf("failed to stop node: %v", err)
}
if err := stack.Stop(); err != ErrNodeStopped {
- t.Fatalf("stop failure mismatch: have %v, want %v ", err, ErrNodeStopped)
+ t.Fatalf("stop failure mismatch: have %v, want %v", err, ErrNodeStopped)
}
}
@@ -87,6 +91,8 @@ func TestNodeUsedDataDir(t *testing.T) {
if err != nil {
t.Fatalf("failed to create original protocol stack: %v", err)
}
+ defer original.Close()
+
if err := original.Start(); err != nil {
t.Fatalf("failed to start original protocol stack: %v", err)
}
@@ -97,6 +103,8 @@ func TestNodeUsedDataDir(t *testing.T) {
if err != nil {
t.Fatalf("failed to create duplicate protocol stack: %v", err)
}
+ defer duplicate.Close()
+
if err := duplicate.Start(); err != ErrDatadirUsed {
t.Fatalf("duplicate datadir failure mismatch: have %v, want %v", err, ErrDatadirUsed)
}
@@ -108,6 +116,8 @@ func TestServiceRegistry(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of unique services and ensure they start successfully
services := []ServiceConstructor{NewNoopServiceA, NewNoopServiceB, NewNoopServiceC}
for i, constructor := range services {
@@ -140,6 +150,8 @@ func TestServiceLifeCycle(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of life-cycle instrumented services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
@@ -190,6 +202,8 @@ func TestServiceRestarts(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Define a service that does not support restarts
var (
running bool
@@ -238,6 +252,8 @@ func TestServiceConstructionAbortion(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Define a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
@@ -285,6 +301,8 @@ func TestServiceStartupAbortion(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
@@ -332,12 +350,14 @@ func TestServiceStartupAbortion(t *testing.T) {
}
// Tests that even if a registered service fails to shut down cleanly, it does
-// not influece the rest of the shutdown invocations.
+// not influence the rest of the shutdown invocations.
func TestServiceTerminationGuarantee(t *testing.T) {
stack, err := New(testNodeConfig())
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of good services
services := map[string]InstrumentingWrapper{
"A": InstrumentedServiceMakerA,
@@ -413,6 +433,8 @@ func TestServiceRetrieval(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
if err := stack.Register(NewNoopService); err != nil {
t.Fatalf("noop service registration failed: %v", err)
}
@@ -448,6 +470,8 @@ func TestProtocolGather(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of services with some configured number of protocols
services := map[string]struct {
Count int
@@ -504,10 +528,12 @@ func TestAPIGather(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
+
// Register a batch of services with some configured APIs
calls := make(chan string, 1)
- makeAPI := func(result string) *OneMethodApi {
- return &OneMethodApi{fun: func() { calls <- result }}
+ makeAPI := func(result string) *OneMethodAPI {
+ return &OneMethodAPI{fun: func() { calls <- result }}
}
services := map[string]struct {
APIs []rpc.API
@@ -572,3 +598,59 @@ func TestAPIGather(t *testing.T) {
}
}
}
+
+func TestWebsocketHTTPOnSamePort_WebsocketRequest(t *testing.T) {
+ node := startHTTP(t)
+ defer node.stopHTTP()
+
+ wsReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:7453", nil)
+ if err != nil {
+ t.Error("could not issue new http request ", err)
+ }
+ wsReq.Header.Set("Connection", "upgrade")
+ wsReq.Header.Set("Upgrade", "websocket")
+ wsReq.Header.Set("Sec-WebSocket-Version", "13")
+ wsReq.Header.Set("Sec-Websocket-Key", "SGVsbG8sIHdvcmxkIQ==")
+
+ resp := doHTTPRequest(t, wsReq)
+ assert.Equal(t, "websocket", resp.Header.Get("Upgrade"))
+}
+
+func TestWebsocketHTTPOnSamePort_HTTPRequest(t *testing.T) {
+ node := startHTTP(t)
+ defer node.stopHTTP()
+
+ httpReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:7453", nil)
+ if err != nil {
+ t.Error("could not issue new http request ", err)
+ }
+ httpReq.Header.Set("Accept-Encoding", "gzip")
+
+ resp := doHTTPRequest(t, httpReq)
+ assert.Equal(t, "gzip", resp.Header.Get("Content-Encoding"))
+}
+
+func startHTTP(t *testing.T) *Node {
+ conf := &Config{HTTPPort: 7453, WSPort: 7453}
+ node, err := New(conf)
+ if err != nil {
+ t.Error("could not create a new node ", err)
+ }
+
+ err = node.startHTTP("127.0.0.1:7453", []rpc.API{}, []string{}, []string{}, []string{}, rpc.HTTPTimeouts{}, []string{})
+ if err != nil {
+ t.Error("could not start http service on node ", err)
+ }
+
+ return node
+}
+
+func doHTTPRequest(t *testing.T, req *http.Request) *http.Response {
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ t.Error("could not issue a GET request to the given endpoint", err)
+ }
+ t.Cleanup(func() { resp.Body.Close() })
+ return resp
+}
diff --git a/node/rpcstack.go b/node/rpcstack.go
new file mode 100644
index 0000000000..2de884f4c3
--- /dev/null
+++ b/node/rpcstack.go
@@ -0,0 +1,170 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package node
+
+import (
+ "compress/gzip"
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/XinFinOrg/XDPoSChain/log"
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+ "github.com/rs/cors"
+)
+
+// NewHTTPHandlerStack returns wrapped http-related handlers
+func NewHTTPHandlerStack(srv http.Handler, cors []string, vhosts []string, timeouts *rpc.HTTPTimeouts) http.Handler {
+ // Wrap the CORS-handler within a host-handler
+ handler := newCorsHandler(srv, cors)
+ handler = newVHostHandler(vhosts, handler)
+ handler = newGzipHandler(handler)
+
+ // make sure timeout values are meaningful
+ CheckTimeouts(timeouts)
+
+ // PR #469: register timeout handler before WebSocket and HTTP
+ handler = http.TimeoutHandler(handler, timeouts.WriteTimeout, `{"error":"http server timeout"}`)
+
+ // add 1 second to let TimeoutHandler works first
+ timeouts.WriteTimeout = timeouts.WriteTimeout + time.Second
+ return handler
+}
+
+func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
+ // disable CORS support if user has not specified a custom CORS configuration
+ if len(allowedOrigins) == 0 {
+ return srv
+ }
+ c := cors.New(cors.Options{
+ AllowedOrigins: allowedOrigins,
+ AllowedMethods: []string{http.MethodPost, http.MethodGet},
+ MaxAge: 600,
+ AllowedHeaders: []string{"*"},
+ })
+ return c.Handler(srv)
+}
+
+// virtualHostHandler is a handler which validates the Host-header of incoming requests.
+// Using virtual hosts can help prevent DNS rebinding attacks, where a 'random' domain name points to
+// the service ip address (but without CORS headers). By verifying the targeted virtual host, we can
+// ensure that it's a destination that the node operator has defined.
+type virtualHostHandler struct {
+ vhosts map[string]struct{}
+ next http.Handler
+}
+
+func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
+ vhostMap := make(map[string]struct{})
+ for _, allowedHost := range vhosts {
+ vhostMap[strings.ToLower(allowedHost)] = struct{}{}
+ }
+ return &virtualHostHandler{vhostMap, next}
+}
+
+// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
+func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // if r.Host is not set, we can continue serving since a browser would set the Host header
+ if r.Host == "" {
+ h.next.ServeHTTP(w, r)
+ return
+ }
+ host, _, err := net.SplitHostPort(r.Host)
+ if err != nil {
+ // Either invalid (too many colons) or no port specified
+ host = r.Host
+ }
+ if ipAddr := net.ParseIP(host); ipAddr != nil {
+ // It's an IP address, we can serve that
+ h.next.ServeHTTP(w, r)
+ return
+
+ }
+ // Not an IP address, but a hostname. Need to validate
+ if _, exist := h.vhosts["*"]; exist {
+ h.next.ServeHTTP(w, r)
+ return
+ }
+ if _, exist := h.vhosts[host]; exist {
+ h.next.ServeHTTP(w, r)
+ return
+ }
+ http.Error(w, "invalid host specified", http.StatusForbidden)
+}
+
+var gzPool = sync.Pool{
+ New: func() interface{} {
+ w := gzip.NewWriter(io.Discard)
+ return w
+ },
+}
+
+type gzipResponseWriter struct {
+ io.Writer
+ http.ResponseWriter
+}
+
+func (w *gzipResponseWriter) WriteHeader(status int) {
+ w.Header().Del("Content-Length")
+ w.ResponseWriter.WriteHeader(status)
+}
+
+func (w *gzipResponseWriter) Write(b []byte) (int, error) {
+ return w.Writer.Write(b)
+}
+
+func newGzipHandler(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
+ next.ServeHTTP(w, r)
+ return
+ }
+
+ w.Header().Set("Content-Encoding", "gzip")
+
+ gz := gzPool.Get().(*gzip.Writer)
+ defer gzPool.Put(gz)
+
+ gz.Reset(w)
+ defer gz.Close()
+
+ next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r)
+ })
+}
+
+// NewWebsocketUpgradeHandler returns a websocket handler that serves an incoming request only if it contains an upgrade
+// request to the websocket protocol. If not, serves the the request with the http handler.
+func NewWebsocketUpgradeHandler(h http.Handler, ws http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if isWebsocket(r) {
+ ws.ServeHTTP(w, r)
+ log.Debug("serving websocket request")
+ return
+ }
+
+ h.ServeHTTP(w, r)
+ })
+}
+
+// isWebsocket checks the header of an http request for a websocket upgrade request.
+func isWebsocket(r *http.Request) bool {
+ return strings.ToLower(r.Header.Get("Upgrade")) == "websocket" &&
+ strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
+}
diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go
new file mode 100644
index 0000000000..38f486e52a
--- /dev/null
+++ b/node/rpcstack_test.go
@@ -0,0 +1,53 @@
+package node
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/rpc"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) {
+ srv := rpc.NewServer()
+
+ handler := NewWebsocketUpgradeHandler(nil, srv.WebsocketHandler([]string{}))
+ ts := httptest.NewServer(handler)
+ defer ts.Close()
+
+ responses := make(chan *http.Response)
+ go func(responses chan *http.Response) {
+ client := &http.Client{}
+
+ req, _ := http.NewRequest(http.MethodGet, ts.URL, nil)
+ req.Header.Set("Connection", "upgrade")
+ req.Header.Set("Upgrade", "websocket")
+ req.Header.Set("Sec-WebSocket-Version", "13")
+ req.Header.Set("Sec-Websocket-Key", "SGVsbG8sIHdvcmxkIQ==")
+
+ resp, err := client.Do(req)
+ if err != nil {
+ t.Error("could not issue a GET request to the test http server", err)
+ }
+ responses <- resp
+ }(responses)
+
+ response := <-responses
+ assert.Equal(t, "websocket", response.Header.Get("Upgrade"))
+}
+
+// TestIsWebsocket tests if an incoming websocket upgrade request is handled properly.
+func TestIsWebsocket(t *testing.T) {
+ r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+ assert.False(t, isWebsocket(r))
+ r.Header.Set("upgrade", "websocket")
+ assert.False(t, isWebsocket(r))
+ r.Header.Set("connection", "upgrade")
+ assert.True(t, isWebsocket(r))
+ r.Header.Set("connection", "upgrade,keep-alive")
+ assert.True(t, isWebsocket(r))
+ r.Header.Set("connection", " UPGRADE,keep-alive")
+ assert.True(t, isWebsocket(r))
+}
diff --git a/node/service_test.go b/node/service_test.go
index 23dc807084..1c275e2ecb 100644
--- a/node/service_test.go
+++ b/node/service_test.go
@@ -67,6 +67,7 @@ func TestContextServices(t *testing.T) {
if err != nil {
t.Fatalf("failed to create protocol stack: %v", err)
}
+ defer stack.Close()
// Define a verifier that ensures a NoopA is before it and NoopB after
verifier := func(ctx *ServiceContext) (Service, error) {
var objA *NoopServiceA
diff --git a/node/utils_test.go b/node/utils_test.go
index 034b92d03a..d6aff608e0 100644
--- a/node/utils_test.go
+++ b/node/utils_test.go
@@ -123,12 +123,12 @@ func InstrumentedServiceMakerC(base ServiceConstructor) ServiceConstructor {
return InstrumentingWrapperMaker(base, reflect.TypeOf(InstrumentedServiceC{}))
}
-// OneMethodApi is a single-method API handler to be returned by test services.
-type OneMethodApi struct {
+// OneMethodAPI is a single-method API handler to be returned by test services.
+type OneMethodAPI struct {
fun func()
}
-func (api *OneMethodApi) TheOneMethod() {
+func (api *OneMethodAPI) TheOneMethod() {
if api.fun != nil {
api.fun()
}
diff --git a/p2p/discover/database_test.go b/p2p/discover/database_test.go
index 6f452a060c..d881c65337 100644
--- a/p2p/discover/database_test.go
+++ b/p2p/discover/database_test.go
@@ -56,14 +56,14 @@ var nodeDBKeyTests = []struct {
func TestNodeDBKeys(t *testing.T) {
for i, tt := range nodeDBKeyTests {
if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
- t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
+ t.Errorf("make test %d: key mismatch: have %#x, want %#x", i, key, tt.key)
}
id, field := splitKey(tt.key)
if !bytes.Equal(id[:], tt.id[:]) {
- t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id)
+ t.Errorf("split test %d: id mismatch: have %#x, want %#x", i, id, tt.id)
}
if field != tt.field {
- t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field)
+ t.Errorf("split test %d: field mismatch: have %#x, want %#x", i, field, tt.field)
}
}
}
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index dff614c1d6..b930cdf0b8 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -23,6 +23,7 @@
package discover
import (
+ "context"
crand "crypto/rand"
"encoding/binary"
"errors"
@@ -72,7 +73,10 @@ type Table struct {
rand *mrand.Rand // source of randomness, periodically reseeded
ips netutil.DistinctNetSet
- db *nodeDB // database of known nodes
+ db *nodeDB // database of known nodes
+ log log.Logger
+
+ // loop channels
refreshReq chan chan struct{}
initDone chan struct{}
closeReq chan struct{}
@@ -118,9 +122,11 @@ func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string
if err != nil {
return nil, err
}
+
tab := &Table{
net: t,
db: db,
+ log: log.Root(),
self: NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port)),
bonding: make(map[NodeID]*bondproc),
bondslots: make(chan struct{}, maxBondingPingPongs),
@@ -322,10 +328,10 @@ func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool) []*Node {
// Bump the failure counter to detect and evacuate non-bonded entries
fails := tab.db.findFails(n.ID) + 1
tab.db.updateFindFails(n.ID, fails)
- log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)
+ tab.log.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)
if fails >= maxFindnodeFailures {
- log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
+ tab.log.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
tab.delete(n)
}
}
@@ -455,8 +461,10 @@ func (tab *Table) loadSeedNodes(bond bool) {
}
for i := range seeds {
seed := seeds[i]
- age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }}
- log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
+ if tab.log.Enabled(context.Background(), log.LevelTrace) {
+ age := time.Since(tab.db.bondTime(seed.ID))
+ tab.log.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
+ }
tab.add(seed)
}
}
@@ -480,16 +488,16 @@ func (tab *Table) doRevalidate(done chan<- struct{}) {
b := tab.buckets[bi]
if err == nil {
// The node responded, move it to the front.
- log.Debug("Revalidated node", "b", bi, "id", last.ID)
+ tab.log.Debug("Revalidated node", "b", bi, "id", last.ID)
b.bump(last)
return
}
// No reply received, pick a replacement or delete the node if there aren't
// any replacements.
if r := tab.replace(b, last); r != nil {
- log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
+ tab.log.Debug("Replaced dead node", "b", bi, "id", last.ID, "ip", last.IP, "r", r.ID, "rip", r.IP)
} else {
- log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
+ tab.log.Debug("Removed dead node", "b", bi, "id", last.ID, "ip", last.IP)
}
}
@@ -599,7 +607,7 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16
age := time.Since(tab.db.bondTime(id))
var result error
if fails > 0 || age > nodeDBNodeExpiration {
- log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age)
+ tab.log.Trace("Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age)
tab.bondmu.Lock()
w := tab.bonding[id]
@@ -724,11 +732,11 @@ func (tab *Table) addIP(b *bucket, ip net.IP) bool {
return true
}
if !tab.ips.Add(ip) {
- log.Debug("IP exceeds table limit", "ip", ip)
+ tab.log.Debug("IP exceeds table limit", "ip", ip)
return false
}
if !b.ips.Add(ip) {
- log.Debug("IP exceeds bucket limit", "ip", ip)
+ tab.log.Debug("IP exceeds bucket limit", "ip", ip)
tab.ips.Remove(ip)
return false
}
diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go
index 701747bb04..bb541ab07a 100644
--- a/p2p/discover/udp.go
+++ b/p2p/discover/udp.go
@@ -228,6 +228,9 @@ type Config struct {
NetRestrict *netutil.Netlist // network whitelist
Bootnodes []*Node // list of bootstrap nodes
Unhandled chan<- ReadPacket // unhandled packets are sent on this channel
+
+ // The options below are useful in very specific cases, like in unit tests.
+ Log log.Logger // if set, log messages go here
}
// ListenUDP returns a new table that listens for UDP packets on laddr.
diff --git a/p2p/discv5/database_test.go b/p2p/discv5/database_test.go
index ff0bc1a174..73ee613a34 100644
--- a/p2p/discv5/database_test.go
+++ b/p2p/discv5/database_test.go
@@ -56,14 +56,14 @@ var nodeDBKeyTests = []struct {
func TestNodeDBKeys(t *testing.T) {
for i, tt := range nodeDBKeyTests {
if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
- t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
+ t.Errorf("make test %d: key mismatch: have %#x, want %#x", i, key, tt.key)
}
id, field := splitKey(tt.key)
if !bytes.Equal(id[:], tt.id[:]) {
- t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id)
+ t.Errorf("split test %d: id mismatch: have %#x, want %#x", i, id, tt.id)
}
if field != tt.field {
- t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field)
+ t.Errorf("split test %d: field mismatch: have %#x, want %#x", i, field, tt.field)
}
}
}
diff --git a/p2p/discv5/net.go b/p2p/discv5/net.go
index 097771553d..13e9cca70e 100644
--- a/p2p/discv5/net.go
+++ b/p2p/discv5/net.go
@@ -27,10 +27,10 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/mclock"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/p2p/netutil"
"github.com/XinFinOrg/XDPoSChain/rlp"
+ "golang.org/x/crypto/sha3"
)
var (
@@ -420,7 +420,6 @@ loop:
// Ingress packet handling.
case pkt := <-net.read:
- //fmt.Println("read", pkt.ev)
log.Trace("<-net.read")
n := net.internNode(&pkt)
prestate := n.state
@@ -428,10 +427,10 @@ loop:
if err := net.handle(n, pkt.ev, &pkt); err != nil {
status = err.Error()
}
- log.Trace("", "msg", log.Lazy{Fn: func() string {
- return fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)",
- net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status)
- }})
+ if log.Enabled(log.LevelTrace) {
+ msg := fmt.Sprintf("<<< (%d) %v from %x@%v: %v -> %v (%v)", net.tab.count, pkt.ev, pkt.remoteID[:8], pkt.remoteAddr, prestate, n.state, status)
+ log.Trace("", "msg", msg)
+ }
// TODO: persist state if n.state goes >= known, delete if it goes <= known
// State transition timeouts.
@@ -447,10 +446,10 @@ loop:
if err := net.handle(timeout.node, timeout.ev, nil); err != nil {
status = err.Error()
}
- log.Trace("", "msg", log.Lazy{Fn: func() string {
- return fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)",
- net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status)
- }})
+ if log.Enabled(log.LevelTrace) {
+ msg := fmt.Sprintf("--- (%d) %v for %x@%v: %v -> %v (%v)", net.tab.count, timeout.ev, timeout.node.ID[:8], timeout.node.addr(), prestate, timeout.node.state, status)
+ log.Trace("", "msg", msg)
+ }
// Querying.
case q := <-net.queryReq:
@@ -648,7 +647,7 @@ loop:
}
log.Trace("loop stopped")
- log.Debug(fmt.Sprintf("shutting down"))
+ log.Debug("shutting down")
if net.conn != nil {
net.conn.Close()
}
@@ -683,15 +682,15 @@ func (net *Network) refresh(done chan<- struct{}) {
return
}
for _, n := range seeds {
- log.Debug("", "msg", log.Lazy{Fn: func() string {
+ if log.Enabled(log.LevelDebug) {
var age string
if net.db != nil {
age = time.Since(net.db.lastPong(n.ID)).String()
} else {
age = "unknown"
}
- return fmt.Sprintf("seed node (age %s): %v", age, n)
- }})
+ log.Debug("", "msg", fmt.Sprintf("seed node (age %s): %v", age, n))
+ }
n = net.internNodeFromDB(n)
if n.state == unknown {
net.transition(n, verifyinit)
@@ -1230,14 +1229,14 @@ func (net *Network) checkTopicRegister(data *topicRegister) (*pong, error) {
if rlpHash(data.Topics) != pongpkt.data.(*pong).TopicHash {
return nil, errors.New("topic hash mismatch")
}
- if data.Idx < 0 || int(data.Idx) >= len(data.Topics) {
+ if int(data.Idx) >= len(data.Topics) {
return nil, errors.New("topic index out of range")
}
return pongpkt.data.(*pong), nil
}
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewKeccak256()
+ hw := sha3.NewLegacyKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
diff --git a/p2p/discv5/nodeevent_string.go b/p2p/discv5/nodeevent_string.go
index eb696fb8be..d7c883aeb0 100644
--- a/p2p/discv5/nodeevent_string.go
+++ b/p2p/discv5/nodeevent_string.go
@@ -16,7 +16,7 @@ var (
func (i nodeEvent) String() string {
switch {
- case 0 <= i && i <= 8:
+ case i <= 8:
return _nodeEvent_name_0[_nodeEvent_index_0[i]:_nodeEvent_index_0[i+1]]
case 265 <= i && i <= 267:
i -= 265
diff --git a/p2p/discv5/ntp.go b/p2p/discv5/ntp.go
index 411b4d0485..a6e279a3cc 100644
--- a/p2p/discv5/ntp.go
+++ b/p2p/discv5/ntp.go
@@ -51,7 +51,7 @@ func checkClockDrift() {
}
if drift < -driftThreshold || drift > driftThreshold {
warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift)
- howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings")
+ howtofix := "Please enable network time synchronisation in system settings"
separator := strings.Repeat("-", len(warning))
log.Warn(separator)
diff --git a/p2p/discv5/ticket.go b/p2p/discv5/ticket.go
index 2a6d40e244..4780e8b866 100644
--- a/p2p/discv5/ticket.go
+++ b/p2p/discv5/ticket.go
@@ -441,7 +441,7 @@ func (s *ticketStore) removeTicketRef(ref ticketRef) {
}
}
if idx == -1 {
- panic(nil)
+ panic("ticketStore removeTicketRef: idx == -1")
}
list = append(list[:idx], list[idx+1:]...)
if len(list) != 0 {
@@ -802,7 +802,7 @@ func (r *topicRadius) chooseLookupBucket(a, b int) int {
rnd--
}
}
- panic(nil) // should never happen
+ panic("topicRadius chooseLookupBucket") // should never happen
}
func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool {
diff --git a/p2p/enr/enr.go b/p2p/enr/enr.go
index 5aca3ab25a..04d6ed4a66 100644
--- a/p2p/enr/enr.go
+++ b/p2p/enr/enr.go
@@ -36,8 +36,8 @@ import (
"sort"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/rlp"
+ "golang.org/x/crypto/sha3"
)
const SizeLimit = 300 // maximum encoded size of a node record in bytes
@@ -243,7 +243,7 @@ func (r *Record) signAndEncode(privkey *ecdsa.PrivateKey) error {
list = r.appendPairs(list)
// Sign the tail of the list.
- h := sha3.NewKeccak256()
+ h := sha3.NewLegacyKeccak256()
rlp.Encode(h, list[1:])
sig, err := crypto.Sign(h.Sum(nil), privkey)
if err != nil {
@@ -281,7 +281,7 @@ func (r *Record) verifySignature() error {
// Verify the signature.
list := make([]interface{}, 0, len(r.pairs)*2+1)
list = r.appendPairs(list)
- h := sha3.NewKeccak256()
+ h := sha3.NewLegacyKeccak256()
rlp.Encode(h, list)
if !crypto.VerifySignature(entry, h.Sum(nil), r.signature) {
return errInvalidSig
diff --git a/p2p/message.go b/p2p/message.go
index d39bcb31f6..58c159ee79 100644
--- a/p2p/message.go
+++ b/p2p/message.go
@@ -47,21 +47,21 @@ type Msg struct {
// the given value, which must be a pointer.
//
// For the decoding rules, please see package rlp.
-func (msg Msg) Decode(val interface{}) error {
- s := rlp.NewStream(msg.Payload, uint64(msg.Size))
+func (m Msg) Decode(val interface{}) error {
+ s := rlp.NewStream(m.Payload, uint64(m.Size))
if err := s.Decode(val); err != nil {
- return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", msg.Code, msg.Size, err)
+ return newPeerError(errInvalidMsg, "(code %x) (size %d) %v", m.Code, m.Size, err)
}
return nil
}
-func (msg Msg) String() string {
- return fmt.Sprintf("msg #%v (%v bytes)", msg.Code, msg.Size)
+func (m Msg) String() string {
+ return fmt.Sprintf("msg #%v (%v bytes)", m.Code, m.Size)
}
// Discard reads any remaining payload data into a black hole.
-func (msg Msg) Discard() error {
- _, err := io.Copy(io.Discard, msg.Payload)
+func (m Msg) Discard() error {
+ _, err := io.Copy(io.Discard, m.Payload)
return err
}
@@ -119,24 +119,24 @@ type eofSignal struct {
// note: when using eofSignal to detect whether a message payload
// has been read, Read might not be called for zero sized messages.
-func (r *eofSignal) Read(buf []byte) (int, error) {
- if r.count == 0 {
- if r.eof != nil {
- r.eof <- struct{}{}
- r.eof = nil
+func (s *eofSignal) Read(buf []byte) (int, error) {
+ if s.count == 0 {
+ if s.eof != nil {
+ s.eof <- struct{}{}
+ s.eof = nil
}
return 0, io.EOF
}
max := len(buf)
- if int(r.count) < len(buf) {
- max = int(r.count)
+ if int(s.count) < len(buf) {
+ max = int(s.count)
}
- n, err := r.wrapped.Read(buf[:max])
- r.count -= uint32(n)
- if (err != nil || r.count == 0) && r.eof != nil {
- r.eof <- struct{}{} // tell Peer that msg has been consumed
- r.eof = nil
+ n, err := s.wrapped.Read(buf[:max])
+ s.count -= uint32(n)
+ if (err != nil || s.count == 0) && s.eof != nil {
+ s.eof <- struct{}{} // tell Peer that msg has been consumed
+ s.eof = nil
}
return n, err
}
@@ -269,15 +269,15 @@ func newMsgEventer(rw MsgReadWriter, feed *event.Feed, peerID discover.NodeID, p
// ReadMsg reads a message from the underlying MsgReadWriter and emits a
// "message received" event
-func (self *msgEventer) ReadMsg() (Msg, error) {
- msg, err := self.MsgReadWriter.ReadMsg()
+func (e *msgEventer) ReadMsg() (Msg, error) {
+ msg, err := e.MsgReadWriter.ReadMsg()
if err != nil {
return msg, err
}
- self.feed.Send(&PeerEvent{
+ e.feed.Send(&PeerEvent{
Type: PeerEventTypeMsgRecv,
- Peer: self.peerID,
- Protocol: self.Protocol,
+ Peer: e.peerID,
+ Protocol: e.Protocol,
MsgCode: &msg.Code,
MsgSize: &msg.Size,
})
@@ -286,15 +286,15 @@ func (self *msgEventer) ReadMsg() (Msg, error) {
// WriteMsg writes a message to the underlying MsgReadWriter and emits a
// "message sent" event
-func (self *msgEventer) WriteMsg(msg Msg) error {
- err := self.MsgReadWriter.WriteMsg(msg)
+func (e *msgEventer) WriteMsg(msg Msg) error {
+ err := e.MsgReadWriter.WriteMsg(msg)
if err != nil {
return err
}
- self.feed.Send(&PeerEvent{
+ e.feed.Send(&PeerEvent{
Type: PeerEventTypeMsgSend,
- Peer: self.peerID,
- Protocol: self.Protocol,
+ Peer: e.peerID,
+ Protocol: e.Protocol,
MsgCode: &msg.Code,
MsgSize: &msg.Size,
})
@@ -303,8 +303,8 @@ func (self *msgEventer) WriteMsg(msg Msg) error {
// Close closes the underlying MsgReadWriter if it implements the io.Closer
// interface
-func (self *msgEventer) Close() error {
- if v, ok := self.MsgReadWriter.(io.Closer); ok {
+func (e *msgEventer) Close() error {
+ if v, ok := e.MsgReadWriter.(io.Closer); ok {
return v.Close()
}
return nil
diff --git a/p2p/metrics.go b/p2p/metrics.go
index 0f1f42b08a..febff6c4ec 100644
--- a/p2p/metrics.go
+++ b/p2p/metrics.go
@@ -42,7 +42,7 @@ type meteredConn struct {
// returns the original object.
func newMeteredConn(conn net.Conn, ingress bool) net.Conn {
// Short circuit if metrics are disabled
- if !metrics.Enabled {
+ if !metrics.Enabled() {
return conn
}
// Otherwise bump the connection counters and wrap the connection
diff --git a/p2p/nat/natupnp_test.go b/p2p/nat/natupnp_test.go
index 79f6d25ae8..258bd4fdb9 100644
--- a/p2p/nat/natupnp_test.go
+++ b/p2p/nat/natupnp_test.go
@@ -218,7 +218,7 @@ func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
func (dev *fakeIGD) replaceListenAddr(resp string) string {
- return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1)
+ return strings.ReplaceAll(resp, "{{listenAddr}}", dev.listener.Addr().String())
}
func (dev *fakeIGD) listen() (err error) {
diff --git a/p2p/netutil/net.go b/p2p/netutil/net.go
index 656abb6825..d5da3c694f 100644
--- a/p2p/netutil/net.go
+++ b/p2p/netutil/net.go
@@ -212,7 +212,7 @@ func sameNet(bits uint, ip, other net.IP) bool {
if mask != 0 && nb < len(ip) && ip[nb]&mask != other[nb]&mask {
return false
}
- return nb <= len(ip) && bytes.Equal(ip[:nb], other[:nb])
+ return nb <= len(ip) && ip[:nb].Equal(other[:nb])
}
// DistinctNetSet tracks IPs, ensuring that at most N of them
diff --git a/p2p/peer.go b/p2p/peer.go
index 1d1cfc8906..ac6c464e7e 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -160,12 +160,12 @@ func (p *Peer) Disconnect(reason DiscReason) {
// String implements fmt.Stringer.
func (p *Peer) String() string {
- return fmt.Sprintf("Peer %x %v ", p.rw.id[:8], p.RemoteAddr())
+ return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr())
}
// Inbound returns true if the peer is an inbound connection
func (p *Peer) Inbound() bool {
- return p.rw.flags&inboundConn != 0
+ return p.rw.is(inboundConn)
}
func newPeer(conn *conn, protocols []Protocol) *Peer {
diff --git a/p2p/peer_error.go b/p2p/peer_error.go
index 64530116e6..3ee1e1ab25 100644
--- a/p2p/peer_error.go
+++ b/p2p/peer_error.go
@@ -48,8 +48,8 @@ func newPeerError(code int, format string, v ...interface{}) *peerError {
return err
}
-func (self *peerError) Error() string {
- return self.message
+func (e *peerError) Error() string {
+ return e.message
}
var errProtocolReturned = errors.New("protocol returned")
diff --git a/p2p/peer_test.go b/p2p/peer_test.go
index a3e1c74fd8..20d020f186 100644
--- a/p2p/peer_test.go
+++ b/p2p/peer_test.go
@@ -150,7 +150,7 @@ func TestPeerDisconnect(t *testing.T) {
// This test is supposed to verify that Peer can reliably handle
// multiple causes of disconnection occurring at the same time.
func TestPeerDisconnectRace(t *testing.T) {
- maybe := func() bool { return rand.Intn(1) == 1 }
+ maybe := func() bool { return rand.Intn(2) == 1 }
for i := 0; i < 1000; i++ {
protoclose := make(chan error)
diff --git a/p2p/rlpx.go b/p2p/rlpx.go
index 5ceb897eae..640e7ce3f2 100644
--- a/p2p/rlpx.go
+++ b/p2p/rlpx.go
@@ -37,19 +37,19 @@ import (
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/ecies"
"github.com/XinFinOrg/XDPoSChain/crypto/secp256k1"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/golang/snappy"
+ "golang.org/x/crypto/sha3"
)
const (
maxUint24 = ^uint32(0) >> 8
- sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
- sigLen = 65 // elliptic S256
- pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
- shaLen = 32 // hash length (for nonce etc)
+ sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
+ sigLen = crypto.SignatureLength // elliptic S256
+ pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
+ shaLen = 32 // hash length (for nonce etc)
authMsgLen = sigLen + shaLen + pubLen + shaLen + 1
authRespLen = pubLen + shaLen + 1
@@ -183,7 +183,7 @@ func (t *rlpx) doEncHandshake(prv *ecdsa.PrivateKey, dial *discover.Node) (disco
if dial == nil {
sec, err = receiverEncHandshake(t.fd, prv, nil)
} else {
- sec, err = initiatorEncHandshake(t.fd, prv, dial.ID, nil)
+ sec, err = initiatorEncHandshake(t.fd, prv, dial.ID)
}
if err != nil {
return discover.NodeID{}, err
@@ -255,10 +255,10 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
}
// setup sha3 instances for the MACs
- mac1 := sha3.NewKeccak256()
+ mac1 := sha3.NewLegacyKeccak256()
mac1.Write(xor(s.MAC, h.respNonce))
mac1.Write(auth)
- mac2 := sha3.NewKeccak256()
+ mac2 := sha3.NewLegacyKeccak256()
mac2.Write(xor(s.MAC, h.initNonce))
mac2.Write(authResp)
if h.initiator {
@@ -280,9 +280,9 @@ func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error)
// it should be called on the dialing side of the connection.
//
// prv is the local client's private key.
-func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID, token []byte) (s secrets, err error) {
+func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID discover.NodeID) (s secrets, err error) {
h := &encHandshake{initiator: true, remoteID: remoteID}
- authMsg, err := h.makeAuthMsg(prv, token)
+ authMsg, err := h.makeAuthMsg(prv)
if err != nil {
return s, err
}
@@ -306,7 +306,7 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remoteID d
}
// makeAuthMsg creates the initiator handshake message.
-func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMsgV4, error) {
+func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
rpub, err := h.remoteID.Pubkey()
if err != nil {
return nil, fmt.Errorf("bad remoteID: %v", err)
@@ -324,7 +324,7 @@ func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey, token []byte) (*authMs
}
// Sign known message: static-shared-secret ^ nonce
- token, err = h.staticSharedSecret(prv)
+ token, err := h.staticSharedSecret(prv)
if err != nil {
return nil, err
}
diff --git a/p2p/rlpx_test.go b/p2p/rlpx_test.go
index 903a91c769..83eb97ad03 100644
--- a/p2p/rlpx_test.go
+++ b/p2p/rlpx_test.go
@@ -31,10 +31,10 @@ import (
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/crypto/ecies"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/davecgh/go-spew/spew"
+ "golang.org/x/crypto/sha3"
)
func TestSharedSecret(t *testing.T) {
@@ -335,8 +335,8 @@ func TestRLPXFrameRW(t *testing.T) {
s1 := secrets{
AES: aesSecret,
MAC: macSecret,
- EgressMAC: sha3.NewKeccak256(),
- IngressMAC: sha3.NewKeccak256(),
+ EgressMAC: sha3.NewLegacyKeccak256(),
+ IngressMAC: sha3.NewLegacyKeccak256(),
}
s1.EgressMAC.Write(egressMACinit)
s1.IngressMAC.Write(ingressMACinit)
@@ -345,8 +345,8 @@ func TestRLPXFrameRW(t *testing.T) {
s2 := secrets{
AES: aesSecret,
MAC: macSecret,
- EgressMAC: sha3.NewKeccak256(),
- IngressMAC: sha3.NewKeccak256(),
+ EgressMAC: sha3.NewLegacyKeccak256(),
+ IngressMAC: sha3.NewLegacyKeccak256(),
}
s2.EgressMAC.Write(ingressMACinit)
s2.IngressMAC.Write(egressMACinit)
diff --git a/p2p/server.go b/p2p/server.go
index 2ccb4cf17a..51579ff7c3 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -22,6 +22,7 @@ import (
"errors"
"net"
"sync"
+ "sync/atomic"
"time"
"github.com/XinFinOrg/XDPoSChain/common"
@@ -168,6 +169,8 @@ type Server struct {
quit chan struct{}
addstatic chan *discover.Node
removestatic chan *discover.Node
+ addtrusted chan *discover.Node
+ removetrusted chan *discover.Node
posthandshake chan *conn
addpeer chan *conn
delpeer chan peerDrop
@@ -184,7 +187,7 @@ type peerDrop struct {
requested bool // true if signaled by the peer
}
-type connFlag int
+type connFlag int32
const (
dynDialedConn connFlag = 1 << iota
@@ -249,7 +252,18 @@ func (f connFlag) String() string {
}
func (c *conn) is(f connFlag) bool {
- return c.flags&f != 0
+ flags := connFlag(atomic.LoadInt32((*int32)(&c.flags)))
+ return flags&f != 0
+}
+
+func (c *conn) set(f connFlag, val bool) {
+ flags := connFlag(atomic.LoadInt32((*int32)(&c.flags)))
+ if val {
+ flags |= f
+ } else {
+ flags &= ^f
+ }
+ atomic.StoreInt32((*int32)(&c.flags), int32(flags))
}
// Peers returns all connected peers.
@@ -300,6 +314,23 @@ func (srv *Server) RemovePeer(node *discover.Node) {
}
}
+// AddTrustedPeer adds the given node to a reserved whitelist which allows the
+// node to always connect, even if the slot are full.
+func (srv *Server) AddTrustedPeer(node *discover.Node) {
+ select {
+ case srv.addtrusted <- node:
+ case <-srv.quit:
+ }
+}
+
+// RemoveTrustedPeer removes the given node from the trusted peer set.
+func (srv *Server) RemoveTrustedPeer(node *discover.Node) {
+ select {
+ case srv.removetrusted <- node:
+ case <-srv.quit:
+ }
+}
+
// SubscribePeers subscribes the given channel to peer events
func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
return srv.peerFeed.Subscribe(ch)
@@ -364,7 +395,7 @@ type sharedUDPConn struct {
func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
packet, ok := <-s.unhandled
if !ok {
- return 0, nil, errors.New("Connection was closed")
+ return 0, nil, errors.New("connection was closed")
}
l := len(packet.Data)
if l > len(b) {
@@ -410,6 +441,8 @@ func (srv *Server) Start() (err error) {
srv.posthandshake = make(chan *conn)
srv.addstatic = make(chan *discover.Node)
srv.removestatic = make(chan *discover.Node)
+ srv.addtrusted = make(chan *discover.Node)
+ srv.removetrusted = make(chan *discover.Node)
srv.peerOp = make(chan peerOpFunc)
srv.peerOpDone = make(chan struct{})
@@ -546,8 +579,7 @@ func (srv *Server) run(dialstate dialer) {
queuedTasks []task // tasks that can't run yet
)
// Put trusted nodes into a map to speed up checks.
- // Trusted peers are loaded on startup and cannot be
- // modified while the server is running.
+ // Trusted peers are loaded on startup or added via AddTrustedPeer RPC.
for _, n := range srv.TrustedNodes {
trusted[n.ID] = true
}
@@ -599,12 +631,30 @@ running:
case n := <-srv.removestatic:
// This channel is used by RemovePeer to send a
// disconnect request to a peer and begin the
- // stop keeping the node connected
+ // stop keeping the node connected.
srv.log.Debug("Removing static node", "node", n)
dialstate.removeStatic(n)
if p, ok := peers[n.ID]; ok {
p.Disconnect(DiscRequested)
}
+ case n := <-srv.addtrusted:
+ // This channel is used by AddTrustedPeer to add an enode
+ // to the trusted node set.
+ srv.log.Trace("Adding trusted node", "node", n)
+ trusted[n.ID] = true
+ // Mark any already-connected peer as trusted
+ if p, ok := peers[n.ID]; ok {
+ p.rw.set(trustedConn, true)
+ }
+ case n := <-srv.removetrusted:
+ // This channel is used by RemoveTrustedPeer to remove an enode
+ // from the trusted node set.
+ srv.log.Trace("Removing trusted node", "node", n)
+ delete(trusted, n.ID)
+ // Unmark any already-connected peer as trusted
+ if p, ok := peers[n.ID]; ok {
+ p.rw.set(trustedConn, false)
+ }
case op := <-srv.peerOp:
// This channel is used by Peers and PeerCount.
op(peers)
diff --git a/p2p/server_test.go b/p2p/server_test.go
index ceda0f8d6b..49739f3b84 100644
--- a/p2p/server_test.go
+++ b/p2p/server_test.go
@@ -26,13 +26,13 @@ import (
"time"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/p2p/discover"
+ "golang.org/x/crypto/sha3"
)
func init() {
- // log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+ // log.SetDefault(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
}
type testTransport struct {
@@ -47,8 +47,8 @@ func newTestTransport(id discover.NodeID, fd net.Conn) transport {
wrapped.rw = newRLPXFrameRW(fd, secrets{
MAC: zero16,
AES: zero16,
- IngressMAC: sha3.NewKeccak256(),
- EgressMAC: sha3.NewKeccak256(),
+ IngressMAC: sha3.NewLegacyKeccak256(),
+ EgressMAC: sha3.NewLegacyKeccak256(),
})
return &testTransport{id: id, rlpx: wrapped}
}
@@ -148,7 +148,8 @@ func TestServerDial(t *testing.T) {
// tell the server to connect
tcpAddr := listener.Addr().(*net.TCPAddr)
- srv.AddPeer(&discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)})
+ node := &discover.Node{ID: remid, IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)}
+ srv.AddPeer(node)
select {
case conn := <-accepted:
@@ -169,6 +170,29 @@ func TestServerDial(t *testing.T) {
if !reflect.DeepEqual(peers, []*Peer{peer}) {
t.Errorf("Peers mismatch: got %v, want %v", peers, []*Peer{peer})
}
+
+ // Test AddTrustedPeer/RemoveTrustedPeer and changing Trusted flags
+ // Particularly for race conditions on changing the flag state.
+ if peer := srv.Peers()[0]; peer.Info().Network.Trusted {
+ t.Errorf("peer is trusted prematurely: %v", peer)
+ }
+ done := make(chan bool)
+ go func() {
+ srv.AddTrustedPeer(node)
+ if peer := srv.Peers()[0]; !peer.Info().Network.Trusted {
+ t.Errorf("peer is not trusted after AddTrustedPeer: %v", peer)
+ }
+ srv.RemoveTrustedPeer(node)
+ if peer := srv.Peers()[0]; peer.Info().Network.Trusted {
+ t.Errorf("peer is trusted after RemoveTrustedPeer: %v", peer)
+ }
+ done <- true
+ }()
+ // Trigger potential race conditions
+ peer = srv.Peers()[0]
+ _ = peer.Inbound()
+ _ = peer.Info()
+ <-done
case <-time.After(1 * time.Second):
t.Error("server did not launch peer within one second")
}
@@ -365,7 +389,8 @@ func TestServerAtCap(t *testing.T) {
}
}
// Try inserting a non-trusted connection.
- c := newconn(randomID())
+ anotherID := randomID()
+ c := newconn(anotherID)
if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers {
t.Error("wrong error for insert:", err)
}
@@ -378,6 +403,87 @@ func TestServerAtCap(t *testing.T) {
t.Error("Server did not set trusted flag")
}
+ // Remove from trusted set and try again
+ srv.RemoveTrustedPeer(&discover.Node{ID: trustedID})
+ c = newconn(trustedID)
+ if err := srv.checkpoint(c, srv.posthandshake); err != DiscTooManyPeers {
+ t.Error("wrong error for insert:", err)
+ }
+
+ // Add anotherID to trusted set and try again
+ srv.AddTrustedPeer(&discover.Node{ID: anotherID})
+ c = newconn(anotherID)
+ if err := srv.checkpoint(c, srv.posthandshake); err != nil {
+ t.Error("unexpected error for trusted conn @posthandshake:", err)
+ }
+ if !c.is(trustedConn) {
+ t.Error("Server did not set trusted flag")
+ }
+}
+
+func TestServerPeerLimits(t *testing.T) {
+ srvkey := newkey()
+
+ clientid := randomID()
+ clientnode := &discover.Node{ID: clientid}
+
+ var tp *setupTransport = &setupTransport{
+ id: clientid,
+ phs: &protoHandshake{
+ ID: clientid,
+ // Force "DiscUselessPeer" due to unmatching caps
+ // Caps: []Cap{discard.cap()},
+ },
+ }
+ var flags connFlag = dynDialedConn
+ var dialDest *discover.Node = &discover.Node{ID: clientid}
+
+ srv := &Server{
+ Config: Config{
+ PrivateKey: srvkey,
+ MaxPeers: 0,
+ NoDial: true,
+ Protocols: []Protocol{discard},
+ },
+ newTransport: func(fd net.Conn) transport { return tp },
+ log: log.New(),
+ }
+ if err := srv.Start(); err != nil {
+ t.Fatalf("couldn't start server: %v", err)
+ }
+ defer srv.Stop()
+
+ // Check that server is full (MaxPeers=0)
+ conn, _ := net.Pipe()
+ srv.SetupConn(conn, flags, dialDest)
+ if tp.closeErr != DiscTooManyPeers {
+ t.Errorf("unexpected close error: %q", tp.closeErr)
+ }
+ conn.Close()
+
+ srv.AddTrustedPeer(clientnode)
+
+ // Check that server allows a trusted peer despite being full.
+ conn, _ = net.Pipe()
+ srv.SetupConn(conn, flags, dialDest)
+ if tp.closeErr == DiscTooManyPeers {
+ t.Errorf("failed to bypass MaxPeers with trusted node: %q", tp.closeErr)
+ }
+
+ if tp.closeErr != DiscUselessPeer {
+ t.Errorf("unexpected close error: %q", tp.closeErr)
+ }
+ conn.Close()
+
+ srv.RemoveTrustedPeer(clientnode)
+
+ // Check that server is full again.
+ conn, _ = net.Pipe()
+ srv.SetupConn(conn, flags, dialDest)
+ if tp.closeErr != DiscTooManyPeers {
+ t.Errorf("unexpected close error: %q", tp.closeErr)
+ }
+ conn.Close()
}
func TestServerSetupConn(t *testing.T) {
diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go
index 1ad3961fea..1ab534f8d0 100644
--- a/p2p/simulations/adapters/exec.go
+++ b/p2p/simulations/adapters/exec.go
@@ -24,6 +24,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"net"
"os"
"os/exec"
@@ -155,8 +156,7 @@ func (n *ExecNode) Client() (*rpc.Client, error) {
var wsAddrPattern = regexp.MustCompile(`ws://[\d.:]+`)
// Start exec's the node passing the ID and service as command line arguments
-// and the node config encoded as JSON in the _P2P_NODE_CONFIG environment
-// variable
+// and the node config encoded as JSON in an environment variable.
func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
if n.Cmd != nil {
return errors.New("already started")
@@ -189,7 +189,7 @@ func (n *ExecNode) Start(snapshots map[string][]byte) (err error) {
cmd := n.newCmd()
cmd.Stdout = os.Stdout
cmd.Stderr = stderr
- cmd.Env = append(os.Environ(), fmt.Sprintf("_P2P_NODE_CONFIG=%s", confData))
+ cmd.Env = append(os.Environ(), envNodeConfig+"="+string(confData))
if err := cmd.Start(); err != nil {
return fmt.Errorf("error starting node: %s", err)
}
@@ -344,25 +344,58 @@ type execNodeConfig struct {
PeerAddrs map[string]string `json:"peer_addrs,omitempty"`
}
-// execP2PNode starts a devp2p node when the current binary is executed with
+func initLogging() {
+ // Initialize the logging by default first.
+ var innerHandler slog.Handler
+ innerHandler = slog.NewTextHandler(os.Stderr, nil)
+ glogger := log.NewGlogHandler(innerHandler)
+ glogger.Verbosity(log.LevelInfo)
+ log.SetDefault(log.NewLogger(glogger))
+
+ confEnv := os.Getenv(envNodeConfig)
+ if confEnv == "" {
+ return
+ }
+ var conf execNodeConfig
+ if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
+ return
+ }
+ var writer = os.Stderr
+ if conf.Node.LogFile != "" {
+ logWriter, err := os.Create(conf.Node.LogFile)
+ if err != nil {
+ return
+ }
+ writer = logWriter
+ }
+ var verbosity = log.LevelInfo
+ if conf.Node.LogVerbosity <= log.LevelTrace && conf.Node.LogVerbosity >= log.LevelCrit {
+ verbosity = log.FromLegacyLevel(int(conf.Node.LogVerbosity))
+ }
+ // Reinitialize the logger
+ innerHandler = log.NewTerminalHandler(writer, true)
+ glogger = log.NewGlogHandler(innerHandler)
+ glogger.Verbosity(verbosity)
+ log.SetDefault(log.NewLogger(glogger))
+}
+
+// execP2PNode starts a simulation node when the current binary is executed with
// argv[0] being "p2p-node", reading the service / ID from argv[1] / argv[2]
-// and the node config from the _P2P_NODE_CONFIG environment variable
+// and the node config from an environment variable.
func execP2PNode() {
- glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
- glogger.Verbosity(log.LvlInfo)
- log.Root().SetHandler(glogger)
+ initLogging()
// read the services from argv
serviceNames := strings.Split(os.Args[1], ",")
// decode the config
- confEnv := os.Getenv("_P2P_NODE_CONFIG")
+ confEnv := os.Getenv(envNodeConfig)
if confEnv == "" {
- log.Crit("missing _P2P_NODE_CONFIG")
+ log.Crit("missing " + envNodeConfig)
}
var conf execNodeConfig
if err := json.Unmarshal([]byte(confEnv), &conf); err != nil {
- log.Crit("error decoding _P2P_NODE_CONFIG", "err", err)
+ log.Crit("error decoding "+envNodeConfig, "err", err)
}
conf.Stack.P2P.PrivateKey = conf.Node.PrivateKey
conf.Stack.Logger = log.New("node.id", conf.Node.ID.String())
@@ -477,6 +510,10 @@ func (s *snapshotService) Stop() error {
return nil
}
+const (
+ envNodeConfig = "_P2P_NODE_CONFIG"
+)
+
// SnapshotAPI provides an RPC method to create snapshots of services
type SnapshotAPI struct {
services map[string]node.Service
diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go
index fce627d906..4363e5eaab 100644
--- a/p2p/simulations/adapters/inproc.go
+++ b/p2p/simulations/adapters/inproc.go
@@ -51,18 +51,18 @@ func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
}
// Name returns the name of the adapter for logging purposes
-func (s *SimAdapter) Name() string {
+func (sa *SimAdapter) Name() string {
return "sim-adapter"
}
// NewNode returns a new SimNode using the given config
-func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
- s.mtx.Lock()
- defer s.mtx.Unlock()
+func (sa *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
+ sa.mtx.Lock()
+ defer sa.mtx.Unlock()
// check a node with the ID doesn't already exist
id := config.ID
- if _, exists := s.nodes[id]; exists {
+ if _, exists := sa.nodes[id]; exists {
return nil, fmt.Errorf("node already exists: %s", id)
}
@@ -71,7 +71,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
return nil, errors.New("node must have at least one service")
}
for _, service := range config.Services {
- if _, exists := s.services[service]; !exists {
+ if _, exists := sa.services[service]; !exists {
return nil, fmt.Errorf("unknown node service %q", service)
}
}
@@ -81,7 +81,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
PrivateKey: config.PrivateKey,
MaxPeers: math.MaxInt32,
NoDiscovery: true,
- Dialer: s,
+ Dialer: sa,
EnableMsgEvents: true,
},
NoUSB: true,
@@ -95,18 +95,18 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
ID: id,
config: config,
node: n,
- adapter: s,
+ adapter: sa,
running: make(map[string]node.Service),
connected: make(map[discover.NodeID]bool),
}
- s.nodes[id] = simNode
+ sa.nodes[id] = simNode
return simNode, nil
}
// Dial implements the p2p.NodeDialer interface by connecting to the node using
// an in-memory net.Pipe connection
-func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
- node, ok := s.GetNode(dest.ID)
+func (sa *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
+ node, ok := sa.GetNode(dest.ID)
if !ok {
return nil, fmt.Errorf("unknown node: %s", dest.ID)
}
@@ -125,8 +125,8 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
// DialRPC implements the RPCDialer interface by creating an in-memory RPC
// client of the given node
-func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
- node, ok := s.GetNode(id)
+func (sa *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
+ node, ok := sa.GetNode(id)
if !ok {
return nil, fmt.Errorf("unknown node: %s", id)
}
@@ -138,10 +138,10 @@ func (s *SimAdapter) DialRPC(id discover.NodeID) (*rpc.Client, error) {
}
// GetNode returns the node with the given ID if it exists
-func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
- s.mtx.RLock()
- defer s.mtx.RUnlock()
- node, ok := s.nodes[id]
+func (sa *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
+ sa.mtx.RLock()
+ defer sa.mtx.RUnlock()
+ node, ok := sa.nodes[id]
return node, ok
}
@@ -160,31 +160,37 @@ type SimNode struct {
connected map[discover.NodeID]bool
}
+// Close closes the underlaying node.Node to release
+// acquired resources.
+func (sn *SimNode) Close() error {
+ return sn.node.Close()
+}
+
// Addr returns the node's discovery address
-func (self *SimNode) Addr() []byte {
- return []byte(self.Node().String())
+func (sn *SimNode) Addr() []byte {
+ return []byte(sn.Node().String())
}
// Node returns a discover.Node representing the SimNode
-func (self *SimNode) Node() *discover.Node {
- return discover.NewNode(self.ID, net.IP{127, 0, 0, 1}, 30303, 30303)
+func (sn *SimNode) Node() *discover.Node {
+ return discover.NewNode(sn.ID, net.IP{127, 0, 0, 1}, 30303, 30303)
}
// Client returns an rpc.Client which can be used to communicate with the
// underlying services (it is set once the node has started)
-func (self *SimNode) Client() (*rpc.Client, error) {
- self.lock.RLock()
- defer self.lock.RUnlock()
- if self.client == nil {
+func (sn *SimNode) Client() (*rpc.Client, error) {
+ sn.lock.RLock()
+ defer sn.lock.RUnlock()
+ if sn.client == nil {
return nil, errors.New("node not started")
}
- return self.client, nil
+ return sn.client, nil
}
// ServeRPC serves RPC requests over the given connection by creating an
// in-memory client to the node's RPC server
-func (self *SimNode) ServeRPC(conn *websocket.Conn) error {
- handler, err := self.node.RPCHandler()
+func (sn *SimNode) ServeRPC(conn *websocket.Conn) error {
+ handler, err := sn.node.RPCHandler()
if err != nil {
return err
}
@@ -195,13 +201,13 @@ func (self *SimNode) ServeRPC(conn *websocket.Conn) error {
// Snapshots creates snapshots of the services by calling the
// simulation_snapshot RPC method
-func (self *SimNode) Snapshots() (map[string][]byte, error) {
- self.lock.RLock()
- services := make(map[string]node.Service, len(self.running))
- for name, service := range self.running {
+func (sn *SimNode) Snapshots() (map[string][]byte, error) {
+ sn.lock.RLock()
+ services := make(map[string]node.Service, len(sn.running))
+ for name, service := range sn.running {
services[name] = service
}
- self.lock.RUnlock()
+ sn.lock.RUnlock()
if len(services) == 0 {
return nil, errors.New("no running services")
}
@@ -221,23 +227,23 @@ func (self *SimNode) Snapshots() (map[string][]byte, error) {
}
// Start registers the services and starts the underlying devp2p node
-func (self *SimNode) Start(snapshots map[string][]byte) error {
+func (sn *SimNode) Start(snapshots map[string][]byte) error {
newService := func(name string) func(ctx *node.ServiceContext) (node.Service, error) {
return func(nodeCtx *node.ServiceContext) (node.Service, error) {
ctx := &ServiceContext{
- RPCDialer: self.adapter,
+ RPCDialer: sn.adapter,
NodeContext: nodeCtx,
- Config: self.config,
+ Config: sn.config,
}
if snapshots != nil {
ctx.Snapshot = snapshots[name]
}
- serviceFunc := self.adapter.services[name]
+ serviceFunc := sn.adapter.services[name]
service, err := serviceFunc(ctx)
if err != nil {
return nil, err
}
- self.running[name] = service
+ sn.running[name] = service
return service, nil
}
}
@@ -245,9 +251,9 @@ func (self *SimNode) Start(snapshots map[string][]byte) error {
// ensure we only register the services once in the case of the node
// being stopped and then started again
var regErr error
- self.registerOnce.Do(func() {
- for _, name := range self.config.Services {
- if err := self.node.Register(newService(name)); err != nil {
+ sn.registerOnce.Do(func() {
+ for _, name := range sn.config.Services {
+ if err := sn.node.Register(newService(name)); err != nil {
regErr = err
return
}
@@ -257,54 +263,54 @@ func (self *SimNode) Start(snapshots map[string][]byte) error {
return regErr
}
- if err := self.node.Start(); err != nil {
+ if err := sn.node.Start(); err != nil {
return err
}
// create an in-process RPC client
- handler, err := self.node.RPCHandler()
+ handler, err := sn.node.RPCHandler()
if err != nil {
return err
}
- self.lock.Lock()
- self.client = rpc.DialInProc(handler)
- self.lock.Unlock()
+ sn.lock.Lock()
+ sn.client = rpc.DialInProc(handler)
+ sn.lock.Unlock()
return nil
}
// Stop closes the RPC client and stops the underlying devp2p node
-func (self *SimNode) Stop() error {
- self.lock.Lock()
- if self.client != nil {
- self.client.Close()
- self.client = nil
+func (sn *SimNode) Stop() error {
+ sn.lock.Lock()
+ if sn.client != nil {
+ sn.client.Close()
+ sn.client = nil
}
- self.lock.Unlock()
- return self.node.Stop()
+ sn.lock.Unlock()
+ return sn.node.Stop()
}
// Services returns a copy of the underlying services
-func (self *SimNode) Services() []node.Service {
- self.lock.RLock()
- defer self.lock.RUnlock()
- services := make([]node.Service, 0, len(self.running))
- for _, service := range self.running {
+func (sn *SimNode) Services() []node.Service {
+ sn.lock.RLock()
+ defer sn.lock.RUnlock()
+ services := make([]node.Service, 0, len(sn.running))
+ for _, service := range sn.running {
services = append(services, service)
}
return services
}
// Server returns the underlying p2p.Server
-func (self *SimNode) Server() *p2p.Server {
- return self.node.Server()
+func (sn *SimNode) Server() *p2p.Server {
+ return sn.node.Server()
}
// SubscribeEvents subscribes the given channel to peer events from the
// underlying p2p.Server
-func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
- srv := self.Server()
+func (sn *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
+ srv := sn.Server()
if srv == nil {
panic("node not running")
}
@@ -312,12 +318,12 @@ func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription
}
// NodeInfo returns information about the node
-func (self *SimNode) NodeInfo() *p2p.NodeInfo {
- server := self.Server()
+func (sn *SimNode) NodeInfo() *p2p.NodeInfo {
+ server := sn.Server()
if server == nil {
return &p2p.NodeInfo{
- ID: self.ID.String(),
- Enode: self.Node().String(),
+ ID: sn.ID.String(),
+ Enode: sn.Node().String(),
}
}
return server.NodeInfo()
diff --git a/p2p/simulations/adapters/state.go b/p2p/simulations/adapters/state.go
index 0d4ecfb0ff..416c504d2a 100644
--- a/p2p/simulations/adapters/state.go
+++ b/p2p/simulations/adapters/state.go
@@ -20,12 +20,12 @@ type SimStateStore struct {
m map[string][]byte
}
-func (self *SimStateStore) Load(s string) ([]byte, error) {
- return self.m[s], nil
+func (sss *SimStateStore) Load(s string) ([]byte, error) {
+ return sss.m[s], nil
}
-func (self *SimStateStore) Save(s string, data []byte) error {
- self.m[s] = data
+func (sss *SimStateStore) Save(s string, data []byte) error {
+ sss.m[s] = data
return nil
}
diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go
index 0d7cf62ca4..59ae2e8ad3 100644
--- a/p2p/simulations/adapters/types.go
+++ b/p2p/simulations/adapters/types.go
@@ -21,6 +21,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
+ "log/slog"
"os"
"github.com/XinFinOrg/XDPoSChain/crypto"
@@ -38,7 +39,6 @@ import (
// * SimNode - An in-memory node
// * ExecNode - A child process node
// * DockerNode - A Docker container node
-//
type Node interface {
// Addr returns the node's address (e.g. an Enode URL)
Addr() []byte
@@ -97,24 +97,39 @@ type NodeConfig struct {
// function to sanction or prevent suggesting a peer
Reachable func(id discover.NodeID) bool
+
+ // LogFile is the log file name of the p2p node at runtime.
+ //
+ // The default value is empty so that the default log writer
+ // is the system standard output.
+ LogFile string
+
+ // LogVerbosity is the log verbosity of the p2p node at runtime.
+ //
+ // The default verbosity is INFO.
+ LogVerbosity slog.Level
}
// nodeConfigJSON is used to encode and decode NodeConfig as JSON by encoding
// all fields as strings
type nodeConfigJSON struct {
- ID string `json:"id"`
- PrivateKey string `json:"private_key"`
- Name string `json:"name"`
- Services []string `json:"services"`
+ ID string `json:"id"`
+ PrivateKey string `json:"private_key"`
+ Name string `json:"name"`
+ Services []string `json:"services"`
+ LogFile string `json:"logfile"`
+ LogVerbosity int `json:"log_verbosity"`
}
// MarshalJSON implements the json.Marshaler interface by encoding the config
// fields as strings
func (n *NodeConfig) MarshalJSON() ([]byte, error) {
confJSON := nodeConfigJSON{
- ID: n.ID.String(),
- Name: n.Name,
- Services: n.Services,
+ ID: n.ID.String(),
+ Name: n.Name,
+ Services: n.Services,
+ LogFile: n.LogFile,
+ LogVerbosity: int(n.LogVerbosity),
}
if n.PrivateKey != nil {
confJSON.PrivateKey = hex.EncodeToString(crypto.FromECDSA(n.PrivateKey))
@@ -152,6 +167,8 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
n.Name = confJSON.Name
n.Services = confJSON.Services
+ n.LogFile = confJSON.LogFile
+ n.LogVerbosity = slog.Level(confJSON.LogVerbosity)
return nil
}
diff --git a/p2p/simulations/examples/ping-pong.go b/p2p/simulations/examples/ping-pong.go
index 488abfb820..450ab04790 100644
--- a/p2p/simulations/examples/ping-pong.go
+++ b/p2p/simulations/examples/ping-pong.go
@@ -42,7 +42,7 @@ func main() {
flag.Parse()
// set the log level to Trace
- log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
+ log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, false)))
// register a single ping-pong service
services := map[string]adapters.ServiceFunc{
diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go
index 7e3aa5c96e..f24695882b 100644
--- a/p2p/simulations/http.go
+++ b/p2p/simulations/http.go
@@ -100,7 +100,7 @@ type SubscribeOpts struct {
// nodes and connections and filtering message events
func (c *Client) SubscribeNetwork(events chan *Event, opts SubscribeOpts) (event.Subscription, error) {
url := fmt.Sprintf("%s/events?current=%t&filter=%s", c.URL, opts.Current, opts.Filter)
- req, err := http.NewRequest("GET", url, nil)
+ req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
@@ -213,18 +213,18 @@ func (c *Client) RPCClient(ctx context.Context, nodeID string) (*rpc.Client, err
// Get performs a HTTP GET request decoding the resulting JSON response
// into "out"
func (c *Client) Get(path string, out interface{}) error {
- return c.Send("GET", path, nil, out)
+ return c.Send(http.MethodGet, path, nil, out)
}
// Post performs a HTTP POST request sending "in" as the JSON body and
// decoding the resulting JSON response into "out"
func (c *Client) Post(path string, in, out interface{}) error {
- return c.Send("POST", path, in, out)
+ return c.Send(http.MethodPost, path, in, out)
}
// Delete performs a HTTP DELETE request
func (c *Client) Delete(path string) error {
- return c.Send("DELETE", path, nil, nil)
+ return c.Send(http.MethodDelete, path, nil, nil)
}
// Send performs a HTTP request, sending "in" as the JSON request body and
@@ -721,6 +721,7 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
http.NotFound(w, req)
return
}
+ //lint:ignore SA1029 This file will be removed later, reference: #30250
ctx = context.WithValue(ctx, "node", node)
}
@@ -735,6 +736,7 @@ func (s *Server) wrapHandler(handler http.HandlerFunc) httprouter.Handle {
http.NotFound(w, req)
return
}
+ //lint:ignore SA1029 This file will be removed later, reference: #30250
ctx = context.WithValue(ctx, "peer", peer)
}
diff --git a/p2p/simulations/mocker_test.go b/p2p/simulations/mocker_test.go
index becde29835..8cc84f8e83 100644
--- a/p2p/simulations/mocker_test.go
+++ b/p2p/simulations/mocker_test.go
@@ -80,14 +80,17 @@ func TestMocker(t *testing.T) {
var opts SubscribeOpts
sub, err := client.SubscribeNetwork(events, opts)
defer sub.Unsubscribe()
- //wait until all nodes are started and connected
- //store every node up event in a map (value is irrelevant, mimic Set datatype)
+
+ // wait until all nodes are started and connected
+ // store every node up event in a map (value is irrelevant, mimic Set datatype)
nodemap := make(map[discover.NodeID]bool)
- wg.Add(1)
nodesComplete := false
connCount := 0
+ wg.Add(1)
go func() {
- for {
+ defer wg.Done()
+
+ for connCount < (nodeCount-1)*2 {
select {
case event := <-events:
//if the event is a node Up event only
@@ -102,14 +105,10 @@ func TestMocker(t *testing.T) {
}
} else if event.Conn != nil && nodesComplete {
connCount += 1
- if connCount == (nodeCount-1)*2 {
- wg.Done()
- return
- }
}
case <-time.After(30 * time.Second):
- wg.Done()
- t.Fatalf("Timeout waiting for nodes being started up!")
+ t.Errorf("Timeout waiting for nodes being started up!")
+ return
}
}
}()
@@ -128,6 +127,7 @@ func TestMocker(t *testing.T) {
if err != nil {
t.Fatalf("Could not start mocker: %s", err)
}
+ resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatalf("Invalid Status Code received for starting mocker, expected 200, got %d", resp.StatusCode)
}
@@ -149,15 +149,17 @@ func TestMocker(t *testing.T) {
if err != nil {
t.Fatalf("Could not stop mocker: %s", err)
}
+ resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatalf("Invalid Status Code received for stopping mocker, expected 200, got %d", resp.StatusCode)
}
//reset the network
- _, err = http.Post(s.URL+"/reset", "", nil)
+ resp, err = http.Post(s.URL+"/reset", "", nil)
if err != nil {
t.Fatalf("Could not reset network: %s", err)
}
+ resp.Body.Close()
//now the number of nodes in the network should be zero
nodes_info, err = client.GetNodes()
diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go
index 5be5697da3..2c4a3d5a3f 100644
--- a/p2p/simulations/network.go
+++ b/p2p/simulations/network.go
@@ -21,6 +21,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "io"
"sync"
"time"
@@ -74,22 +75,22 @@ func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network
}
// Events returns the output event feed of the Network.
-func (self *Network) Events() *event.Feed {
- return &self.events
+func (net *Network) Events() *event.Feed {
+ return &net.events
}
// NewNode adds a new node to the network with a random ID
-func (self *Network) NewNode() (*Node, error) {
+func (net *Network) NewNode() (*Node, error) {
conf := adapters.RandomNodeConfig()
- conf.Services = []string{self.DefaultService}
- return self.NewNodeWithConfig(conf)
+ conf.Services = []string{net.DefaultService}
+ return net.NewNodeWithConfig(conf)
}
// NewNodeWithConfig adds a new node to the network with the given config,
// returning an error if a node with the same ID or name already exists
-func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) {
+ net.lock.Lock()
+ defer net.lock.Unlock()
// create a random ID and PrivateKey if not set
if conf.ID == (discover.NodeID{}) {
@@ -100,31 +101,31 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error)
id := conf.ID
if conf.Reachable == nil {
conf.Reachable = func(otherID discover.NodeID) bool {
- _, err := self.InitConn(conf.ID, otherID)
+ _, err := net.InitConn(conf.ID, otherID)
return err == nil
}
}
// assign a name to the node if not set
if conf.Name == "" {
- conf.Name = fmt.Sprintf("node%02d", len(self.Nodes)+1)
+ conf.Name = fmt.Sprintf("node%02d", len(net.Nodes)+1)
}
// check the node doesn't already exist
- if node := self.getNode(id); node != nil {
+ if node := net.getNode(id); node != nil {
return nil, fmt.Errorf("node with ID %q already exists", id)
}
- if node := self.getNodeByName(conf.Name); node != nil {
+ if node := net.getNodeByName(conf.Name); node != nil {
return nil, fmt.Errorf("node with name %q already exists", conf.Name)
}
// if no services are configured, use the default service
if len(conf.Services) == 0 {
- conf.Services = []string{self.DefaultService}
+ conf.Services = []string{net.DefaultService}
}
// use the NodeAdapter to create the node
- adapterNode, err := self.nodeAdapter.NewNode(conf)
+ adapterNode, err := net.nodeAdapter.NewNode(conf)
if err != nil {
return nil, err
}
@@ -133,27 +134,27 @@ func (self *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error)
Config: conf,
}
log.Trace(fmt.Sprintf("node %v created", id))
- self.nodeMap[id] = len(self.Nodes)
- self.Nodes = append(self.Nodes, node)
+ net.nodeMap[id] = len(net.Nodes)
+ net.Nodes = append(net.Nodes, node)
// emit a "control" event
- self.events.Send(ControlEvent(node))
+ net.events.Send(ControlEvent(node))
return node, nil
}
// Config returns the network configuration
-func (self *Network) Config() *NetworkConfig {
- return &self.NetworkConfig
+func (net *Network) Config() *NetworkConfig {
+ return &net.NetworkConfig
}
// StartAll starts all nodes in the network
-func (self *Network) StartAll() error {
- for _, node := range self.Nodes {
+func (net *Network) StartAll() error {
+ for _, node := range net.Nodes {
if node.Up {
continue
}
- if err := self.Start(node.ID()); err != nil {
+ if err := net.Start(node.ID()); err != nil {
return err
}
}
@@ -161,12 +162,12 @@ func (self *Network) StartAll() error {
}
// StopAll stops all nodes in the network
-func (self *Network) StopAll() error {
- for _, node := range self.Nodes {
+func (net *Network) StopAll() error {
+ for _, node := range net.Nodes {
if !node.Up {
continue
}
- if err := self.Stop(node.ID()); err != nil {
+ if err := net.Stop(node.ID()); err != nil {
return err
}
}
@@ -174,21 +175,21 @@ func (self *Network) StopAll() error {
}
// Start starts the node with the given ID
-func (self *Network) Start(id discover.NodeID) error {
- return self.startWithSnapshots(id, nil)
+func (net *Network) Start(id discover.NodeID) error {
+ return net.startWithSnapshots(id, nil)
}
// startWithSnapshots starts the node with the given ID using the give
// snapshots
-func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error {
- node := self.GetNode(id)
+func (net *Network) startWithSnapshots(id discover.NodeID, snapshots map[string][]byte) error {
+ node := net.GetNode(id)
if node == nil {
return fmt.Errorf("node %v does not exist", id)
}
if node.Up {
return fmt.Errorf("node %v already up", id)
}
- log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name()))
+ log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, net.nodeAdapter.Name()))
if err := node.Start(snapshots); err != nil {
log.Warn(fmt.Sprintf("start up failed: %v", err))
return err
@@ -196,7 +197,7 @@ func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string
node.Up = true
log.Info(fmt.Sprintf("started node %v: %v", id, node.Up))
- self.events.Send(NewEvent(node))
+ net.events.Send(NewEvent(node))
// subscribe to peer events
client, err := node.Client()
@@ -208,22 +209,22 @@ func (self *Network) startWithSnapshots(id discover.NodeID, snapshots map[string
if err != nil {
return fmt.Errorf("error getting peer events for node %v: %s", id, err)
}
- go self.watchPeerEvents(id, events, sub)
+ go net.watchPeerEvents(id, events, sub)
return nil
}
// watchPeerEvents reads peer events from the given channel and emits
// corresponding network events
-func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) {
+func (net *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEvent, sub event.Subscription) {
defer func() {
sub.Unsubscribe()
// assume the node is now down
- self.lock.Lock()
- node := self.getNode(id)
+ net.lock.Lock()
+ node := net.getNode(id)
node.Up = false
- self.lock.Unlock()
- self.events.Send(NewEvent(node))
+ net.lock.Unlock()
+ net.events.Send(NewEvent(node))
}()
for {
select {
@@ -235,16 +236,16 @@ func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEv
switch event.Type {
case p2p.PeerEventTypeAdd:
- self.DidConnect(id, peer)
+ net.DidConnect(id, peer)
case p2p.PeerEventTypeDrop:
- self.DidDisconnect(id, peer)
+ net.DidDisconnect(id, peer)
case p2p.PeerEventTypeMsgSend:
- self.DidSend(id, peer, event.Protocol, *event.MsgCode)
+ net.DidSend(id, peer, event.Protocol, *event.MsgCode)
case p2p.PeerEventTypeMsgRecv:
- self.DidReceive(peer, id, event.Protocol, *event.MsgCode)
+ net.DidReceive(peer, id, event.Protocol, *event.MsgCode)
}
@@ -258,8 +259,8 @@ func (self *Network) watchPeerEvents(id discover.NodeID, events chan *p2p.PeerEv
}
// Stop stops the node with the given ID
-func (self *Network) Stop(id discover.NodeID) error {
- node := self.GetNode(id)
+func (net *Network) Stop(id discover.NodeID) error {
+ node := net.GetNode(id)
if node == nil {
return fmt.Errorf("node %v does not exist", id)
}
@@ -272,15 +273,15 @@ func (self *Network) Stop(id discover.NodeID) error {
node.Up = false
log.Info(fmt.Sprintf("stop node %v: %v", id, node.Up))
- self.events.Send(ControlEvent(node))
+ net.events.Send(ControlEvent(node))
return nil
}
// Connect connects two nodes together by calling the "admin_addPeer" RPC
// method on the "one" node so that it connects to the "other" node
-func (self *Network) Connect(oneID, otherID discover.NodeID) error {
+func (net *Network) Connect(oneID, otherID discover.NodeID) error {
log.Debug(fmt.Sprintf("connecting %s to %s", oneID, otherID))
- conn, err := self.InitConn(oneID, otherID)
+ conn, err := net.InitConn(oneID, otherID)
if err != nil {
return err
}
@@ -288,14 +289,14 @@ func (self *Network) Connect(oneID, otherID discover.NodeID) error {
if err != nil {
return err
}
- self.events.Send(ControlEvent(conn))
+ net.events.Send(ControlEvent(conn))
return client.Call(nil, "admin_addPeer", string(conn.other.Addr()))
}
// Disconnect disconnects two nodes by calling the "admin_removePeer" RPC
// method on the "one" node so that it disconnects from the "other" node
-func (self *Network) Disconnect(oneID, otherID discover.NodeID) error {
- conn := self.GetConn(oneID, otherID)
+func (net *Network) Disconnect(oneID, otherID discover.NodeID) error {
+ conn := net.GetConn(oneID, otherID)
if conn == nil {
return fmt.Errorf("connection between %v and %v does not exist", oneID, otherID)
}
@@ -306,13 +307,13 @@ func (self *Network) Disconnect(oneID, otherID discover.NodeID) error {
if err != nil {
return err
}
- self.events.Send(ControlEvent(conn))
+ net.events.Send(ControlEvent(conn))
return client.Call(nil, "admin_removePeer", string(conn.other.Addr()))
}
// DidConnect tracks the fact that the "one" node connected to the "other" node
-func (self *Network) DidConnect(one, other discover.NodeID) error {
- conn, err := self.GetOrCreateConn(one, other)
+func (net *Network) DidConnect(one, other discover.NodeID) error {
+ conn, err := net.GetOrCreateConn(one, other)
if err != nil {
return fmt.Errorf("connection between %v and %v does not exist", one, other)
}
@@ -320,14 +321,14 @@ func (self *Network) DidConnect(one, other discover.NodeID) error {
return fmt.Errorf("%v and %v already connected", one, other)
}
conn.Up = true
- self.events.Send(NewEvent(conn))
+ net.events.Send(NewEvent(conn))
return nil
}
// DidDisconnect tracks the fact that the "one" node disconnected from the
// "other" node
-func (self *Network) DidDisconnect(one, other discover.NodeID) error {
- conn := self.GetConn(one, other)
+func (net *Network) DidDisconnect(one, other discover.NodeID) error {
+ conn := net.GetConn(one, other)
if conn == nil {
return fmt.Errorf("connection between %v and %v does not exist", one, other)
}
@@ -336,12 +337,12 @@ func (self *Network) DidDisconnect(one, other discover.NodeID) error {
}
conn.Up = false
conn.initiated = time.Now().Add(-dialBanTimeout)
- self.events.Send(NewEvent(conn))
+ net.events.Send(NewEvent(conn))
return nil
}
// DidSend tracks the fact that "sender" sent a message to "receiver"
-func (self *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error {
+func (net *Network) DidSend(sender, receiver discover.NodeID, proto string, code uint64) error {
msg := &Msg{
One: sender,
Other: receiver,
@@ -349,12 +350,12 @@ func (self *Network) DidSend(sender, receiver discover.NodeID, proto string, cod
Code: code,
Received: false,
}
- self.events.Send(NewEvent(msg))
+ net.events.Send(NewEvent(msg))
return nil
}
// DidReceive tracks the fact that "receiver" received a message from "sender"
-func (self *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error {
+func (net *Network) DidReceive(sender, receiver discover.NodeID, proto string, code uint64) error {
msg := &Msg{
One: sender,
Other: receiver,
@@ -362,36 +363,36 @@ func (self *Network) DidReceive(sender, receiver discover.NodeID, proto string,
Code: code,
Received: true,
}
- self.events.Send(NewEvent(msg))
+ net.events.Send(NewEvent(msg))
return nil
}
// GetNode gets the node with the given ID, returning nil if the node does not
// exist
-func (self *Network) GetNode(id discover.NodeID) *Node {
- self.lock.Lock()
- defer self.lock.Unlock()
- return self.getNode(id)
+func (net *Network) GetNode(id discover.NodeID) *Node {
+ net.lock.Lock()
+ defer net.lock.Unlock()
+ return net.getNode(id)
}
// GetNode gets the node with the given name, returning nil if the node does
// not exist
-func (self *Network) GetNodeByName(name string) *Node {
- self.lock.Lock()
- defer self.lock.Unlock()
- return self.getNodeByName(name)
+func (net *Network) GetNodeByName(name string) *Node {
+ net.lock.Lock()
+ defer net.lock.Unlock()
+ return net.getNodeByName(name)
}
-func (self *Network) getNode(id discover.NodeID) *Node {
- i, found := self.nodeMap[id]
+func (net *Network) getNode(id discover.NodeID) *Node {
+ i, found := net.nodeMap[id]
if !found {
return nil
}
- return self.Nodes[i]
+ return net.Nodes[i]
}
-func (self *Network) getNodeByName(name string) *Node {
- for _, node := range self.Nodes {
+func (net *Network) getNodeByName(name string) *Node {
+ for _, node := range net.Nodes {
if node.Config.Name == name {
return node
}
@@ -400,40 +401,40 @@ func (self *Network) getNodeByName(name string) *Node {
}
// GetNodes returns the existing nodes
-func (self *Network) GetNodes() (nodes []*Node) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (net *Network) GetNodes() (nodes []*Node) {
+ net.lock.Lock()
+ defer net.lock.Unlock()
- nodes = append(nodes, self.Nodes...)
+ nodes = append(nodes, net.Nodes...)
return nodes
}
// GetConn returns the connection which exists between "one" and "other"
// regardless of which node initiated the connection
-func (self *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
- self.lock.Lock()
- defer self.lock.Unlock()
- return self.getConn(oneID, otherID)
+func (net *Network) GetConn(oneID, otherID discover.NodeID) *Conn {
+ net.lock.Lock()
+ defer net.lock.Unlock()
+ return net.getConn(oneID, otherID)
}
// GetOrCreateConn is like GetConn but creates the connection if it doesn't
// already exist
-func (self *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
- self.lock.Lock()
- defer self.lock.Unlock()
- return self.getOrCreateConn(oneID, otherID)
+func (net *Network) GetOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
+ net.lock.Lock()
+ defer net.lock.Unlock()
+ return net.getOrCreateConn(oneID, otherID)
}
-func (self *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
- if conn := self.getConn(oneID, otherID); conn != nil {
+func (net *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, error) {
+ if conn := net.getConn(oneID, otherID); conn != nil {
return conn, nil
}
- one := self.getNode(oneID)
+ one := net.getNode(oneID)
if one == nil {
return nil, fmt.Errorf("node %v does not exist", oneID)
}
- other := self.getNode(otherID)
+ other := net.getNode(otherID)
if other == nil {
return nil, fmt.Errorf("node %v does not exist", otherID)
}
@@ -444,18 +445,18 @@ func (self *Network) getOrCreateConn(oneID, otherID discover.NodeID) (*Conn, err
other: other,
}
label := ConnLabel(oneID, otherID)
- self.connMap[label] = len(self.Conns)
- self.Conns = append(self.Conns, conn)
+ net.connMap[label] = len(net.Conns)
+ net.Conns = append(net.Conns, conn)
return conn, nil
}
-func (self *Network) getConn(oneID, otherID discover.NodeID) *Conn {
+func (net *Network) getConn(oneID, otherID discover.NodeID) *Conn {
label := ConnLabel(oneID, otherID)
- i, found := self.connMap[label]
+ i, found := net.connMap[label]
if !found {
return nil
}
- return self.Conns[i]
+ return net.Conns[i]
}
// InitConn(one, other) retrieves the connectiton model for the connection between
@@ -466,13 +467,13 @@ func (self *Network) getConn(oneID, otherID discover.NodeID) *Conn {
// it also checks whether there has been recent attempt to connect the peers
// this is cheating as the simulation is used as an oracle and know about
// remote peers attempt to connect to a node which will then not initiate the connection
-func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (net *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
+ net.lock.Lock()
+ defer net.lock.Unlock()
if oneID == otherID {
return nil, fmt.Errorf("refusing to connect to self %v", oneID)
}
- conn, err := self.getOrCreateConn(oneID, otherID)
+ conn, err := net.getOrCreateConn(oneID, otherID)
if err != nil {
return nil, err
}
@@ -491,28 +492,34 @@ func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
}
// Shutdown stops all nodes in the network and closes the quit channel
-func (self *Network) Shutdown() {
- for _, node := range self.Nodes {
+func (net *Network) Shutdown() {
+ for _, node := range net.Nodes {
log.Debug(fmt.Sprintf("stopping node %s", node.ID().TerminalString()))
if err := node.Stop(); err != nil {
log.Warn(fmt.Sprintf("error stopping node %s", node.ID().TerminalString()), "err", err)
}
+ // If the node has the close method, call it.
+ if closer, ok := node.Node.(io.Closer); ok {
+ if err := closer.Close(); err != nil {
+ log.Warn("Can't close node", "id", node.ID(), "err", err)
+ }
+ }
}
- close(self.quitc)
+ close(net.quitc)
}
-//Reset resets all network properties:
-//emtpies the nodes and the connection list
-func (self *Network) Reset() {
- self.lock.Lock()
- defer self.lock.Unlock()
+// Reset resets all network properties:
+// emtpies the nodes and the connection list
+func (net *Network) Reset() {
+ net.lock.Lock()
+ defer net.lock.Unlock()
//re-initialize the maps
- self.connMap = make(map[string]int)
- self.nodeMap = make(map[discover.NodeID]int)
+ net.connMap = make(map[string]int)
+ net.nodeMap = make(map[discover.NodeID]int)
- self.Nodes = nil
- self.Conns = nil
+ net.Nodes = nil
+ net.Conns = nil
}
// Node is a wrapper around adapters.Node which is used to track the status
@@ -528,37 +535,37 @@ type Node struct {
}
// ID returns the ID of the node
-func (self *Node) ID() discover.NodeID {
- return self.Config.ID
+func (n *Node) ID() discover.NodeID {
+ return n.Config.ID
}
// String returns a log-friendly string
-func (self *Node) String() string {
- return fmt.Sprintf("Node %v", self.ID().TerminalString())
+func (n *Node) String() string {
+ return fmt.Sprintf("Node %v", n.ID().TerminalString())
}
// NodeInfo returns information about the node
-func (self *Node) NodeInfo() *p2p.NodeInfo {
+func (n *Node) NodeInfo() *p2p.NodeInfo {
// avoid a panic if the node is not started yet
- if self.Node == nil {
+ if n.Node == nil {
return nil
}
- info := self.Node.NodeInfo()
- info.Name = self.Config.Name
+ info := n.Node.NodeInfo()
+ info.Name = n.Config.Name
return info
}
// MarshalJSON implements the json.Marshaler interface so that the encoded
// JSON includes the NodeInfo
-func (self *Node) MarshalJSON() ([]byte, error) {
+func (n *Node) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Info *p2p.NodeInfo `json:"info,omitempty"`
Config *adapters.NodeConfig `json:"config,omitempty"`
Up bool `json:"up"`
}{
- Info: self.NodeInfo(),
- Config: self.Config,
- Up: self.Up,
+ Info: n.NodeInfo(),
+ Config: n.Config,
+ Up: n.Up,
})
}
@@ -580,19 +587,19 @@ type Conn struct {
}
// nodesUp returns whether both nodes are currently up
-func (self *Conn) nodesUp() error {
- if !self.one.Up {
- return fmt.Errorf("one %v is not up", self.One)
+func (c *Conn) nodesUp() error {
+ if !c.one.Up {
+ return fmt.Errorf("one %v is not up", c.One)
}
- if !self.other.Up {
- return fmt.Errorf("other %v is not up", self.Other)
+ if !c.other.Up {
+ return fmt.Errorf("other %v is not up", c.Other)
}
return nil
}
// String returns a log-friendly string
-func (self *Conn) String() string {
- return fmt.Sprintf("Conn %v->%v", self.One.TerminalString(), self.Other.TerminalString())
+func (c *Conn) String() string {
+ return fmt.Sprintf("Conn %v->%v", c.One.TerminalString(), c.Other.TerminalString())
}
// Msg represents a p2p message sent between two nodes in the network
@@ -605,8 +612,8 @@ type Msg struct {
}
// String returns a log-friendly string
-func (self *Msg) String() string {
- return fmt.Sprintf("Msg(%d) %v->%v", self.Code, self.One.TerminalString(), self.Other.TerminalString())
+func (m *Msg) String() string {
+ return fmt.Sprintf("Msg(%d) %v->%v", m.Code, m.One.TerminalString(), m.Other.TerminalString())
}
// ConnLabel generates a deterministic string which represents a connection
@@ -640,14 +647,14 @@ type NodeSnapshot struct {
}
// Snapshot creates a network snapshot
-func (self *Network) Snapshot() (*Snapshot, error) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (net *Network) Snapshot() (*Snapshot, error) {
+ net.lock.Lock()
+ defer net.lock.Unlock()
snap := &Snapshot{
- Nodes: make([]NodeSnapshot, len(self.Nodes)),
- Conns: make([]Conn, len(self.Conns)),
+ Nodes: make([]NodeSnapshot, len(net.Nodes)),
+ Conns: make([]Conn, len(net.Conns)),
}
- for i, node := range self.Nodes {
+ for i, node := range net.Nodes {
snap.Nodes[i] = NodeSnapshot{Node: *node}
if !node.Up {
continue
@@ -658,33 +665,33 @@ func (self *Network) Snapshot() (*Snapshot, error) {
}
snap.Nodes[i].Snapshots = snapshots
}
- for i, conn := range self.Conns {
+ for i, conn := range net.Conns {
snap.Conns[i] = *conn
}
return snap, nil
}
// Load loads a network snapshot
-func (self *Network) Load(snap *Snapshot) error {
+func (net *Network) Load(snap *Snapshot) error {
for _, n := range snap.Nodes {
- if _, err := self.NewNodeWithConfig(n.Node.Config); err != nil {
+ if _, err := net.NewNodeWithConfig(n.Node.Config); err != nil {
return err
}
if !n.Node.Up {
continue
}
- if err := self.startWithSnapshots(n.Node.Config.ID, n.Snapshots); err != nil {
+ if err := net.startWithSnapshots(n.Node.Config.ID, n.Snapshots); err != nil {
return err
}
}
for _, conn := range snap.Conns {
- if !self.GetNode(conn.One).Up || !self.GetNode(conn.Other).Up {
+ if !net.GetNode(conn.One).Up || !net.GetNode(conn.Other).Up {
//in this case, at least one of the nodes of a connection is not up,
//so it would result in the snapshot `Load` to fail
continue
}
- if err := self.Connect(conn.One, conn.Other); err != nil {
+ if err := net.Connect(conn.One, conn.Other); err != nil {
return err
}
}
@@ -692,7 +699,7 @@ func (self *Network) Load(snap *Snapshot) error {
}
// Subscribe reads control events from a channel and executes them
-func (self *Network) Subscribe(events chan *Event) {
+func (net *Network) Subscribe(events chan *Event) {
for {
select {
case event, ok := <-events:
@@ -700,23 +707,23 @@ func (self *Network) Subscribe(events chan *Event) {
return
}
if event.Control {
- self.executeControlEvent(event)
+ net.executeControlEvent(event)
}
- case <-self.quitc:
+ case <-net.quitc:
return
}
}
}
-func (self *Network) executeControlEvent(event *Event) {
+func (net *Network) executeControlEvent(event *Event) {
log.Trace("execute control event", "type", event.Type, "event", event)
switch event.Type {
case EventTypeNode:
- if err := self.executeNodeEvent(event); err != nil {
+ if err := net.executeNodeEvent(event); err != nil {
log.Error("error executing node event", "event", event, "err", err)
}
case EventTypeConn:
- if err := self.executeConnEvent(event); err != nil {
+ if err := net.executeConnEvent(event); err != nil {
log.Error("error executing conn event", "event", event, "err", err)
}
case EventTypeMsg:
@@ -724,21 +731,21 @@ func (self *Network) executeControlEvent(event *Event) {
}
}
-func (self *Network) executeNodeEvent(e *Event) error {
+func (net *Network) executeNodeEvent(e *Event) error {
if !e.Node.Up {
- return self.Stop(e.Node.ID())
+ return net.Stop(e.Node.ID())
}
- if _, err := self.NewNodeWithConfig(e.Node.Config); err != nil {
+ if _, err := net.NewNodeWithConfig(e.Node.Config); err != nil {
return err
}
- return self.Start(e.Node.ID())
+ return net.Start(e.Node.ID())
}
-func (self *Network) executeConnEvent(e *Event) error {
+func (net *Network) executeConnEvent(e *Event) error {
if e.Conn.Up {
- return self.Connect(e.Conn.One, e.Conn.Other)
+ return net.Connect(e.Conn.One, e.Conn.Other)
} else {
- return self.Disconnect(e.Conn.One, e.Conn.Other)
+ return net.Disconnect(e.Conn.One, e.Conn.Other)
}
}
diff --git a/p2p/testing/peerpool.go b/p2p/testing/peerpool.go
index 6f8a5d7a52..d34b4c0780 100644
--- a/p2p/testing/peerpool.go
+++ b/p2p/testing/peerpool.go
@@ -39,29 +39,29 @@ func NewTestPeerPool() *TestPeerPool {
return &TestPeerPool{peers: make(map[discover.NodeID]TestPeer)}
}
-func (self *TestPeerPool) Add(p TestPeer) {
- self.lock.Lock()
- defer self.lock.Unlock()
+func (pp *TestPeerPool) Add(p TestPeer) {
+ pp.lock.Lock()
+ defer pp.lock.Unlock()
log.Trace(fmt.Sprintf("pp add peer %v", p.ID()))
- self.peers[p.ID()] = p
+ pp.peers[p.ID()] = p
}
-func (self *TestPeerPool) Remove(p TestPeer) {
- self.lock.Lock()
- defer self.lock.Unlock()
- delete(self.peers, p.ID())
+func (pp *TestPeerPool) Remove(p TestPeer) {
+ pp.lock.Lock()
+ defer pp.lock.Unlock()
+ delete(pp.peers, p.ID())
}
-func (self *TestPeerPool) Has(id discover.NodeID) bool {
- self.lock.Lock()
- defer self.lock.Unlock()
- _, ok := self.peers[id]
+func (pp *TestPeerPool) Has(id discover.NodeID) bool {
+ pp.lock.Lock()
+ defer pp.lock.Unlock()
+ _, ok := pp.peers[id]
return ok
}
-func (self *TestPeerPool) Get(id discover.NodeID) TestPeer {
- self.lock.Lock()
- defer self.lock.Unlock()
- return self.peers[id]
+func (pp *TestPeerPool) Get(id discover.NodeID) TestPeer {
+ pp.lock.Lock()
+ defer pp.lock.Unlock()
+ return pp.peers[id]
}
diff --git a/p2p/testing/protocolsession.go b/p2p/testing/protocolsession.go
index 2c0133b111..39ccc70bd0 100644
--- a/p2p/testing/protocolsession.go
+++ b/p2p/testing/protocolsession.go
@@ -78,10 +78,10 @@ type Disconnect struct {
}
// trigger sends messages from peers
-func (self *ProtocolSession) trigger(trig Trigger) error {
- simNode, ok := self.adapter.GetNode(trig.Peer)
+func (ps *ProtocolSession) trigger(trig Trigger) error {
+ simNode, ok := ps.adapter.GetNode(trig.Peer)
if !ok {
- return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.IDs))
+ return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(ps.IDs))
}
mockNode, ok := simNode.Services()[0].(*mockNode)
if !ok {
@@ -107,7 +107,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error {
}
// expect checks an expectation of a message sent out by the pivot node
-func (self *ProtocolSession) expect(exps []Expect) error {
+func (ps *ProtocolSession) expect(exps []Expect) error {
// construct a map of expectations for each node
peerExpects := make(map[discover.NodeID][]Expect)
for _, exp := range exps {
@@ -120,9 +120,9 @@ func (self *ProtocolSession) expect(exps []Expect) error {
// construct a map of mockNodes for each node
mockNodes := make(map[discover.NodeID]*mockNode)
for nodeID := range peerExpects {
- simNode, ok := self.adapter.GetNode(nodeID)
+ simNode, ok := ps.adapter.GetNode(nodeID)
if !ok {
- return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(self.IDs))
+ return fmt.Errorf("trigger: peer %v does not exist (1- %v)", nodeID, len(ps.IDs))
}
mockNode, ok := simNode.Services()[0].(*mockNode)
if !ok {
@@ -202,9 +202,9 @@ func (self *ProtocolSession) expect(exps []Expect) error {
}
// TestExchanges tests a series of exchanges against the session
-func (self *ProtocolSession) TestExchanges(exchanges ...Exchange) error {
+func (ps *ProtocolSession) TestExchanges(exchanges ...Exchange) error {
for i, e := range exchanges {
- if err := self.testExchange(e); err != nil {
+ if err := ps.testExchange(e); err != nil {
return fmt.Errorf("exchange #%d %q: %v", i, e.Label, err)
}
log.Trace(fmt.Sprintf("exchange #%d %q: run successfully", i, e.Label))
@@ -214,14 +214,14 @@ func (self *ProtocolSession) TestExchanges(exchanges ...Exchange) error {
// testExchange tests a single Exchange.
// Default timeout value is 2 seconds.
-func (self *ProtocolSession) testExchange(e Exchange) error {
+func (ps *ProtocolSession) testExchange(e Exchange) error {
errc := make(chan error)
done := make(chan struct{})
defer close(done)
go func() {
for _, trig := range e.Triggers {
- err := self.trigger(trig)
+ err := ps.trigger(trig)
if err != nil {
errc <- err
return
@@ -229,7 +229,7 @@ func (self *ProtocolSession) testExchange(e Exchange) error {
}
select {
- case errc <- self.expect(e.Expects):
+ case errc <- ps.expect(e.Expects):
case <-done:
}
}()
@@ -250,7 +250,7 @@ func (self *ProtocolSession) testExchange(e Exchange) error {
// TestDisconnected tests the disconnections given as arguments
// the disconnect structs describe what disconnect error is expected on which peer
-func (self *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
+func (ps *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
expects := make(map[discover.NodeID]error)
for _, disconnect := range disconnects {
expects[disconnect.Peer] = disconnect.Error
@@ -259,7 +259,7 @@ func (self *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error
timeout := time.After(time.Second)
for len(expects) > 0 {
select {
- case event := <-self.events:
+ case event := <-ps.events:
if event.Type != p2p.PeerEventTypeDrop {
continue
}
diff --git a/p2p/testing/protocoltester.go b/p2p/testing/protocoltester.go
index 21a57fd09c..622d63bfe3 100644
--- a/p2p/testing/protocoltester.go
+++ b/p2p/testing/protocoltester.go
@@ -100,24 +100,24 @@ func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Pe
}
// Stop stops the p2p server
-func (self *ProtocolTester) Stop() error {
- self.Server.Stop()
+func (pt *ProtocolTester) Stop() error {
+ pt.Server.Stop()
return nil
}
// Connect brings up the remote peer node and connects it using the
// p2p/simulations network connection with the in memory network adapter
-func (self *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) {
+func (pt *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) {
for _, peer := range peers {
log.Trace(fmt.Sprintf("start node %v", peer.ID))
- if _, err := self.network.NewNodeWithConfig(peer); err != nil {
+ if _, err := pt.network.NewNodeWithConfig(peer); err != nil {
panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
}
- if err := self.network.Start(peer.ID); err != nil {
+ if err := pt.network.Start(peer.ID); err != nil {
panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
}
log.Trace(fmt.Sprintf("connect to %v", peer.ID))
- if err := self.network.Connect(selfID, peer.ID); err != nil {
+ if err := pt.network.Connect(selfID, peer.ID); err != nil {
panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err))
}
}
@@ -130,25 +130,25 @@ type testNode struct {
run func(*p2p.Peer, p2p.MsgReadWriter) error
}
-func (t *testNode) Protocols() []p2p.Protocol {
+func (tn *testNode) Protocols() []p2p.Protocol {
return []p2p.Protocol{{
Length: 100,
- Run: t.run,
+ Run: tn.run,
}}
}
-func (t *testNode) APIs() []rpc.API {
+func (tn *testNode) APIs() []rpc.API {
return nil
}
-func (t *testNode) Start(server *p2p.Server) error {
+func (tn *testNode) Start(server *p2p.Server) error {
return nil
}
-func (t *testNode) SaveData() {
+func (tn *testNode) SaveData() {
}
-func (t *testNode) Stop() error {
+func (tn *testNode) Stop() error {
return nil
}
@@ -178,34 +178,34 @@ func newMockNode() *mockNode {
// Run is a protocol run function which just loops waiting for tests to
// instruct it to either trigger or expect a message from the peer
-func (m *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
+func (mn *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
for {
select {
- case trig := <-m.trigger:
- m.err <- p2p.Send(rw, trig.Code, trig.Msg)
- case exps := <-m.expect:
- m.err <- expectMsgs(rw, exps)
- case <-m.stop:
+ case trig := <-mn.trigger:
+ mn.err <- p2p.Send(rw, trig.Code, trig.Msg)
+ case exps := <-mn.expect:
+ mn.err <- expectMsgs(rw, exps)
+ case <-mn.stop:
return nil
}
}
}
-func (m *mockNode) Trigger(trig *Trigger) error {
- m.trigger <- trig
- return <-m.err
+func (mn *mockNode) Trigger(trig *Trigger) error {
+ mn.trigger <- trig
+ return <-mn.err
}
-func (m *mockNode) Expect(exp ...Expect) error {
- m.expect <- exp
- return <-m.err
+func (mn *mockNode) Expect(exp ...Expect) error {
+ mn.expect <- exp
+ return <-mn.err
}
-func (m *mockNode) SaveData() {
+func (mn *mockNode) SaveData() {
}
-func (m *mockNode) Stop() error {
- m.stopOnce.Do(func() { close(m.stop) })
+func (mn *mockNode) Stop() error {
+ mn.stopOnce.Do(func() { close(mn.stop) })
return nil
}
diff --git a/params/config.go b/params/config.go
index 73e54dd6c0..ba9b45212a 100644
--- a/params/config.go
+++ b/params/config.go
@@ -131,7 +131,7 @@ var (
SwitchRound: 13625855,
CertThreshold: 0.4,
TimeoutSyncThreshold: 3,
- TimeoutPeriod: 30,
+ TimeoutPeriod: 60,
MinePeriod: 2,
},
}
@@ -546,6 +546,26 @@ func (c *ChainConfig) String() string {
default:
engine = "unknown"
}
+ berlinBlock := common.BerlinBlock
+ if c.BerlinBlock != nil {
+ berlinBlock = c.BerlinBlock
+ }
+ londonBlock := common.LondonBlock
+ if c.LondonBlock != nil {
+ londonBlock = c.LondonBlock
+ }
+ mergeBlock := common.MergeBlock
+ if c.MergeBlock != nil {
+ mergeBlock = c.MergeBlock
+ }
+ shanghaiBlock := common.ShanghaiBlock
+ if c.ShanghaiBlock != nil {
+ shanghaiBlock = c.ShanghaiBlock
+ }
+ eip1559Block := common.Eip1559Block
+ if c.Eip1559Block != nil {
+ eip1559Block = c.Eip1559Block
+ }
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Istanbul: %v BerlinBlock: %v LondonBlock: %v MergeBlock: %v ShanghaiBlock: %v Eip1559Block: %v Engine: %v}",
c.ChainId,
c.HomesteadBlock,
@@ -557,11 +577,11 @@ func (c *ChainConfig) String() string {
c.ByzantiumBlock,
c.ConstantinopleBlock,
common.TIPXDCXCancellationFee,
- common.BerlinBlock,
- common.LondonBlock,
- common.MergeBlock,
- common.ShanghaiBlock,
- common.Eip1559Block,
+ berlinBlock,
+ londonBlock,
+ mergeBlock,
+ shanghaiBlock,
+ eip1559Block,
engine,
)
}
diff --git a/params/protocol_params.go b/params/protocol_params.go
index b8cd64f723..1799a24b0c 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -23,10 +23,10 @@ var (
)
const (
- GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
- MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
+ GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
+ MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
MaxGasLimit uint64 = 0x7fffffffffffffff // Maximum the gas limit (2^63-1).
- GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
+ GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
XDCGenesisGasLimit uint64 = 84000000
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
@@ -42,8 +42,10 @@ const (
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
CallStipend uint64 = 2300 // Free gas given at beginning of call.
- Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
- Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
+ Keccak256Gas uint64 = 30 // Once per KECCAK256 operation.
+ Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data.
+ InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract.
+
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change.
SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
@@ -64,10 +66,13 @@ const (
TxAccessListAddressGas uint64 = 2400 // Per address specified in EIP 2930 access list
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
-
- TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
- MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
+ TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
+
+ InitialBaseFee = 12500000000 // Initial base fee for EIP-1559 blocks.
+
+ MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
+ MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
// Precompiled contract gas prices
@@ -78,12 +83,16 @@ const (
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
IdentityBaseGas uint64 = 15 // Base price for a data copy operation
IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
- ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation
Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check
XDCXPriceGas uint64 = 1
+
+ // The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529,
+ // up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529
+ RefundQuotient uint64 = 2
+ RefundQuotientEIP3529 uint64 = 5
)
var (
@@ -108,6 +117,16 @@ const (
SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else
SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
+ ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
+ ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
+ WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
+
+ // In EIP-2200: SstoreResetGas was 5000.
+ // In EIP-2929: SstoreResetGas was changed to '5000 - COLD_SLOAD_COST'.
+ // In EIP-3529: SSTORE_CLEARS_SCHEDULE is defined as SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST
+ // Which becomes: 5000 - 2100 + 1900 = 4800
+ SstoreClearsScheduleRefundEIP3529 uint64 = SstoreResetGasEIP2200 - ColdSloadCostEIP2929 + TxAccessListStorageKeyGas
+
Create2Gas uint64 = 32000 // Once per CREATE2 operation
SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go
index ed502c09a7..fb9b12e0a1 100644
--- a/rlp/rlpgen/gen.go
+++ b/rlp/rlpgen/gen.go
@@ -440,7 +440,7 @@ func (op ptrOp) genWrite(ctx *genContext, v string) string {
var b bytes.Buffer
fmt.Fprintf(&b, "if %s == nil {\n", v)
- fmt.Fprintf(&b, " w.Write([]byte{0x%X})\n", op.nilValue)
+ fmt.Fprintf(&b, " w.Write([]byte{%#X})\n", op.nilValue)
fmt.Fprintf(&b, "} else {\n")
fmt.Fprintf(&b, " %s", op.elem.genWrite(ctx, vv))
fmt.Fprintf(&b, "}\n")
diff --git a/rpc/client.go b/rpc/client.go
index bc0375c521..a0fb5ebf43 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -112,6 +112,7 @@ type clientConn struct {
func (c *Client) newClientConn(conn ServerCodec) *clientConn {
ctx := context.WithValue(context.Background(), clientContextKey{}, c)
+ ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo())
handler := newHandler(ctx, conn, c.idgen, c.services)
return &clientConn{conn, handler}
}
@@ -473,7 +474,7 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
// Check type of channel first.
chanVal := reflect.ValueOf(channel)
if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 {
- panic("first argument to Subscribe must be a writable channel")
+ panic(fmt.Sprintf("channel argument of Subscribe has type %T, need writable channel", channel))
}
if chanVal.IsNil() {
panic("channel given to Subscribe must not be nil")
@@ -532,8 +533,8 @@ func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error
}
func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error {
- // The previous write failed. Try to establish a new connection.
if c.writeConn == nil {
+ // The previous write failed. Try to establish a new connection.
if err := c.reconnect(ctx); err != nil {
return err
}
diff --git a/rpc/endpoints.go b/rpc/endpoints.go
index 1f5770a45d..0b543102fc 100644
--- a/rpc/endpoints.go
+++ b/rpc/endpoints.go
@@ -36,6 +36,7 @@ func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, er
log.Info("IPC registration failed", "namespace", api.Namespace, "error", err)
return nil, nil, err
}
+ log.Debug("IPC registered", "namespace", api.Namespace)
if _, ok := regMap[api.Namespace]; !ok {
registered = append(registered, api.Namespace)
regMap[api.Namespace] = struct{}{}
diff --git a/rpc/handler.go b/rpc/handler.go
index 0e407abec9..777e47e7c8 100644
--- a/rpc/handler.go
+++ b/rpc/handler.go
@@ -340,12 +340,12 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
if callb != h.unsubscribeCb {
rpcRequestGauge.Inc(1)
if answer.Error != nil {
- failedReqeustGauge.Inc(1)
+ failedRequestGauge.Inc(1)
} else {
successfulRequestGauge.Inc(1)
}
rpcServingTimer.UpdateSince(start)
- newRPCServingTimer(msg.Method, answer.Error == nil).UpdateSince(start)
+ updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start))
}
return answer
}
diff --git a/rpc/http.go b/rpc/http.go
index e7b90acbe3..41c168d219 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -24,15 +24,10 @@ import (
"fmt"
"io"
"mime"
- "net"
"net/http"
"net/url"
- "strings"
"sync"
"time"
-
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/rs/cors"
)
const (
@@ -52,11 +47,18 @@ type httpConn struct {
headers http.Header
}
-// httpConn is treated specially by Client.
+// httpConn implements ServerCodec, but it is treated specially by Client
+// and some methods don't work. The panic() stubs here exist to ensure
+// this special treatment is correct.
+
func (hc *httpConn) writeJSON(context.Context, interface{}) error {
panic("writeJSON called on httpConn")
}
+func (hc *httpConn) peerInfo() PeerInfo {
+ panic("peerInfo called on httpConn")
+}
+
func (hc *httpConn) remoteAddr() string {
return hc.url
}
@@ -184,7 +186,7 @@ func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadClos
if err != nil {
return nil, err
}
- req, err := http.NewRequestWithContext(ctx, "POST", hc.url, io.NopCloser(bytes.NewReader(body)))
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, hc.url, io.NopCloser(bytes.NewReader(body)))
if err != nil {
return nil, err
}
@@ -230,23 +232,6 @@ func (t *httpServerConn) RemoteAddr() string {
// SetWriteDeadline does nothing and always returns nil.
func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil }
-// NewHTTPServer creates a new HTTP RPC server around an API provider.
-//
-// Deprecated: Server implements http.Handler
-func NewHTTPServer(cors []string, vhosts []string, srv *Server, writeTimeout time.Duration) *http.Server {
- // Wrap the CORS-handler within a host-handler
- handler := newCorsHandler(srv, cors)
- handler = newVHostHandler(vhosts, handler)
- handler = http.TimeoutHandler(handler, writeTimeout, `{"error":"http server timeout"}`)
- log.Info("NewHTTPServer", "writeTimeout", writeTimeout)
- return &http.Server{
- Handler: handler,
- ReadTimeout: 5 * time.Second,
- WriteTimeout: writeTimeout + time.Second,
- IdleTimeout: 120 * time.Second,
- }
-}
-
// ServeHTTP serves JSON-RPC requests over HTTP.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Permit dumb empty requests for remote health-checks (AWS)
@@ -258,20 +243,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), code)
return
}
+
+ // Create request-scoped context.
+ connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr}
+ connInfo.HTTP.Version = r.Proto
+ connInfo.HTTP.Host = r.Host
+ connInfo.HTTP.Origin = r.Header.Get("Origin")
+ connInfo.HTTP.UserAgent = r.Header.Get("User-Agent")
+ ctx := r.Context()
+ ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo)
+
// All checks passed, create a codec that reads directly from the request body
// until EOF, writes the response to w, and orders the server to process a
// single request.
- ctx := r.Context()
- ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
- ctx = context.WithValue(ctx, "scheme", r.Proto)
- ctx = context.WithValue(ctx, "local", r.Host)
- if ua := r.Header.Get("User-Agent"); ua != "" {
- ctx = context.WithValue(ctx, "User-Agent", ua)
- }
- if origin := r.Header.Get("Origin"); origin != "" {
- ctx = context.WithValue(ctx, "Origin", origin)
- }
-
w.Header().Set("content-type", contentType)
codec := newHTTPServerConn(r, w)
defer codec.close()
@@ -304,64 +288,3 @@ func validateRequest(r *http.Request) (int, error) {
err := fmt.Errorf("invalid content type, only %s is supported", contentType)
return http.StatusUnsupportedMediaType, err
}
-
-func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
- // disable CORS support if user has not specified a custom CORS configuration
- if len(allowedOrigins) == 0 {
- return srv
- }
- c := cors.New(cors.Options{
- AllowedOrigins: allowedOrigins,
- AllowedMethods: []string{http.MethodPost, http.MethodGet},
- MaxAge: 600,
- AllowedHeaders: []string{"*"},
- })
- return c.Handler(srv)
-}
-
-// virtualHostHandler is a handler which validates the Host-header of incoming requests.
-// The virtualHostHandler can prevent DNS rebinding attacks, which do not utilize CORS-headers,
-// since they do in-domain requests against the RPC api. Instead, we can see on the Host-header
-// which domain was used, and validate that against a whitelist.
-type virtualHostHandler struct {
- vhosts map[string]struct{}
- next http.Handler
-}
-
-func newVHostHandler(vhosts []string, next http.Handler) http.Handler {
- vhostMap := make(map[string]struct{})
- for _, allowedHost := range vhosts {
- vhostMap[strings.ToLower(allowedHost)] = struct{}{}
- }
- return &virtualHostHandler{vhostMap, next}
-}
-
-// ServeHTTP serves JSON-RPC requests over HTTP, implements http.Handler
-func (h *virtualHostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // if r.Host is not set, we can continue serving since a browser would set the Host header
- if r.Host == "" {
- h.next.ServeHTTP(w, r)
- return
- }
- host, _, err := net.SplitHostPort(r.Host)
- if err != nil {
- // Either invalid (too many colons) or no port specified
- host = r.Host
- }
- if ipAddr := net.ParseIP(host); ipAddr != nil {
- // It's an IP address, we can serve that
- h.next.ServeHTTP(w, r)
- return
-
- }
- // Not an ip address, but a hostname. Need to validate
- if _, exist := h.vhosts["*"]; exist {
- h.next.ServeHTTP(w, r)
- return
- }
- if _, exist := h.vhosts[host]; exist {
- h.next.ServeHTTP(w, r)
- return
- }
- http.Error(w, "invalid host specified", http.StatusForbidden)
-}
diff --git a/rpc/http_test.go b/rpc/http_test.go
index b75af67c52..73124fb350 100644
--- a/rpc/http_test.go
+++ b/rpc/http_test.go
@@ -92,6 +92,7 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body
if err != nil {
t.Fatalf("request failed: %v", err)
}
+ resp.Body.Close()
confirmStatusCode(t, resp.StatusCode, expectedStatusCode)
}
@@ -123,3 +124,39 @@ func TestHTTPRespBodyUnlimited(t *testing.T) {
t.Fatalf("response has wrong length %d, want %d", len(r), respLength)
}
}
+
+func TestHTTPPeerInfo(t *testing.T) {
+ s := newTestServer()
+ defer s.Stop()
+ ts := httptest.NewServer(s)
+ defer ts.Close()
+
+ c, err := Dial(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.SetHeader("user-agent", "ua-testing")
+ c.SetHeader("origin", "origin.example.com")
+
+ // Request peer information.
+ var info PeerInfo
+ if err := c.Call(&info, "test_peerInfo"); err != nil {
+ t.Fatal(err)
+ }
+
+ if info.RemoteAddr == "" {
+ t.Error("RemoteAddr not set")
+ }
+ if info.Transport != "http" {
+ t.Errorf("wrong Transport %q", info.Transport)
+ }
+ if info.HTTP.Version != "HTTP/1.1" {
+ t.Errorf("wrong HTTP.Version %q", info.HTTP.Version)
+ }
+ if info.HTTP.UserAgent != "ua-testing" {
+ t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent)
+ }
+ if info.HTTP.Origin != "origin.example.com" {
+ t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent)
+ }
+}
diff --git a/rpc/ipc.go b/rpc/ipc.go
index 77e06b4919..e617e37ca7 100644
--- a/rpc/ipc.go
+++ b/rpc/ipc.go
@@ -24,23 +24,17 @@ import (
"github.com/XinFinOrg/XDPoSChain/p2p/netutil"
)
-// CreateIPCListener creates an listener, on Unix platforms this is a unix socket, on
-// Windows this is a named pipe
-func CreateIPCListener(endpoint string) (net.Listener, error) {
- return ipcListen(endpoint)
-}
-
-// ServeListener accepts connections on l, serving JSON-RPC on them.
+// ServeListener accepts connections on l, serving IPC-RPC on them.
func (s *Server) ServeListener(l net.Listener) error {
for {
conn, err := l.Accept()
if netutil.IsTemporaryError(err) {
- log.Warn("RPC accept error", "err", err)
+ log.Warn("IPC accept error", "err", err)
continue
} else if err != nil {
return err
}
- log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr())
+ log.Trace("IPC accepted connection")
go s.ServeCodec(NewCodec(conn), 0)
}
}
diff --git a/rpc/json.go b/rpc/json.go
index 1daee3db82..6024f1e7dc 100644
--- a/rpc/json.go
+++ b/rpc/json.go
@@ -198,6 +198,11 @@ func NewCodec(conn Conn) ServerCodec {
return NewFuncCodec(conn, enc.Encode, dec.Decode)
}
+func (c *jsonCodec) peerInfo() PeerInfo {
+ // This returns "ipc" because all other built-in transports have a separate codec type.
+ return PeerInfo{Transport: "ipc", RemoteAddr: c.remote}
+}
+
func (c *jsonCodec) remoteAddr() string {
return c.remote
}
diff --git a/rpc/metrics.go b/rpc/metrics.go
index ebb407fa3d..ea8837f666 100644
--- a/rpc/metrics.go
+++ b/rpc/metrics.go
@@ -18,6 +18,7 @@ package rpc
import (
"fmt"
+ "time"
"github.com/XinFinOrg/XDPoSChain/metrics"
)
@@ -25,15 +26,25 @@ import (
var (
rpcRequestGauge = metrics.NewRegisteredGauge("rpc/requests", nil)
successfulRequestGauge = metrics.NewRegisteredGauge("rpc/success", nil)
- failedReqeustGauge = metrics.NewRegisteredGauge("rpc/failure", nil)
- rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil)
+ failedRequestGauge = metrics.NewRegisteredGauge("rpc/failure", nil)
+
+ // serveTimeHistName is the prefix of the per-request serving time histograms.
+ serveTimeHistName = "rpc/duration"
+
+ rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil)
)
-func newRPCServingTimer(method string, valid bool) metrics.Timer {
- flag := "success"
- if !valid {
- flag = "failure"
+// updateServeTimeHistogram tracks the serving time of a remote RPC call.
+func updateServeTimeHistogram(method string, success bool, elapsed time.Duration) {
+ note := "success"
+ if !success {
+ note = "failure"
}
- m := fmt.Sprintf("rpc/duration/%s/%s", method, flag)
- return metrics.GetOrRegisterTimer(m, nil)
+ h := fmt.Sprintf("%s/%s/%s", serveTimeHistName, method, note)
+ sampler := func() metrics.Sample {
+ return metrics.ResettingSample(
+ metrics.NewExpDecaySample(1028, 0.015),
+ )
+ }
+ metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds())
}
diff --git a/rpc/server.go b/rpc/server.go
index 43573a2a41..a1686bd008 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -145,3 +145,38 @@ func (s *RPCService) Modules() map[string]string {
}
return modules
}
+
+// PeerInfo contains information about the remote end of the network connection.
+//
+// This is available within RPC method handlers through the context. Call
+// PeerInfoFromContext to get information about the client connection related to
+// the current method call.
+type PeerInfo struct {
+ // Transport is name of the protocol used by the client.
+ // This can be "http", "ws" or "ipc".
+ Transport string
+
+ // Address of client. This will usually contain the IP address and port.
+ RemoteAddr string
+
+ // Addditional information for HTTP and WebSocket connections.
+ HTTP struct {
+ // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket.
+ Version string
+ // Header values sent by the client.
+ UserAgent string
+ Origin string
+ Host string
+ }
+}
+
+type peerInfoContextKey struct{}
+
+// PeerInfoFromContext returns information about the client's network connection.
+// Use this with the context passed to RPC method handler functions.
+//
+// The zero value is returned if no connection info is present in ctx.
+func PeerInfoFromContext(ctx context.Context) PeerInfo {
+ info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo)
+ return info
+}
diff --git a/rpc/server_test.go b/rpc/server_test.go
index 1af285bc84..f641603a96 100644
--- a/rpc/server_test.go
+++ b/rpc/server_test.go
@@ -45,7 +45,7 @@ func TestServerRegisterName(t *testing.T) {
t.Fatalf("Expected service calc to be registered")
}
- wantCallbacks := 10
+ wantCallbacks := 11
if len(svc.callbacks) != wantCallbacks {
t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks))
}
diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go
index 22bd64d2f6..584473bef0 100644
--- a/rpc/testservice_test.go
+++ b/rpc/testservice_test.go
@@ -80,6 +80,10 @@ func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *
return echoResult{str, i, args}
}
+func (s *testService) PeerInfo(ctx context.Context) PeerInfo {
+ return PeerInfoFromContext(ctx)
+}
+
func (s *testService) Sleep(ctx context.Context, duration time.Duration) {
time.Sleep(duration)
}
diff --git a/rpc/types.go b/rpc/types.go
index f3b50a259b..366d6d1e98 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -51,8 +51,10 @@ type DataError interface {
// a RPC session. Implementations must be go-routine safe since the codec can be called in
// multiple go-routines concurrently.
type ServerCodec interface {
+ peerInfo() PeerInfo
readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error)
close()
+
jsonWriter
}
diff --git a/rpc/websocket.go b/rpc/websocket.go
index c1c5b1675b..7c7cae4554 100644
--- a/rpc/websocket.go
+++ b/rpc/websocket.go
@@ -59,18 +59,11 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler {
log.Debug("WebSocket upgrade failed", "err", err)
return
}
- codec := newWebsocketCodec(conn)
+ codec := newWebsocketCodec(conn, r.Host, r.Header)
s.ServeCodec(codec, 0)
})
}
-// NewWSServer creates a new websocket RPC server around an API provider.
-//
-// Deprecated: use Server.WebsocketHandler
-func NewWSServer(allowedOrigins []string, srv *Server) *http.Server {
- return &http.Server{Handler: srv.WebsocketHandler(allowedOrigins)}
-}
-
// wsHandshakeValidator returns a handler that verifies the origin during the
// websocket upgrade process. When a '*' is specified as an allowed origins all
// connections are accepted.
@@ -103,7 +96,7 @@ func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool {
if _, ok := req.Header["Origin"]; !ok {
return true
}
- // Verify origin against whitelist.
+ // Verify origin against allow list.
origin := strings.ToLower(req.Header.Get("Origin"))
if allowAllOrigins || originIsAllowed(origins, origin) {
return true
@@ -203,7 +196,7 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale
}
return nil, hErr
}
- return newWebsocketCodec(conn), nil
+ return newWebsocketCodec(conn, endpoint, header), nil
})
}
@@ -241,18 +234,28 @@ func wsClientHeaders(endpoint, origin string) (string, http.Header, error) {
type websocketCodec struct {
*jsonCodec
conn *websocket.Conn
+ info PeerInfo
wg sync.WaitGroup
pingReset chan struct{}
}
-func newWebsocketCodec(conn *websocket.Conn) ServerCodec {
+func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header) ServerCodec {
conn.SetReadLimit(wsMessageSizeLimit)
wc := &websocketCodec{
jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec),
conn: conn,
pingReset: make(chan struct{}, 1),
+ info: PeerInfo{
+ Transport: "ws",
+ RemoteAddr: conn.RemoteAddr().String(),
+ },
}
+ // Fill in connection details.
+ wc.info.HTTP.Host = host
+ wc.info.HTTP.Origin = req.Get("Origin")
+ wc.info.HTTP.UserAgent = req.Get("User-Agent")
+ // Start pinger.
wc.wg.Add(1)
go wc.pingLoop()
return wc
@@ -263,6 +266,10 @@ func (wc *websocketCodec) close() {
wc.wg.Wait()
}
+func (wc *websocketCodec) peerInfo() PeerInfo {
+ return wc.info
+}
+
func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error {
err := wc.jsonCodec.writeJSON(ctx, v)
if err == nil {
diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go
index 37ed19476f..50b52ba227 100644
--- a/rpc/websocket_test.go
+++ b/rpc/websocket_test.go
@@ -113,6 +113,41 @@ func TestWebsocketLargeCall(t *testing.T) {
}
}
+func TestWebsocketPeerInfo(t *testing.T) {
+ var (
+ s = newTestServer()
+ ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"}))
+ tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:")
+ )
+ defer s.Stop()
+ defer ts.Close()
+
+ ctx := context.Background()
+ c, err := DialWebsocket(ctx, tsurl, "origin.example.com")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Request peer information.
+ var connInfo PeerInfo
+ if err := c.Call(&connInfo, "test_peerInfo"); err != nil {
+ t.Fatal(err)
+ }
+
+ if connInfo.RemoteAddr == "" {
+ t.Error("RemoteAddr not set")
+ }
+ if connInfo.Transport != "ws" {
+ t.Errorf("wrong Transport %q", connInfo.Transport)
+ }
+ if connInfo.HTTP.UserAgent != "Go-http-client/1.1" {
+ t.Errorf("wrong HTTP.UserAgent %q", connInfo.HTTP.UserAgent)
+ }
+ if connInfo.HTTP.Origin != "origin.example.com" {
+ t.Errorf("wrong HTTP.Origin %q", connInfo.HTTP.UserAgent)
+ }
+}
+
// This test checks that client handles WebSocket ping frames correctly.
func TestClientWebsocketPing(t *testing.T) {
t.Parallel()
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 72e9f21080..44d1c1b4d7 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -21,17 +21,16 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
- "errors"
"fmt"
"math/big"
-
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
+ "os"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
+ "github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
@@ -58,9 +57,10 @@ type btJSON struct {
}
type btBlock struct {
- BlockHeader *btHeader
- Rlp string
- UncleHeaders []*btHeader
+ BlockHeader *btHeader
+ ExpectException string
+ Rlp string
+ UncleHeaders []*btHeader
}
//go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
@@ -82,6 +82,7 @@ type btHeader struct {
GasLimit uint64
GasUsed uint64
Timestamp *big.Int
+ BaseFee *big.Int
}
type btHeaderMarshaling struct {
@@ -91,6 +92,7 @@ type btHeaderMarshaling struct {
GasLimit math.HexOrDecimal64
GasUsed math.HexOrDecimal64
Timestamp *math.HexOrDecimal256
+ BaseFee *math.HexOrDecimal256
}
func (t *BlockTest) Run() error {
@@ -106,7 +108,7 @@ func (t *BlockTest) Run() error {
return err
}
if gblock.Hash() != t.json.Genesis.Hash {
- return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", gblock.Hash().Bytes(), t.json.Genesis.Hash)
+ return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes(), t.json.Genesis.Hash)
}
if gblock.Root() != t.json.Genesis.StateRoot {
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes(), t.json.Genesis.StateRoot)
@@ -149,6 +151,7 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
Mixhash: t.json.Genesis.MixHash,
Coinbase: t.json.Genesis.Coinbase,
Alloc: t.json.Pre,
+ BaseFee: t.json.Genesis.BaseFee,
}
}
@@ -168,13 +171,13 @@ See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
validBlocks := make([]btBlock, 0)
// insert the test blocks, which will execute all transactions
- for _, b := range t.json.Blocks {
+ for bi, b := range t.json.Blocks {
cb, err := b.decode()
if err != nil {
if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block
} else {
- return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
+ return nil, fmt.Errorf("block RLP decoding failed when expected to succeed: %v", err)
}
}
// RLP decoding worked, try to insert into chain:
@@ -184,16 +187,21 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block
} else {
- return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
+ return nil, fmt.Errorf("block #%v insertion into chain failed: %v", blocks[i].Number(), err)
}
}
if b.BlockHeader == nil {
- return nil, errors.New("Block insertion should have failed")
+ if data, err := json.MarshalIndent(cb.Header(), "", " "); err == nil {
+ fmt.Fprintf(os.Stderr, "block (index %d) insertion should have failed due to: %v:\n%v\n",
+ bi, b.ExpectException, string(data))
+ }
+ return nil, fmt.Errorf("block (index %d) insertion should have failed due to: %v",
+ bi, b.ExpectException)
}
// validate RLP decoding by checking all values against test file JSON
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
- return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
+ return nil, fmt.Errorf("deserialised block header validation failed: %v", err)
}
validBlocks = append(validBlocks, b)
}
@@ -202,49 +210,49 @@ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error)
func validateHeader(h *btHeader, h2 *types.Header) error {
if h.Bloom != h2.Bloom {
- return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
+ return fmt.Errorf("mismatch Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
}
if h.Coinbase != h2.Coinbase {
- return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
+ return fmt.Errorf("mismatch Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
}
if h.MixHash != h2.MixDigest {
- return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
+ return fmt.Errorf("mismatch MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
}
if h.Nonce != h2.Nonce {
- return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
+ return fmt.Errorf("mismatch Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
}
if h.Number.Cmp(h2.Number) != 0 {
- return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
+ return fmt.Errorf("mismatch Number: want: %v have: %v", h.Number, h2.Number)
}
if h.ParentHash != h2.ParentHash {
- return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
+ return fmt.Errorf("mismatch Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
}
if h.ReceiptTrie != h2.ReceiptHash {
- return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
+ return fmt.Errorf("mismatch Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
}
if h.TransactionsTrie != h2.TxHash {
- return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
+ return fmt.Errorf("mismatch tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
}
if h.StateRoot != h2.Root {
- return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
+ return fmt.Errorf("mismatch state hash: want: %x have: %x", h.StateRoot, h2.Root)
}
if h.UncleHash != h2.UncleHash {
- return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
+ return fmt.Errorf("mismatch UncleHash: want: %x have: %x", h.UncleHash, h2.UncleHash)
}
if !bytes.Equal(h.ExtraData, h2.Extra) {
- return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
+ return fmt.Errorf("mismatch ExtraData: want: %x have: %x", h.ExtraData, h2.Extra)
}
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
- return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
+ return fmt.Errorf("mismatch difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
}
if h.GasLimit != h2.GasLimit {
- return fmt.Errorf("GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
+ return fmt.Errorf("mismatch GasLimit: want: %d have: %d", h.GasLimit, h2.GasLimit)
}
if h.GasUsed != h2.GasUsed {
- return fmt.Errorf("GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
+ return fmt.Errorf("mismatch GasUsed: want: %d have: %d", h.GasUsed, h2.GasUsed)
}
if h.Timestamp.Cmp(h2.Time) != 0 {
- return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
+ return fmt.Errorf("mismatch Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
}
return nil
}
@@ -282,7 +290,7 @@ func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []b
// be part of the longest chain until last block is imported.
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
- return fmt.Errorf("Imported block header validation failed: %v", err)
+ return fmt.Errorf("imported block header validation failed: %v", err)
}
}
return nil
diff --git a/common/bitutil/compress_fuzz.go b/tests/fuzzers/bitutil/compress_test.go
similarity index 53%
rename from common/bitutil/compress_fuzz.go
rename to tests/fuzzers/bitutil/compress_test.go
index 1b87f50edc..0c6ce21c9d 100644
--- a/common/bitutil/compress_fuzz.go
+++ b/tests/fuzzers/bitutil/compress_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,43 +14,55 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build gofuzz
-
package bitutil
-import "bytes"
+import (
+ "bytes"
+ "testing"
-// Fuzz implements a go-fuzz fuzzer method to test various encoding method
-// invocations.
-func Fuzz(data []byte) int {
- if len(data) == 0 {
- return -1
- }
- if data[0]%2 == 0 {
- return fuzzEncode(data[1:])
- }
- return fuzzDecode(data[1:])
+ "github.com/XinFinOrg/XDPoSChain/common/bitutil"
+)
+
+func FuzzEncoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzzEncode(data)
+ })
+}
+func FuzzDecoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzzDecode(data)
+ })
}
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
// decoding algorithm.
-func fuzzEncode(data []byte) int {
- proc, _ := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
+func fuzzEncode(data []byte) {
+ proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
if !bytes.Equal(data, proc) {
panic("content mismatch")
}
- return 0
}
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
// reencoding algorithm.
-func fuzzDecode(data []byte) int {
- blob, err := bitsetDecodeBytes(data, 1024)
+func fuzzDecode(data []byte) {
+ blob, err := bitutil.DecompressBytes(data, 1024)
if err != nil {
- return 0
+ return
}
- if comp := bitsetEncodeBytes(blob); !bytes.Equal(comp, data) {
+ // re-compress it (it's OK if the re-compressed differs from the
+ // original - the first input may not have been compressed at all)
+ comp := bitutil.CompressBytes(blob)
+ if len(comp) > len(blob) {
+ // After compression, it must be smaller or equal
+ panic("bad compression")
+ }
+ // But decompressing it once again should work
+ decomp, err := bitutil.DecompressBytes(data, 1024)
+ if err != nil {
+ panic(err)
+ }
+ if !bytes.Equal(decomp, blob) {
panic("content mismatch")
}
- return 0
}
diff --git a/tests/fuzzers/bn256/bn256_fuzz.go b/tests/fuzzers/bn256/bn256_fuzz.go
new file mode 100644
index 0000000000..972eaf4b5e
--- /dev/null
+++ b/tests/fuzzers/bn256/bn256_fuzz.go
@@ -0,0 +1,183 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bn256
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math/big"
+
+ cloudflare "github.com/XinFinOrg/XDPoSChain/crypto/bn256/cloudflare"
+ google "github.com/XinFinOrg/XDPoSChain/crypto/bn256/google"
+ "github.com/consensys/gnark-crypto/ecc/bn254"
+)
+
+func getG1Points(input io.Reader) (*cloudflare.G1, *google.G1, *bn254.G1Affine) {
+ _, xc, err := cloudflare.RandomG1(input)
+ if err != nil {
+ // insufficient input
+ return nil, nil, nil
+ }
+ xg := new(google.G1)
+ if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
+ panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err))
+ }
+ xs := new(bn254.G1Affine)
+ if err := xs.Unmarshal(xc.Marshal()); err != nil {
+ panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err))
+ }
+ return xc, xg, xs
+}
+
+func getG2Points(input io.Reader) (*cloudflare.G2, *google.G2, *bn254.G2Affine) {
+ _, xc, err := cloudflare.RandomG2(input)
+ if err != nil {
+ // insufficient input
+ return nil, nil, nil
+ }
+ xg := new(google.G2)
+ if _, err := xg.Unmarshal(xc.Marshal()); err != nil {
+ panic(fmt.Sprintf("Could not marshal cloudflare -> google: %v", err))
+ }
+ xs := new(bn254.G2Affine)
+ if err := xs.Unmarshal(xc.Marshal()); err != nil {
+ panic(fmt.Sprintf("Could not marshal cloudflare -> gnark: %v", err))
+ }
+ return xc, xg, xs
+}
+
+// fuzzAdd fuzzez bn256 addition between the Google and Cloudflare libraries.
+func fuzzAdd(data []byte) int {
+ input := bytes.NewReader(data)
+ xc, xg, xs := getG1Points(input)
+ if xc == nil {
+ return 0
+ }
+ yc, yg, ys := getG1Points(input)
+ if yc == nil {
+ return 0
+ }
+ // Ensure both libs can parse the second curve point
+ // Add the two points and ensure they result in the same output
+ rc := new(cloudflare.G1)
+ rc.Add(xc, yc)
+
+ rg := new(google.G1)
+ rg.Add(xg, yg)
+
+ tmpX := new(bn254.G1Jac).FromAffine(xs)
+ tmpY := new(bn254.G1Jac).FromAffine(ys)
+ rs := new(bn254.G1Affine).FromJacobian(tmpX.AddAssign(tmpY))
+
+ if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
+ panic("add mismatch: cloudflare/google")
+ }
+
+ if !bytes.Equal(rc.Marshal(), rs.Marshal()) {
+ panic("add mismatch: cloudflare/gnark")
+ }
+ return 1
+}
+
+// fuzzMul fuzzez bn256 scalar multiplication between the Google and Cloudflare
+// libraries.
+func fuzzMul(data []byte) int {
+ input := bytes.NewReader(data)
+ pc, pg, ps := getG1Points(input)
+ if pc == nil {
+ return 0
+ }
+ // Add the two points and ensure they result in the same output
+ remaining := input.Len()
+ if remaining == 0 {
+ return 0
+ }
+ if remaining > 128 {
+ // The evm only ever uses 32 byte integers, we need to cap this otherwise
+ // we run into slow exec. A 236Kb byte integer cause oss-fuzz to report it as slow.
+ // 128 bytes should be fine though
+ return 0
+ }
+ buf := make([]byte, remaining)
+ input.Read(buf)
+
+ rc := new(cloudflare.G1)
+ rc.ScalarMult(pc, new(big.Int).SetBytes(buf))
+
+ rg := new(google.G1)
+ rg.ScalarMult(pg, new(big.Int).SetBytes(buf))
+
+ rs := new(bn254.G1Jac)
+ psJac := new(bn254.G1Jac).FromAffine(ps)
+ rs.ScalarMultiplication(psJac, new(big.Int).SetBytes(buf))
+ rsAffine := new(bn254.G1Affine).FromJacobian(rs)
+
+ if !bytes.Equal(rc.Marshal(), rg.Marshal()) {
+ panic("scalar mul mismatch: cloudflare/google")
+ }
+ if !bytes.Equal(rc.Marshal(), rsAffine.Marshal()) {
+ panic("scalar mul mismatch: cloudflare/gnark")
+ }
+ return 1
+}
+
+func fuzzPair(data []byte) int {
+ input := bytes.NewReader(data)
+ pc, pg, ps := getG1Points(input)
+ if pc == nil {
+ return 0
+ }
+ tc, tg, ts := getG2Points(input)
+ if tc == nil {
+ return 0
+ }
+
+ // Pair the two points and ensure they result in the same output
+ clPair := cloudflare.Pair(pc, tc).Marshal()
+ gPair := google.Pair(pg, tg).Marshal()
+ if !bytes.Equal(clPair, gPair) {
+ panic("pairing mismatch: cloudflare/google")
+ }
+ cPair, err := bn254.Pair([]bn254.G1Affine{*ps}, []bn254.G2Affine{*ts})
+ if err != nil {
+ panic(fmt.Sprintf("gnark/bn254 encountered error: %v", err))
+ }
+
+ // gnark uses a different pairing algorithm which might produce
+ // different but also correct outputs, we need to scale the output by s
+
+ u, _ := new(big.Int).SetString("0x44e992b44a6909f1", 0)
+ u_exp2 := new(big.Int).Exp(u, big.NewInt(2), nil) // u^2
+ u_6_exp2 := new(big.Int).Mul(big.NewInt(6), u_exp2) // 6*u^2
+ u_3 := new(big.Int).Mul(big.NewInt(3), u) // 3*u
+ inner := u_6_exp2.Add(u_6_exp2, u_3) // 6*u^2 + 3*u
+ inner.Add(inner, big.NewInt(1)) // 6*u^2 + 3*u + 1
+ u_2 := new(big.Int).Mul(big.NewInt(2), u) // 2*u
+ s := u_2.Mul(u_2, inner) // 2*u(6*u^2 + 3*u + 1)
+
+ gRes := new(bn254.GT)
+ if err := gRes.SetBytes(clPair); err != nil {
+ panic(err)
+ }
+ gRes = gRes.Exp(*gRes, s)
+ if !bytes.Equal(cPair.Marshal(), gRes.Marshal()) {
+ panic("pairing mismatch: cloudflare/gnark")
+ }
+
+ return 1
+}
diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go
new file mode 100644
index 0000000000..1ffd845ba0
--- /dev/null
+++ b/tests/fuzzers/rlp/rlp_fuzzer.go
@@ -0,0 +1,143 @@
+// Copyright 2019 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package rlp
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+
+ "github.com/XinFinOrg/XDPoSChain/core/types"
+ "github.com/XinFinOrg/XDPoSChain/rlp"
+ "github.com/holiman/uint256"
+)
+
+func decodeEncode(input []byte, val interface{}, i int) {
+ if err := rlp.DecodeBytes(input, val); err == nil {
+ output, err := rlp.EncodeToBytes(val)
+ if err != nil {
+ panic(err)
+ }
+ if !bytes.Equal(input, output) {
+ panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output))
+ }
+ }
+}
+
+func fuzz(input []byte) int {
+ if len(input) == 0 {
+ return 0
+ }
+ if len(input) > 500*1024 {
+ return 0
+ }
+
+ var i int
+ {
+ rlp.Split(input)
+ }
+ {
+ if elems, _, err := rlp.SplitList(input); err == nil {
+ rlp.CountValues(elems)
+ }
+ }
+
+ {
+ rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
+ }
+
+ {
+ decodeEncode(input, new(interface{}), i)
+ i++
+ }
+ {
+ var v struct {
+ Int uint
+ String string
+ Bytes []byte
+ }
+ decodeEncode(input, &v, i)
+ i++
+ }
+
+ {
+ type Types struct {
+ Bool bool
+ Raw rlp.RawValue
+ Slice []*Types
+ Iface []interface{}
+ }
+ var v Types
+ decodeEncode(input, &v, i)
+ i++
+ }
+ {
+ type AllTypes struct {
+ Int uint
+ String string
+ Bytes []byte
+ Bool bool
+ Raw rlp.RawValue
+ Slice []*AllTypes
+ Array [3]*AllTypes
+ Iface []interface{}
+ }
+ var v AllTypes
+ decodeEncode(input, &v, i)
+ i++
+ }
+ {
+ decodeEncode(input, [10]byte{}, i)
+ i++
+ }
+ {
+ var v struct {
+ Byte [10]byte
+ Rool [10]bool
+ }
+ decodeEncode(input, &v, i)
+ i++
+ }
+ {
+ var h types.Header
+ decodeEncode(input, &h, i)
+ i++
+ var b types.Block
+ decodeEncode(input, &b, i)
+ i++
+ var t types.Transaction
+ decodeEncode(input, &t, i)
+ i++
+ var txs types.Transactions
+ decodeEncode(input, &txs, i)
+ i++
+ var rs types.Receipts
+ decodeEncode(input, &rs, i)
+ }
+ {
+ i++
+ var v struct {
+ AnIntPtr *big.Int
+ AnInt big.Int
+ AnU256Ptr *uint256.Int
+ AnU256 uint256.Int
+ NotAnU256 [4]uint64
+ }
+ decodeEncode(input, &v, i)
+ }
+ return 1
+}
diff --git a/mobile/geth_ios.go b/tests/fuzzers/rlp/rlp_test.go
similarity index 81%
rename from mobile/geth_ios.go
rename to tests/fuzzers/rlp/rlp_test.go
index 307cd08580..377b3961bf 100644
--- a/mobile/geth_ios.go
+++ b/tests/fuzzers/rlp/rlp_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,9 +14,12 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build ios
+package rlp
-package geth
+import "testing"
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "iGeth"
+func Fuzz(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzz(data)
+ })
+}
diff --git a/mobile/geth_android.go b/tests/fuzzers/runtime/runtime_test.go
similarity index 72%
rename from mobile/geth_android.go
rename to tests/fuzzers/runtime/runtime_test.go
index 8e4ebe638f..97d7cdc71f 100644
--- a/mobile/geth_android.go
+++ b/tests/fuzzers/runtime/runtime_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,9 +14,18 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build android
+package runtime
-package geth
+import (
+ "testing"
-// clientIdentifier is a hard coded identifier to report into the network.
-var clientIdentifier = "GethDroid"
+ "github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
+)
+
+func Fuzz(f *testing.F) {
+ f.Fuzz(func(t *testing.T, code, input []byte) {
+ runtime.Execute(code, input, &runtime.Config{
+ GasLimit: 12000000,
+ })
+ })
+}
diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go
new file mode 100644
index 0000000000..59bfe07cc2
--- /dev/null
+++ b/tests/fuzzers/secp256k1/secp_test.go
@@ -0,0 +1,54 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package secp256k1
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/XinFinOrg/XDPoSChain/crypto/secp256k1"
+ "github.com/btcsuite/btcd/btcec/v2"
+)
+
+func TestFuzzer(t *testing.T) {
+ a, b := "00000000N0000000/R0000000000000000", "0U0000S0000000mkhP000000000000000U"
+ fuzz([]byte(a), []byte(b))
+}
+
+func Fuzz(f *testing.F) {
+ f.Fuzz(func(t *testing.T, a, b []byte) {
+ fuzz(a, b)
+ })
+}
+
+func fuzz(dataP1, dataP2 []byte) int {
+ var (
+ curveA = secp256k1.S256()
+ curveB = btcec.S256()
+ )
+ // first point
+ x1, y1 := curveB.ScalarBaseMult(dataP1)
+ // second points
+ x2, y2 := curveB.ScalarBaseMult(dataP2)
+ resAX, resAY := curveA.Add(x1, y1, x2, y2)
+ resBX, resBY := curveB.Add(x1, y1, x2, y2)
+ if resAX.Cmp(resBX) != 0 || resAY.Cmp(resBY) != 0 {
+ fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
+ panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
+ }
+ return 0
+}
diff --git a/tests/gen_btheader.go b/tests/gen_btheader.go
index 003ca5fb39..abe99159cc 100644
--- a/tests/gen_btheader.go
+++ b/tests/gen_btheader.go
@@ -14,6 +14,7 @@ import (
var _ = (*btHeaderMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (b btHeader) MarshalJSON() ([]byte, error) {
type btHeader struct {
Bloom types.Bloom
@@ -32,6 +33,7 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
GasLimit math.HexOrDecimal64
GasUsed math.HexOrDecimal64
Timestamp *math.HexOrDecimal256
+ BaseFee *math.HexOrDecimal256
}
var enc btHeader
enc.Bloom = b.Bloom
@@ -50,9 +52,11 @@ func (b btHeader) MarshalJSON() ([]byte, error) {
enc.GasLimit = math.HexOrDecimal64(b.GasLimit)
enc.GasUsed = math.HexOrDecimal64(b.GasUsed)
enc.Timestamp = (*math.HexOrDecimal256)(b.Timestamp)
+ enc.BaseFee = (*math.HexOrDecimal256)(b.BaseFee)
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (b *btHeader) UnmarshalJSON(input []byte) error {
type btHeader struct {
Bloom *types.Bloom
@@ -71,6 +75,7 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
GasLimit *math.HexOrDecimal64
GasUsed *math.HexOrDecimal64
Timestamp *math.HexOrDecimal256
+ BaseFee *math.HexOrDecimal256
}
var dec btHeader
if err := json.Unmarshal(input, &dec); err != nil {
@@ -124,5 +129,8 @@ func (b *btHeader) UnmarshalJSON(input []byte) error {
if dec.Timestamp != nil {
b.Timestamp = (*big.Int)(dec.Timestamp)
}
+ if dec.BaseFee != nil {
+ b.BaseFee = (*big.Int)(dec.BaseFee)
+ }
return nil
}
diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go
index b4f90f6210..5a43270a9a 100644
--- a/tests/gen_stenv.go
+++ b/tests/gen_stenv.go
@@ -13,6 +13,7 @@ import (
var _ = (*stEnvMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct {
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
@@ -20,6 +21,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
}
var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
@@ -27,9 +29,11 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
enc.Number = math.HexOrDecimal64(s.Number)
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
+ enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (s *stEnv) UnmarshalJSON(input []byte) error {
type stEnv struct {
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
@@ -37,6 +41,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
}
var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil {
@@ -62,5 +67,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'currentTimestamp' for stEnv")
}
s.Timestamp = uint64(*dec.Timestamp)
+ if dec.BaseFee != nil {
+ s.BaseFee = (*big.Int)(dec.BaseFee)
+ }
return nil
}
diff --git a/tests/gen_sttransaction.go b/tests/gen_sttransaction.go
index d2ff5a9743..ac89893727 100644
--- a/tests/gen_sttransaction.go
+++ b/tests/gen_sttransaction.go
@@ -8,25 +8,33 @@ import (
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
+ "github.com/XinFinOrg/XDPoSChain/core/types"
)
var _ = (*stTransactionMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (s stTransaction) MarshalJSON() ([]byte, error) {
type stTransaction struct {
- GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
- Nonce math.HexOrDecimal64 `json:"nonce"`
- To string `json:"to"`
- Data []string `json:"data"`
- GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
- Value []string `json:"value"`
- PrivateKey hexutil.Bytes `json:"secretKey"`
+ GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
+ MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"`
+ MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"`
+ Nonce math.HexOrDecimal64 `json:"nonce"`
+ To string `json:"to"`
+ Data []string `json:"data"`
+ AccessLists []*types.AccessList `json:"accessLists,omitempty"`
+ GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
+ Value []string `json:"value"`
+ PrivateKey hexutil.Bytes `json:"secretKey"`
}
var enc stTransaction
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
+ enc.MaxFeePerGas = (*math.HexOrDecimal256)(s.MaxFeePerGas)
+ enc.MaxPriorityFeePerGas = (*math.HexOrDecimal256)(s.MaxPriorityFeePerGas)
enc.Nonce = math.HexOrDecimal64(s.Nonce)
enc.To = s.To
enc.Data = s.Data
+ enc.AccessLists = s.AccessLists
if s.GasLimit != nil {
enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit))
for k, v := range s.GasLimit {
@@ -38,15 +46,19 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (s *stTransaction) UnmarshalJSON(input []byte) error {
type stTransaction struct {
- GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
- Nonce *math.HexOrDecimal64 `json:"nonce"`
- To *string `json:"to"`
- Data []string `json:"data"`
- GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
- Value []string `json:"value"`
- PrivateKey *hexutil.Bytes `json:"secretKey"`
+ GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
+ MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"`
+ MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"`
+ Nonce *math.HexOrDecimal64 `json:"nonce"`
+ To *string `json:"to"`
+ Data []string `json:"data"`
+ AccessLists []*types.AccessList `json:"accessLists,omitempty"`
+ GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
+ Value []string `json:"value"`
+ PrivateKey *hexutil.Bytes `json:"secretKey"`
}
var dec stTransaction
if err := json.Unmarshal(input, &dec); err != nil {
@@ -55,6 +67,12 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
if dec.GasPrice != nil {
s.GasPrice = (*big.Int)(dec.GasPrice)
}
+ if dec.MaxFeePerGas != nil {
+ s.MaxFeePerGas = (*big.Int)(dec.MaxFeePerGas)
+ }
+ if dec.MaxPriorityFeePerGas != nil {
+ s.MaxPriorityFeePerGas = (*big.Int)(dec.MaxPriorityFeePerGas)
+ }
if dec.Nonce != nil {
s.Nonce = uint64(*dec.Nonce)
}
@@ -64,6 +82,9 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
if dec.Data != nil {
s.Data = dec.Data
}
+ if dec.AccessLists != nil {
+ s.AccessLists = dec.AccessLists
+ }
if dec.GasLimit != nil {
s.GasLimit = make([]uint64, len(dec.GasLimit))
for k, v := range dec.GasLimit {
diff --git a/tests/gen_vmexec.go b/tests/gen_vmexec.go
index f539a288f3..e75c23489d 100644
--- a/tests/gen_vmexec.go
+++ b/tests/gen_vmexec.go
@@ -14,6 +14,7 @@ import (
var _ = (*vmExecMarshaling)(nil)
+// MarshalJSON marshals as JSON.
func (v vmExec) MarshalJSON() ([]byte, error) {
type vmExec struct {
Address common.UnprefixedAddress `json:"address" gencodec:"required"`
@@ -37,6 +38,7 @@ func (v vmExec) MarshalJSON() ([]byte, error) {
return json.Marshal(&enc)
}
+// UnmarshalJSON unmarshals from JSON.
func (v *vmExec) UnmarshalJSON(input []byte) error {
type vmExec struct {
Address *common.UnprefixedAddress `json:"address" gencodec:"required"`
diff --git a/tests/init_test.go b/tests/init_test.go
index 53fe8b4120..b6499a2e19 100644
--- a/tests/init_test.go
+++ b/tests/init_test.go
@@ -87,10 +87,11 @@ func findLine(data []byte, offset int64) (line int) {
// testMatcher controls skipping and chain config assignment to tests.
type testMatcher struct {
- configpat []testConfig
- failpat []testFailure
- skiploadpat []*regexp.Regexp
- skipshortpat []*regexp.Regexp
+ configpat []testConfig
+ failpat []testFailure
+ skiploadpat []*regexp.Regexp
+ skipshortpat []*regexp.Regexp
+ runonlylistpat *regexp.Regexp
}
type testConfig struct {
@@ -121,6 +122,10 @@ func (tm *testMatcher) fails(pattern string, reason string) {
tm.failpat = append(tm.failpat, testFailure{regexp.MustCompile(pattern), reason})
}
+func (tm *testMatcher) runonly(pattern string) {
+ tm.runonlylistpat = regexp.MustCompile(pattern)
+}
+
// config defines chain config for tests matching the pattern.
func (tm *testMatcher) config(pattern string, cfg params.ChainConfig) {
tm.configpat = append(tm.configpat, testConfig{regexp.MustCompile(pattern), cfg})
@@ -209,6 +214,11 @@ func (tm *testMatcher) runTestFile(t *testing.T, path, name string, runTest inte
if r, _ := tm.findSkip(name); r != "" {
t.Skip(r)
}
+ if tm.runonlylistpat != nil {
+ if !tm.runonlylistpat.MatchString(name) {
+ t.Skip("Skipped by runonly")
+ }
+ }
t.Parallel()
// Load the file as map[string].
@@ -262,3 +272,14 @@ func runTestFunc(runTest interface{}, t *testing.T, name string, m reflect.Value
m.MapIndex(reflect.ValueOf(key)),
})
}
+
+func TestMatcherRunonlylist(t *testing.T) {
+ t.Parallel()
+ tm := new(testMatcher)
+ tm.runonly("invalid*")
+ tm.walk(t, rlpTestDir, func(t *testing.T, name string, test *RLPTest) {
+ if name[:len("invalidRLPTest.json")] != "invalidRLPTest.json" {
+ t.Fatalf("invalid test found: %s != invalidRLPTest.json", name)
+ }
+ })
+}
diff --git a/tests/state_test.go b/tests/state_test.go
index 79a7dc2a15..8bf60af264 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -31,9 +31,16 @@ func TestState(t *testing.T) {
st := new(testMatcher)
// Long tests:
st.skipShortMode(`^stQuadraticComplexityTest/`)
+
// Broken tests:
st.skipLoad(`^stTransactionTest/OverflowGasRequire\.json`) // gasLimit > 256 bits
st.skipLoad(`^stTransactionTest/zeroSigTransa[^/]*\.json`) // EIP-86 is not supported yet
+
+ // Uses 1GB RAM per tested fork
+ st.skipLoad(`^stStaticCall/static_Call1MB`)
+ // Un-skip this when https://github.com/ethereum/tests/issues/908 is closed
+ st.skipLoad(`^stQuadraticComplexityTest/QuadraticComplexitySolidity_CallDataCopy`)
+
// Expected failures:
st.fails(`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`, "bug in test")
st.fails(`^stRevertTest/RevertPrefoundEmptyOOG\.json/EIP158`, "bug in test")
@@ -67,17 +74,21 @@ func TestState(t *testing.T) {
const traceErrorLimit = 400000
func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
- err := test(vm.Config{})
+ // Use config from command line arguments.
+ config := vm.Config{}
+ err := test(config)
if err == nil {
return
}
- t.Error(err)
+
+ // Test failed, re-run with tracing enabled.
if gasLimit > traceErrorLimit {
t.Log("gas limit too high for EVM trace")
return
}
tracer := vm.NewStructLogger(nil)
- err2 := test(vm.Config{Debug: true, Tracer: tracer})
+ config.Debug, config.Tracer = true, tracer
+ err2 := test(config)
if !reflect.DeepEqual(err, err2) {
t.Errorf("different error for second run: %v", err2)
}
@@ -88,6 +99,6 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
} else {
t.Log("EVM operation log:\n" + buf.String())
}
- t.Logf("EVM output: 0x%x", tracer.Output())
+ t.Logf("EVM output: %#x", tracer.Output())
t.Logf("EVM error: %v", tracer.Error())
}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 742d2c5955..1452ecd5d6 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -19,6 +19,7 @@ package tests
import (
"encoding/hex"
"encoding/json"
+ "errors"
"fmt"
"math/big"
"strings"
@@ -32,10 +33,10 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
"github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/sha3"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
+ "golang.org/x/crypto/sha3"
)
// StateTest checks transaction processing without block context.
@@ -80,6 +81,7 @@ type stEnv struct {
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
}
type stEnvMarshaling struct {
@@ -88,25 +90,31 @@ type stEnvMarshaling struct {
GasLimit math.HexOrDecimal64
Number math.HexOrDecimal64
Timestamp math.HexOrDecimal64
+ BaseFee *math.HexOrDecimal256
}
//go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
type stTransaction struct {
- GasPrice *big.Int `json:"gasPrice"`
- Nonce uint64 `json:"nonce"`
- To string `json:"to"`
- Data []string `json:"data"`
- GasLimit []uint64 `json:"gasLimit"`
- Value []string `json:"value"`
- PrivateKey []byte `json:"secretKey"`
+ GasPrice *big.Int `json:"gasPrice"`
+ MaxFeePerGas *big.Int `json:"maxFeePerGas"`
+ MaxPriorityFeePerGas *big.Int `json:"maxPriorityFeePerGas"`
+ Nonce uint64 `json:"nonce"`
+ To string `json:"to"`
+ Data []string `json:"data"`
+ AccessLists []*types.AccessList `json:"accessLists,omitempty"`
+ GasLimit []uint64 `json:"gasLimit"`
+ Value []string `json:"value"`
+ PrivateKey []byte `json:"secretKey"`
}
type stTransactionMarshaling struct {
- GasPrice *math.HexOrDecimal256
- Nonce math.HexOrDecimal64
- GasLimit []math.HexOrDecimal64
- PrivateKey hexutil.Bytes
+ GasPrice *math.HexOrDecimal256
+ MaxFeePerGas *math.HexOrDecimal256
+ MaxPriorityFeePerGas *math.HexOrDecimal256
+ Nonce math.HexOrDecimal64
+ GasLimit []math.HexOrDecimal64
+ PrivateKey hexutil.Bytes
}
// Subtests returns all valid subtests of the test.
@@ -130,16 +138,27 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
db := rawdb.NewMemoryDatabase()
statedb := MakePreState(db, t.json.Pre)
+ var baseFee *big.Int
+ if config.IsEIP1559(new(big.Int)) {
+ baseFee = t.json.Env.BaseFee
+ if baseFee == nil {
+ // Retesteth uses `0x10` for genesis baseFee. Therefore, it defaults to
+ // parent - 2 : 0xa as the basefee for 'this' context.
+ baseFee = big.NewInt(common.BaseFee.Int64())
+ }
+ }
post := t.json.Post[subtest.Fork][subtest.Index]
- msg, err := t.json.Tx.toMessage(post, block.Number())
+ msg, err := t.json.Tx.toMessage(post, block.Number(), baseFee)
if err != nil {
return nil, err
}
// Prepare the EVM.
- context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
+ txContext := core.NewEVMTxContext(msg)
+ context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
context.GetHash = vmTestBlockHash
- evm := vm.NewEVM(context, statedb, nil, config, vmconfig)
+ context.BaseFee = baseFee
+ evm := vm.NewEVM(context, txContext, statedb, nil, config, vmconfig)
// Execute the message.
snapshot := statedb.Snapshot()
@@ -147,7 +166,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
gaspool.AddGas(block.GasLimit())
coinbase := &t.json.Env.Coinbase
- if _, _, _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
+ if _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
statedb.RevertToSnapshot(snapshot)
}
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
@@ -195,7 +214,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
}
}
-func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Message, error) {
+func (tx *stTransaction) toMessage(ps stPostState, number *big.Int, baseFee *big.Int) (core.Message, error) {
// Derive sender from private key if present.
var from common.Address
if len(tx.PrivateKey) > 0 {
@@ -240,12 +259,35 @@ func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Messag
if err != nil {
return nil, fmt.Errorf("invalid tx data %q", dataHex)
}
- msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, nil, true, nil, number)
+ var accessList types.AccessList
+ if tx.AccessLists != nil && tx.AccessLists[ps.Indexes.Data] != nil {
+ accessList = *tx.AccessLists[ps.Indexes.Data]
+ }
+ // If baseFee provided, set gasPrice to effectiveGasPrice.
+ gasPrice := tx.GasPrice
+ if baseFee != nil {
+ if tx.MaxFeePerGas == nil {
+ tx.MaxFeePerGas = gasPrice
+ }
+ if tx.MaxFeePerGas == nil {
+ tx.MaxFeePerGas = new(big.Int)
+ }
+ if tx.MaxPriorityFeePerGas == nil {
+ tx.MaxPriorityFeePerGas = tx.MaxFeePerGas
+ }
+ gasPrice = math.BigMin(new(big.Int).Add(tx.MaxPriorityFeePerGas, baseFee),
+ tx.MaxFeePerGas)
+ }
+ if gasPrice == nil {
+ return nil, errors.New("no gas price provided")
+ }
+
+ msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, tx.MaxFeePerGas, tx.MaxPriorityFeePerGas, data, accessList, false, nil, number)
return msg, nil
}
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewKeccak256()
+ hw := sha3.NewLegacyKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go
index 803e0f8d53..1b889c7e3a 100644
--- a/tests/transaction_test_util.go
+++ b/tests/transaction_test_util.go
@@ -83,7 +83,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
return err
}
if sender != common.BytesToAddress(tt.json.Sender) {
- return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender)
+ return fmt.Errorf("mismatch Sender: got %x, want %x", sender, tt.json.Sender)
}
// Check decoded fields.
err = tt.json.Transaction.verify(signer, tx)
@@ -98,36 +98,36 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error {
func (tt *ttTransaction) verify(signer types.Signer, tx *types.Transaction) error {
if !bytes.Equal(tx.Data(), tt.Data) {
- return fmt.Errorf("Tx input data mismatch: got %x want %x", tx.Data(), tt.Data)
+ return fmt.Errorf("mismatch tx input data: got %x want %x", tx.Data(), tt.Data)
}
if tx.Gas() != tt.GasLimit {
- return fmt.Errorf("GasLimit mismatch: got %d, want %d", tx.Gas(), tt.GasLimit)
+ return fmt.Errorf("mismatch GasLimit: got %d, want %d", tx.Gas(), tt.GasLimit)
}
if tx.GasPrice().Cmp(tt.GasPrice) != 0 {
- return fmt.Errorf("GasPrice mismatch: got %v, want %v", tx.GasPrice(), tt.GasPrice)
+ return fmt.Errorf("mismatch GasPrice: got %v, want %v", tx.GasPrice(), tt.GasPrice)
}
if tx.Nonce() != tt.Nonce {
- return fmt.Errorf("Nonce mismatch: got %v, want %v", tx.Nonce(), tt.Nonce)
+ return fmt.Errorf("mismatch Nonce: got %v, want %v", tx.Nonce(), tt.Nonce)
}
v, r, s := tx.RawSignatureValues()
if r.Cmp(tt.R) != 0 {
- return fmt.Errorf("R mismatch: got %v, want %v", r, tt.R)
+ return fmt.Errorf("mismatch R: got %v, want %v", r, tt.R)
}
if s.Cmp(tt.S) != 0 {
- return fmt.Errorf("S mismatch: got %v, want %v", s, tt.S)
+ return fmt.Errorf("mismatch S: got %v, want %v", s, tt.S)
}
if v.Cmp(tt.V) != 0 {
- return fmt.Errorf("V mismatch: got %v, want %v", v, tt.V)
+ return fmt.Errorf("mismatch V: got %v, want %v", v, tt.V)
}
if tx.To() == nil {
if tt.To != (common.Address{}) {
- return fmt.Errorf("To mismatch when recipient is nil (contract creation): %x", tt.To)
+ return fmt.Errorf("mismatch To when recipient is nil (contract creation): %x", tt.To)
}
} else if *tx.To() != tt.To {
- return fmt.Errorf("To mismatch: got %x, want %x", *tx.To(), tt.To)
+ return fmt.Errorf("mismatch To: got %x, want %x", *tx.To(), tt.To)
}
if tx.Value().Cmp(tt.Value) != 0 {
- return fmt.Errorf("Value mismatch: got %x, want %x", tx.Value(), tt.Value)
+ return fmt.Errorf("mismatch Value: got %x, want %x", tx.Value(), tt.Value)
}
return nil
}
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index b9bdffe485..0dfa4f6349 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -132,19 +132,21 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM {
return core.CanTransfer(db, address, amount)
}
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {}
- context := vm.Context{
+ txContext := vm.TxContext{
+ Origin: t.json.Exec.Origin,
+ GasPrice: t.json.Exec.GasPrice,
+ }
+ context := vm.BlockContext{
CanTransfer: canTransfer,
Transfer: transfer,
GetHash: vmTestBlockHash,
- Origin: t.json.Exec.Origin,
Coinbase: t.json.Env.Coinbase,
BlockNumber: new(big.Int).SetUint64(t.json.Env.Number),
Time: new(big.Int).SetUint64(t.json.Env.Timestamp),
GasLimit: t.json.Env.GasLimit,
Difficulty: t.json.Env.Difficulty,
- GasPrice: t.json.Exec.GasPrice,
}
- return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig)
+ return vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vmconfig)
}
func vmTestBlockHash(n uint64) common.Hash {
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index 532d541436..eb79f8dedb 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte {
h := newHasher(false)
h.sha.Reset()
h.sha.Write(key)
- buf := h.sha.Sum(t.hashKeyBuf[:0])
+ h.sha.Read(t.hashKeyBuf[:])
returnHasherToPool(h)
- return buf
+ return t.hashKeyBuf[:]
}
// getSecKeyCache returns the current secure key Cache, creating a new one if
diff --git a/trie/trie.go b/trie/trie.go
index f163182091..dea4ddbf1c 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -28,6 +28,8 @@ import (
)
var (
+ // TODO: remove file core/types/derive_sha.go, then remove emptyRoot and emptyState
+
// emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
@@ -241,7 +243,7 @@ func (t *Trie) tryGetAllLeftKeyAndValue(origNode Node, prefix []byte, limit []by
if err != nil {
return nil, nil, n, false, err
}
- if err == nil && didResolve {
+ if didResolve {
n = n.copy()
n.Children[i] = newnode
}
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 202fc76d80..9311cbae64 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -422,7 +422,7 @@ func runRandTest(rt randTest) bool {
v := tr.Get(step.key)
want := values[string(step.key)]
if string(v) != want {
- rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
+ rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want)
}
case opCommit:
_, rt[i].err = tr.Commit(nil)
diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go
deleted file mode 100644
index 4b668330e6..0000000000
--- a/whisper/mailserver/mailserver.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package mailserver
-
-import (
- "encoding/binary"
- "fmt"
-
- "github.com/XinFinOrg/XDPoSChain/cmd/utils"
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
- "github.com/syndtr/goleveldb/leveldb"
- "github.com/syndtr/goleveldb/leveldb/util"
-)
-
-type WMailServer struct {
- db *leveldb.DB
- w *whisper.Whisper
- pow float64
- key []byte
-}
-
-type DBKey struct {
- timestamp uint32
- hash common.Hash
- raw []byte
-}
-
-func NewDbKey(t uint32, h common.Hash) *DBKey {
- const sz = common.HashLength + 4
- var k DBKey
- k.timestamp = t
- k.hash = h
- k.raw = make([]byte, sz)
- binary.BigEndian.PutUint32(k.raw, k.timestamp)
- copy(k.raw[4:], k.hash[:])
- return &k
-}
-
-func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) {
- var err error
- if len(path) == 0 {
- utils.Fatalf("DB file is not specified")
- }
-
- if len(password) == 0 {
- utils.Fatalf("Password is not specified for MailServer")
- }
-
- s.db, err = leveldb.OpenFile(path, nil)
- if err != nil {
- utils.Fatalf("Failed to open DB file: %s", err)
- }
-
- s.w = shh
- s.pow = pow
-
- MailServerKeyID, err := s.w.AddSymKeyFromPassword(password)
- if err != nil {
- utils.Fatalf("Failed to create symmetric key for MailServer: %s", err)
- }
- s.key, err = s.w.GetSymKey(MailServerKeyID)
- if err != nil {
- utils.Fatalf("Failed to save symmetric key for MailServer")
- }
-}
-
-func (s *WMailServer) Close() {
- if s.db != nil {
- s.db.Close()
- }
-}
-
-func (s *WMailServer) Archive(env *whisper.Envelope) {
- key := NewDbKey(env.Expiry-env.TTL, env.Hash())
- rawEnvelope, err := rlp.EncodeToBytes(env)
- if err != nil {
- log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err))
- } else {
- err = s.db.Put(key.raw, rawEnvelope, nil)
- if err != nil {
- log.Error(fmt.Sprintf("Writing to DB failed: %s", err))
- }
- }
-}
-
-func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) {
- if peer == nil {
- log.Error("Whisper peer is nil")
- return
- }
-
- ok, lower, upper, bloom := s.validateRequest(peer.ID(), request)
- if ok {
- s.processRequest(peer, lower, upper, bloom)
- }
-}
-
-func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope {
- ret := make([]*whisper.Envelope, 0)
- var err error
- var zero common.Hash
- kl := NewDbKey(lower, zero)
- ku := NewDbKey(upper, zero)
- i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil)
- defer i.Release()
-
- for i.Next() {
- var envelope whisper.Envelope
- err = rlp.DecodeBytes(i.Value(), &envelope)
- if err != nil {
- log.Error(fmt.Sprintf("RLP decoding failed: %s", err))
- }
-
- if whisper.BloomFilterMatch(bloom, envelope.Bloom()) {
- if peer == nil {
- // used for test purposes
- ret = append(ret, &envelope)
- } else {
- err = s.w.SendP2PDirect(peer, &envelope)
- if err != nil {
- log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err))
- return nil
- }
- }
- }
- }
-
- err = i.Error()
- if err != nil {
- log.Error(fmt.Sprintf("Level DB iterator error: %s", err))
- }
-
- return ret
-}
-
-func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) {
- if s.pow > 0.0 && request.PoW() < s.pow {
- return false, 0, 0, nil
- }
-
- f := whisper.Filter{KeySym: s.key}
- decrypted := request.Open(&f)
- if decrypted == nil {
- log.Warn(fmt.Sprintf("Failed to decrypt p2p request"))
- return false, 0, 0, nil
- }
-
- src := crypto.FromECDSAPub(decrypted.Src)
- if len(src)-len(peerID) == 1 {
- src = src[1:]
- }
-
- // if you want to check the signature, you can do it here. e.g.:
- // if !bytes.Equal(peerID, src) {
- if src == nil {
- log.Warn(fmt.Sprintf("Wrong signature of p2p request"))
- return false, 0, 0, nil
- }
-
- var bloom []byte
- payloadSize := len(decrypted.Payload)
- if payloadSize < 8 {
- log.Warn(fmt.Sprintf("Undersized p2p request"))
- return false, 0, 0, nil
- } else if payloadSize == 8 {
- bloom = whisper.MakeFullNodeBloom()
- } else if payloadSize < 8+whisper.BloomFilterSize {
- log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request"))
- return false, 0, 0, nil
- } else {
- bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize]
- }
-
- lower := binary.BigEndian.Uint32(decrypted.Payload[:4])
- upper := binary.BigEndian.Uint32(decrypted.Payload[4:8])
- return true, lower, upper, bloom
-}
diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go
deleted file mode 100644
index ced1c156c2..0000000000
--- a/whisper/mailserver/server_test.go
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package mailserver
-
-import (
- "bytes"
- "crypto/ecdsa"
- "encoding/binary"
- "math/rand"
- "os"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
-)
-
-const powRequirement = 0.00001
-
-var keyID string
-var shh *whisper.Whisper
-var seed = time.Now().Unix()
-
-type ServerTestParams struct {
- topic whisper.TopicType
- low uint32
- upp uint32
- key *ecdsa.PrivateKey
-}
-
-func assert(statement bool, text string, t *testing.T) {
- if !statement {
- t.Fatal(text)
- }
-}
-
-func TestDBKey(t *testing.T) {
- var h common.Hash
- i := uint32(time.Now().Unix())
- k := NewDbKey(i, h)
- assert(len(k.raw) == common.HashLength+4, "wrong DB key length", t)
- assert(byte(i%0x100) == k.raw[3], "raw representation should be big endian", t)
- assert(byte(i/0x1000000) == k.raw[0], "big endian expected", t)
-}
-
-func generateEnvelope(t *testing.T) *whisper.Envelope {
- h := crypto.Keccak256Hash([]byte("test sample data"))
- params := &whisper.MessageParams{
- KeySym: h[:],
- Topic: whisper.TopicType{0x1F, 0x7E, 0xA1, 0x7F},
- Payload: []byte("test payload"),
- PoW: powRequirement,
- WorkTime: 2,
- }
-
- msg, err := whisper.NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed to wrap with seed %d: %s.", seed, err)
- }
- return env
-}
-
-func TestMailServer(t *testing.T) {
- const password = "password_for_this_test"
- const dbPath = "whisper-server-test"
-
- dir, err := os.MkdirTemp("", dbPath)
- if err != nil {
- t.Fatal(err)
- }
-
- var server WMailServer
- shh = whisper.New(&whisper.DefaultConfig)
- shh.RegisterServer(&server)
-
- server.Init(shh, dir, password, powRequirement)
- defer server.Close()
-
- keyID, err = shh.AddSymKeyFromPassword(password)
- if err != nil {
- t.Fatalf("Failed to create symmetric key for mail request: %s", err)
- }
-
- rand.Seed(seed)
- env := generateEnvelope(t)
- server.Archive(env)
- deliverTest(t, &server, env)
-}
-
-func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) {
- id, err := shh.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair with seed %d: %s.", seed, err)
- }
- testPeerID, err := shh.GetPrivateKey(id)
- if err != nil {
- t.Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err)
- }
- birth := env.Expiry - env.TTL
- p := &ServerTestParams{
- topic: env.Topic,
- low: birth - 1,
- upp: birth + 1,
- key: testPeerID,
- }
-
- singleRequest(t, server, env, p, true)
-
- p.low, p.upp = birth+1, 0xffffffff
- singleRequest(t, server, env, p, false)
-
- p.low, p.upp = 0, birth-1
- singleRequest(t, server, env, p, false)
-
- p.low = birth - 1
- p.upp = birth + 1
- p.topic[0] = 0xFF
- singleRequest(t, server, env, p, false)
-}
-
-func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *ServerTestParams, expect bool) {
- request := createRequest(t, p)
- src := crypto.FromECDSAPub(&p.key.PublicKey)
- ok, lower, upper, bloom := server.validateRequest(src, request)
- if !ok {
- t.Fatalf("request validation failed, seed: %d.", seed)
- }
- if lower != p.low {
- t.Fatalf("request validation failed (lower bound), seed: %d.", seed)
- }
- if upper != p.upp {
- t.Fatalf("request validation failed (upper bound), seed: %d.", seed)
- }
- expectedBloom := whisper.TopicToBloom(p.topic)
- if !bytes.Equal(bloom, expectedBloom) {
- t.Fatalf("request validation failed (topic), seed: %d.", seed)
- }
-
- var exist bool
- mail := server.processRequest(nil, p.low, p.upp, bloom)
- for _, msg := range mail {
- if msg.Hash() == env.Hash() {
- exist = true
- break
- }
- }
-
- if exist != expect {
- t.Fatalf("error: exist = %v, seed: %d.", exist, seed)
- }
-
- src[0]++
- ok, lower, upper, bloom = server.validateRequest(src, request)
- if !ok {
- // request should be valid regardless of signature
- t.Fatalf("request validation false negative, seed: %d (lower: %d, upper: %d).", seed, lower, upper)
- }
-}
-
-func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
- bloom := whisper.TopicToBloom(p.topic)
- data := make([]byte, 8)
- binary.BigEndian.PutUint32(data, p.low)
- binary.BigEndian.PutUint32(data[4:], p.upp)
- data = append(data, bloom...)
-
- key, err := shh.GetSymKey(keyID)
- if err != nil {
- t.Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err)
- }
-
- params := &whisper.MessageParams{
- KeySym: key,
- Topic: p.topic,
- Payload: data,
- PoW: powRequirement * 2,
- WorkTime: 2,
- Src: p.key,
- }
-
- msg, err := whisper.NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed to wrap with seed %d: %s.", seed, err)
- }
- return env
-}
diff --git a/whisper/shhclient/client.go b/whisper/shhclient/client.go
deleted file mode 100644
index f77bae0207..0000000000
--- a/whisper/shhclient/client.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package shhclient
-
-import (
- "context"
-
- "github.com/XinFinOrg/XDPoSChain"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/rpc"
- whisper "github.com/XinFinOrg/XDPoSChain/whisper/whisperv6"
-)
-
-// Client defines typed wrappers for the Whisper v6 RPC API.
-type Client struct {
- c *rpc.Client
-}
-
-// Dial connects a client to the given URL.
-func Dial(rawurl string) (*Client, error) {
- c, err := rpc.Dial(rawurl)
- if err != nil {
- return nil, err
- }
- return NewClient(c), nil
-}
-
-// NewClient creates a client that uses the given RPC client.
-func NewClient(c *rpc.Client) *Client {
- return &Client{c}
-}
-
-// Version returns the Whisper sub-protocol version.
-func (sc *Client) Version(ctx context.Context) (string, error) {
- var result string
- err := sc.c.CallContext(ctx, &result, "shh_version")
- return result, err
-}
-
-// Info returns diagnostic information about the whisper node.
-func (sc *Client) Info(ctx context.Context) (whisper.Info, error) {
- var info whisper.Info
- err := sc.c.CallContext(ctx, &info, "shh_info")
- return info, err
-}
-
-// SetMaxMessageSize sets the maximal message size allowed by this node. Incoming
-// and outgoing messages with a larger size will be rejected. Whisper message size
-// can never exceed the limit imposed by the underlying P2P protocol (10 Mb).
-func (sc *Client) SetMaxMessageSize(ctx context.Context, size uint32) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_setMaxMessageSize", size)
-}
-
-// SetMinimumPoW (experimental) sets the minimal PoW required by this node.
-
-// This experimental function was introduced for the future dynamic adjustment of
-// PoW requirement. If the node is overwhelmed with messages, it should raise the
-// PoW requirement and notify the peers. The new value should be set relative to
-// the old value (e.g. double). The old value could be obtained via shh_info call.
-func (sc *Client) SetMinimumPoW(ctx context.Context, pow float64) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_setMinPoW", pow)
-}
-
-// Marks specific peer trusted, which will allow it to send historic (expired) messages.
-// Note This function is not adding new nodes, the node needs to exists as a peer.
-func (sc *Client) MarkTrustedPeer(ctx context.Context, enode string) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_markTrustedPeer", enode)
-}
-
-// NewKeyPair generates a new public and private key pair for message decryption and encryption.
-// It returns an identifier that can be used to refer to the key.
-func (sc *Client) NewKeyPair(ctx context.Context) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_newKeyPair")
-}
-
-// AddPrivateKey stored the key pair, and returns its ID.
-func (sc *Client) AddPrivateKey(ctx context.Context, key []byte) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_addPrivateKey", hexutil.Bytes(key))
-}
-
-// DeleteKeyPair delete the specifies key.
-func (sc *Client) DeleteKeyPair(ctx context.Context, id string) (string, error) {
- var ignored bool
- return id, sc.c.CallContext(ctx, &ignored, "shh_deleteKeyPair", id)
-}
-
-// HasKeyPair returns an indication if the node has a private key or
-// key pair matching the given ID.
-func (sc *Client) HasKeyPair(ctx context.Context, id string) (bool, error) {
- var has bool
- return has, sc.c.CallContext(ctx, &has, "shh_hasKeyPair", id)
-}
-
-// PublicKey return the public key for a key ID.
-func (sc *Client) PublicKey(ctx context.Context, id string) ([]byte, error) {
- var key hexutil.Bytes
- return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPublicKey", id)
-}
-
-// PrivateKey return the private key for a key ID.
-func (sc *Client) PrivateKey(ctx context.Context, id string) ([]byte, error) {
- var key hexutil.Bytes
- return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPrivateKey", id)
-}
-
-// NewSymmetricKey generates a random symmetric key and returns its identifier.
-// Can be used encrypting and decrypting messages where the key is known to both parties.
-func (sc *Client) NewSymmetricKey(ctx context.Context) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_newSymKey")
-}
-
-// AddSymmetricKey stores the key, and returns its identifier.
-func (sc *Client) AddSymmetricKey(ctx context.Context, key []byte) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_addSymKey", hexutil.Bytes(key))
-}
-
-// GenerateSymmetricKeyFromPassword generates the key from password, stores it, and returns its identifier.
-func (sc *Client) GenerateSymmetricKeyFromPassword(ctx context.Context, passwd []byte) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_generateSymKeyFromPassword", hexutil.Bytes(passwd))
-}
-
-// HasSymmetricKey returns an indication if the key associated with the given id is stored in the node.
-func (sc *Client) HasSymmetricKey(ctx context.Context, id string) (bool, error) {
- var found bool
- return found, sc.c.CallContext(ctx, &found, "shh_hasSymKey", id)
-}
-
-// GetSymmetricKey returns the symmetric key associated with the given identifier.
-func (sc *Client) GetSymmetricKey(ctx context.Context, id string) ([]byte, error) {
- var key hexutil.Bytes
- return []byte(key), sc.c.CallContext(ctx, &key, "shh_getSymKey", id)
-}
-
-// DeleteSymmetricKey deletes the symmetric key associated with the given identifier.
-func (sc *Client) DeleteSymmetricKey(ctx context.Context, id string) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_deleteSymKey", id)
-}
-
-// Post a message onto the network.
-func (sc *Client) Post(ctx context.Context, message whisper.NewMessage) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_post", message)
-}
-
-// SubscribeMessages subscribes to messages that match the given criteria. This method
-// is only supported on bi-directional connections such as websockets and IPC.
-// NewMessageFilter uses polling and is supported over HTTP.
-func (sc *Client) SubscribeMessages(ctx context.Context, criteria whisper.Criteria, ch chan<- *whisper.Message) (XDPoSChain.Subscription, error) {
- return sc.c.ShhSubscribe(ctx, ch, "messages", criteria)
-}
-
-// NewMessageFilter creates a filter within the node. This filter can be used to poll
-// for new messages (see FilterMessages) that satisfy the given criteria. A filter can
-// timeout when it was polled for in whisper.filterTimeout.
-func (sc *Client) NewMessageFilter(ctx context.Context, criteria whisper.Criteria) (string, error) {
- var id string
- return id, sc.c.CallContext(ctx, &id, "shh_newMessageFilter", criteria)
-}
-
-// DeleteMessageFilter removes the filter associated with the given id.
-func (sc *Client) DeleteMessageFilter(ctx context.Context, id string) error {
- var ignored bool
- return sc.c.CallContext(ctx, &ignored, "shh_deleteMessageFilter", id)
-}
-
-// FilterMessages retrieves all messages that are received between the last call to
-// this function and match the criteria that where given when the filter was created.
-func (sc *Client) FilterMessages(ctx context.Context, id string) ([]*whisper.Message, error) {
- var messages []*whisper.Message
- return messages, sc.c.CallContext(ctx, &messages, "shh_getFilterMessages", id)
-}
diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go
deleted file mode 100644
index b28ea5075d..0000000000
--- a/whisper/whisperv5/api.go
+++ /dev/null
@@ -1,565 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "context"
- "crypto/ecdsa"
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p/discover"
- "github.com/XinFinOrg/XDPoSChain/rpc"
-)
-
-const (
- filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds
-)
-
-var (
- ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key")
- ErrInvalidSymmetricKey = errors.New("invalid symmetric key")
- ErrInvalidPublicKey = errors.New("invalid public key")
- ErrInvalidSigningPubKey = errors.New("invalid signing public key")
- ErrTooLowPoW = errors.New("message rejected, PoW too low")
- ErrNoTopics = errors.New("missing topic(s)")
-)
-
-// PublicWhisperAPI provides the whisper RPC service that can be
-// use publicly without security implications.
-type PublicWhisperAPI struct {
- w *Whisper
-
- mu sync.Mutex
- lastUsed map[string]time.Time // keeps track when a filter was polled for the last time.
-}
-
-// NewPublicWhisperAPI create a new RPC whisper service.
-func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
- api := &PublicWhisperAPI{
- w: w,
- lastUsed: make(map[string]time.Time),
- }
- return api
-}
-
-// Version returns the Whisper sub-protocol version.
-func (api *PublicWhisperAPI) Version(ctx context.Context) string {
- return ProtocolVersionStr
-}
-
-// Info contains diagnostic information.
-type Info struct {
- Memory int `json:"memory"` // Memory size of the floating messages in bytes.
- Messages int `json:"messages"` // Number of floating messages.
- MinPow float64 `json:"minPow"` // Minimal accepted PoW
- MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size
-}
-
-// Info returns diagnostic information about the whisper node.
-func (api *PublicWhisperAPI) Info(ctx context.Context) Info {
- stats := api.w.Stats()
- return Info{
- Memory: stats.memoryUsed,
- Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue),
- MinPow: api.w.MinPow(),
- MaxMessageSize: api.w.MaxMessageSize(),
- }
-}
-
-// SetMaxMessageSize sets the maximum message size that is accepted.
-// Upper limit is defined in whisperv5.MaxMessageSize.
-func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) {
- return true, api.w.SetMaxMessageSize(size)
-}
-
-// SetMinPow sets the minimum PoW for a message before it is accepted.
-func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) {
- return true, api.w.SetMinimumPoW(pow)
-}
-
-// MarkTrustedPeer marks a peer trusted. , which will allow it to send historic (expired) messages.
-// Note: This function is not adding new nodes, the node needs to exists as a peer.
-func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) {
- n, err := discover.ParseNode(enode)
- if err != nil {
- return false, err
- }
- return true, api.w.AllowP2PMessagesFromPeer(n.ID[:])
-}
-
-// NewKeyPair generates a new public and private key pair for message decryption and encryption.
-// It returns an ID that can be used to refer to the keypair.
-func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) {
- return api.w.NewKeyPair()
-}
-
-// AddPrivateKey imports the given private key.
-func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) {
- key, err := crypto.ToECDSA(privateKey)
- if err != nil {
- return "", err
- }
- return api.w.AddKeyPair(key)
-}
-
-// DeleteKeyPair removes the key with the given key if it exists.
-func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) {
- if ok := api.w.DeleteKeyPair(key); ok {
- return true, nil
- }
- return false, fmt.Errorf("key pair %s not found", key)
-}
-
-// HasKeyPair returns an indication if the node has a key pair that is associated with the given id.
-func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool {
- return api.w.HasKeyPair(id)
-}
-
-// GetPublicKey returns the public key associated with the given key. The key is the hex
-// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
-func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- key, err := api.w.GetPrivateKey(id)
- if err != nil {
- return hexutil.Bytes{}, err
- }
- return crypto.FromECDSAPub(&key.PublicKey), nil
-}
-
-// GetPublicKey returns the private key associated with the given key. The key is the hex
-// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
-func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- key, err := api.w.GetPrivateKey(id)
- if err != nil {
- return hexutil.Bytes{}, err
- }
- return crypto.FromECDSA(key), nil
-}
-
-// NewSymKey generate a random symmetric key.
-// It returns an ID that can be used to refer to the key.
-// Can be used encrypting and decrypting messages where the key is known to both parties.
-func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) {
- return api.w.GenerateSymKey()
-}
-
-// AddSymKey import a symmetric key.
-// It returns an ID that can be used to refer to the key.
-// Can be used encrypting and decrypting messages where the key is known to both parties.
-func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) {
- return api.w.AddSymKeyDirect([]byte(key))
-}
-
-// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID.
-func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) {
- return api.w.AddSymKeyFromPassword(passwd)
-}
-
-// HasSymKey returns an indication if the node has a symmetric key associated with the given key.
-func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool {
- return api.w.HasSymKey(id)
-}
-
-// GetSymKey returns the symmetric key associated with the given id.
-func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- return api.w.GetSymKey(id)
-}
-
-// DeleteSymKey deletes the symmetric key that is associated with the given id.
-func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool {
- return api.w.DeleteSymKey(id)
-}
-
-//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go
-
-// NewMessage represents a new whisper message that is posted through the RPC.
-type NewMessage struct {
- SymKeyID string `json:"symKeyID"`
- PublicKey []byte `json:"pubKey"`
- Sig string `json:"sig"`
- TTL uint32 `json:"ttl"`
- Topic TopicType `json:"topic"`
- Payload []byte `json:"payload"`
- Padding []byte `json:"padding"`
- PowTime uint32 `json:"powTime"`
- PowTarget float64 `json:"powTarget"`
- TargetPeer string `json:"targetPeer"`
-}
-
-type newMessageOverride struct {
- PublicKey hexutil.Bytes
- Payload hexutil.Bytes
- Padding hexutil.Bytes
-}
-
-// Post a message on the Whisper network.
-func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) {
- var (
- symKeyGiven = len(req.SymKeyID) > 0
- pubKeyGiven = len(req.PublicKey) > 0
- err error
- )
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) {
- return false, ErrSymAsym
- }
-
- params := &MessageParams{
- TTL: req.TTL,
- Payload: req.Payload,
- Padding: req.Padding,
- WorkTime: req.PowTime,
- PoW: req.PowTarget,
- Topic: req.Topic,
- }
-
- // Set key that is used to sign the message
- if len(req.Sig) > 0 {
- if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil {
- return false, err
- }
- }
-
- // Set symmetric key that is used to encrypt the message
- if symKeyGiven {
- if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption
- return false, ErrNoTopics
- }
- if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
- return false, err
- }
- if !validateSymmetricKey(params.KeySym) {
- return false, ErrInvalidSymmetricKey
- }
- }
-
- // Set asymmetric key that is used to encrypt the message
- if pubKeyGiven {
- if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
- return false, ErrInvalidPublicKey
- }
- }
-
- // encrypt and sent message
- whisperMsg, err := NewSentMessage(params)
- if err != nil {
- return false, err
- }
-
- env, err := whisperMsg.Wrap(params)
- if err != nil {
- return false, err
- }
-
- // send to specific node (skip PoW check)
- if len(req.TargetPeer) > 0 {
- n, err := discover.ParseNode(req.TargetPeer)
- if err != nil {
- return false, fmt.Errorf("failed to parse target peer: %s", err)
- }
- return true, api.w.SendP2PMessage(n.ID[:], env)
- }
-
- // ensure that the message PoW meets the node's minimum accepted PoW
- if req.PowTarget < api.w.MinPow() {
- return false, ErrTooLowPoW
- }
-
- return true, api.w.Send(env)
-}
-
-//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
-
-// Criteria holds various filter options for inbound messages.
-type Criteria struct {
- SymKeyID string `json:"symKeyID"`
- PrivateKeyID string `json:"privateKeyID"`
- Sig []byte `json:"sig"`
- MinPow float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P bool `json:"allowP2P"`
-}
-
-type criteriaOverride struct {
- Sig hexutil.Bytes
-}
-
-// Messages set up a subscription that fires events when messages arrive that match
-// the given set of criteria.
-func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) {
- var (
- symKeyGiven = len(crit.SymKeyID) > 0
- pubKeyGiven = len(crit.PrivateKeyID) > 0
- err error
- )
-
- // ensure that the RPC connection supports subscriptions
- notifier, supported := rpc.NotifierFromContext(ctx)
- if !supported {
- return nil, rpc.ErrNotificationsUnsupported
- }
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) {
- return nil, ErrSymAsym
- }
-
- filter := Filter{
- PoW: crit.MinPow,
- Messages: make(map[common.Hash]*ReceivedMessage),
- AllowP2P: crit.AllowP2P,
- }
-
- if len(crit.Sig) > 0 {
- if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
- return nil, ErrInvalidSigningPubKey
- }
- }
-
- for i, bt := range crit.Topics {
- if len(bt) == 0 || len(bt) > 4 {
- return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt))
- }
- filter.Topics = append(filter.Topics, bt[:])
- }
-
- // listen for message that are encrypted with the given symmetric key
- if symKeyGiven {
- if len(filter.Topics) == 0 {
- return nil, ErrNoTopics
- }
- key, err := api.w.GetSymKey(crit.SymKeyID)
- if err != nil {
- return nil, err
- }
- if !validateSymmetricKey(key) {
- return nil, ErrInvalidSymmetricKey
- }
- filter.KeySym = key
- filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
- }
-
- // listen for messages that are encrypted with the given public key
- if pubKeyGiven {
- filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID)
- if err != nil || filter.KeyAsym == nil {
- return nil, ErrInvalidPublicKey
- }
- }
-
- id, err := api.w.Subscribe(&filter)
- if err != nil {
- return nil, err
- }
-
- // create subscription and start waiting for message events
- rpcSub := notifier.CreateSubscription()
- go func() {
- // for now poll internally, refactor whisper internal for channel support
- ticker := time.NewTicker(250 * time.Millisecond)
- defer ticker.Stop()
-
- for {
- select {
- case <-ticker.C:
- if filter := api.w.GetFilter(id); filter != nil {
- for _, rpcMessage := range toMessage(filter.Retrieve()) {
- if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil {
- log.Error("Failed to send notification", "err", err)
- }
- }
- }
- case <-rpcSub.Err():
- api.w.Unsubscribe(id)
- return
- case <-notifier.Closed():
- api.w.Unsubscribe(id)
- return
- }
- }
- }()
-
- return rpcSub, nil
-}
-
-//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go
-
-// Message is the RPC representation of a whisper message.
-type Message struct {
- Sig []byte `json:"sig,omitempty"`
- TTL uint32 `json:"ttl"`
- Timestamp uint32 `json:"timestamp"`
- Topic TopicType `json:"topic"`
- Payload []byte `json:"payload"`
- Padding []byte `json:"padding"`
- PoW float64 `json:"pow"`
- Hash []byte `json:"hash"`
- Dst []byte `json:"recipientPublicKey,omitempty"`
-}
-
-type messageOverride struct {
- Sig hexutil.Bytes
- Payload hexutil.Bytes
- Padding hexutil.Bytes
- Hash hexutil.Bytes
- Dst hexutil.Bytes
-}
-
-// ToWhisperMessage converts an internal message into an API version.
-func ToWhisperMessage(message *ReceivedMessage) *Message {
- msg := Message{
- Payload: message.Payload,
- Padding: message.Padding,
- Timestamp: message.Sent,
- TTL: message.TTL,
- PoW: message.PoW,
- Hash: message.EnvelopeHash.Bytes(),
- Topic: message.Topic,
- }
-
- if message.Dst != nil {
- b := crypto.FromECDSAPub(message.Dst)
- if b != nil {
- msg.Dst = b
- }
- }
-
- if isMessageSigned(message.Raw[0]) {
- b := crypto.FromECDSAPub(message.SigToPubKey())
- if b != nil {
- msg.Sig = b
- }
- }
-
- return &msg
-}
-
-// toMessage converts a set of messages to its RPC representation.
-func toMessage(messages []*ReceivedMessage) []*Message {
- msgs := make([]*Message, len(messages))
- for i, msg := range messages {
- msgs[i] = ToWhisperMessage(msg)
- }
- return msgs
-}
-
-// GetFilterMessages returns the messages that match the filter criteria and
-// are received between the last poll and now.
-func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) {
- api.mu.Lock()
- f := api.w.GetFilter(id)
- if f == nil {
- api.mu.Unlock()
- return nil, errors.New("filter not found")
- }
- api.lastUsed[id] = time.Now()
- api.mu.Unlock()
-
- receivedMessages := f.Retrieve()
- messages := make([]*Message, 0, len(receivedMessages))
- for _, msg := range receivedMessages {
- messages = append(messages, ToWhisperMessage(msg))
- }
-
- return messages, nil
-}
-
-// DeleteMessageFilter deletes a filter.
-func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) {
- api.mu.Lock()
- defer api.mu.Unlock()
-
- delete(api.lastUsed, id)
- return true, api.w.Unsubscribe(id)
-}
-
-// NewMessageFilter creates a new filter that can be used to poll for
-// (new) messages that satisfy the given criteria.
-func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
- var (
- src *ecdsa.PublicKey
- keySym []byte
- keyAsym *ecdsa.PrivateKey
- topics [][]byte
-
- symKeyGiven = len(req.SymKeyID) > 0
- asymKeyGiven = len(req.PrivateKeyID) > 0
-
- err error
- )
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) {
- return "", ErrSymAsym
- }
-
- if len(req.Sig) > 0 {
- if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
- return "", ErrInvalidSigningPubKey
- }
- }
-
- if symKeyGiven {
- if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
- return "", err
- }
- if !validateSymmetricKey(keySym) {
- return "", ErrInvalidSymmetricKey
- }
- }
-
- if asymKeyGiven {
- if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil {
- return "", err
- }
- }
-
- if len(req.Topics) > 0 {
- topics = make([][]byte, 0, len(req.Topics))
- for _, topic := range req.Topics {
- topics = append(topics, topic[:])
- }
- }
-
- f := &Filter{
- Src: src,
- KeySym: keySym,
- KeyAsym: keyAsym,
- PoW: req.MinPow,
- AllowP2P: req.AllowP2P,
- Topics: topics,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- id, err := api.w.Subscribe(f)
- if err != nil {
- return "", err
- }
-
- api.mu.Lock()
- api.lastUsed[id] = time.Now()
- api.mu.Unlock()
-
- return id, nil
-}
diff --git a/whisper/whisperv5/benchmarks_test.go b/whisper/whisperv5/benchmarks_test.go
deleted file mode 100644
index 12d8cb657e..0000000000
--- a/whisper/whisperv5/benchmarks_test.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/crypto"
-)
-
-func BenchmarkDeriveKeyMaterial(b *testing.B) {
- for i := 0; i < b.N; i++ {
- deriveKeyMaterial([]byte("test"), 0)
- }
-}
-
-func BenchmarkEncryptionSym(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < b.N; i++ {
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Errorf("failed Wrap with seed %d: %s.", seed, err)
- b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload))
- return
- }
- }
-}
-
-func BenchmarkEncryptionAsym(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
-
- for i := 0; i < b.N; i++ {
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- }
-}
-
-func BenchmarkDecryptionSymValid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- f := Filter{KeySym: params.KeySym}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg == nil {
- b.Fatalf("failed to open with seed %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionSymInvalid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- f := Filter{KeySym: []byte("arbitrary stuff here")}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg != nil {
- b.Fatalf("opened envelope with invalid key, seed: %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionAsymValid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- f := Filter{KeyAsym: key}
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg == nil {
- b.Fatalf("fail to open, seed: %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionAsymInvalid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- key, err = crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- f := Filter{KeyAsym: key}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg != nil {
- b.Fatalf("opened envelope with invalid key, seed: %d.", seed)
- }
- }
-}
-
-func increment(x []byte) {
- for i := 0; i < len(x); i++ {
- x[i]++
- if x[i] != 0 {
- break
- }
- }
-}
-
-func BenchmarkPoW(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- params.Payload = make([]byte, 32)
- params.PoW = 10.0
- params.TTL = 1
-
- for i := 0; i < b.N; i++ {
- increment(params.Payload)
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- }
-}
diff --git a/whisper/whisperv5/config.go b/whisper/whisperv5/config.go
deleted file mode 100644
index fcc2307046..0000000000
--- a/whisper/whisperv5/config.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-type Config struct {
- MaxMessageSize uint32 `toml:",omitempty"`
- MinimumAcceptedPOW float64 `toml:",omitempty"`
-}
-
-var DefaultConfig = Config{
- MaxMessageSize: DefaultMaxMessageSize,
- MinimumAcceptedPOW: DefaultMinimumPoW,
-}
diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go
deleted file mode 100644
index 7a57488bd7..0000000000
--- a/whisper/whisperv5/doc.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-/*
-Package whisper implements the Whisper protocol (version 5).
-
-Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
-As such it may be likened and compared to both, not dissimilar to the
-matter/energy duality (apologies to physicists for the blatant abuse of a
-fundamental and beautiful natural principle).
-
-Whisper is a pure identity-based messaging system. Whisper provides a low-level
-(non-application-specific) but easily-accessible API without being based upon
-or prejudiced by the low-level hardware attributes and characteristics,
-particularly the notion of singular endpoints.
-*/
-package whisperv5
-
-import (
- "fmt"
- "time"
-)
-
-const (
- EnvelopeVersion = uint64(0)
- ProtocolVersion = uint64(5)
- ProtocolVersionStr = "5.0"
- ProtocolName = "shh"
-
- statusCode = 0 // used by whisper protocol
- messagesCode = 1 // normal whisper message
- p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
- p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol
- NumberOfMessageCodes = 64
-
- paddingMask = byte(3)
- signatureFlag = byte(4)
-
- TopicLength = 4
- signatureLength = 65
- aesKeyLength = 32
- AESNonceLength = 12
- keyIdSize = 32
-
- MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
- DefaultMaxMessageSize = uint32(1024 * 1024)
- DefaultMinimumPoW = 0.2
-
- padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
- messageQueueLimit = 1024
-
- expirationCycle = time.Second
- transmissionCycle = 300 * time.Millisecond
-
- DefaultTTL = 50 // seconds
- SynchAllowance = 10 // seconds
-)
-
-type unknownVersionError uint64
-
-func (e unknownVersionError) Error() string {
- return fmt.Sprintf("invalid envelope version %d", uint64(e))
-}
-
-// MailServer represents a mail server, capable of
-// archiving the old messages for subsequent delivery
-// to the peers. Any implementation must ensure that both
-// functions are thread-safe. Also, they must return ASAP.
-// DeliverMail should use directMessagesCode for delivery,
-// in order to bypass the expiry checks.
-type MailServer interface {
- Archive(env *Envelope)
- DeliverMail(whisperPeer *Peer, request *Envelope)
-}
diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go
deleted file mode 100644
index 588d9b85a4..0000000000
--- a/whisper/whisperv5/envelope.go
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Envelope element.
-
-package whisperv5
-
-import (
- "crypto/ecdsa"
- "encoding/binary"
- "fmt"
- gmath "math"
- "math/big"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/math"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/ecies"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-// Envelope represents a clear-text data packet to transmit through the Whisper
-// network. Its contents may or may not be encrypted and signed.
-type Envelope struct {
- Version []byte
- Expiry uint32
- TTL uint32
- Topic TopicType
- AESNonce []byte
- Data []byte
- EnvNonce uint64
-
- pow float64 // Message-specific PoW as described in the Whisper specification.
- hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
- // Don't access hash directly, use Hash() function instead.
-}
-
-// size returns the size of envelope as it is sent (i.e. public fields only)
-func (e *Envelope) size() int {
- return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data)
-}
-
-// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
-func (e *Envelope) rlpWithoutNonce() []byte {
- res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data})
- return res
-}
-
-// NewEnvelope wraps a Whisper message with expiration and destination data
-// included into an envelope for network forwarding.
-func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope {
- env := Envelope{
- Version: make([]byte, 1),
- Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
- TTL: ttl,
- Topic: topic,
- AESNonce: aesNonce,
- Data: msg.Raw,
- EnvNonce: 0,
- }
-
- if EnvelopeVersion < 256 {
- env.Version[0] = byte(EnvelopeVersion)
- } else {
- panic("please increase the size of Envelope.Version before releasing this version")
- }
-
- return &env
-}
-
-func (e *Envelope) IsSymmetric() bool {
- return len(e.AESNonce) > 0
-}
-
-func (e *Envelope) isAsymmetric() bool {
- return !e.IsSymmetric()
-}
-
-func (e *Envelope) Ver() uint64 {
- return bytesToUintLittleEndian(e.Version)
-}
-
-// Seal closes the envelope by spending the requested amount of time as a proof
-// of work on hashing the data.
-func (e *Envelope) Seal(options *MessageParams) error {
- var target, bestBit int
- if options.PoW == 0 {
- // adjust for the duration of Seal() execution only if execution time is predefined unconditionally
- e.Expiry += options.WorkTime
- } else {
- target = e.powToFirstBit(options.PoW)
- if target < 1 {
- target = 1
- }
- }
-
- buf := make([]byte, 64)
- h := crypto.Keccak256(e.rlpWithoutNonce())
- copy(buf[:32], h)
-
- finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano()
- for nonce := uint64(0); time.Now().UnixNano() < finish; {
- for i := 0; i < 1024; i++ {
- binary.BigEndian.PutUint64(buf[56:], nonce)
- d := new(big.Int).SetBytes(crypto.Keccak256(buf))
- firstBit := math.FirstBitSet(d)
- if firstBit > bestBit {
- e.EnvNonce, bestBit = nonce, firstBit
- if target > 0 && bestBit >= target {
- return nil
- }
- }
- nonce++
- }
- }
-
- if target > 0 && bestBit < target {
- return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
- }
-
- return nil
-}
-
-func (e *Envelope) PoW() float64 {
- if e.pow == 0 {
- e.calculatePoW(0)
- }
- return e.pow
-}
-
-func (e *Envelope) calculatePoW(diff uint32) {
- buf := make([]byte, 64)
- h := crypto.Keccak256(e.rlpWithoutNonce())
- copy(buf[:32], h)
- binary.BigEndian.PutUint64(buf[56:], e.EnvNonce)
- d := new(big.Int).SetBytes(crypto.Keccak256(buf))
- firstBit := math.FirstBitSet(d)
- x := gmath.Pow(2, float64(firstBit))
- x /= float64(e.size())
- x /= float64(e.TTL + diff)
- e.pow = x
-}
-
-func (e *Envelope) powToFirstBit(pow float64) int {
- x := pow
- x *= float64(e.size())
- x *= float64(e.TTL)
- bits := gmath.Log2(x)
- bits = gmath.Ceil(bits)
- return int(bits)
-}
-
-// Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
-func (e *Envelope) Hash() common.Hash {
- if (e.hash == common.Hash{}) {
- encoded, _ := rlp.EncodeToBytes(e)
- e.hash = crypto.Keccak256Hash(encoded)
- }
- return e.hash
-}
-
-// DecodeRLP decodes an Envelope from an RLP data stream.
-func (e *Envelope) DecodeRLP(s *rlp.Stream) error {
- raw, err := s.Raw()
- if err != nil {
- return err
- }
- // The decoding of Envelope uses the struct fields but also needs
- // to compute the hash of the whole RLP-encoded envelope. This
- // type has the same structure as Envelope but is not an
- // rlp.Decoder (does not implement DecodeRLP function).
- // Only public members will be encoded.
- type rlpenv Envelope
- if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil {
- return err
- }
- e.hash = crypto.Keccak256Hash(raw)
- return nil
-}
-
-// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
-func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) {
- message := &ReceivedMessage{Raw: e.Data}
- err := message.decryptAsymmetric(key)
- switch err {
- case nil:
- return message, nil
- case ecies.ErrInvalidPublicKey: // addressed to somebody else
- return nil, err
- default:
- return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err)
- }
-}
-
-// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
-func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
- msg = &ReceivedMessage{Raw: e.Data}
- err = msg.decryptSymmetric(key, e.AESNonce)
- if err != nil {
- msg = nil
- }
- return msg, err
-}
-
-// Open tries to decrypt an envelope, and populates the message fields in case of success.
-func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
- if e.isAsymmetric() {
- msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
- if msg != nil {
- msg.Dst = &watcher.KeyAsym.PublicKey
- }
- } else if e.IsSymmetric() {
- msg, _ = e.OpenSymmetric(watcher.KeySym)
- if msg != nil {
- msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
- }
- }
-
- if msg != nil {
- ok := msg.Validate()
- if !ok {
- return nil
- }
- msg.Topic = e.Topic
- msg.PoW = e.PoW()
- msg.TTL = e.TTL
- msg.Sent = e.Expiry - e.TTL
- msg.EnvelopeHash = e.Hash()
- msg.EnvelopeVersion = e.Ver()
- }
- return msg
-}
diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go
deleted file mode 100644
index 5c64d46910..0000000000
--- a/whisper/whisperv5/filter.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "crypto/ecdsa"
- "errors"
- "fmt"
- "sync"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
-)
-
-type Filter struct {
- Src *ecdsa.PublicKey // Sender of the message
- KeyAsym *ecdsa.PrivateKey // Private Key of recipient
- KeySym []byte // Key associated with the Topic
- Topics [][]byte // Topics to filter messages with
- PoW float64 // Proof of work as described in the Whisper spec
- AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
- SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
-
- Messages map[common.Hash]*ReceivedMessage
- mutex sync.RWMutex
-}
-
-type Filters struct {
- watchers map[string]*Filter
- whisper *Whisper
- mutex sync.RWMutex
-}
-
-func NewFilters(w *Whisper) *Filters {
- return &Filters{
- watchers: make(map[string]*Filter),
- whisper: w,
- }
-}
-
-func (fs *Filters) Install(watcher *Filter) (string, error) {
- if watcher.Messages == nil {
- watcher.Messages = make(map[common.Hash]*ReceivedMessage)
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", err
- }
-
- fs.mutex.Lock()
- defer fs.mutex.Unlock()
-
- if fs.watchers[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
-
- if watcher.expectsSymmetricEncryption() {
- watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
- }
-
- fs.watchers[id] = watcher
- return id, err
-}
-
-func (fs *Filters) Uninstall(id string) bool {
- fs.mutex.Lock()
- defer fs.mutex.Unlock()
- if fs.watchers[id] != nil {
- delete(fs.watchers, id)
- return true
- }
- return false
-}
-
-func (fs *Filters) Get(id string) *Filter {
- fs.mutex.RLock()
- defer fs.mutex.RUnlock()
- return fs.watchers[id]
-}
-
-func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
- var msg *ReceivedMessage
-
- fs.mutex.RLock()
- defer fs.mutex.RUnlock()
-
- i := -1 // only used for logging info
- for _, watcher := range fs.watchers {
- i++
- if p2pMessage && !watcher.AllowP2P {
- log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
- continue
- }
-
- var match bool
- if msg != nil {
- match = watcher.MatchMessage(msg)
- } else {
- match = watcher.MatchEnvelope(env)
- if match {
- msg = env.Open(watcher)
- if msg == nil {
- log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
- }
- } else {
- log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
- }
- }
-
- if match && msg != nil {
- log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
- if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
- watcher.Trigger(msg)
- }
- }
- }
-}
-
-func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
- if f.MatchEnvelope(env) {
- msg := env.Open(f)
- if msg != nil {
- return msg
- } else {
- log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
- }
- } else {
- log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
- }
- return nil
-}
-
-func (f *Filter) expectsAsymmetricEncryption() bool {
- return f.KeyAsym != nil
-}
-
-func (f *Filter) expectsSymmetricEncryption() bool {
- return f.KeySym != nil
-}
-
-func (f *Filter) Trigger(msg *ReceivedMessage) {
- f.mutex.Lock()
- defer f.mutex.Unlock()
-
- if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
- f.Messages[msg.EnvelopeHash] = msg
- }
-}
-
-func (f *Filter) Retrieve() (all []*ReceivedMessage) {
- f.mutex.Lock()
- defer f.mutex.Unlock()
-
- all = make([]*ReceivedMessage, 0, len(f.Messages))
- for _, msg := range f.Messages {
- all = append(all, msg)
- }
-
- f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
- return all
-}
-
-func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
- if f.PoW > 0 && msg.PoW < f.PoW {
- return false
- }
-
- if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
- return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic)
- } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
- return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic)
- }
- return false
-}
-
-func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
- if f.PoW > 0 && envelope.pow < f.PoW {
- return false
- }
-
- if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
- return f.MatchTopic(envelope.Topic)
- } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
- return f.MatchTopic(envelope.Topic)
- }
- return false
-}
-
-func (f *Filter) MatchTopic(topic TopicType) bool {
- if len(f.Topics) == 0 {
- // any topic matches
- return true
- }
-
- for _, bt := range f.Topics {
- if matchSingleTopic(topic, bt) {
- return true
- }
- }
- return false
-}
-
-func matchSingleTopic(topic TopicType, bt []byte) bool {
- if len(bt) > TopicLength {
- bt = bt[:TopicLength]
- }
-
- if len(bt) < TopicLength {
- return false
- }
-
- for j, b := range bt {
- if topic[j] != b {
- return false
- }
- }
- return true
-}
-
-func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
- if !ValidatePublicKey(a) {
- return false
- } else if !ValidatePublicKey(b) {
- return false
- }
- // the curve is always the same, just compare the points
- return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
-}
diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go
deleted file mode 100644
index fc8b5c98b6..0000000000
--- a/whisper/whisperv5/filter_test.go
+++ /dev/null
@@ -1,848 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "math/big"
- mrand "math/rand"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
-)
-
-var seed int64
-
-// InitSingleTest should be called in the beginning of every
-// test, which uses RNG, in order to make the tests
-// reproduciblity independent of their sequence.
-func InitSingleTest() {
- seed = time.Now().Unix()
- mrand.Seed(seed)
-}
-
-func InitDebugTest(i int64) {
- seed = i
- mrand.Seed(seed)
-}
-
-type FilterTestCase struct {
- f *Filter
- id string
- alive bool
- msgCnt int
-}
-
-func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
- var f Filter
- f.Messages = make(map[common.Hash]*ReceivedMessage)
-
- const topicNum = 8
- f.Topics = make([][]byte, topicNum)
- for i := 0; i < topicNum; i++ {
- f.Topics[i] = make([]byte, 4)
- mrand.Read(f.Topics[i][:])
- f.Topics[i][0] = 0x01
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("generateFilter 1 failed with seed %d.", seed)
- return nil, err
- }
- f.Src = &key.PublicKey
-
- if symmetric {
- f.KeySym = make([]byte, aesKeyLength)
- mrand.Read(f.KeySym)
- f.SymKeyHash = crypto.Keccak256Hash(f.KeySym)
- } else {
- f.KeyAsym, err = crypto.GenerateKey()
- if err != nil {
- t.Fatalf("generateFilter 2 failed with seed %d.", seed)
- return nil, err
- }
- }
-
- // AcceptP2P & PoW are not set
- return &f, nil
-}
-
-func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
- cases := make([]FilterTestCase, SizeTestFilters)
- for i := 0; i < SizeTestFilters; i++ {
- f, _ := generateFilter(t, true)
- cases[i].f = f
- cases[i].alive = mrand.Int()&int(1) == 0
- }
- return cases
-}
-
-func TestInstallFilters(t *testing.T) {
- InitSingleTest()
-
- const SizeTestFilters = 256
- w := New(&Config{})
- filters := NewFilters(w)
- tst := generateTestCases(t, SizeTestFilters)
-
- var err error
- var j string
- for i := 0; i < SizeTestFilters; i++ {
- j, err = filters.Install(tst[i].f)
- if err != nil {
- t.Fatalf("seed %d: failed to install filter: %s", seed, err)
- }
- tst[i].id = j
- if len(j) != keyIdSize*2 {
- t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
- }
- }
-
- for _, testCase := range tst {
- if !testCase.alive {
- filters.Uninstall(testCase.id)
- }
- }
-
- for i, testCase := range tst {
- fil := filters.Get(testCase.id)
- exist := fil != nil
- if exist != testCase.alive {
- t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
- }
- if exist && fil.PoW != testCase.f.PoW {
- t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive)
- }
- }
-}
-
-func TestInstallSymKeyGeneratesHash(t *testing.T) {
- InitSingleTest()
-
- w := New(&Config{})
- filters := NewFilters(w)
- filter, _ := generateFilter(t, true)
-
- // save the current SymKeyHash for comparison
- initialSymKeyHash := filter.SymKeyHash
-
- // ensure the SymKeyHash is invalid, for Install to recreate it
- var invalid common.Hash
- filter.SymKeyHash = invalid
-
- _, err := filters.Install(filter)
-
- if err != nil {
- t.Fatalf("Error installing the filter: %s", err)
- }
-
- for i, b := range filter.SymKeyHash {
- if b != initialSymKeyHash[i] {
- t.Fatalf("The filter's symmetric key hash was not properly generated by Install")
- }
- }
-}
-
-func TestInstallIdenticalFilters(t *testing.T) {
- InitSingleTest()
-
- w := New(&Config{})
- filters := NewFilters(w)
- filter1, _ := generateFilter(t, true)
-
- // Copy the first filter since some of its fields
- // are randomly gnerated.
- filter2 := &Filter{
- KeySym: filter1.KeySym,
- Topics: filter1.Topics,
- PoW: filter1.PoW,
- AllowP2P: filter1.AllowP2P,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- _, err := filters.Install(filter1)
-
- if err != nil {
- t.Fatalf("Error installing the first filter with seed %d: %s", seed, err)
- }
-
- _, err = filters.Install(filter2)
-
- if err != nil {
- t.Fatalf("Error installing the second filter with seed %d: %s", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("Error generating message parameters with seed %d: %s", seed, err)
- }
-
- params.KeySym = filter1.KeySym
- params.Topic = BytesToTopic(filter1.Topics[0])
-
- filter1.Src = ¶ms.Src.PublicKey
- filter2.Src = ¶ms.Src.PublicKey
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(filter1)
- if msg == nil {
- t.Fatalf("failed to Open with filter1")
- }
-
- if !filter1.MatchEnvelope(env) {
- t.Fatalf("failed matching with the first filter")
- }
-
- if !filter2.MatchEnvelope(env) {
- t.Fatalf("failed matching with the first filter")
- }
-
- if !filter1.MatchMessage(msg) {
- t.Fatalf("failed matching with the second filter")
- }
-
- if !filter2.MatchMessage(msg) {
- t.Fatalf("failed matching with the second filter")
- }
-}
-
-func TestComparePubKey(t *testing.T) {
- InitSingleTest()
-
- key1, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate first key with seed %d: %s.", seed, err)
- }
- key2, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate second key with seed %d: %s.", seed, err)
- }
- if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) {
- t.Fatalf("public keys are equal, seed %d.", seed)
- }
-
- // generate key3 == key1
- mrand.Seed(seed)
- key3, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate third key with seed %d: %s.", seed, err)
- }
- if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) {
- t.Fatalf("key1 == key3, seed %d.", seed)
- }
-}
-
-func TestMatchEnvelope(t *testing.T) {
- InitSingleTest()
-
- fsym, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- fasym, err := generateFilter(t, false)
- if err != nil {
- t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.Topic[0] = 0xFF // ensure mismatch
-
- // mismatch with pseudo-random data
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- match := fsym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope symmetric with seed %d.", seed)
- }
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope asymmetric with seed %d.", seed)
- }
-
- // encrypt symmetrically
- i := mrand.Int() % 4
- fsym.Topics[i] = params.Topic[:]
- fasym.Topics[i] = params.Topic[:]
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
- }
-
- // symmetric + matching topic: match
- match = fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
- }
-
- // asymmetric + matching topic: mismatch
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed)
- }
-
- // symmetric + matching topic + insufficient PoW: mismatch
- fsym.PoW = env.PoW() + 1.0
- match = fsym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed)
- }
-
- // symmetric + matching topic + sufficient PoW: match
- fsym.PoW = env.PoW() / 2
- match = fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed)
- }
-
- // symmetric + topics are nil (wildcard): match
- prevTopics := fsym.Topics
- fsym.Topics = nil
- match = fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed)
- }
- fsym.Topics = prevTopics
-
- // encrypt asymmetrically
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
- }
-
- // encryption method mismatch
- match = fsym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-
- // asymmetric + mismatching topic: mismatch
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed)
- }
-
- // asymmetric + matching topic: match
- fasym.Topics[i] = fasym.Topics[i+1]
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed)
- }
-
- // asymmetric + filter without topic (wildcard): match
- fasym.Topics = nil
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed)
- }
-
- // asymmetric + insufficient PoW: mismatch
- fasym.PoW = env.PoW() + 1.0
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed)
- }
-
- // asymmetric + sufficient PoW: match
- fasym.PoW = env.PoW() / 2
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed)
- }
-
- // filter without topic + envelope without topic: match
- env.Topic = TopicType{}
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
- }
-
- // filter with topic + envelope without topic: mismatch
- fasym.Topics = fsym.Topics
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
- }
-}
-
-func TestMatchMessageSym(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- const index = 1
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[index])
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(f)
- if msg == nil {
- t.Fatalf("failed Open with seed %d.", seed)
- }
-
- // Src: match
- *f.Src.X = *params.Src.PublicKey.X
- *f.Src.Y = *params.Src.PublicKey.Y
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed)
- }
-
- // insufficient PoW: mismatch
- f.PoW = msg.PoW + 1.0
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
- }
-
- // sufficient PoW: match
- f.PoW = msg.PoW / 2
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
- }
-
- // topic mismatch
- f.Topics[index][0]++
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
- }
- f.Topics[index][0]--
-
- // key mismatch
- f.SymKeyHash[0]++
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
- }
- f.SymKeyHash[0]--
-
- // Src absent: match
- f.Src = nil
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
- }
-
- // key hash mismatch
- h := f.SymKeyHash
- f.SymKeyHash = common.Hash{}
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed)
- }
- f.SymKeyHash = h
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed)
- }
-
- // encryption method mismatch
- f.KeySym = nil
- f.KeyAsym, err = crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-}
-
-func TestMatchMessageAsym(t *testing.T) {
- InitSingleTest()
-
- f, err := generateFilter(t, false)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- const index = 1
- params.Topic = BytesToTopic(f.Topics[index])
- params.Dst = &f.KeyAsym.PublicKey
- keySymOrig := params.KeySym
- params.KeySym = nil
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(f)
- if msg == nil {
- t.Fatalf("failed to open with seed %d.", seed)
- }
-
- // Src: match
- *f.Src.X = *params.Src.PublicKey.X
- *f.Src.Y = *params.Src.PublicKey.Y
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchMessage(src match) with seed %d.", seed)
- }
-
- // insufficient PoW: mismatch
- f.PoW = msg.PoW + 1.0
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
- }
-
- // sufficient PoW: match
- f.PoW = msg.PoW / 2
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
- }
-
- // topic mismatch
- f.Topics[index][0]++
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
- }
- f.Topics[index][0]--
-
- // key mismatch
- prev := *f.KeyAsym.PublicKey.X
- zero := *big.NewInt(0)
- *f.KeyAsym.PublicKey.X = zero
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
- }
- *f.KeyAsym.PublicKey.X = prev
-
- // Src absent: match
- f.Src = nil
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
- }
-
- // encryption method mismatch
- f.KeySym = keySymOrig
- f.KeyAsym = nil
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-}
-
-func cloneFilter(orig *Filter) *Filter {
- var clone Filter
- clone.Messages = make(map[common.Hash]*ReceivedMessage)
- clone.Src = orig.Src
- clone.KeyAsym = orig.KeyAsym
- clone.KeySym = orig.KeySym
- clone.Topics = orig.Topics
- clone.PoW = orig.PoW
- clone.AllowP2P = orig.AllowP2P
- clone.SymKeyHash = orig.SymKeyHash
- return &clone
-}
-
-func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- return nil
- }
-
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[2])
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- return nil
- }
- return env
-}
-
-func TestWatchers(t *testing.T) {
- InitSingleTest()
-
- const NumFilters = 16
- const NumMessages = 256
- var i int
- var j uint32
- var e *Envelope
- var x, firstID string
- var err error
-
- w := New(&Config{})
- filters := NewFilters(w)
- tst := generateTestCases(t, NumFilters)
- for i = 0; i < NumFilters; i++ {
- tst[i].f.Src = nil
- x, err = filters.Install(tst[i].f)
- if err != nil {
- t.Fatalf("failed to install filter with seed %d: %s.", seed, err)
- }
- tst[i].id = x
- if len(firstID) == 0 {
- firstID = x
- }
- }
-
- lastID := x
-
- var envelopes [NumMessages]*Envelope
- for i = 0; i < NumMessages; i++ {
- j = mrand.Uint32() % NumFilters
- e = generateCompatibeEnvelope(t, tst[j].f)
- envelopes[i] = e
- tst[j].msgCnt++
- }
-
- for i = 0; i < NumMessages; i++ {
- filters.NotifyWatchers(envelopes[i], false)
- }
-
- var total int
- var mail []*ReceivedMessage
- var count [NumFilters]int
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- count[i] = len(mail)
- total += len(mail)
- }
-
- if total != NumMessages {
- t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages)
- }
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- if len(mail) != 0 {
- t.Fatalf("failed with seed %d: i = %d.", seed, i)
- }
-
- if tst[i].msgCnt != count[i] {
- t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
- }
- }
-
- // another round with a cloned filter
-
- clone := cloneFilter(tst[0].f)
- filters.Uninstall(lastID)
- total = 0
- last := NumFilters - 1
- tst[last].f = clone
- filters.Install(clone)
- for i = 0; i < NumFilters; i++ {
- tst[i].msgCnt = 0
- count[i] = 0
- }
-
- // make sure that the first watcher receives at least one message
- e = generateCompatibeEnvelope(t, tst[0].f)
- envelopes[0] = e
- tst[0].msgCnt++
- for i = 1; i < NumMessages; i++ {
- j = mrand.Uint32() % NumFilters
- e = generateCompatibeEnvelope(t, tst[j].f)
- envelopes[i] = e
- tst[j].msgCnt++
- }
-
- for i = 0; i < NumMessages; i++ {
- filters.NotifyWatchers(envelopes[i], false)
- }
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- count[i] = len(mail)
- total += len(mail)
- }
-
- combined := tst[0].msgCnt + tst[last].msgCnt
- if total != NumMessages+count[0] {
- t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0])
- }
-
- if combined != count[0] {
- t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0])
- }
-
- if combined != count[last] {
- t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last])
- }
-
- for i = 1; i < NumFilters-1; i++ {
- mail = tst[i].f.Retrieve()
- if len(mail) != 0 {
- t.Fatalf("failed with seed %d: i = %d.", seed, i)
- }
-
- if tst[i].msgCnt != count[i] {
- t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
- }
- }
-
- // test AcceptP2P
-
- total = 0
- filters.NotifyWatchers(envelopes[0], true)
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- total += len(mail)
- }
-
- if total != 0 {
- t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total)
- }
-
- f := filters.Get(firstID)
- if f == nil {
- t.Fatalf("failed to get the filter with seed %d.", seed)
- }
- f.AllowP2P = true
- total = 0
- filters.NotifyWatchers(envelopes[0], true)
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- total += len(mail)
- }
-
- if total != 1 {
- t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
- }
-}
-
-func TestVariableTopics(t *testing.T) {
- InitSingleTest()
-
- const lastTopicByte = 3
- var match bool
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < 4; i++ {
- env.Topic = BytesToTopic(f.Topics[i])
- match = f.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
- }
-
- f.Topics[i][lastTopicByte]++
- match = f.MatchEnvelope(env)
- if match {
- t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i)
- }
- }
-}
-
-func TestMatchSingleTopic_ReturnTrue(t *testing.T) {
- bt := []byte("test")
- topic := BytesToTopic(bt)
-
- if !matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) {
- bt := []byte("test with tail")
- topic := BytesToTopic([]byte("test"))
-
- if !matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) {
- bt := []byte("tes")
- topic := BytesToTopic(bt)
-
- if matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) {
- bt := []byte("test")
- topic := BytesToTopic([]byte("not_equal"))
-
- if matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
diff --git a/whisper/whisperv5/gen_criteria_json.go b/whisper/whisperv5/gen_criteria_json.go
deleted file mode 100644
index 3a0766954c..0000000000
--- a/whisper/whisperv5/gen_criteria_json.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv5
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*criteriaOverride)(nil)
-
-func (c Criteria) MarshalJSON() ([]byte, error) {
- type Criteria struct {
- SymKeyID string `json:"symKeyID"`
- PrivateKeyID string `json:"privateKeyID"`
- Sig hexutil.Bytes `json:"sig"`
- MinPow float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P bool `json:"allowP2P"`
- }
- var enc Criteria
- enc.SymKeyID = c.SymKeyID
- enc.PrivateKeyID = c.PrivateKeyID
- enc.Sig = c.Sig
- enc.MinPow = c.MinPow
- enc.Topics = c.Topics
- enc.AllowP2P = c.AllowP2P
- return json.Marshal(&enc)
-}
-
-func (c *Criteria) UnmarshalJSON(input []byte) error {
- type Criteria struct {
- SymKeyID *string `json:"symKeyID"`
- PrivateKeyID *string `json:"privateKeyID"`
- Sig *hexutil.Bytes `json:"sig"`
- MinPow *float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P *bool `json:"allowP2P"`
- }
- var dec Criteria
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.SymKeyID != nil {
- c.SymKeyID = *dec.SymKeyID
- }
- if dec.PrivateKeyID != nil {
- c.PrivateKeyID = *dec.PrivateKeyID
- }
- if dec.Sig != nil {
- c.Sig = *dec.Sig
- }
- if dec.MinPow != nil {
- c.MinPow = *dec.MinPow
- }
- if dec.Topics != nil {
- c.Topics = dec.Topics
- }
- if dec.AllowP2P != nil {
- c.AllowP2P = *dec.AllowP2P
- }
- return nil
-}
diff --git a/whisper/whisperv5/gen_message_json.go b/whisper/whisperv5/gen_message_json.go
deleted file mode 100644
index 16e95a6e14..0000000000
--- a/whisper/whisperv5/gen_message_json.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv5
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*messageOverride)(nil)
-
-func (m Message) MarshalJSON() ([]byte, error) {
- type Message struct {
- Sig hexutil.Bytes `json:"sig,omitempty"`
- TTL uint32 `json:"ttl"`
- Timestamp uint32 `json:"timestamp"`
- Topic TopicType `json:"topic"`
- Payload hexutil.Bytes `json:"payload"`
- Padding hexutil.Bytes `json:"padding"`
- PoW float64 `json:"pow"`
- Hash hexutil.Bytes `json:"hash"`
- Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"`
- }
- var enc Message
- enc.Sig = m.Sig
- enc.TTL = m.TTL
- enc.Timestamp = m.Timestamp
- enc.Topic = m.Topic
- enc.Payload = m.Payload
- enc.Padding = m.Padding
- enc.PoW = m.PoW
- enc.Hash = m.Hash
- enc.Dst = m.Dst
- return json.Marshal(&enc)
-}
-
-func (m *Message) UnmarshalJSON(input []byte) error {
- type Message struct {
- Sig *hexutil.Bytes `json:"sig,omitempty"`
- TTL *uint32 `json:"ttl"`
- Timestamp *uint32 `json:"timestamp"`
- Topic *TopicType `json:"topic"`
- Payload *hexutil.Bytes `json:"payload"`
- Padding *hexutil.Bytes `json:"padding"`
- PoW *float64 `json:"pow"`
- Hash *hexutil.Bytes `json:"hash"`
- Dst *hexutil.Bytes `json:"recipientPublicKey,omitempty"`
- }
- var dec Message
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.Sig != nil {
- m.Sig = *dec.Sig
- }
- if dec.TTL != nil {
- m.TTL = *dec.TTL
- }
- if dec.Timestamp != nil {
- m.Timestamp = *dec.Timestamp
- }
- if dec.Topic != nil {
- m.Topic = *dec.Topic
- }
- if dec.Payload != nil {
- m.Payload = *dec.Payload
- }
- if dec.Padding != nil {
- m.Padding = *dec.Padding
- }
- if dec.PoW != nil {
- m.PoW = *dec.PoW
- }
- if dec.Hash != nil {
- m.Hash = *dec.Hash
- }
- if dec.Dst != nil {
- m.Dst = *dec.Dst
- }
- return nil
-}
diff --git a/whisper/whisperv5/gen_newmessage_json.go b/whisper/whisperv5/gen_newmessage_json.go
deleted file mode 100644
index dabe66c0b6..0000000000
--- a/whisper/whisperv5/gen_newmessage_json.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv5
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*newMessageOverride)(nil)
-
-func (n NewMessage) MarshalJSON() ([]byte, error) {
- type NewMessage struct {
- SymKeyID string `json:"symKeyID"`
- PublicKey hexutil.Bytes `json:"pubKey"`
- Sig string `json:"sig"`
- TTL uint32 `json:"ttl"`
- Topic TopicType `json:"topic"`
- Payload hexutil.Bytes `json:"payload"`
- Padding hexutil.Bytes `json:"padding"`
- PowTime uint32 `json:"powTime"`
- PowTarget float64 `json:"powTarget"`
- TargetPeer string `json:"targetPeer"`
- }
- var enc NewMessage
- enc.SymKeyID = n.SymKeyID
- enc.PublicKey = n.PublicKey
- enc.Sig = n.Sig
- enc.TTL = n.TTL
- enc.Topic = n.Topic
- enc.Payload = n.Payload
- enc.Padding = n.Padding
- enc.PowTime = n.PowTime
- enc.PowTarget = n.PowTarget
- enc.TargetPeer = n.TargetPeer
- return json.Marshal(&enc)
-}
-
-func (n *NewMessage) UnmarshalJSON(input []byte) error {
- type NewMessage struct {
- SymKeyID *string `json:"symKeyID"`
- PublicKey *hexutil.Bytes `json:"pubKey"`
- Sig *string `json:"sig"`
- TTL *uint32 `json:"ttl"`
- Topic *TopicType `json:"topic"`
- Payload *hexutil.Bytes `json:"payload"`
- Padding *hexutil.Bytes `json:"padding"`
- PowTime *uint32 `json:"powTime"`
- PowTarget *float64 `json:"powTarget"`
- TargetPeer *string `json:"targetPeer"`
- }
- var dec NewMessage
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.SymKeyID != nil {
- n.SymKeyID = *dec.SymKeyID
- }
- if dec.PublicKey != nil {
- n.PublicKey = *dec.PublicKey
- }
- if dec.Sig != nil {
- n.Sig = *dec.Sig
- }
- if dec.TTL != nil {
- n.TTL = *dec.TTL
- }
- if dec.Topic != nil {
- n.Topic = *dec.Topic
- }
- if dec.Payload != nil {
- n.Payload = *dec.Payload
- }
- if dec.Padding != nil {
- n.Padding = *dec.Padding
- }
- if dec.PowTime != nil {
- n.PowTime = *dec.PowTime
- }
- if dec.PowTarget != nil {
- n.PowTarget = *dec.PowTarget
- }
- if dec.TargetPeer != nil {
- n.TargetPeer = *dec.TargetPeer
- }
- return nil
-}
diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go
deleted file mode 100644
index 70745aa9f1..0000000000
--- a/whisper/whisperv5/message.go
+++ /dev/null
@@ -1,352 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Message element.
-
-package whisperv5
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/ecdsa"
- crand "crypto/rand"
- "encoding/binary"
- "errors"
- "strconv"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/ecies"
- "github.com/XinFinOrg/XDPoSChain/log"
-)
-
-// Options specifies the exact way a message should be wrapped into an Envelope.
-type MessageParams struct {
- TTL uint32
- Src *ecdsa.PrivateKey
- Dst *ecdsa.PublicKey
- KeySym []byte
- Topic TopicType
- WorkTime uint32
- PoW float64
- Payload []byte
- Padding []byte
-}
-
-// SentMessage represents an end-user data packet to transmit through the
-// Whisper protocol. These are wrapped into Envelopes that need not be
-// understood by intermediate nodes, just forwarded.
-type sentMessage struct {
- Raw []byte
-}
-
-// ReceivedMessage represents a data packet to be received through the
-// Whisper protocol.
-type ReceivedMessage struct {
- Raw []byte
-
- Payload []byte
- Padding []byte
- Signature []byte
-
- PoW float64 // Proof of work as described in the Whisper spec
- Sent uint32 // Time when the message was posted into the network
- TTL uint32 // Maximum time to live allowed for the message
- Src *ecdsa.PublicKey // Message recipient (identity used to decode the message)
- Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
- Topic TopicType
-
- SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic
- EnvelopeHash common.Hash // Message envelope hash to act as a unique id
- EnvelopeVersion uint64
-}
-
-func isMessageSigned(flags byte) bool {
- return (flags & signatureFlag) != 0
-}
-
-func (msg *ReceivedMessage) isSymmetricEncryption() bool {
- return msg.SymKeyHash != common.Hash{}
-}
-
-func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
- return msg.Dst != nil
-}
-
-// NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
-func NewSentMessage(params *MessageParams) (*sentMessage, error) {
- msg := sentMessage{}
- msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
- msg.Raw[0] = 0 // set all the flags to zero
- err := msg.appendPadding(params)
- if err != nil {
- return nil, err
- }
- msg.Raw = append(msg.Raw, params.Payload...)
- return &msg, nil
-}
-
-// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes)
-func getSizeOfLength(b []byte) (sz int, err error) {
- sz = intSize(len(b)) // first iteration
- sz = intSize(len(b) + sz) // second iteration
- if sz > 3 {
- err = errors.New("oversized padding parameter")
- }
- return sz, err
-}
-
-// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value
-func intSize(i int) (s int) {
- for s = 1; i >= 256; s++ {
- i /= 256
- }
- return s
-}
-
-// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
-// The last byte contains the size of padding (thus, its size must not exceed 256).
-func (msg *sentMessage) appendPadding(params *MessageParams) error {
- rawSize := len(params.Payload) + 1
- if params.Src != nil {
- rawSize += signatureLength
- }
- odd := rawSize % padSizeLimit
-
- if len(params.Padding) != 0 {
- padSize := len(params.Padding)
- padLengthSize, err := getSizeOfLength(params.Padding)
- if err != nil {
- return err
- }
- totalPadSize := padSize + padLengthSize
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint32(buf, uint32(totalPadSize))
- buf = buf[:padLengthSize]
- msg.Raw = append(msg.Raw, buf...)
- msg.Raw = append(msg.Raw, params.Padding...)
- msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size
- } else if odd != 0 {
- totalPadSize := padSizeLimit - odd
- if totalPadSize > 255 {
- // this algorithm is only valid if padSizeLimit < 256.
- // if padSizeLimit will ever change, please fix the algorithm
- // (please see also ReceivedMessage.extractPadding() function).
- panic("please fix the padding algorithm before releasing new version")
- }
- buf := make([]byte, totalPadSize)
- _, err := crand.Read(buf[1:])
- if err != nil {
- return err
- }
- if totalPadSize > 6 && !validateSymmetricKey(buf) {
- return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize))
- }
- buf[0] = byte(totalPadSize)
- msg.Raw = append(msg.Raw, buf...)
- msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
- }
- return nil
-}
-
-// sign calculates and sets the cryptographic signature for the message,
-// also setting the sign flag.
-func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error {
- if isMessageSigned(msg.Raw[0]) {
- // this should not happen, but no reason to panic
- log.Error("failed to sign the message: already signed")
- return nil
- }
-
- msg.Raw[0] |= signatureFlag
- hash := crypto.Keccak256(msg.Raw)
- signature, err := crypto.Sign(hash, key)
- if err != nil {
- msg.Raw[0] &= ^signatureFlag // clear the flag
- return err
- }
- msg.Raw = append(msg.Raw, signature...)
- return nil
-}
-
-// encryptAsymmetric encrypts a message with a public key.
-func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
- if !ValidatePublicKey(key) {
- return errors.New("invalid public key provided for asymmetric encryption")
- }
- encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil)
- if err == nil {
- msg.Raw = encrypted
- }
- return err
-}
-
-// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
-// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
-func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) {
- if !validateSymmetricKey(key) {
- return nil, errors.New("invalid key provided for symmetric encryption")
- }
-
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return nil, err
- }
-
- // never use more than 2^32 random nonces with a given key
- nonce = make([]byte, aesgcm.NonceSize())
- _, err = crand.Read(nonce)
- if err != nil {
- return nil, err
- } else if !validateSymmetricKey(nonce) {
- return nil, errors.New("crypto/rand failed to generate nonce")
- }
-
- msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil)
- return nonce, nil
-}
-
-// Wrap bundles the message into an Envelope to transmit over the network.
-func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
- if options.TTL == 0 {
- options.TTL = DefaultTTL
- }
- if options.Src != nil {
- if err = msg.sign(options.Src); err != nil {
- return nil, err
- }
- }
- var nonce []byte
- if options.Dst != nil {
- err = msg.encryptAsymmetric(options.Dst)
- } else if options.KeySym != nil {
- nonce, err = msg.encryptSymmetric(options.KeySym)
- } else {
- err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
- }
- if err != nil {
- return nil, err
- }
-
- envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
- if err = envelope.Seal(options); err != nil {
- return nil, err
- }
- return envelope, nil
-}
-
-// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
-// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
-func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
- block, err := aes.NewCipher(key)
- if err != nil {
- return err
- }
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return err
- }
- if len(nonce) != aesgcm.NonceSize() {
- log.Error("decrypting the message", "AES nonce size", len(nonce))
- return errors.New("wrong AES nonce size")
- }
- decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil)
- if err != nil {
- return err
- }
- msg.Raw = decrypted
- return nil
-}
-
-// decryptAsymmetric decrypts an encrypted payload with a private key.
-func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error {
- decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil)
- if err == nil {
- msg.Raw = decrypted
- }
- return err
-}
-
-// Validate checks the validity and extracts the fields in case of success
-func (msg *ReceivedMessage) Validate() bool {
- end := len(msg.Raw)
- if end < 1 {
- return false
- }
-
- if isMessageSigned(msg.Raw[0]) {
- end -= signatureLength
- if end <= 1 {
- return false
- }
- msg.Signature = msg.Raw[end:]
- msg.Src = msg.SigToPubKey()
- if msg.Src == nil {
- return false
- }
- }
-
- padSize, ok := msg.extractPadding(end)
- if !ok {
- return false
- }
-
- msg.Payload = msg.Raw[1+padSize : end]
- return true
-}
-
-// extractPadding extracts the padding from raw message.
-// although we don't support sending messages with padding size
-// exceeding 255 bytes, such messages are perfectly valid, and
-// can be successfully decrypted.
-func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
- paddingSize := 0
- sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes)
- // could be zero -- it means no padding
- if sz != 0 {
- paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz]))
- if paddingSize < sz || paddingSize+1 > end {
- return 0, false
- }
- msg.Padding = msg.Raw[1+sz : 1+paddingSize]
- }
- return paddingSize, true
-}
-
-// Recover retrieves the public key of the message signer.
-func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
- defer func() { recover() }() // in case of invalid signature
-
- pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
- if err != nil {
- log.Error("failed to recover public key from signature", "err", err)
- return nil
- }
- return pub
-}
-
-// hash calculates the SHA3 checksum of the message flags, payload and padding.
-func (msg *ReceivedMessage) hash() []byte {
- if isMessageSigned(msg.Raw[0]) {
- sz := len(msg.Raw) - signatureLength
- return crypto.Keccak256(msg.Raw[:sz])
- }
- return crypto.Keccak256(msg.Raw)
-}
diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go
deleted file mode 100644
index c1ebcf4d89..0000000000
--- a/whisper/whisperv5/message_test.go
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "bytes"
- mrand "math/rand"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-func generateMessageParams() (*MessageParams, error) {
- // set all the parameters except p.Dst and p.Padding
-
- buf := make([]byte, 4)
- mrand.Read(buf)
- sz := mrand.Intn(400)
-
- var p MessageParams
- p.PoW = 0.01
- p.WorkTime = 1
- p.TTL = uint32(mrand.Intn(1024))
- p.Payload = make([]byte, sz)
- p.KeySym = make([]byte, aesKeyLength)
- mrand.Read(p.Payload)
- mrand.Read(p.KeySym)
- p.Topic = BytesToTopic(buf)
-
- var err error
- p.Src, err = crypto.GenerateKey()
- if err != nil {
- return nil, err
- }
-
- return &p, nil
-}
-
-func singleMessageTest(t *testing.T, symmetric bool) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
-
- if !symmetric {
- params.KeySym = nil
- params.Dst = &key.PublicKey
- }
-
- text := make([]byte, 0, 512)
- text = append(text, params.Payload...)
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- var decrypted *ReceivedMessage
- if symmetric {
- decrypted, err = env.OpenSymmetric(params.KeySym)
- } else {
- decrypted, err = env.OpenAsymmetric(key)
- }
-
- if err != nil {
- t.Fatalf("failed to encrypt with seed %d: %s.", seed, err)
- }
-
- if !decrypted.Validate() {
- t.Fatalf("failed to validate with seed %d.", seed)
- }
-
- if !bytes.Equal(text, decrypted.Payload) {
- t.Fatalf("failed with seed %d: compare payload.", seed)
- }
- if !isMessageSigned(decrypted.Raw[0]) {
- t.Fatalf("failed with seed %d: unsigned.", seed)
- }
- if len(decrypted.Signature) != signatureLength {
- t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
- }
- if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
- t.Fatalf("failed with seed %d: signature mismatch.", seed)
- }
-}
-
-func TestMessageEncryption(t *testing.T) {
- InitSingleTest()
-
- var symmetric bool
- for i := 0; i < 256; i++ {
- singleMessageTest(t, symmetric)
- symmetric = !symmetric
- }
-}
-
-func TestMessageWrap(t *testing.T) {
- seed = int64(1777444222)
- mrand.Seed(seed)
- target := 128.0
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1
- params.WorkTime = 12
- params.PoW = target
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- pow := env.PoW()
- if pow < target {
- t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target)
- }
-
- // set PoW target too high, expect error
- msg2, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1000000
- params.WorkTime = 1
- params.PoW = 10000000.0
- _, err = msg2.Wrap(params)
- if err == nil {
- t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed)
- }
-}
-
-func TestMessageSeal(t *testing.T) {
- // this test depends on deterministic choice of seed (1976726903)
- seed = int64(1976726903)
- mrand.Seed(seed)
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1
- aesnonce := make([]byte, 12)
- mrand.Read(aesnonce)
-
- env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- env.Expiry = uint32(seed) // make it deterministic
- target := 32.0
- params.WorkTime = 4
- params.PoW = target
- env.Seal(params)
-
- env.calculatePoW(0)
- pow := env.PoW()
- if pow < target {
- t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target)
- }
-
- params.WorkTime = 1
- params.PoW = 1000000000.0
- env.Seal(params)
- env.calculatePoW(0)
- pow = env.PoW()
- if pow < 2*target {
- t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow)
- }
-}
-
-func TestEnvelopeOpen(t *testing.T) {
- InitSingleTest()
-
- var symmetric bool
- for i := 0; i < 256; i++ {
- singleEnvelopeOpenTest(t, symmetric)
- symmetric = !symmetric
- }
-}
-
-func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
-
- if !symmetric {
- params.KeySym = nil
- params.Dst = &key.PublicKey
- }
-
- text := make([]byte, 0, 512)
- text = append(text, params.Payload...)
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- f := Filter{KeyAsym: key, KeySym: params.KeySym}
- decrypted := env.Open(&f)
- if decrypted == nil {
- t.Fatalf("failed to open with seed %d.", seed)
- }
-
- if !bytes.Equal(text, decrypted.Payload) {
- t.Fatalf("failed with seed %d: compare payload.", seed)
- }
- if !isMessageSigned(decrypted.Raw[0]) {
- t.Fatalf("failed with seed %d: unsigned.", seed)
- }
- if len(decrypted.Signature) != signatureLength {
- t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
- }
- if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
- t.Fatalf("failed with seed %d: signature mismatch.", seed)
- }
- if decrypted.isAsymmetricEncryption() == symmetric {
- t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric)
- }
- if decrypted.isSymmetricEncryption() != symmetric {
- t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric)
- }
- if !symmetric {
- if decrypted.Dst == nil {
- t.Fatalf("failed with seed %d: dst is nil.", seed)
- }
- if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) {
- t.Fatalf("failed with seed %d: Dst.", seed)
- }
- }
-}
-
-func TestEncryptWithZeroKey(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = make([]byte, aesKeyLength)
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with zero key, seed: %d.", seed)
- }
-
- params, err = generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = make([]byte, 0)
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with empty key, seed: %d.", seed)
- }
-
- params, err = generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with nil key, seed: %d.", seed)
- }
-}
-
-func TestRlpEncode(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("wrapped with zero key, seed: %d.", seed)
- }
-
- raw, err := rlp.EncodeToBytes(env)
- if err != nil {
- t.Fatalf("RLP encode failed: %s.", err)
- }
-
- var decoded Envelope
- rlp.DecodeBytes(raw, &decoded)
- if err != nil {
- t.Fatalf("RLP decode failed: %s.", err)
- }
-
- he := env.Hash()
- hd := decoded.Hash()
-
- if he != hd {
- t.Fatalf("Hashes are not equal: %x vs. %x", he, hd)
- }
-}
-
-func singlePaddingTest(t *testing.T, padSize int) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err)
- }
- params.Padding = make([]byte, padSize)
- params.PoW = 0.0000000001
- pad := make([]byte, padSize)
- _, err = mrand.Read(pad)
- if err != nil {
- t.Fatalf("padding is not generated (seed %d): %s", seed, err)
- }
- n := copy(params.Padding, pad)
- if n != padSize {
- t.Fatalf("padding is not copied (seed %d): %s", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize)
- }
- f := Filter{KeySym: params.KeySym}
- decrypted := env.Open(&f)
- if decrypted == nil {
- t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize)
- }
- if !bytes.Equal(pad, decrypted.Padding) {
- t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding)
- }
-}
-
-func TestPadding(t *testing.T) {
- InitSingleTest()
-
- for i := 1; i < 260; i++ {
- singlePaddingTest(t, i)
- }
-
- lim := 256 * 256
- for i := lim - 5; i < lim+2; i++ {
- singlePaddingTest(t, i)
- }
-
- for i := 0; i < 256; i++ {
- n := mrand.Intn(256*254) + 256
- singlePaddingTest(t, n)
- }
-
- for i := 0; i < 256; i++ {
- n := mrand.Intn(256*1024) + 256*256
- singlePaddingTest(t, n)
- }
-}
diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go
deleted file mode 100644
index 59da72a8e4..0000000000
--- a/whisper/whisperv5/peer.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "fmt"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- mapset "github.com/deckarep/golang-set"
-)
-
-// peer represents a whisper protocol peer connection.
-type Peer struct {
- host *Whisper
- peer *p2p.Peer
- ws p2p.MsgReadWriter
- trusted bool
-
- known mapset.Set // Messages already known by the peer to avoid wasting bandwidth
-
- quit chan struct{}
-}
-
-// newPeer creates a new whisper peer object, but does not run the handshake itself.
-func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
- return &Peer{
- host: host,
- peer: remote,
- ws: rw,
- trusted: false,
- known: mapset.NewSet(),
- quit: make(chan struct{}),
- }
-}
-
-// start initiates the peer updater, periodically broadcasting the whisper packets
-// into the network.
-func (p *Peer) start() {
- go p.update()
- log.Trace("start", "peer", p.ID())
-}
-
-// stop terminates the peer updater, stopping message forwarding to it.
-func (p *Peer) stop() {
- close(p.quit)
- log.Trace("stop", "peer", p.ID())
-}
-
-// handshake sends the protocol initiation status message to the remote peer and
-// verifies the remote status too.
-func (p *Peer) handshake() error {
- // Send the handshake status message asynchronously
- errc := make(chan error, 1)
- go func() {
- errc <- p2p.Send(p.ws, statusCode, ProtocolVersion)
- }()
- // Fetch the remote status packet and verify protocol match
- packet, err := p.ws.ReadMsg()
- if err != nil {
- return err
- }
- if packet.Code != statusCode {
- return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code)
- }
- s := rlp.NewStream(packet.Payload, uint64(packet.Size))
- peerVersion, err := s.Uint()
- if err != nil {
- return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err)
- }
- if peerVersion != ProtocolVersion {
- return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion)
- }
- // Wait until out own status is consumed too
- if err := <-errc; err != nil {
- return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err)
- }
- return nil
-}
-
-// update executes periodic operations on the peer, including message transmission
-// and expiration.
-func (p *Peer) update() {
- // Start the tickers for the updates
- expire := time.NewTicker(expirationCycle)
- transmit := time.NewTicker(transmissionCycle)
-
- // Loop and transmit until termination is requested
- for {
- select {
- case <-expire.C:
- p.expire()
-
- case <-transmit.C:
- if err := p.broadcast(); err != nil {
- log.Trace("broadcast failed", "reason", err, "peer", p.ID())
- return
- }
-
- case <-p.quit:
- return
- }
- }
-}
-
-// mark marks an envelope known to the peer so that it won't be sent back.
-func (peer *Peer) mark(envelope *Envelope) {
- peer.known.Add(envelope.Hash())
-}
-
-// marked checks if an envelope is already known to the remote peer.
-func (peer *Peer) marked(envelope *Envelope) bool {
- return peer.known.Contains(envelope.Hash())
-}
-
-// expire iterates over all the known envelopes in the host and removes all
-// expired (unknown) ones from the known list.
-func (peer *Peer) expire() {
- unmark := make(map[common.Hash]struct{})
- peer.known.Each(func(v interface{}) bool {
- if !peer.host.isEnvelopeCached(v.(common.Hash)) {
- unmark[v.(common.Hash)] = struct{}{}
- }
- return true
- })
- // Dump all known but no longer cached
- for hash := range unmark {
- peer.known.Remove(hash)
- }
-}
-
-// broadcast iterates over the collection of envelopes and transmits yet unknown
-// ones over the network.
-func (p *Peer) broadcast() error {
- var cnt int
- envelopes := p.host.Envelopes()
- for _, envelope := range envelopes {
- if !p.marked(envelope) {
- err := p2p.Send(p.ws, messagesCode, envelope)
- if err != nil {
- return err
- } else {
- p.mark(envelope)
- cnt++
- }
- }
- }
- if cnt > 0 {
- log.Trace("broadcast", "num. messages", cnt)
- }
- return nil
-}
-
-func (p *Peer) ID() []byte {
- id := p.peer.ID()
- return id[:]
-}
diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go
deleted file mode 100644
index 0ed18fc018..0000000000
--- a/whisper/whisperv5/peer_test.go
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "bytes"
- "crypto/ecdsa"
- "fmt"
- "net"
- "sync"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/p2p/discover"
- "github.com/XinFinOrg/XDPoSChain/p2p/nat"
-)
-
-var keys []string = []string{
- "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9",
- "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98",
- "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc",
- "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837",
- "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf",
- "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5",
- "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68",
- "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c",
- "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0",
- "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9",
- "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4",
- "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620",
- "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590",
- "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1",
- "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a",
- "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f",
- "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8",
- "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805",
- "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045",
- "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5",
- "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7",
- "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd",
- "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f",
- "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211",
- "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611",
- "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f",
- "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b",
- "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5",
- "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15",
- "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c",
- "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2",
- "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a",
- "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4",
-}
-
-const NumNodes = 16 // must not exceed the number of keys (32)
-
-type TestData struct {
- counter [NumNodes]int
- mutex sync.RWMutex
-}
-
-type TestNode struct {
- shh *Whisper
- id *ecdsa.PrivateKey
- server *p2p.Server
- filerId string
-}
-
-var result TestData
-var nodes [NumNodes]*TestNode
-var sharedKey []byte = []byte("some arbitrary data here")
-var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0}
-var expectedMessage []byte = []byte("per rectum ad astra")
-
-// This test does the following:
-// 1. creates a chain of whisper nodes,
-// 2. installs the filters with shared (predefined) parameters,
-// 3. each node sends a number of random (undecryptable) messages,
-// 4. first node sends one expected (decryptable) message,
-// 5. checks if each node have received and decrypted exactly one message.
-func TestSimulation(t *testing.T) {
- t.Skip("TODO: PR-136 Broken test due to EVM upgrade!")
- initialize(t)
-
- for i := 0; i < NumNodes; i++ {
- sendMsg(t, false, i)
- }
-
- sendMsg(t, true, 0)
- checkPropagation(t)
- stopServers()
-}
-
-func initialize(t *testing.T) {
- var err error
- ip := net.IPv4(127, 0, 0, 1)
- port0 := 30303
-
- for i := 0; i < NumNodes; i++ {
- var node TestNode
- node.shh = New(&DefaultConfig)
- err = node.shh.SetMinimumPoW(0.00000001)
- if err != nil {
- t.Fatal(err)
- }
- err = node.shh.Start(nil)
- if err != nil {
- t.Fatal(err)
- }
- topics := make([]TopicType, 0)
- topics = append(topics, sharedTopic)
- f := Filter{KeySym: sharedKey}
- f.Topics = [][]byte{topics[0][:]}
- node.filerId, err = node.shh.Subscribe(&f)
- if err != nil {
- t.Fatalf("failed to install the filter: %s.", err)
- }
- node.id, err = crypto.HexToECDSA(keys[i])
- if err != nil {
- t.Fatalf("failed convert the key: %s.", keys[i])
- }
- port := port0 + i
- addr := fmt.Sprintf(":%d", port) // e.g. ":30303"
- name := common.MakeName("whisper-go", "2.0")
- var peers []*discover.Node
- if i > 0 {
- peerNodeId := nodes[i-1].id
- peerPort := uint16(port - 1)
- peerNode := discover.PubkeyID(&peerNodeId.PublicKey)
- peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
- peers = append(peers, peer)
- }
-
- node.server = &p2p.Server{
- Config: p2p.Config{
- PrivateKey: node.id,
- MaxPeers: NumNodes/2 + 1,
- Name: name,
- Protocols: node.shh.Protocols(),
- ListenAddr: addr,
- NAT: nat.Any(),
- BootstrapNodes: peers,
- StaticNodes: peers,
- TrustedNodes: peers,
- },
- }
-
- err = node.server.Start()
- if err != nil {
- t.Fatalf("failed to start server %d.", i)
- }
-
- nodes[i] = &node
- }
-}
-
-func stopServers() {
- for i := 0; i < NumNodes; i++ {
- n := nodes[i]
- if n != nil {
- n.shh.Unsubscribe(n.filerId)
- n.shh.Stop()
- n.server.Stop()
- }
- }
-}
-
-func checkPropagation(t *testing.T) {
- if t.Failed() {
- return
- }
-
- const cycle = 100
- const iterations = 100
-
- for j := 0; j < iterations; j++ {
- time.Sleep(cycle * time.Millisecond)
-
- for i := 0; i < NumNodes; i++ {
- f := nodes[i].shh.GetFilter(nodes[i].filerId)
- if f == nil {
- t.Fatalf("failed to get filterId %s from node %d.", nodes[i].filerId, i)
- }
-
- mail := f.Retrieve()
- if !validateMail(t, i, mail) {
- return
- }
-
- if isTestComplete() {
- return
- }
- }
- }
-
- t.Fatalf("Test was not complete: timeout %d seconds.", iterations*cycle/1000)
-}
-
-func validateMail(t *testing.T, index int, mail []*ReceivedMessage) bool {
- var cnt int
- for _, m := range mail {
- if bytes.Equal(m.Payload, expectedMessage) {
- cnt++
- }
- }
-
- if cnt == 0 {
- // no messages received yet: nothing is wrong
- return true
- }
- if cnt > 1 {
- t.Fatalf("node %d received %d.", index, cnt)
- return false
- }
-
- if cnt > 0 {
- result.mutex.Lock()
- defer result.mutex.Unlock()
- result.counter[index] += cnt
- if result.counter[index] > 1 {
- t.Fatalf("node %d accumulated %d.", index, result.counter[index])
- }
- }
- return true
-}
-
-func isTestComplete() bool {
- result.mutex.RLock()
- defer result.mutex.RUnlock()
-
- for i := 0; i < NumNodes; i++ {
- if result.counter[i] < 1 {
- return false
- }
- }
-
- for i := 0; i < NumNodes; i++ {
- envelopes := nodes[i].shh.Envelopes()
- if len(envelopes) < 2 {
- return false
- }
- }
-
- return true
-}
-
-func sendMsg(t *testing.T, expected bool, id int) {
- if t.Failed() {
- return
- }
-
- opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1}
- if !expected {
- opt.KeySym[0]++
- opt.Topic[0]++
- opt.Payload = opt.Payload[1:]
- }
-
- msg, err := NewSentMessage(&opt)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- envelope, err := msg.Wrap(&opt)
- if err != nil {
- t.Fatalf("failed to seal message: %s", err)
- }
-
- err = nodes[id].shh.Send(envelope)
- if err != nil {
- t.Fatalf("failed to send message: %s", err)
- }
-}
-
-func TestPeerBasic(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d.", seed)
- }
-
- params.PoW = 0.001
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d.", seed)
- }
-
- p := newPeer(nil, nil, nil)
- p.mark(env)
- if !p.marked(env) {
- t.Fatalf("failed mark with seed %d.", seed)
- }
-}
diff --git a/whisper/whisperv5/topic.go b/whisper/whisperv5/topic.go
deleted file mode 100644
index b4c5b27a60..0000000000
--- a/whisper/whisperv5/topic.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Topic element.
-
-package whisperv5
-
-import (
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-// Topic represents a cryptographically secure, probabilistic partial
-// classifications of a message, determined as the first (left) 4 bytes of the
-// SHA3 hash of some arbitrary data given by the original author of the message.
-type TopicType [TopicLength]byte
-
-func BytesToTopic(b []byte) (t TopicType) {
- sz := TopicLength
- if x := len(b); x < TopicLength {
- sz = x
- }
- for i := 0; i < sz; i++ {
- t[i] = b[i]
- }
- return t
-}
-
-// String converts a topic byte array to a string representation.
-func (t *TopicType) String() string {
- return common.ToHex(t[:])
-}
-
-// MarshalText returns the hex representation of t.
-func (t TopicType) MarshalText() ([]byte, error) {
- return hexutil.Bytes(t[:]).MarshalText()
-}
-
-// UnmarshalText parses a hex representation to a topic.
-func (t *TopicType) UnmarshalText(input []byte) error {
- return hexutil.UnmarshalFixedText("Topic", input, t[:])
-}
diff --git a/whisper/whisperv5/topic_test.go b/whisper/whisperv5/topic_test.go
deleted file mode 100644
index 54bbeaf85e..0000000000
--- a/whisper/whisperv5/topic_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "encoding/json"
- "testing"
-)
-
-var topicStringTests = []struct {
- topic TopicType
- str string
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"},
- {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"},
- {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"},
-}
-
-func TestTopicString(t *testing.T) {
- for i, tst := range topicStringTests {
- s := tst.topic.String()
- if s != tst.str {
- t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str)
- }
- }
-}
-
-var bytesToTopicTests = []struct {
- data []byte
- topic TopicType
-}{
- {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}},
- {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}},
- {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}},
- {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}},
- {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil},
-}
-
-var unmarshalTestsGood = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)},
- {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)},
- {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)},
-}
-
-var unmarshalTestsBad = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"abcdefg0"`)},
-}
-
-var unmarshalTestsUgly = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte(`"0x00000001"`)},
-}
-
-func TestBytesToTopic(t *testing.T) {
- for i, tst := range bytesToTopicTests {
- top := BytesToTopic(tst.data)
- if top != tst.topic {
- t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic)
- }
- }
-}
-
-func TestUnmarshalTestsGood(t *testing.T) {
- for i, tst := range unmarshalTestsGood {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err != nil {
- t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err)
- } else if top != tst.topic {
- t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
- }
- }
-}
-
-func TestUnmarshalTestsBad(t *testing.T) {
- // in this test UnmarshalJSON() is supposed to fail
- for i, tst := range unmarshalTestsBad {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err == nil {
- t.Fatalf("failed test %d. input: %v.", i, tst.data)
- }
- }
-}
-
-func TestUnmarshalTestsUgly(t *testing.T) {
- // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong
- for i, tst := range unmarshalTestsUgly {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err != nil {
- t.Errorf("failed test %d. input: %v.", i, tst.data)
- } else if top == tst.topic {
- t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic)
- }
- }
-}
diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go
deleted file mode 100644
index 9bbb1cae01..0000000000
--- a/whisper/whisperv5/whisper.go
+++ /dev/null
@@ -1,858 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "bytes"
- "crypto/ecdsa"
- crand "crypto/rand"
- "crypto/sha256"
- "fmt"
- "runtime"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/rpc"
- mapset "github.com/deckarep/golang-set"
- "github.com/syndtr/goleveldb/leveldb/errors"
- "golang.org/x/crypto/pbkdf2"
- "golang.org/x/sync/syncmap"
-)
-
-type Statistics struct {
- messagesCleared int
- memoryCleared int
- memoryUsed int
- cycles int
- totalMessagesCleared int
-}
-
-const (
- minPowIdx = iota // Minimal PoW required by the whisper node
- maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
- overflowIdx = iota // Indicator of message queue overflow
-)
-
-// Whisper represents a dark communication interface through the Ethereum
-// network, using its very own P2P communication layer.
-type Whisper struct {
- protocol p2p.Protocol // Protocol description and parameters
- filters *Filters // Message filters installed with Subscribe function
-
- privateKeys map[string]*ecdsa.PrivateKey // Private key storage
- symKeys map[string][]byte // Symmetric key storage
- keyMu sync.RWMutex // Mutex associated with key storages
-
- poolMu sync.RWMutex // Mutex to sync the message and expiration pools
- envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
- expirations map[uint32]mapset.Set // Message expiration pool
-
- peerMu sync.RWMutex // Mutex to sync the active peer set
- peers map[*Peer]struct{} // Set of currently active peers
-
- messageQueue chan *Envelope // Message queue for normal whisper messages
- p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further)
- quit chan struct{} // Channel used for graceful exit
-
- settings syncmap.Map // holds configuration settings that can be dynamically changed
-
- statsMu sync.Mutex // guard stats
- stats Statistics // Statistics of whisper node
-
- mailServer MailServer // MailServer interface
-}
-
-// New creates a Whisper client ready to communicate through the Ethereum P2P network.
-func New(cfg *Config) *Whisper {
- if cfg == nil {
- cfg = &DefaultConfig
- }
-
- whisper := &Whisper{
- privateKeys: make(map[string]*ecdsa.PrivateKey),
- symKeys: make(map[string][]byte),
- envelopes: make(map[common.Hash]*Envelope),
- expirations: make(map[uint32]mapset.Set),
- peers: make(map[*Peer]struct{}),
- messageQueue: make(chan *Envelope, messageQueueLimit),
- p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
- quit: make(chan struct{}),
- }
-
- whisper.filters = NewFilters(whisper)
-
- whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
- whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
- whisper.settings.Store(overflowIdx, false)
-
- // p2p whisper sub protocol handler
- whisper.protocol = p2p.Protocol{
- Name: ProtocolName,
- Version: uint(ProtocolVersion),
- Length: NumberOfMessageCodes,
- Run: whisper.HandlePeer,
- NodeInfo: func() interface{} {
- return map[string]interface{}{
- "version": ProtocolVersionStr,
- "maxMessageSize": whisper.MaxMessageSize(),
- "minimumPoW": whisper.MinPow(),
- }
- },
- }
-
- return whisper
-}
-
-func (w *Whisper) MinPow() float64 {
- val, _ := w.settings.Load(minPowIdx)
- return val.(float64)
-}
-
-// MaxMessageSize returns the maximum accepted message size.
-func (w *Whisper) MaxMessageSize() uint32 {
- val, _ := w.settings.Load(maxMsgSizeIdx)
- return val.(uint32)
-}
-
-// Overflow returns an indication if the message queue is full.
-func (w *Whisper) Overflow() bool {
- val, _ := w.settings.Load(overflowIdx)
- return val.(bool)
-}
-
-// APIs returns the RPC descriptors the Whisper implementation offers
-func (w *Whisper) APIs() []rpc.API {
- return []rpc.API{
- {
- Namespace: ProtocolName,
- Version: ProtocolVersionStr,
- Service: NewPublicWhisperAPI(w),
- Public: true,
- },
- }
-}
-
-// RegisterServer registers MailServer interface.
-// MailServer will process all the incoming messages with p2pRequestCode.
-func (w *Whisper) RegisterServer(server MailServer) {
- w.mailServer = server
-}
-
-// Protocols returns the whisper sub-protocols ran by this particular client.
-func (w *Whisper) Protocols() []p2p.Protocol {
- return []p2p.Protocol{w.protocol}
-}
-
-// Version returns the whisper sub-protocols version number.
-func (w *Whisper) Version() uint {
- return w.protocol.Version
-}
-
-// SetMaxMessageSize sets the maximal message size allowed by this node
-func (w *Whisper) SetMaxMessageSize(size uint32) error {
- if size > MaxMessageSize {
- return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize)
- }
- w.settings.Store(maxMsgSizeIdx, size)
- return nil
-}
-
-// SetMinimumPoW sets the minimal PoW required by this node
-func (w *Whisper) SetMinimumPoW(val float64) error {
- if val <= 0.0 {
- return fmt.Errorf("invalid PoW: %f", val)
- }
- w.settings.Store(minPowIdx, val)
- return nil
-}
-
-// getPeer retrieves peer by ID
-func (w *Whisper) getPeer(peerID []byte) (*Peer, error) {
- w.peerMu.Lock()
- defer w.peerMu.Unlock()
- for p := range w.peers {
- id := p.peer.ID()
- if bytes.Equal(peerID, id[:]) {
- return p, nil
- }
- }
- return nil, fmt.Errorf("Could not find peer with ID: %x", peerID)
-}
-
-// AllowP2PMessagesFromPeer marks specific peer trusted,
-// which will allow it to send historic (expired) messages.
-func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error {
- p, err := w.getPeer(peerID)
- if err != nil {
- return err
- }
- p.trusted = true
- return nil
-}
-
-// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer,
-// which is known to implement MailServer interface, and is supposed to process this
-// request and respond with a number of peer-to-peer messages (possibly expired),
-// which are not supposed to be forwarded any further.
-// The whisper protocol is agnostic of the format and contents of envelope.
-func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
- p, err := w.getPeer(peerID)
- if err != nil {
- return err
- }
- p.trusted = true
- return p2p.Send(p.ws, p2pRequestCode, envelope)
-}
-
-// SendP2PMessage sends a peer-to-peer message to a specific peer.
-func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
- p, err := w.getPeer(peerID)
- if err != nil {
- return err
- }
- return w.SendP2PDirect(p, envelope)
-}
-
-// SendP2PDirect sends a peer-to-peer message to a specific peer.
-func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
- return p2p.Send(peer.ws, p2pCode, envelope)
-}
-
-// NewKeyPair generates a new cryptographic identity for the client, and injects
-// it into the known identities for message decryption. Returns ID of the new key pair.
-func (w *Whisper) NewKeyPair() (string, error) {
- key, err := crypto.GenerateKey()
- if err != nil || !validatePrivateKey(key) {
- key, err = crypto.GenerateKey() // retry once
- }
- if err != nil {
- return "", err
- }
- if !validatePrivateKey(key) {
- return "", errors.New("failed to generate valid key")
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
-
- if w.privateKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- w.privateKeys[id] = key
- return id, nil
-}
-
-// DeleteKeyPair deletes the specified key if it exists.
-func (w *Whisper) DeleteKeyPair(key string) bool {
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
-
- if w.privateKeys[key] != nil {
- delete(w.privateKeys, key)
- return true
- }
- return false
-}
-
-// AddKeyPair imports a asymmetric private key and returns it identifier.
-func (w *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- w.keyMu.Lock()
- w.privateKeys[id] = key
- w.keyMu.Unlock()
-
- return id, nil
-}
-
-// HasKeyPair checks if the the whisper node is configured with the private key
-// of the specified public pair.
-func (w *Whisper) HasKeyPair(id string) bool {
- w.keyMu.RLock()
- defer w.keyMu.RUnlock()
- return w.privateKeys[id] != nil
-}
-
-// GetPrivateKey retrieves the private key of the specified identity.
-func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
- w.keyMu.RLock()
- defer w.keyMu.RUnlock()
- key := w.privateKeys[id]
- if key == nil {
- return nil, errors.New("invalid id")
- }
- return key, nil
-}
-
-// GenerateSymKey generates a random symmetric key and stores it under id,
-// which is then returned. Will be used in the future for session key exchange.
-func (w *Whisper) GenerateSymKey() (string, error) {
- key := make([]byte, aesKeyLength)
- _, err := crand.Read(key)
- if err != nil {
- return "", err
- } else if !validateSymmetricKey(key) {
- return "", errors.New("error in GenerateSymKey: crypto/rand failed to generate random data")
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
-
- if w.symKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- w.symKeys[id] = key
- return id, nil
-}
-
-// AddSymKeyDirect stores the key, and returns its id.
-func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) {
- if len(key) != aesKeyLength {
- return "", fmt.Errorf("wrong key size: %d", len(key))
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
-
- if w.symKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- w.symKeys[id] = key
- return id, nil
-}
-
-// AddSymKeyFromPassword generates the key from password, stores it, and returns its id.
-func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) {
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
- if w.HasSymKey(id) {
- return "", errors.New("failed to generate unique ID")
- }
-
- derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion)
- if err != nil {
- return "", err
- }
-
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
-
- // double check is necessary, because deriveKeyMaterial() is very slow
- if w.symKeys[id] != nil {
- return "", errors.New("critical error: failed to generate unique ID")
- }
- w.symKeys[id] = derived
- return id, nil
-}
-
-// HasSymKey returns true if there is a key associated with the given id.
-// Otherwise returns false.
-func (w *Whisper) HasSymKey(id string) bool {
- w.keyMu.RLock()
- defer w.keyMu.RUnlock()
- return w.symKeys[id] != nil
-}
-
-// DeleteSymKey deletes the key associated with the name string if it exists.
-func (w *Whisper) DeleteSymKey(id string) bool {
- w.keyMu.Lock()
- defer w.keyMu.Unlock()
- if w.symKeys[id] != nil {
- delete(w.symKeys, id)
- return true
- }
- return false
-}
-
-// GetSymKey returns the symmetric key associated with the given id.
-func (w *Whisper) GetSymKey(id string) ([]byte, error) {
- w.keyMu.RLock()
- defer w.keyMu.RUnlock()
- if w.symKeys[id] != nil {
- return w.symKeys[id], nil
- }
- return nil, errors.New("non-existent key ID")
-}
-
-// Subscribe installs a new message handler used for filtering, decrypting
-// and subsequent storing of incoming messages.
-func (w *Whisper) Subscribe(f *Filter) (string, error) {
- return w.filters.Install(f)
-}
-
-// GetFilter returns the filter by id.
-func (w *Whisper) GetFilter(id string) *Filter {
- return w.filters.Get(id)
-}
-
-// Unsubscribe removes an installed message handler.
-func (w *Whisper) Unsubscribe(id string) error {
- ok := w.filters.Uninstall(id)
- if !ok {
- return errors.New("Unsubscribe: Invalid ID")
- }
- return nil
-}
-
-// Send injects a message into the whisper send queue, to be distributed in the
-// network in the coming cycles.
-func (w *Whisper) Send(envelope *Envelope) error {
- ok, err := w.add(envelope)
- if err != nil {
- return err
- }
- if !ok {
- return errors.New("failed to add envelope")
- }
- return err
-}
-
-// Start implements node.Service, starting the background data propagation thread
-// of the Whisper protocol.
-func (w *Whisper) Start(*p2p.Server) error {
- log.Info("started whisper v." + ProtocolVersionStr)
- go w.update()
-
- numCPU := runtime.NumCPU()
- for i := 0; i < numCPU; i++ {
- go w.processQueue()
- }
-
- return nil
-}
-
-// Stop implements node.Service, stopping the background data propagation thread
-// of the Whisper protocol.
-func (w *Whisper) Stop() error {
- close(w.quit)
- log.Info("whisper stopped")
- return nil
-}
-
-// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol
-// connection is negotiated.
-func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
- // Create the new peer and start tracking it
- whisperPeer := newPeer(wh, peer, rw)
-
- wh.peerMu.Lock()
- wh.peers[whisperPeer] = struct{}{}
- wh.peerMu.Unlock()
-
- defer func() {
- wh.peerMu.Lock()
- delete(wh.peers, whisperPeer)
- wh.peerMu.Unlock()
- }()
-
- // Run the peer handshake and state updates
- if err := whisperPeer.handshake(); err != nil {
- return err
- }
- whisperPeer.start()
- defer whisperPeer.stop()
-
- return wh.runMessageLoop(whisperPeer, rw)
-}
-
-// runMessageLoop reads and processes inbound messages directly to merge into client-global state.
-func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
- for {
- // fetch the next packet
- packet, err := rw.ReadMsg()
- if err != nil {
- log.Warn("message loop", "peer", p.peer.ID(), "err", err)
- return err
- }
- if packet.Size > wh.MaxMessageSize() {
- log.Warn("oversized message received", "peer", p.peer.ID())
- return errors.New("oversized message received")
- }
-
- switch packet.Code {
- case statusCode:
- // this should not happen, but no need to panic; just ignore this message.
- log.Warn("unxepected status message received", "peer", p.peer.ID())
- case messagesCode:
- // decode the contained envelopes
- var envelope Envelope
- if err := packet.Decode(&envelope); err != nil {
- log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid envelope")
- }
- cached, err := wh.add(&envelope)
- if err != nil {
- log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid envelope")
- }
- if cached {
- p.mark(&envelope)
- }
- case p2pCode:
- // peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
- // this message is not supposed to be forwarded to other peers, and
- // therefore might not satisfy the PoW, expiry and other requirements.
- // these messages are only accepted from the trusted peer.
- if p.trusted {
- var envelope Envelope
- if err := packet.Decode(&envelope); err != nil {
- log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid direct message")
- }
- wh.postEvent(&envelope, true)
- }
- case p2pRequestCode:
- // Must be processed if mail server is implemented. Otherwise ignore.
- if wh.mailServer != nil {
- var request Envelope
- if err := packet.Decode(&request); err != nil {
- log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid p2p request")
- }
- wh.mailServer.DeliverMail(p, &request)
- }
- default:
- // New message types might be implemented in the future versions of Whisper.
- // For forward compatibility, just ignore.
- }
-
- packet.Discard()
- }
-}
-
-// add inserts a new envelope into the message pool to be distributed within the
-// whisper network. It also inserts the envelope into the expiration pool at the
-// appropriate time-stamp. In case of error, connection should be dropped.
-func (wh *Whisper) add(envelope *Envelope) (bool, error) {
- now := uint32(time.Now().Unix())
- sent := envelope.Expiry - envelope.TTL
-
- if sent > now {
- if sent-SynchAllowance > now {
- return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash())
- } else {
- // recalculate PoW, adjusted for the time difference, plus one second for latency
- envelope.calculatePoW(sent - now + 1)
- }
- }
-
- if envelope.Expiry < now {
- if envelope.Expiry+SynchAllowance*2 < now {
- return false, errors.New("very old message")
- } else {
- log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex())
- return false, nil // drop envelope without error
- }
- }
-
- if uint32(envelope.size()) > wh.MaxMessageSize() {
- return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
- }
-
- if len(envelope.Version) > 4 {
- return false, fmt.Errorf("oversized version [%x]", envelope.Hash())
- }
-
- aesNonceSize := len(envelope.AESNonce)
- if aesNonceSize != 0 && aesNonceSize != AESNonceLength {
- // the standard AES GCM nonce size is 12 bytes,
- // but constant gcmStandardNonceSize cannot be accessed (not exported)
- return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
- }
-
- if envelope.PoW() < wh.MinPow() {
- log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
- return false, nil // drop envelope without error
- }
-
- hash := envelope.Hash()
-
- wh.poolMu.Lock()
- _, alreadyCached := wh.envelopes[hash]
- if !alreadyCached {
- wh.envelopes[hash] = envelope
- if wh.expirations[envelope.Expiry] == nil {
- wh.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet()
- }
- if !wh.expirations[envelope.Expiry].Contains(hash) {
- wh.expirations[envelope.Expiry].Add(hash)
- }
- }
- wh.poolMu.Unlock()
-
- if alreadyCached {
- log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex())
- } else {
- log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex())
- wh.statsMu.Lock()
- wh.stats.memoryUsed += envelope.size()
- wh.statsMu.Unlock()
- wh.postEvent(envelope, false) // notify the local node about the new message
- if wh.mailServer != nil {
- wh.mailServer.Archive(envelope)
- }
- }
- return true, nil
-}
-
-// postEvent queues the message for further processing.
-func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) {
- // if the version of incoming message is higher than
- // currently supported version, we can not decrypt it,
- // and therefore just ignore this message
- if envelope.Ver() <= EnvelopeVersion {
- if isP2P {
- w.p2pMsgQueue <- envelope
- } else {
- w.checkOverflow()
- w.messageQueue <- envelope
- }
- }
-}
-
-// checkOverflow checks if message queue overflow occurs and reports it if necessary.
-func (w *Whisper) checkOverflow() {
- queueSize := len(w.messageQueue)
-
- if queueSize == messageQueueLimit {
- if !w.Overflow() {
- w.settings.Store(overflowIdx, true)
- log.Warn("message queue overflow")
- }
- } else if queueSize <= messageQueueLimit/2 {
- if w.Overflow() {
- w.settings.Store(overflowIdx, false)
- log.Warn("message queue overflow fixed (back to normal)")
- }
- }
-}
-
-// processQueue delivers the messages to the watchers during the lifetime of the whisper node.
-func (w *Whisper) processQueue() {
- var e *Envelope
- for {
- select {
- case <-w.quit:
- return
-
- case e = <-w.messageQueue:
- w.filters.NotifyWatchers(e, false)
-
- case e = <-w.p2pMsgQueue:
- w.filters.NotifyWatchers(e, true)
- }
- }
-}
-
-// update loops until the lifetime of the whisper node, updating its internal
-// state by expiring stale messages from the pool.
-func (w *Whisper) update() {
- // Start a ticker to check for expirations
- expire := time.NewTicker(expirationCycle)
-
- // Repeat updates until termination is requested
- for {
- select {
- case <-expire.C:
- w.expire()
-
- case <-w.quit:
- return
- }
- }
-}
-
-// expire iterates over all the expiration timestamps, removing all stale
-// messages from the pools.
-func (w *Whisper) expire() {
- w.poolMu.Lock()
- defer w.poolMu.Unlock()
-
- w.statsMu.Lock()
- defer w.statsMu.Unlock()
- w.stats.reset()
- now := uint32(time.Now().Unix())
- for expiry, hashSet := range w.expirations {
- if expiry < now {
- // Dump all expired messages and remove timestamp
- hashSet.Each(func(v interface{}) bool {
- sz := w.envelopes[v.(common.Hash)].size()
- delete(w.envelopes, v.(common.Hash))
- w.stats.messagesCleared++
- w.stats.memoryCleared += sz
- w.stats.memoryUsed -= sz
- return true
- })
- w.expirations[expiry].Clear()
- delete(w.expirations, expiry)
- }
- }
-}
-
-// Stats returns the whisper node statistics.
-func (w *Whisper) Stats() Statistics {
- w.statsMu.Lock()
- defer w.statsMu.Unlock()
-
- return w.stats
-}
-
-// Envelopes retrieves all the messages currently pooled by the node.
-func (w *Whisper) Envelopes() []*Envelope {
- w.poolMu.RLock()
- defer w.poolMu.RUnlock()
-
- all := make([]*Envelope, 0, len(w.envelopes))
- for _, envelope := range w.envelopes {
- all = append(all, envelope)
- }
- return all
-}
-
-// Messages iterates through all currently floating envelopes
-// and retrieves all the messages, that this filter could decrypt.
-func (w *Whisper) Messages(id string) []*ReceivedMessage {
- result := make([]*ReceivedMessage, 0)
- w.poolMu.RLock()
- defer w.poolMu.RUnlock()
-
- if filter := w.filters.Get(id); filter != nil {
- for _, env := range w.envelopes {
- msg := filter.processEnvelope(env)
- if msg != nil {
- result = append(result, msg)
- }
- }
- }
- return result
-}
-
-// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
-func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
- w.poolMu.Lock()
- defer w.poolMu.Unlock()
-
- _, exist := w.envelopes[hash]
- return exist
-}
-
-// reset resets the node's statistics after each expiry cycle.
-func (s *Statistics) reset() {
- s.cycles++
- s.totalMessagesCleared += s.messagesCleared
-
- s.memoryCleared = 0
- s.messagesCleared = 0
-}
-
-// ValidatePublicKey checks the format of the given public key.
-func ValidatePublicKey(k *ecdsa.PublicKey) bool {
- return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0
-}
-
-// validatePrivateKey checks the format of the given private key.
-func validatePrivateKey(k *ecdsa.PrivateKey) bool {
- if k == nil || k.D == nil || k.D.Sign() == 0 {
- return false
- }
- return ValidatePublicKey(&k.PublicKey)
-}
-
-// validateSymmetricKey returns false if the key contains all zeros
-func validateSymmetricKey(k []byte) bool {
- return len(k) > 0 && !containsOnlyZeros(k)
-}
-
-// containsOnlyZeros checks if the data contain only zeros.
-func containsOnlyZeros(data []byte) bool {
- for _, b := range data {
- if b != 0 {
- return false
- }
- }
- return true
-}
-
-// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer.
-func bytesToUintLittleEndian(b []byte) (res uint64) {
- mul := uint64(1)
- for i := 0; i < len(b); i++ {
- res += uint64(b[i]) * mul
- mul *= 256
- }
- return res
-}
-
-// BytesToUintBigEndian converts the slice to 64-bit unsigned integer.
-func BytesToUintBigEndian(b []byte) (res uint64) {
- for i := 0; i < len(b); i++ {
- res *= 256
- res += uint64(b[i])
- }
- return res
-}
-
-// deriveKeyMaterial derives symmetric key material from the key or password.
-// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
-func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
- if version == 0 {
- // kdf should run no less than 0.1 seconds on average compute,
- // because it's a once in a session experience
- derivedKey := pbkdf2.Key(key, nil, 65356, aesKeyLength, sha256.New)
- return derivedKey, nil
- } else {
- return nil, unknownVersionError(version)
- }
-}
-
-// GenerateRandomID generates a random string, which is then returned to be used as a key id
-func GenerateRandomID() (id string, err error) {
- buf := make([]byte, keyIdSize)
- _, err = crand.Read(buf)
- if err != nil {
- return "", err
- }
- if !validateSymmetricKey(buf) {
- return "", errors.New("error in generateRandomID: crypto/rand failed to generate random data")
- }
- id = common.Bytes2Hex(buf)
- return id, err
-}
diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go
deleted file mode 100644
index 64c0d9b494..0000000000
--- a/whisper/whisperv5/whisper_test.go
+++ /dev/null
@@ -1,851 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv5
-
-import (
- "bytes"
- "crypto/ecdsa"
- mrand "math/rand"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
-)
-
-func TestWhisperBasic(t *testing.T) {
- w := New(&DefaultConfig)
- p := w.Protocols()
- shh := p[0]
- if shh.Name != ProtocolName {
- t.Fatalf("failed Protocol Name: %v.", shh.Name)
- }
- if uint64(shh.Version) != ProtocolVersion {
- t.Fatalf("failed Protocol Version: %v.", shh.Version)
- }
- if shh.Length != NumberOfMessageCodes {
- t.Fatalf("failed Protocol Length: %v.", shh.Length)
- }
- if shh.Run == nil {
- t.Fatalf("failed shh.Run.")
- }
- if uint64(w.Version()) != ProtocolVersion {
- t.Fatalf("failed whisper Version: %v.", shh.Version)
- }
- if w.GetFilter("non-existent") != nil {
- t.Fatalf("failed GetFilter.")
- }
-
- peerID := make([]byte, 64)
- mrand.Read(peerID)
- peer, _ := w.getPeer(peerID)
- if peer != nil {
- t.Fatal("found peer for random key.")
- }
- if err := w.AllowP2PMessagesFromPeer(peerID); err == nil {
- t.Fatalf("failed MarkPeerTrusted.")
- }
- exist := w.HasSymKey("non-existing")
- if exist {
- t.Fatalf("failed HasSymKey.")
- }
- key, err := w.GetSymKey("non-existing")
- if err == nil {
- t.Fatalf("failed GetSymKey(non-existing): false positive.")
- }
- if key != nil {
- t.Fatalf("failed GetSymKey: false positive.")
- }
- mail := w.Envelopes()
- if len(mail) != 0 {
- t.Fatalf("failed w.Envelopes().")
- }
- m := w.Messages("non-existent")
- if len(m) != 0 {
- t.Fatalf("failed w.Messages.")
- }
-
- var derived []byte
- ver := uint64(0xDEADBEEF)
- if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) {
- t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err)
- }
- derived, err = deriveKeyMaterial(peerID, 0)
- if err != nil {
- t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err)
- }
- if !validateSymmetricKey(derived) {
- t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
- }
- if containsOnlyZeros(derived) {
- t.Fatalf("failed containsOnlyZeros with param = %v.", derived)
- }
-
- buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0}
- le := bytesToUintLittleEndian(buf)
- be := BytesToUintBigEndian(buf)
- if le != uint64(0x280e5ff) {
- t.Fatalf("failed bytesToIntLittleEndian: %d.", le)
- }
- if be != uint64(0xffe5800200) {
- t.Fatalf("failed BytesToIntBigEndian: %d.", be)
- }
-
- id, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- pk, err := w.GetPrivateKey(id)
- if err != nil {
- t.Fatalf("failed to retrieve new key pair: %s.", err)
- }
- if !validatePrivateKey(pk) {
- t.Fatalf("failed validatePrivateKey: %v.", pk)
- }
- if !ValidatePublicKey(&pk.PublicKey) {
- t.Fatalf("failed ValidatePublicKey: %v.", pk)
- }
-}
-
-func TestWhisperAsymmetricKeyImport(t *testing.T) {
- var (
- w = New(&DefaultConfig)
- privateKeys []*ecdsa.PrivateKey
- )
-
- for i := 0; i < 50; i++ {
- id, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("could not generate key: %v", err)
- }
-
- pk, err := w.GetPrivateKey(id)
- if err != nil {
- t.Fatalf("could not export private key: %v", err)
- }
-
- privateKeys = append(privateKeys, pk)
-
- if !w.DeleteKeyPair(id) {
- t.Fatalf("could not delete private key")
- }
- }
-
- for _, pk := range privateKeys {
- if _, err := w.AddKeyPair(pk); err != nil {
- t.Fatalf("could not import private key: %v", err)
- }
- }
-}
-
-func TestWhisperIdentityManagement(t *testing.T) {
- w := New(&DefaultConfig)
- id1, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- id2, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- pk1, err := w.GetPrivateKey(id1)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- pk2, err := w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
-
- if !w.HasKeyPair(id1) {
- t.Fatalf("failed HasIdentity(pk1).")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed HasIdentity(pk2).")
- }
- if pk1 == nil {
- t.Fatalf("failed GetIdentity(pk1).")
- }
- if pk2 == nil {
- t.Fatalf("failed GetIdentity(pk2).")
- }
-
- if !validatePrivateKey(pk1) {
- t.Fatalf("pk1 is invalid.")
- }
- if !validatePrivateKey(pk2) {
- t.Fatalf("pk2 is invalid.")
- }
-
- // Delete one identity
- done := w.DeleteKeyPair(id1)
- if !done {
- t.Fatalf("failed to delete id1.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed DeleteIdentity(pub1): still exist.")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed DeleteIdentity(pub1): first key still exist.")
- }
- if pk2 == nil {
- t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.")
- }
-
- // Delete again non-existing identity
- done = w.DeleteKeyPair(id1)
- if done {
- t.Fatalf("delete id1: false positive.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed delete non-existing identity: exist.")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed delete non-existing identity: pub2 does not exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed delete non-existing identity: first key exist.")
- }
- if pk2 == nil {
- t.Fatalf("failed delete non-existing identity: second key does not exist.")
- }
-
- // Delete second identity
- done = w.DeleteKeyPair(id2)
- if !done {
- t.Fatalf("failed to delete id2.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed delete second identity: first identity exist.")
- }
- if w.HasKeyPair(id2) {
- t.Fatalf("failed delete second identity: still exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed delete second identity: first key exist.")
- }
- if pk2 != nil {
- t.Fatalf("failed delete second identity: second key exist.")
- }
-}
-
-func TestWhisperSymKeyManagement(t *testing.T) {
- InitSingleTest()
-
- var err error
- var k1, k2 []byte
- w := New(&DefaultConfig)
- id1 := string("arbitrary-string-1")
- id2 := string("arbitrary-string-2")
-
- id1, err = w.GenerateSymKey()
- if err != nil {
- t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
- }
-
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed GetSymKey(id2): false positive.")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("failed HasSymKey(id1).")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed HasSymKey(id2): false positive.")
- }
- if k1 == nil {
- t.Fatalf("first key does not exist.")
- }
- if k2 != nil {
- t.Fatalf("second key still exist.")
- }
-
- // add existing id, nothing should change
- randomKey := make([]byte, aesKeyLength)
- mrand.Read(randomKey)
- id1, err = w.AddSymKeyDirect(randomKey)
- if err != nil {
- t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err)
- }
-
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id2): false positive.")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("failed w.HasSymKey(id1).")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed w.HasSymKey(id2): false positive.")
- }
- if k1 == nil {
- t.Fatalf("first key does not exist.")
- }
- if !bytes.Equal(k1, randomKey) {
- t.Fatalf("k1 != randomKey.")
- }
- if k2 != nil {
- t.Fatalf("second key already exist.")
- }
-
- id2, err = w.AddSymKeyDirect(randomKey)
- if err != nil {
- t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err)
- }
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("HasSymKey(id1) failed.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("HasSymKey(id2) failed.")
- }
- if k1 == nil {
- t.Fatalf("k1 does not exist.")
- }
- if k2 == nil {
- t.Fatalf("k2 does not exist.")
- }
- if !bytes.Equal(k1, k2) {
- t.Fatalf("k1 != k2.")
- }
- if !bytes.Equal(k1, randomKey) {
- t.Fatalf("k1 != randomKey.")
- }
- if len(k1) != aesKeyLength {
- t.Fatalf("wrong length of k1.")
- }
- if len(k2) != aesKeyLength {
- t.Fatalf("wrong length of k2.")
- }
-
- w.DeleteSymKey(id1)
- k1, err = w.GetSymKey(id1)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id1): false positive.")
- }
- if k1 != nil {
- t.Fatalf("failed GetSymKey(id1): false positive.")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if w.HasSymKey(id1) {
- t.Fatalf("failed to delete first key: still exist.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("failed to delete first key: second key does not exist.")
- }
- if k1 != nil {
- t.Fatalf("failed to delete first key.")
- }
- if k2 == nil {
- t.Fatalf("failed to delete first key: second key is nil.")
- }
-
- w.DeleteSymKey(id1)
- w.DeleteSymKey(id2)
- k1, err = w.GetSymKey(id1)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id1): false positive.")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id2): false positive.")
- }
- if k1 != nil || k2 != nil {
- t.Fatalf("k1 or k2 is not nil")
- }
- if w.HasSymKey(id1) {
- t.Fatalf("failed to delete second key: first key exist.")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed to delete second key: still exist.")
- }
- if k1 != nil {
- t.Fatalf("failed to delete second key: first key is not nil.")
- }
- if k2 != nil {
- t.Fatalf("failed to delete second key: second key is not nil.")
- }
-
- randomKey = make([]byte, aesKeyLength+1)
- mrand.Read(randomKey)
- _, err = w.AddSymKeyDirect(randomKey)
- if err == nil {
- t.Fatalf("added the key with wrong size, seed %d.", seed)
- }
-
- const password = "arbitrary data here"
- id1, err = w.AddSymKeyFromPassword(password)
- if err != nil {
- t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err)
- }
- id2, err = w.AddSymKeyFromPassword(password)
- if err != nil {
- t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err)
- }
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("HasSymKey(id1) failed.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("HasSymKey(id2) failed.")
- }
- if k1 == nil {
- t.Fatalf("k1 does not exist.")
- }
- if k2 == nil {
- t.Fatalf("k2 does not exist.")
- }
- if !bytes.Equal(k1, k2) {
- t.Fatalf("k1 != k2.")
- }
- if len(k1) != aesKeyLength {
- t.Fatalf("wrong length of k1.")
- }
- if len(k2) != aesKeyLength {
- t.Fatalf("wrong length of k2.")
- }
- if !validateSymmetricKey(k2) {
- t.Fatalf("key validation failed.")
- }
-}
-
-func TestExpiry(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- w.SetMinimumPoW(0.0000001)
- defer w.SetMinimumPoW(DefaultMinimumPoW)
- w.Start(nil)
- defer w.Stop()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.TTL = 1
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
- }
-
- // wait till received or timeout
- var received, expired bool
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // wait till expired or timeout
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) == 0 {
- expired = true
- break
- }
- }
-
- if !expired {
- t.Fatalf("expire failed, seed: %d.", seed)
- }
-}
-
-func TestCustomization(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPoW(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- const smallPoW = 0.00001
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[2])
- params.PoW = smallPoW
- params.TTL = 3600 * 24 // one day
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err == nil {
- t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed)
- }
-
- w.SetMinimumPoW(smallPoW / 2)
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
- }
-
- params.TTL++
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- w.SetMaxMessageSize(uint32(env.size() - 1))
- err = w.Send(env)
- if err == nil {
- t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed)
- }
-
- w.SetMaxMessageSize(DefaultMaxMessageSize)
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) > 1 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- id, err := w.Subscribe(f)
- if err != nil {
- t.Fatalf("failed subscribe with seed %d: %s.", seed, err)
- }
- time.Sleep(5 * time.Millisecond)
- mail := f.Retrieve()
- if len(mail) > 0 {
- t.Fatalf("received premature mail")
- }
-
- mail = w.Messages(id)
- if len(mail) != 2 {
- t.Fatalf("failed to get whisper messages")
- }
-}
-
-func TestSymmetricSendCycle(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPoW(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter1, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter1.PoW = DefaultMinimumPoW
-
- // Copy the first filter since some of its fields
- // are randomly gnerated.
- filter2 := &Filter{
- KeySym: filter1.KeySym,
- Topics: filter1.Topics,
- PoW: filter1.PoW,
- AllowP2P: filter1.AllowP2P,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- filter1.Src = ¶ms.Src.PublicKey
- filter2.Src = ¶ms.Src.PublicKey
-
- params.KeySym = filter1.KeySym
- params.Topic = BytesToTopic(filter1.Topics[2])
- params.PoW = filter1.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter1)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter2)
- if err != nil {
- t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail1 := filter1.Retrieve()
- mail2 := filter2.Retrieve()
- if len(mail2) == 0 {
- t.Fatalf("did not receive any email for filter 2")
- }
- if len(mail1) == 0 {
- t.Fatalf("did not receive any email for filter 1")
- }
-
-}
-
-func TestSymmetricSendWithoutAKey(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPoW(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter.PoW = DefaultMinimumPoW
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- filter.Src = nil
-
- params.KeySym = filter.KeySym
- params.Topic = BytesToTopic(filter.Topics[2])
- params.PoW = filter.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail := filter.Retrieve()
- if len(mail) == 0 {
- t.Fatalf("did not receive message in spite of not setting a public key")
- }
-}
-
-func TestSymmetricSendKeyMismatch(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPoW(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter.PoW = DefaultMinimumPoW
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.KeySym = filter.KeySym
- params.Topic = BytesToTopic(filter.Topics[2])
- params.PoW = filter.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail := filter.Retrieve()
- if len(mail) > 0 {
- t.Fatalf("received a message when keys weren't matching")
- }
-}
diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go
deleted file mode 100644
index 8711d6b195..0000000000
--- a/whisper/whisperv6/api.go
+++ /dev/null
@@ -1,585 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "context"
- "crypto/ecdsa"
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p/discover"
- "github.com/XinFinOrg/XDPoSChain/rpc"
-)
-
-const (
- filterTimeout = 300 // filters are considered timeout out after filterTimeout seconds
-)
-
-// List of errors
-var (
- ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key")
- ErrInvalidSymmetricKey = errors.New("invalid symmetric key")
- ErrInvalidPublicKey = errors.New("invalid public key")
- ErrInvalidSigningPubKey = errors.New("invalid signing public key")
- ErrTooLowPoW = errors.New("message rejected, PoW too low")
- ErrNoTopics = errors.New("missing topic(s)")
-)
-
-// PublicWhisperAPI provides the whisper RPC service that can be
-// use publicly without security implications.
-type PublicWhisperAPI struct {
- w *Whisper
-
- mu sync.Mutex
- lastUsed map[string]time.Time // keeps track when a filter was polled for the last time.
-}
-
-// NewPublicWhisperAPI create a new RPC whisper service.
-func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
- api := &PublicWhisperAPI{
- w: w,
- lastUsed: make(map[string]time.Time),
- }
- return api
-}
-
-// Version returns the Whisper sub-protocol version.
-func (api *PublicWhisperAPI) Version(ctx context.Context) string {
- return ProtocolVersionStr
-}
-
-// Info contains diagnostic information.
-type Info struct {
- Memory int `json:"memory"` // Memory size of the floating messages in bytes.
- Messages int `json:"messages"` // Number of floating messages.
- MinPow float64 `json:"minPow"` // Minimal accepted PoW
- MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size
-}
-
-// Info returns diagnostic information about the whisper node.
-func (api *PublicWhisperAPI) Info(ctx context.Context) Info {
- stats := api.w.Stats()
- return Info{
- Memory: stats.memoryUsed,
- Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue),
- MinPow: api.w.MinPow(),
- MaxMessageSize: api.w.MaxMessageSize(),
- }
-}
-
-// SetMaxMessageSize sets the maximum message size that is accepted.
-// Upper limit is defined by MaxMessageSize.
-func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) {
- return true, api.w.SetMaxMessageSize(size)
-}
-
-// SetMinPoW sets the minimum PoW, and notifies the peers.
-func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) {
- return true, api.w.SetMinimumPoW(pow)
-}
-
-// SetBloomFilter sets the new value of bloom filter, and notifies the peers.
-func (api *PublicWhisperAPI) SetBloomFilter(ctx context.Context, bloom hexutil.Bytes) (bool, error) {
- return true, api.w.SetBloomFilter(bloom)
-}
-
-// MarkTrustedPeer marks a peer trusted, which will allow it to send historic (expired) messages.
-// Note: This function is not adding new nodes, the node needs to exists as a peer.
-func (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) {
- n, err := discover.ParseNode(enode)
- if err != nil {
- return false, err
- }
- return true, api.w.AllowP2PMessagesFromPeer(n.ID[:])
-}
-
-// NewKeyPair generates a new public and private key pair for message decryption and encryption.
-// It returns an ID that can be used to refer to the keypair.
-func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) {
- return api.w.NewKeyPair()
-}
-
-// AddPrivateKey imports the given private key.
-func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) {
- key, err := crypto.ToECDSA(privateKey)
- if err != nil {
- return "", err
- }
- return api.w.AddKeyPair(key)
-}
-
-// DeleteKeyPair removes the key with the given key if it exists.
-func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) {
- if ok := api.w.DeleteKeyPair(key); ok {
- return true, nil
- }
- return false, fmt.Errorf("key pair %s not found", key)
-}
-
-// HasKeyPair returns an indication if the node has a key pair that is associated with the given id.
-func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool {
- return api.w.HasKeyPair(id)
-}
-
-// GetPublicKey returns the public key associated with the given key. The key is the hex
-// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
-func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- key, err := api.w.GetPrivateKey(id)
- if err != nil {
- return hexutil.Bytes{}, err
- }
- return crypto.FromECDSAPub(&key.PublicKey), nil
-}
-
-// GetPrivateKey returns the private key associated with the given key. The key is the hex
-// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62.
-func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- key, err := api.w.GetPrivateKey(id)
- if err != nil {
- return hexutil.Bytes{}, err
- }
- return crypto.FromECDSA(key), nil
-}
-
-// NewSymKey generate a random symmetric key.
-// It returns an ID that can be used to refer to the key.
-// Can be used encrypting and decrypting messages where the key is known to both parties.
-func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) {
- return api.w.GenerateSymKey()
-}
-
-// AddSymKey import a symmetric key.
-// It returns an ID that can be used to refer to the key.
-// Can be used encrypting and decrypting messages where the key is known to both parties.
-func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) {
- return api.w.AddSymKeyDirect([]byte(key))
-}
-
-// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID.
-func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) {
- return api.w.AddSymKeyFromPassword(passwd)
-}
-
-// HasSymKey returns an indication if the node has a symmetric key associated with the given key.
-func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool {
- return api.w.HasSymKey(id)
-}
-
-// GetSymKey returns the symmetric key associated with the given id.
-func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) {
- return api.w.GetSymKey(id)
-}
-
-// DeleteSymKey deletes the symmetric key that is associated with the given id.
-func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool {
- return api.w.DeleteSymKey(id)
-}
-
-// MakeLightClient turns the node into light client, which does not forward
-// any incoming messages, and sends only messages originated in this node.
-func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool {
- api.w.lightClient = true
- return api.w.lightClient
-}
-
-// CancelLightClient cancels light client mode.
-func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool {
- api.w.lightClient = false
- return !api.w.lightClient
-}
-
-//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go
-
-// NewMessage represents a new whisper message that is posted through the RPC.
-type NewMessage struct {
- SymKeyID string `json:"symKeyID"`
- PublicKey []byte `json:"pubKey"`
- Sig string `json:"sig"`
- TTL uint32 `json:"ttl"`
- Topic TopicType `json:"topic"`
- Payload []byte `json:"payload"`
- Padding []byte `json:"padding"`
- PowTime uint32 `json:"powTime"`
- PowTarget float64 `json:"powTarget"`
- TargetPeer string `json:"targetPeer"`
-}
-
-type newMessageOverride struct {
- PublicKey hexutil.Bytes
- Payload hexutil.Bytes
- Padding hexutil.Bytes
-}
-
-// Post a message on the Whisper network.
-func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) {
- var (
- symKeyGiven = len(req.SymKeyID) > 0
- pubKeyGiven = len(req.PublicKey) > 0
- err error
- )
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) {
- return false, ErrSymAsym
- }
-
- params := &MessageParams{
- TTL: req.TTL,
- Payload: req.Payload,
- Padding: req.Padding,
- WorkTime: req.PowTime,
- PoW: req.PowTarget,
- Topic: req.Topic,
- }
-
- // Set key that is used to sign the message
- if len(req.Sig) > 0 {
- if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil {
- return false, err
- }
- }
-
- // Set symmetric key that is used to encrypt the message
- if symKeyGiven {
- if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption
- return false, ErrNoTopics
- }
- if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
- return false, err
- }
- if !validateDataIntegrity(params.KeySym, aesKeyLength) {
- return false, ErrInvalidSymmetricKey
- }
- }
-
- // Set asymmetric key that is used to encrypt the message
- if pubKeyGiven {
- if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil {
- return false, ErrInvalidPublicKey
- }
- }
-
- // encrypt and sent message
- whisperMsg, err := NewSentMessage(params)
- if err != nil {
- return false, err
- }
-
- env, err := whisperMsg.Wrap(params)
- if err != nil {
- return false, err
- }
-
- // send to specific node (skip PoW check)
- if len(req.TargetPeer) > 0 {
- n, err := discover.ParseNode(req.TargetPeer)
- if err != nil {
- return false, fmt.Errorf("failed to parse target peer: %s", err)
- }
- return true, api.w.SendP2PMessage(n.ID[:], env)
- }
-
- // ensure that the message PoW meets the node's minimum accepted PoW
- if req.PowTarget < api.w.MinPow() {
- return false, ErrTooLowPoW
- }
-
- return true, api.w.Send(env)
-}
-
-//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
-
-// Criteria holds various filter options for inbound messages.
-type Criteria struct {
- SymKeyID string `json:"symKeyID"`
- PrivateKeyID string `json:"privateKeyID"`
- Sig []byte `json:"sig"`
- MinPow float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P bool `json:"allowP2P"`
-}
-
-type criteriaOverride struct {
- Sig hexutil.Bytes
-}
-
-// Messages set up a subscription that fires events when messages arrive that match
-// the given set of criteria.
-func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) {
- var (
- symKeyGiven = len(crit.SymKeyID) > 0
- pubKeyGiven = len(crit.PrivateKeyID) > 0
- err error
- )
-
- // ensure that the RPC connection supports subscriptions
- notifier, supported := rpc.NotifierFromContext(ctx)
- if !supported {
- return nil, rpc.ErrNotificationsUnsupported
- }
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) {
- return nil, ErrSymAsym
- }
-
- filter := Filter{
- PoW: crit.MinPow,
- Messages: make(map[common.Hash]*ReceivedMessage),
- AllowP2P: crit.AllowP2P,
- }
-
- if len(crit.Sig) > 0 {
- if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil {
- return nil, ErrInvalidSigningPubKey
- }
- }
-
- for i, bt := range crit.Topics {
- if len(bt) == 0 || len(bt) > 4 {
- return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt))
- }
- filter.Topics = append(filter.Topics, bt[:])
- }
-
- // listen for message that are encrypted with the given symmetric key
- if symKeyGiven {
- if len(filter.Topics) == 0 {
- return nil, ErrNoTopics
- }
- key, err := api.w.GetSymKey(crit.SymKeyID)
- if err != nil {
- return nil, err
- }
- if !validateDataIntegrity(key, aesKeyLength) {
- return nil, ErrInvalidSymmetricKey
- }
- filter.KeySym = key
- filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
- }
-
- // listen for messages that are encrypted with the given public key
- if pubKeyGiven {
- filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID)
- if err != nil || filter.KeyAsym == nil {
- return nil, ErrInvalidPublicKey
- }
- }
-
- id, err := api.w.Subscribe(&filter)
- if err != nil {
- return nil, err
- }
-
- // create subscription and start waiting for message events
- rpcSub := notifier.CreateSubscription()
- go func() {
- // for now poll internally, refactor whisper internal for channel support
- ticker := time.NewTicker(250 * time.Millisecond)
- defer ticker.Stop()
-
- for {
- select {
- case <-ticker.C:
- if filter := api.w.GetFilter(id); filter != nil {
- for _, rpcMessage := range toMessage(filter.Retrieve()) {
- if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil {
- log.Error("Failed to send notification", "err", err)
- }
- }
- }
- case <-rpcSub.Err():
- api.w.Unsubscribe(id)
- return
- case <-notifier.Closed():
- api.w.Unsubscribe(id)
- return
- }
- }
- }()
-
- return rpcSub, nil
-}
-
-//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go
-
-// Message is the RPC representation of a whisper message.
-type Message struct {
- Sig []byte `json:"sig,omitempty"`
- TTL uint32 `json:"ttl"`
- Timestamp uint32 `json:"timestamp"`
- Topic TopicType `json:"topic"`
- Payload []byte `json:"payload"`
- Padding []byte `json:"padding"`
- PoW float64 `json:"pow"`
- Hash []byte `json:"hash"`
- Dst []byte `json:"recipientPublicKey,omitempty"`
-}
-
-type messageOverride struct {
- Sig hexutil.Bytes
- Payload hexutil.Bytes
- Padding hexutil.Bytes
- Hash hexutil.Bytes
- Dst hexutil.Bytes
-}
-
-// ToWhisperMessage converts an internal message into an API version.
-func ToWhisperMessage(message *ReceivedMessage) *Message {
- msg := Message{
- Payload: message.Payload,
- Padding: message.Padding,
- Timestamp: message.Sent,
- TTL: message.TTL,
- PoW: message.PoW,
- Hash: message.EnvelopeHash.Bytes(),
- Topic: message.Topic,
- }
-
- if message.Dst != nil {
- b := crypto.FromECDSAPub(message.Dst)
- if b != nil {
- msg.Dst = b
- }
- }
-
- if isMessageSigned(message.Raw[0]) {
- b := crypto.FromECDSAPub(message.SigToPubKey())
- if b != nil {
- msg.Sig = b
- }
- }
-
- return &msg
-}
-
-// toMessage converts a set of messages to its RPC representation.
-func toMessage(messages []*ReceivedMessage) []*Message {
- msgs := make([]*Message, len(messages))
- for i, msg := range messages {
- msgs[i] = ToWhisperMessage(msg)
- }
- return msgs
-}
-
-// GetFilterMessages returns the messages that match the filter criteria and
-// are received between the last poll and now.
-func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) {
- api.mu.Lock()
- f := api.w.GetFilter(id)
- if f == nil {
- api.mu.Unlock()
- return nil, errors.New("filter not found")
- }
- api.lastUsed[id] = time.Now()
- api.mu.Unlock()
-
- receivedMessages := f.Retrieve()
- messages := make([]*Message, 0, len(receivedMessages))
- for _, msg := range receivedMessages {
- messages = append(messages, ToWhisperMessage(msg))
- }
-
- return messages, nil
-}
-
-// DeleteMessageFilter deletes a filter.
-func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) {
- api.mu.Lock()
- defer api.mu.Unlock()
-
- delete(api.lastUsed, id)
- return true, api.w.Unsubscribe(id)
-}
-
-// NewMessageFilter creates a new filter that can be used to poll for
-// (new) messages that satisfy the given criteria.
-func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) {
- var (
- src *ecdsa.PublicKey
- keySym []byte
- keyAsym *ecdsa.PrivateKey
- topics [][]byte
-
- symKeyGiven = len(req.SymKeyID) > 0
- asymKeyGiven = len(req.PrivateKeyID) > 0
-
- err error
- )
-
- // user must specify either a symmetric or an asymmetric key
- if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) {
- return "", ErrSymAsym
- }
-
- if len(req.Sig) > 0 {
- if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil {
- return "", ErrInvalidSigningPubKey
- }
- }
-
- if symKeyGiven {
- if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil {
- return "", err
- }
- if !validateDataIntegrity(keySym, aesKeyLength) {
- return "", ErrInvalidSymmetricKey
- }
- }
-
- if asymKeyGiven {
- if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil {
- return "", err
- }
- }
-
- if len(req.Topics) > 0 {
- topics = make([][]byte, len(req.Topics))
- for i, topic := range req.Topics {
- topics[i] = make([]byte, TopicLength)
- copy(topics[i], topic[:])
- }
- }
-
- f := &Filter{
- Src: src,
- KeySym: keySym,
- KeyAsym: keyAsym,
- PoW: req.MinPow,
- AllowP2P: req.AllowP2P,
- Topics: topics,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- id, err := api.w.Subscribe(f)
- if err != nil {
- return "", err
- }
-
- api.mu.Lock()
- api.lastUsed[id] = time.Now()
- api.mu.Unlock()
-
- return id, nil
-}
diff --git a/whisper/whisperv6/api_test.go b/whisper/whisperv6/api_test.go
deleted file mode 100644
index 99e3667356..0000000000
--- a/whisper/whisperv6/api_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "bytes"
- "crypto/ecdsa"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- mapset "github.com/deckarep/golang-set"
-)
-
-func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
- w := &Whisper{
- privateKeys: make(map[string]*ecdsa.PrivateKey),
- symKeys: make(map[string][]byte),
- envelopes: make(map[common.Hash]*Envelope),
- expirations: make(map[uint32]mapset.Set),
- peers: make(map[*Peer]struct{}),
- messageQueue: make(chan *Envelope, messageQueueLimit),
- p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
- quit: make(chan struct{}),
- syncAllowance: DefaultSyncAllowance,
- }
- w.filters = NewFilters(w)
-
- keyID, err := w.GenerateSymKey()
- if err != nil {
- t.Fatalf("Error generating symmetric key: %v", err)
- }
- api := PublicWhisperAPI{
- w: w,
- lastUsed: make(map[string]time.Time),
- }
-
- t1 := [4]byte{0xde, 0xea, 0xbe, 0xef}
- t2 := [4]byte{0xca, 0xfe, 0xde, 0xca}
-
- crit := Criteria{
- SymKeyID: keyID,
- Topics: []TopicType{TopicType(t1), TopicType(t2)},
- }
-
- _, err = api.NewMessageFilter(crit)
- if err != nil {
- t.Fatalf("Error creating the filter: %v", err)
- }
-
- found := false
- candidates := w.filters.getWatchersByTopic(TopicType(t1))
- for _, f := range candidates {
- if len(f.Topics) == 2 {
- if bytes.Equal(f.Topics[0], t1[:]) && bytes.Equal(f.Topics[1], t2[:]) {
- found = true
- }
- }
- }
-
- if !found {
- t.Fatalf("Could not find filter with both topics")
- }
-}
diff --git a/whisper/whisperv6/benchmarks_test.go b/whisper/whisperv6/benchmarks_test.go
deleted file mode 100644
index 89cf40e8b3..0000000000
--- a/whisper/whisperv6/benchmarks_test.go
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "crypto/sha256"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "golang.org/x/crypto/pbkdf2"
-)
-
-func BenchmarkDeriveKeyMaterial(b *testing.B) {
- for i := 0; i < b.N; i++ {
- pbkdf2.Key([]byte("test"), nil, 65356, aesKeyLength, sha256.New)
- }
-}
-
-func BenchmarkEncryptionSym(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < b.N; i++ {
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Errorf("failed Wrap with seed %d: %s.", seed, err)
- b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload))
- return
- }
- }
-}
-
-func BenchmarkEncryptionAsym(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
-
- for i := 0; i < b.N; i++ {
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- }
-}
-
-func BenchmarkDecryptionSymValid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- f := Filter{KeySym: params.KeySym}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg == nil {
- b.Fatalf("failed to open with seed %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionSymInvalid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- f := Filter{KeySym: []byte("arbitrary stuff here")}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg != nil {
- b.Fatalf("opened envelope with invalid key, seed: %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionAsymValid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- f := Filter{KeyAsym: key}
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg == nil {
- b.Fatalf("fail to open, seed: %d.", seed)
- }
- }
-}
-
-func BenchmarkDecryptionAsymInvalid(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- key, err := crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, _ := NewSentMessage(params)
- env, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- key, err = crypto.GenerateKey()
- if err != nil {
- b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- f := Filter{KeyAsym: key}
-
- for i := 0; i < b.N; i++ {
- msg := env.Open(&f)
- if msg != nil {
- b.Fatalf("opened envelope with invalid key, seed: %d.", seed)
- }
- }
-}
-
-func increment(x []byte) {
- for i := 0; i < len(x); i++ {
- x[i]++
- if x[i] != 0 {
- break
- }
- }
-}
-
-func BenchmarkPoW(b *testing.B) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- params.Payload = make([]byte, 32)
- params.PoW = 10.0
- params.TTL = 1
-
- for i := 0; i < b.N; i++ {
- increment(params.Payload)
- msg, _ := NewSentMessage(params)
- _, err := msg.Wrap(params)
- if err != nil {
- b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- }
-}
diff --git a/whisper/whisperv6/config.go b/whisper/whisperv6/config.go
deleted file mode 100644
index 61419de007..0000000000
--- a/whisper/whisperv6/config.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-// Config represents the configuration state of a whisper node.
-type Config struct {
- MaxMessageSize uint32 `toml:",omitempty"`
- MinimumAcceptedPOW float64 `toml:",omitempty"`
-}
-
-// DefaultConfig represents (shocker!) the default configuration.
-var DefaultConfig = Config{
- MaxMessageSize: DefaultMaxMessageSize,
- MinimumAcceptedPOW: DefaultMinimumPoW,
-}
diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
deleted file mode 100644
index 066a9766d4..0000000000
--- a/whisper/whisperv6/doc.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-/*
-Package whisper implements the Whisper protocol (version 6).
-
-Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP).
-As such it may be likened and compared to both, not dissimilar to the
-matter/energy duality (apologies to physicists for the blatant abuse of a
-fundamental and beautiful natural principle).
-
-Whisper is a pure identity-based messaging system. Whisper provides a low-level
-(non-application-specific) but easily-accessible API without being based upon
-or prejudiced by the low-level hardware attributes and characteristics,
-particularly the notion of singular endpoints.
-*/
-
-// Contains the Whisper protocol constant definitions
-
-package whisperv6
-
-import (
- "fmt"
- "time"
-)
-
-// Whisper protocol parameters
-const (
- ProtocolVersion = uint64(6) // Protocol version number
- ProtocolVersionStr = "6.0" // The same, as a string
- ProtocolName = "shh" // Nickname of the protocol in geth
-
- // whisper protocol message codes, according to EIP-627
- statusCode = 0 // used by whisper protocol
- messagesCode = 1 // normal whisper message
- powRequirementCode = 2 // PoW requirement
- bloomFilterExCode = 3 // bloom filter exchange
- p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol
- p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further)
- NumberOfMessageCodes = 128
-
- SizeMask = byte(3) // mask used to extract the size of payload size field from the flags
- signatureFlag = byte(4)
-
- TopicLength = 4 // in bytes
- signatureLength = 65 // in bytes
- aesKeyLength = 32 // in bytes
- aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
- keyIDSize = 32 // in bytes
- BloomFilterSize = 64 // in bytes
- flagsLength = 1
-
- EnvelopeHeaderLength = 20
-
- MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
- DefaultMaxMessageSize = uint32(1024 * 1024)
- DefaultMinimumPoW = 0.2
-
- padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
- messageQueueLimit = 1024
-
- expirationCycle = time.Second
- transmissionCycle = 300 * time.Millisecond
-
- DefaultTTL = 50 // seconds
- DefaultSyncAllowance = 10 // seconds
-)
-
-type unknownVersionError uint64
-
-func (e unknownVersionError) Error() string {
- return fmt.Sprintf("invalid envelope version %d", uint64(e))
-}
-
-// MailServer represents a mail server, capable of
-// archiving the old messages for subsequent delivery
-// to the peers. Any implementation must ensure that both
-// functions are thread-safe. Also, they must return ASAP.
-// DeliverMail should use directMessagesCode for delivery,
-// in order to bypass the expiry checks.
-type MailServer interface {
- Archive(env *Envelope)
- DeliverMail(whisperPeer *Peer, request *Envelope)
-}
diff --git a/whisper/whisperv6/envelope.go b/whisper/whisperv6/envelope.go
deleted file mode 100644
index 5bcc59c99b..0000000000
--- a/whisper/whisperv6/envelope.go
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Envelope element.
-
-package whisperv6
-
-import (
- "crypto/ecdsa"
- "encoding/binary"
- "fmt"
- gmath "math"
- "math/big"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/math"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/ecies"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-// Envelope represents a clear-text data packet to transmit through the Whisper
-// network. Its contents may or may not be encrypted and signed.
-type Envelope struct {
- Expiry uint32
- TTL uint32
- Topic TopicType
- Data []byte
- Nonce uint64
-
- pow float64 // Message-specific PoW as described in the Whisper specification.
-
- // the following variables should not be accessed directly, use the corresponding function instead: Hash(), Bloom()
- hash common.Hash // Cached hash of the envelope to avoid rehashing every time.
- bloom []byte
-}
-
-// size returns the size of envelope as it is sent (i.e. public fields only)
-func (e *Envelope) size() int {
- return EnvelopeHeaderLength + len(e.Data)
-}
-
-// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
-func (e *Envelope) rlpWithoutNonce() []byte {
- res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Data})
- return res
-}
-
-// NewEnvelope wraps a Whisper message with expiration and destination data
-// included into an envelope for network forwarding.
-func NewEnvelope(ttl uint32, topic TopicType, msg *sentMessage) *Envelope {
- env := Envelope{
- Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
- TTL: ttl,
- Topic: topic,
- Data: msg.Raw,
- Nonce: 0,
- }
-
- return &env
-}
-
-// Seal closes the envelope by spending the requested amount of time as a proof
-// of work on hashing the data.
-func (e *Envelope) Seal(options *MessageParams) error {
- if options.PoW == 0 {
- // PoW is not required
- return nil
- }
-
- var target, bestBit int
- if options.PoW < 0 {
- // target is not set - the function should run for a period
- // of time specified in WorkTime param. Since we can predict
- // the execution time, we can also adjust Expiry.
- e.Expiry += options.WorkTime
- } else {
- target = e.powToFirstBit(options.PoW)
- }
-
- buf := make([]byte, 64)
- h := crypto.Keccak256(e.rlpWithoutNonce())
- copy(buf[:32], h)
-
- finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano()
- for nonce := uint64(0); time.Now().UnixNano() < finish; {
- for i := 0; i < 1024; i++ {
- binary.BigEndian.PutUint64(buf[56:], nonce)
- d := new(big.Int).SetBytes(crypto.Keccak256(buf))
- firstBit := math.FirstBitSet(d)
- if firstBit > bestBit {
- e.Nonce, bestBit = nonce, firstBit
- if target > 0 && bestBit >= target {
- return nil
- }
- }
- nonce++
- }
- }
-
- if target > 0 && bestBit < target {
- return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
- }
-
- return nil
-}
-
-// PoW computes (if necessary) and returns the proof of work target
-// of the envelope.
-func (e *Envelope) PoW() float64 {
- if e.pow == 0 {
- e.calculatePoW(0)
- }
- return e.pow
-}
-
-func (e *Envelope) calculatePoW(diff uint32) {
- buf := make([]byte, 64)
- h := crypto.Keccak256(e.rlpWithoutNonce())
- copy(buf[:32], h)
- binary.BigEndian.PutUint64(buf[56:], e.Nonce)
- d := new(big.Int).SetBytes(crypto.Keccak256(buf))
- firstBit := math.FirstBitSet(d)
- x := gmath.Pow(2, float64(firstBit))
- x /= float64(e.size())
- x /= float64(e.TTL + diff)
- e.pow = x
-}
-
-func (e *Envelope) powToFirstBit(pow float64) int {
- x := pow
- x *= float64(e.size())
- x *= float64(e.TTL)
- bits := gmath.Log2(x)
- bits = gmath.Ceil(bits)
- res := int(bits)
- if res < 1 {
- res = 1
- }
- return res
-}
-
-// Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
-func (e *Envelope) Hash() common.Hash {
- if (e.hash == common.Hash{}) {
- encoded, _ := rlp.EncodeToBytes(e)
- e.hash = crypto.Keccak256Hash(encoded)
- }
- return e.hash
-}
-
-// DecodeRLP decodes an Envelope from an RLP data stream.
-func (e *Envelope) DecodeRLP(s *rlp.Stream) error {
- raw, err := s.Raw()
- if err != nil {
- return err
- }
- // The decoding of Envelope uses the struct fields but also needs
- // to compute the hash of the whole RLP-encoded envelope. This
- // type has the same structure as Envelope but is not an
- // rlp.Decoder (does not implement DecodeRLP function).
- // Only public members will be encoded.
- type rlpenv Envelope
- if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil {
- return err
- }
- e.hash = crypto.Keccak256Hash(raw)
- return nil
-}
-
-// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
-func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) {
- message := &ReceivedMessage{Raw: e.Data}
- err := message.decryptAsymmetric(key)
- switch err {
- case nil:
- return message, nil
- case ecies.ErrInvalidPublicKey: // addressed to somebody else
- return nil, err
- default:
- return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err)
- }
-}
-
-// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
-func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
- msg = &ReceivedMessage{Raw: e.Data}
- err = msg.decryptSymmetric(key)
- if err != nil {
- msg = nil
- }
- return msg, err
-}
-
-// Open tries to decrypt an envelope, and populates the message fields in case of success.
-func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
- if watcher == nil {
- return nil
- }
-
- // The API interface forbids filters doing both symmetric and asymmetric encryption.
- if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() {
- return nil
- }
-
- if watcher.expectsAsymmetricEncryption() {
- msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
- if msg != nil {
- msg.Dst = &watcher.KeyAsym.PublicKey
- }
- } else if watcher.expectsSymmetricEncryption() {
- msg, _ = e.OpenSymmetric(watcher.KeySym)
- if msg != nil {
- msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
- }
- }
-
- if msg != nil {
- ok := msg.ValidateAndParse()
- if !ok {
- return nil
- }
- msg.Topic = e.Topic
- msg.PoW = e.PoW()
- msg.TTL = e.TTL
- msg.Sent = e.Expiry - e.TTL
- msg.EnvelopeHash = e.Hash()
- }
- return msg
-}
-
-// Bloom maps 4-bytes Topic into 64-byte bloom filter with 3 bits set (at most).
-func (e *Envelope) Bloom() []byte {
- if e.bloom == nil {
- e.bloom = TopicToBloom(e.Topic)
- }
- return e.bloom
-}
-
-// TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes)
-func TopicToBloom(topic TopicType) []byte {
- b := make([]byte, BloomFilterSize)
- var index [3]int
- for j := 0; j < 3; j++ {
- index[j] = int(topic[j])
- if (topic[3] & (1 << uint(j))) != 0 {
- index[j] += 256
- }
- }
-
- for j := 0; j < 3; j++ {
- byteIndex := index[j] / 8
- bitIndex := index[j] % 8
- b[byteIndex] = (1 << uint(bitIndex))
- }
- return b
-}
-
-// GetEnvelope retrieves an envelope from the message queue by its hash.
-// It returns nil if the envelope can not be found.
-func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope {
- w.poolMu.RLock()
- defer w.poolMu.RUnlock()
- return w.envelopes[hash]
-}
diff --git a/whisper/whisperv6/envelope_test.go b/whisper/whisperv6/envelope_test.go
deleted file mode 100644
index 4d0330a1f1..0000000000
--- a/whisper/whisperv6/envelope_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the tests associated with the Whisper protocol Envelope object.
-
-package whisperv6
-
-import (
- mrand "math/rand"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/crypto"
-)
-
-func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) {
- symKey := make([]byte, aesKeyLength)
- mrand.Read(symKey)
-
- asymKey, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
-
- params := MessageParams{
- PoW: 0.01,
- WorkTime: 1,
- TTL: uint32(mrand.Intn(1024)),
- Payload: make([]byte, 50),
- KeySym: symKey,
- Dst: nil,
- }
-
- mrand.Read(params.Payload)
-
- msg, err := NewSentMessage(¶ms)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
-
- e, err := msg.Wrap(¶ms)
- if err != nil {
- t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err)
- }
-
- f := Filter{KeySym: symKey, KeyAsym: asymKey}
-
- decrypted := e.Open(&f)
- if decrypted != nil {
- t.Fatalf("Managed to decrypt a message with an invalid filter, seed %d", seed)
- }
-}
diff --git a/whisper/whisperv6/filter.go b/whisper/whisperv6/filter.go
deleted file mode 100644
index 801954c7c3..0000000000
--- a/whisper/whisperv6/filter.go
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "crypto/ecdsa"
- "errors"
- "fmt"
- "sync"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
-)
-
-// Filter represents a Whisper message filter
-type Filter struct {
- Src *ecdsa.PublicKey // Sender of the message
- KeyAsym *ecdsa.PrivateKey // Private Key of recipient
- KeySym []byte // Key associated with the Topic
- Topics [][]byte // Topics to filter messages with
- PoW float64 // Proof of work as described in the Whisper spec
- AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
- SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
- id string // unique identifier
-
- Messages map[common.Hash]*ReceivedMessage
- mutex sync.RWMutex
-}
-
-// Filters represents a collection of filters
-type Filters struct {
- watchers map[string]*Filter
-
- topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic
- allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is
-
- whisper *Whisper
- mutex sync.RWMutex
-}
-
-// NewFilters returns a newly created filter collection
-func NewFilters(w *Whisper) *Filters {
- return &Filters{
- watchers: make(map[string]*Filter),
- topicMatcher: make(map[TopicType]map[*Filter]struct{}),
- allTopicsMatcher: make(map[*Filter]struct{}),
- whisper: w,
- }
-}
-
-// Install will add a new filter to the filter collection
-func (fs *Filters) Install(watcher *Filter) (string, error) {
- if watcher.KeySym != nil && watcher.KeyAsym != nil {
- return "", errors.New("filters must choose between symmetric and asymmetric keys")
- }
-
- if watcher.Messages == nil {
- watcher.Messages = make(map[common.Hash]*ReceivedMessage)
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", err
- }
-
- fs.mutex.Lock()
- defer fs.mutex.Unlock()
-
- if fs.watchers[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
-
- if watcher.expectsSymmetricEncryption() {
- watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
- }
-
- watcher.id = id
- fs.watchers[id] = watcher
- fs.addTopicMatcher(watcher)
- return id, err
-}
-
-// Uninstall will remove a filter whose id has been specified from
-// the filter collection
-func (fs *Filters) Uninstall(id string) bool {
- fs.mutex.Lock()
- defer fs.mutex.Unlock()
- if fs.watchers[id] != nil {
- fs.removeFromTopicMatchers(fs.watchers[id])
- delete(fs.watchers, id)
- return true
- }
- return false
-}
-
-// addTopicMatcher adds a filter to the topic matchers.
-// If the filter's Topics array is empty, it will be tried on every topic.
-// Otherwise, it will be tried on the topics specified.
-func (fs *Filters) addTopicMatcher(watcher *Filter) {
- if len(watcher.Topics) == 0 {
- fs.allTopicsMatcher[watcher] = struct{}{}
- } else {
- for _, t := range watcher.Topics {
- topic := BytesToTopic(t)
- if fs.topicMatcher[topic] == nil {
- fs.topicMatcher[topic] = make(map[*Filter]struct{})
- }
- fs.topicMatcher[topic][watcher] = struct{}{}
- }
- }
-}
-
-// removeFromTopicMatchers removes a filter from the topic matchers
-func (fs *Filters) removeFromTopicMatchers(watcher *Filter) {
- delete(fs.allTopicsMatcher, watcher)
- for _, topic := range watcher.Topics {
- delete(fs.topicMatcher[BytesToTopic(topic)], watcher)
- }
-}
-
-// getWatchersByTopic returns a slice containing the filters that
-// match a specific topic
-func (fs *Filters) getWatchersByTopic(topic TopicType) []*Filter {
- res := make([]*Filter, 0, len(fs.allTopicsMatcher))
- for watcher := range fs.allTopicsMatcher {
- res = append(res, watcher)
- }
- for watcher := range fs.topicMatcher[topic] {
- res = append(res, watcher)
- }
- return res
-}
-
-// Get returns a filter from the collection with a specific ID
-func (fs *Filters) Get(id string) *Filter {
- fs.mutex.RLock()
- defer fs.mutex.RUnlock()
- return fs.watchers[id]
-}
-
-// NotifyWatchers notifies any filter that has declared interest
-// for the envelope's topic.
-func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
- var msg *ReceivedMessage
-
- fs.mutex.RLock()
- defer fs.mutex.RUnlock()
-
- candidates := fs.getWatchersByTopic(env.Topic)
- for _, watcher := range candidates {
- if p2pMessage && !watcher.AllowP2P {
- log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), watcher.id))
- continue
- }
-
- var match bool
- if msg != nil {
- match = watcher.MatchMessage(msg)
- } else {
- match = watcher.MatchEnvelope(env)
- if match {
- msg = env.Open(watcher)
- if msg == nil {
- log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", watcher.id)
- }
- } else {
- log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", watcher.id)
- }
- }
-
- if match && msg != nil {
- log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
- if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) {
- watcher.Trigger(msg)
- }
- }
- }
-}
-
-func (f *Filter) expectsAsymmetricEncryption() bool {
- return f.KeyAsym != nil
-}
-
-func (f *Filter) expectsSymmetricEncryption() bool {
- return f.KeySym != nil
-}
-
-// Trigger adds a yet-unknown message to the filter's list of
-// received messages.
-func (f *Filter) Trigger(msg *ReceivedMessage) {
- f.mutex.Lock()
- defer f.mutex.Unlock()
-
- if _, exist := f.Messages[msg.EnvelopeHash]; !exist {
- f.Messages[msg.EnvelopeHash] = msg
- }
-}
-
-// Retrieve will return the list of all received messages associated
-// to a filter.
-func (f *Filter) Retrieve() (all []*ReceivedMessage) {
- f.mutex.Lock()
- defer f.mutex.Unlock()
-
- all = make([]*ReceivedMessage, 0, len(f.Messages))
- for _, msg := range f.Messages {
- all = append(all, msg)
- }
-
- f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
- return all
-}
-
-// MatchMessage checks if the filter matches an already decrypted
-// message (i.e. a Message that has already been handled by
-// MatchEnvelope when checked by a previous filter).
-// Topics are not checked here, since this is done by topic matchers.
-func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
- if f.PoW > 0 && msg.PoW < f.PoW {
- return false
- }
-
- if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() {
- return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst)
- } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() {
- return f.SymKeyHash == msg.SymKeyHash
- }
- return false
-}
-
-// MatchEnvelope checks if it's worth decrypting the message. If
-// it returns `true`, client code is expected to attempt decrypting
-// the message and subsequently call MatchMessage.
-// Topics are not checked here, since this is done by topic matchers.
-func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
- return f.PoW <= 0 || envelope.pow >= f.PoW
-}
-
-func matchSingleTopic(topic TopicType, bt []byte) bool {
- if len(bt) > TopicLength {
- bt = bt[:TopicLength]
- }
-
- if len(bt) < TopicLength {
- return false
- }
-
- for j, b := range bt {
- if topic[j] != b {
- return false
- }
- }
- return true
-}
-
-// IsPubKeyEqual checks that two public keys are equal
-func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
- if !ValidatePublicKey(a) {
- return false
- } else if !ValidatePublicKey(b) {
- return false
- }
- // the curve is always the same, just compare the points
- return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
-}
diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go
deleted file mode 100644
index 03449b88a3..0000000000
--- a/whisper/whisperv6/filter_test.go
+++ /dev/null
@@ -1,867 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "math/big"
- mrand "math/rand"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
-)
-
-var seed int64
-
-// InitSingleTest should be called in the beginning of every
-// test, which uses RNG, in order to make the tests
-// reproduciblity independent of their sequence.
-func InitSingleTest() {
- seed = time.Now().Unix()
- mrand.Seed(seed)
-}
-
-func InitDebugTest(i int64) {
- seed = i
- mrand.Seed(seed)
-}
-
-type FilterTestCase struct {
- f *Filter
- id string
- alive bool
- msgCnt int
-}
-
-func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
- var f Filter
- f.Messages = make(map[common.Hash]*ReceivedMessage)
-
- const topicNum = 8
- f.Topics = make([][]byte, topicNum)
- for i := 0; i < topicNum; i++ {
- f.Topics[i] = make([]byte, 4)
- mrand.Read(f.Topics[i][:])
- f.Topics[i][0] = 0x01
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("generateFilter 1 failed with seed %d.", seed)
- return nil, err
- }
- f.Src = &key.PublicKey
-
- if symmetric {
- f.KeySym = make([]byte, aesKeyLength)
- mrand.Read(f.KeySym)
- f.SymKeyHash = crypto.Keccak256Hash(f.KeySym)
- } else {
- f.KeyAsym, err = crypto.GenerateKey()
- if err != nil {
- t.Fatalf("generateFilter 2 failed with seed %d.", seed)
- return nil, err
- }
- }
-
- // AcceptP2P & PoW are not set
- return &f, nil
-}
-
-func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
- cases := make([]FilterTestCase, SizeTestFilters)
- for i := 0; i < SizeTestFilters; i++ {
- f, _ := generateFilter(t, true)
- cases[i].f = f
- cases[i].alive = mrand.Int()&int(1) == 0
- }
- return cases
-}
-
-func TestInstallFilters(t *testing.T) {
- InitSingleTest()
-
- const SizeTestFilters = 256
- w := New(&Config{})
- filters := NewFilters(w)
- tst := generateTestCases(t, SizeTestFilters)
-
- var err error
- var j string
- for i := 0; i < SizeTestFilters; i++ {
- j, err = filters.Install(tst[i].f)
- if err != nil {
- t.Fatalf("seed %d: failed to install filter: %s", seed, err)
- }
- tst[i].id = j
- if len(j) != keyIDSize*2 {
- t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
- }
- }
-
- for _, testCase := range tst {
- if !testCase.alive {
- filters.Uninstall(testCase.id)
- }
- }
-
- for i, testCase := range tst {
- fil := filters.Get(testCase.id)
- exist := fil != nil
- if exist != testCase.alive {
- t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
- }
- if exist && fil.PoW != testCase.f.PoW {
- t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive)
- }
- }
-}
-
-func TestInstallSymKeyGeneratesHash(t *testing.T) {
- InitSingleTest()
-
- w := New(&Config{})
- filters := NewFilters(w)
- filter, _ := generateFilter(t, true)
-
- // save the current SymKeyHash for comparison
- initialSymKeyHash := filter.SymKeyHash
-
- // ensure the SymKeyHash is invalid, for Install to recreate it
- var invalid common.Hash
- filter.SymKeyHash = invalid
-
- _, err := filters.Install(filter)
-
- if err != nil {
- t.Fatalf("Error installing the filter: %s", err)
- }
-
- for i, b := range filter.SymKeyHash {
- if b != initialSymKeyHash[i] {
- t.Fatalf("The filter's symmetric key hash was not properly generated by Install")
- }
- }
-}
-
-func TestInstallIdenticalFilters(t *testing.T) {
- InitSingleTest()
-
- w := New(&Config{})
- filters := NewFilters(w)
- filter1, _ := generateFilter(t, true)
-
- // Copy the first filter since some of its fields
- // are randomly gnerated.
- filter2 := &Filter{
- KeySym: filter1.KeySym,
- Topics: filter1.Topics,
- PoW: filter1.PoW,
- AllowP2P: filter1.AllowP2P,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- _, err := filters.Install(filter1)
-
- if err != nil {
- t.Fatalf("Error installing the first filter with seed %d: %s", seed, err)
- }
-
- _, err = filters.Install(filter2)
-
- if err != nil {
- t.Fatalf("Error installing the second filter with seed %d: %s", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("Error generating message parameters with seed %d: %s", seed, err)
- }
-
- params.KeySym = filter1.KeySym
- params.Topic = BytesToTopic(filter1.Topics[0])
-
- filter1.Src = ¶ms.Src.PublicKey
- filter2.Src = ¶ms.Src.PublicKey
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(filter1)
- if msg == nil {
- t.Fatalf("failed to Open with filter1")
- }
-
- if !filter1.MatchEnvelope(env) {
- t.Fatalf("failed matching with the first filter")
- }
-
- if !filter2.MatchEnvelope(env) {
- t.Fatalf("failed matching with the first filter")
- }
-
- if !filter1.MatchMessage(msg) {
- t.Fatalf("failed matching with the second filter")
- }
-
- if !filter2.MatchMessage(msg) {
- t.Fatalf("failed matching with the second filter")
- }
-}
-
-func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
- InitSingleTest()
-
- w := New(&Config{})
- filters := NewFilters(w)
- filter1, _ := generateFilter(t, true)
-
- asymKey, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("Unable to create asymetric keys: %v", err)
- }
-
- // Copy the first filter since some of its fields
- // are randomly gnerated.
- filter := &Filter{
- KeySym: filter1.KeySym,
- KeyAsym: asymKey,
- Topics: filter1.Topics,
- PoW: filter1.PoW,
- AllowP2P: filter1.AllowP2P,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- _, err = filters.Install(filter)
-
- if err == nil {
- t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed)
- }
-}
-
-func TestComparePubKey(t *testing.T) {
- InitSingleTest()
-
- key1, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate first key with seed %d: %s.", seed, err)
- }
- key2, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate second key with seed %d: %s.", seed, err)
- }
- if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) {
- t.Fatalf("public keys are equal, seed %d.", seed)
- }
-
- // generate key3 == key1
- mrand.Seed(seed)
- key3, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed to generate third key with seed %d: %s.", seed, err)
- }
- if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) {
- t.Fatalf("key1 == key3, seed %d.", seed)
- }
-}
-
-func TestMatchEnvelope(t *testing.T) {
- InitSingleTest()
-
- fsym, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- fasym, err := generateFilter(t, false)
- if err != nil {
- t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.Topic[0] = 0xFF // topic mismatch
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- // encrypt symmetrically
- i := mrand.Int() % 4
- fsym.Topics[i] = params.Topic[:]
- fasym.Topics[i] = params.Topic[:]
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
- }
-
- // symmetric + matching topic: match
- match := fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
- }
-
- // symmetric + matching topic + insufficient PoW: mismatch
- fsym.PoW = env.PoW() + 1.0
- match = fsym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed)
- }
-
- // symmetric + matching topic + sufficient PoW: match
- fsym.PoW = env.PoW() / 2
- match = fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed)
- }
-
- // symmetric + topics are nil (wildcard): match
- prevTopics := fsym.Topics
- fsym.Topics = nil
- match = fsym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed)
- }
- fsym.Topics = prevTopics
-
- // encrypt asymmetrically
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- params.Dst = &key.PublicKey
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
- }
-
- // encryption method mismatch
- match = fsym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-
- // asymmetric + mismatching topic: mismatch
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed)
- }
-
- // asymmetric + matching topic: match
- fasym.Topics[i] = fasym.Topics[i+1]
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed)
- }
-
- // asymmetric + filter without topic (wildcard): match
- fasym.Topics = nil
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed)
- }
-
- // asymmetric + insufficient PoW: mismatch
- fasym.PoW = env.PoW() + 1.0
- match = fasym.MatchEnvelope(env)
- if match {
- t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed)
- }
-
- // asymmetric + sufficient PoW: match
- fasym.PoW = env.PoW() / 2
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed)
- }
-
- // filter without topic + envelope without topic: match
- env.Topic = TopicType{}
- match = fasym.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
- }
-
- // filter with topic + envelope without topic: mismatch
- fasym.Topics = fsym.Topics
- match = fasym.MatchEnvelope(env)
- if !match {
- // topic mismatch should have no affect, as topics are handled by topic matchers
- t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
- }
-}
-
-func TestMatchMessageSym(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- const index = 1
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[index])
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(f)
- if msg == nil {
- t.Fatalf("failed Open with seed %d.", seed)
- }
-
- // Src: match
- *f.Src.X = *params.Src.PublicKey.X
- *f.Src.Y = *params.Src.PublicKey.Y
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed)
- }
-
- // insufficient PoW: mismatch
- f.PoW = msg.PoW + 1.0
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
- }
-
- // sufficient PoW: match
- f.PoW = msg.PoW / 2
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
- }
-
- // topic mismatch
- f.Topics[index][0]++
- if !f.MatchMessage(msg) {
- // topic mismatch should have no affect, as topics are handled by topic matchers
- t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
- }
- f.Topics[index][0]--
-
- // key mismatch
- f.SymKeyHash[0]++
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
- }
- f.SymKeyHash[0]--
-
- // Src absent: match
- f.Src = nil
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
- }
-
- // key hash mismatch
- h := f.SymKeyHash
- f.SymKeyHash = common.Hash{}
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed)
- }
- f.SymKeyHash = h
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed)
- }
-
- // encryption method mismatch
- f.KeySym = nil
- f.KeyAsym, err = crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-}
-
-func TestMatchMessageAsym(t *testing.T) {
- InitSingleTest()
-
- f, err := generateFilter(t, false)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- const index = 1
- params.Topic = BytesToTopic(f.Topics[index])
- params.Dst = &f.KeyAsym.PublicKey
- keySymOrig := params.KeySym
- params.KeySym = nil
-
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- msg := env.Open(f)
- if msg == nil {
- t.Fatalf("failed to open with seed %d.", seed)
- }
-
- // Src: match
- *f.Src.X = *params.Src.PublicKey.X
- *f.Src.Y = *params.Src.PublicKey.Y
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchMessage(src match) with seed %d.", seed)
- }
-
- // insufficient PoW: mismatch
- f.PoW = msg.PoW + 1.0
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
- }
-
- // sufficient PoW: match
- f.PoW = msg.PoW / 2
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
- }
-
- // topic mismatch
- f.Topics[index][0]++
- if !f.MatchMessage(msg) {
- // topic mismatch should have no affect, as topics are handled by topic matchers
- t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
- }
- f.Topics[index][0]--
-
- // key mismatch
- prev := *f.KeyAsym.PublicKey.X
- zero := *big.NewInt(0)
- *f.KeyAsym.PublicKey.X = zero
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
- }
- *f.KeyAsym.PublicKey.X = prev
-
- // Src absent: match
- f.Src = nil
- if !f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
- }
-
- // encryption method mismatch
- f.KeySym = keySymOrig
- f.KeyAsym = nil
- if f.MatchMessage(msg) {
- t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
- }
-}
-
-func cloneFilter(orig *Filter) *Filter {
- var clone Filter
- clone.Messages = make(map[common.Hash]*ReceivedMessage)
- clone.Src = orig.Src
- clone.KeyAsym = orig.KeyAsym
- clone.KeySym = orig.KeySym
- clone.Topics = orig.Topics
- clone.PoW = orig.PoW
- clone.AllowP2P = orig.AllowP2P
- clone.SymKeyHash = orig.SymKeyHash
- return &clone
-}
-
-func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- return nil
- }
-
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[2])
- sentMessage, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := sentMessage.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- return nil
- }
- return env
-}
-
-func TestWatchers(t *testing.T) {
- InitSingleTest()
-
- const NumFilters = 16
- const NumMessages = 256
- var i int
- var j uint32
- var e *Envelope
- var x, firstID string
- var err error
-
- w := New(&Config{})
- filters := NewFilters(w)
- tst := generateTestCases(t, NumFilters)
- for i = 0; i < NumFilters; i++ {
- tst[i].f.Src = nil
- x, err = filters.Install(tst[i].f)
- if err != nil {
- t.Fatalf("failed to install filter with seed %d: %s.", seed, err)
- }
- tst[i].id = x
- if len(firstID) == 0 {
- firstID = x
- }
- }
-
- lastID := x
-
- var envelopes [NumMessages]*Envelope
- for i = 0; i < NumMessages; i++ {
- j = mrand.Uint32() % NumFilters
- e = generateCompatibeEnvelope(t, tst[j].f)
- envelopes[i] = e
- tst[j].msgCnt++
- }
-
- for i = 0; i < NumMessages; i++ {
- filters.NotifyWatchers(envelopes[i], false)
- }
-
- var total int
- var mail []*ReceivedMessage
- var count [NumFilters]int
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- count[i] = len(mail)
- total += len(mail)
- }
-
- if total != NumMessages {
- t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages)
- }
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- if len(mail) != 0 {
- t.Fatalf("failed with seed %d: i = %d.", seed, i)
- }
-
- if tst[i].msgCnt != count[i] {
- t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
- }
- }
-
- // another round with a cloned filter
-
- clone := cloneFilter(tst[0].f)
- filters.Uninstall(lastID)
- total = 0
- last := NumFilters - 1
- tst[last].f = clone
- filters.Install(clone)
- for i = 0; i < NumFilters; i++ {
- tst[i].msgCnt = 0
- count[i] = 0
- }
-
- // make sure that the first watcher receives at least one message
- e = generateCompatibeEnvelope(t, tst[0].f)
- envelopes[0] = e
- tst[0].msgCnt++
- for i = 1; i < NumMessages; i++ {
- j = mrand.Uint32() % NumFilters
- e = generateCompatibeEnvelope(t, tst[j].f)
- envelopes[i] = e
- tst[j].msgCnt++
- }
-
- for i = 0; i < NumMessages; i++ {
- filters.NotifyWatchers(envelopes[i], false)
- }
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- count[i] = len(mail)
- total += len(mail)
- }
-
- combined := tst[0].msgCnt + tst[last].msgCnt
- if total != NumMessages+count[0] {
- t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0])
- }
-
- if combined != count[0] {
- t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0])
- }
-
- if combined != count[last] {
- t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last])
- }
-
- for i = 1; i < NumFilters-1; i++ {
- mail = tst[i].f.Retrieve()
- if len(mail) != 0 {
- t.Fatalf("failed with seed %d: i = %d.", seed, i)
- }
-
- if tst[i].msgCnt != count[i] {
- t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
- }
- }
-
- // test AcceptP2P
-
- total = 0
- filters.NotifyWatchers(envelopes[0], true)
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- total += len(mail)
- }
-
- if total != 0 {
- t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total)
- }
-
- f := filters.Get(firstID)
- if f == nil {
- t.Fatalf("failed to get the filter with seed %d.", seed)
- }
- f.AllowP2P = true
- total = 0
- filters.NotifyWatchers(envelopes[0], true)
-
- for i = 0; i < NumFilters; i++ {
- mail = tst[i].f.Retrieve()
- total += len(mail)
- }
-
- if total != 1 {
- t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
- }
-}
-
-func TestVariableTopics(t *testing.T) {
- InitSingleTest()
-
- const lastTopicByte = 3
- var match bool
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
- }
-
- for i := 0; i < 4; i++ {
- env.Topic = BytesToTopic(f.Topics[i])
- match = f.MatchEnvelope(env)
- if !match {
- t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
- }
-
- f.Topics[i][lastTopicByte]++
- match = f.MatchEnvelope(env)
- if !match {
- // topic mismatch should have no affect, as topics are handled by topic matchers
- t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i)
- }
- }
-}
-
-func TestMatchSingleTopic_ReturnTrue(t *testing.T) {
- bt := []byte("test")
- topic := BytesToTopic(bt)
-
- if !matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) {
- bt := []byte("test with tail")
- topic := BytesToTopic([]byte("test"))
-
- if !matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) {
- bt := []byte("tes")
- topic := BytesToTopic(bt)
-
- if matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
-
-func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) {
- bt := []byte("test")
- topic := BytesToTopic([]byte("not_equal"))
-
- if matchSingleTopic(topic, bt) {
- t.FailNow()
- }
-}
diff --git a/whisper/whisperv6/gen_criteria_json.go b/whisper/whisperv6/gen_criteria_json.go
deleted file mode 100644
index ba942516db..0000000000
--- a/whisper/whisperv6/gen_criteria_json.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv6
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*criteriaOverride)(nil)
-
-// MarshalJSON marshals type Criteria to a json string
-func (c Criteria) MarshalJSON() ([]byte, error) {
- type Criteria struct {
- SymKeyID string `json:"symKeyID"`
- PrivateKeyID string `json:"privateKeyID"`
- Sig hexutil.Bytes `json:"sig"`
- MinPow float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P bool `json:"allowP2P"`
- }
- var enc Criteria
- enc.SymKeyID = c.SymKeyID
- enc.PrivateKeyID = c.PrivateKeyID
- enc.Sig = c.Sig
- enc.MinPow = c.MinPow
- enc.Topics = c.Topics
- enc.AllowP2P = c.AllowP2P
- return json.Marshal(&enc)
-}
-
-// UnmarshalJSON unmarshals type Criteria to a json string
-func (c *Criteria) UnmarshalJSON(input []byte) error {
- type Criteria struct {
- SymKeyID *string `json:"symKeyID"`
- PrivateKeyID *string `json:"privateKeyID"`
- Sig *hexutil.Bytes `json:"sig"`
- MinPow *float64 `json:"minPow"`
- Topics []TopicType `json:"topics"`
- AllowP2P *bool `json:"allowP2P"`
- }
- var dec Criteria
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.SymKeyID != nil {
- c.SymKeyID = *dec.SymKeyID
- }
- if dec.PrivateKeyID != nil {
- c.PrivateKeyID = *dec.PrivateKeyID
- }
- if dec.Sig != nil {
- c.Sig = *dec.Sig
- }
- if dec.MinPow != nil {
- c.MinPow = *dec.MinPow
- }
- if dec.Topics != nil {
- c.Topics = dec.Topics
- }
- if dec.AllowP2P != nil {
- c.AllowP2P = *dec.AllowP2P
- }
- return nil
-}
diff --git a/whisper/whisperv6/gen_message_json.go b/whisper/whisperv6/gen_message_json.go
deleted file mode 100644
index 754941919f..0000000000
--- a/whisper/whisperv6/gen_message_json.go
+++ /dev/null
@@ -1,84 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv6
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*messageOverride)(nil)
-
-// MarshalJSON marshals type Message to a json string
-func (m Message) MarshalJSON() ([]byte, error) {
- type Message struct {
- Sig hexutil.Bytes `json:"sig,omitempty"`
- TTL uint32 `json:"ttl"`
- Timestamp uint32 `json:"timestamp"`
- Topic TopicType `json:"topic"`
- Payload hexutil.Bytes `json:"payload"`
- Padding hexutil.Bytes `json:"padding"`
- PoW float64 `json:"pow"`
- Hash hexutil.Bytes `json:"hash"`
- Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"`
- }
- var enc Message
- enc.Sig = m.Sig
- enc.TTL = m.TTL
- enc.Timestamp = m.Timestamp
- enc.Topic = m.Topic
- enc.Payload = m.Payload
- enc.Padding = m.Padding
- enc.PoW = m.PoW
- enc.Hash = m.Hash
- enc.Dst = m.Dst
- return json.Marshal(&enc)
-}
-
-// UnmarshalJSON unmarshals type Message to a json string
-func (m *Message) UnmarshalJSON(input []byte) error {
- type Message struct {
- Sig *hexutil.Bytes `json:"sig,omitempty"`
- TTL *uint32 `json:"ttl"`
- Timestamp *uint32 `json:"timestamp"`
- Topic *TopicType `json:"topic"`
- Payload *hexutil.Bytes `json:"payload"`
- Padding *hexutil.Bytes `json:"padding"`
- PoW *float64 `json:"pow"`
- Hash *hexutil.Bytes `json:"hash"`
- Dst *hexutil.Bytes `json:"recipientPublicKey,omitempty"`
- }
- var dec Message
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.Sig != nil {
- m.Sig = *dec.Sig
- }
- if dec.TTL != nil {
- m.TTL = *dec.TTL
- }
- if dec.Timestamp != nil {
- m.Timestamp = *dec.Timestamp
- }
- if dec.Topic != nil {
- m.Topic = *dec.Topic
- }
- if dec.Payload != nil {
- m.Payload = *dec.Payload
- }
- if dec.Padding != nil {
- m.Padding = *dec.Padding
- }
- if dec.PoW != nil {
- m.PoW = *dec.PoW
- }
- if dec.Hash != nil {
- m.Hash = *dec.Hash
- }
- if dec.Dst != nil {
- m.Dst = *dec.Dst
- }
- return nil
-}
diff --git a/whisper/whisperv6/gen_newmessage_json.go b/whisper/whisperv6/gen_newmessage_json.go
deleted file mode 100644
index 04dc4af7c0..0000000000
--- a/whisper/whisperv6/gen_newmessage_json.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-
-package whisperv6
-
-import (
- "encoding/json"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-var _ = (*newMessageOverride)(nil)
-
-// MarshalJSON marshals type NewMessage to a json string
-func (n NewMessage) MarshalJSON() ([]byte, error) {
- type NewMessage struct {
- SymKeyID string `json:"symKeyID"`
- PublicKey hexutil.Bytes `json:"pubKey"`
- Sig string `json:"sig"`
- TTL uint32 `json:"ttl"`
- Topic TopicType `json:"topic"`
- Payload hexutil.Bytes `json:"payload"`
- Padding hexutil.Bytes `json:"padding"`
- PowTime uint32 `json:"powTime"`
- PowTarget float64 `json:"powTarget"`
- TargetPeer string `json:"targetPeer"`
- }
- var enc NewMessage
- enc.SymKeyID = n.SymKeyID
- enc.PublicKey = n.PublicKey
- enc.Sig = n.Sig
- enc.TTL = n.TTL
- enc.Topic = n.Topic
- enc.Payload = n.Payload
- enc.Padding = n.Padding
- enc.PowTime = n.PowTime
- enc.PowTarget = n.PowTarget
- enc.TargetPeer = n.TargetPeer
- return json.Marshal(&enc)
-}
-
-// UnmarshalJSON unmarshals type NewMessage to a json string
-func (n *NewMessage) UnmarshalJSON(input []byte) error {
- type NewMessage struct {
- SymKeyID *string `json:"symKeyID"`
- PublicKey *hexutil.Bytes `json:"pubKey"`
- Sig *string `json:"sig"`
- TTL *uint32 `json:"ttl"`
- Topic *TopicType `json:"topic"`
- Payload *hexutil.Bytes `json:"payload"`
- Padding *hexutil.Bytes `json:"padding"`
- PowTime *uint32 `json:"powTime"`
- PowTarget *float64 `json:"powTarget"`
- TargetPeer *string `json:"targetPeer"`
- }
- var dec NewMessage
- if err := json.Unmarshal(input, &dec); err != nil {
- return err
- }
- if dec.SymKeyID != nil {
- n.SymKeyID = *dec.SymKeyID
- }
- if dec.PublicKey != nil {
- n.PublicKey = *dec.PublicKey
- }
- if dec.Sig != nil {
- n.Sig = *dec.Sig
- }
- if dec.TTL != nil {
- n.TTL = *dec.TTL
- }
- if dec.Topic != nil {
- n.Topic = *dec.Topic
- }
- if dec.Payload != nil {
- n.Payload = *dec.Payload
- }
- if dec.Padding != nil {
- n.Padding = *dec.Padding
- }
- if dec.PowTime != nil {
- n.PowTime = *dec.PowTime
- }
- if dec.PowTarget != nil {
- n.PowTarget = *dec.PowTarget
- }
- if dec.TargetPeer != nil {
- n.TargetPeer = *dec.TargetPeer
- }
- return nil
-}
diff --git a/whisper/whisperv6/message.go b/whisper/whisperv6/message.go
deleted file mode 100644
index e1d2a46c85..0000000000
--- a/whisper/whisperv6/message.go
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Message element.
-
-package whisperv6
-
-import (
- "crypto/aes"
- "crypto/cipher"
- "crypto/ecdsa"
- crand "crypto/rand"
- "encoding/binary"
- "errors"
- mrand "math/rand"
- "strconv"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/crypto/ecies"
- "github.com/XinFinOrg/XDPoSChain/log"
-)
-
-// MessageParams specifies the exact way a message should be wrapped
-// into an Envelope.
-type MessageParams struct {
- TTL uint32
- Src *ecdsa.PrivateKey
- Dst *ecdsa.PublicKey
- KeySym []byte
- Topic TopicType
- WorkTime uint32
- PoW float64
- Payload []byte
- Padding []byte
-}
-
-// SentMessage represents an end-user data packet to transmit through the
-// Whisper protocol. These are wrapped into Envelopes that need not be
-// understood by intermediate nodes, just forwarded.
-type sentMessage struct {
- Raw []byte
-}
-
-// ReceivedMessage represents a data packet to be received through the
-// Whisper protocol and successfully decrypted.
-type ReceivedMessage struct {
- Raw []byte
-
- Payload []byte
- Padding []byte
- Signature []byte
- Salt []byte
-
- PoW float64 // Proof of work as described in the Whisper spec
- Sent uint32 // Time when the message was posted into the network
- TTL uint32 // Maximum time to live allowed for the message
- Src *ecdsa.PublicKey // Message recipient (identity used to decode the message)
- Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
- Topic TopicType
-
- SymKeyHash common.Hash // The Keccak256Hash of the key
- EnvelopeHash common.Hash // Message envelope hash to act as a unique id
-}
-
-func isMessageSigned(flags byte) bool {
- return (flags & signatureFlag) != 0
-}
-
-func (msg *ReceivedMessage) isSymmetricEncryption() bool {
- return msg.SymKeyHash != common.Hash{}
-}
-
-func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
- return msg.Dst != nil
-}
-
-// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message.
-func NewSentMessage(params *MessageParams) (*sentMessage, error) {
- const payloadSizeFieldMaxSize = 4
- msg := sentMessage{}
- msg.Raw = make([]byte, 1,
- flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
- msg.Raw[0] = 0 // set all the flags to zero
- msg.addPayloadSizeField(params.Payload)
- msg.Raw = append(msg.Raw, params.Payload...)
- err := msg.appendPadding(params)
- return &msg, err
-}
-
-// addPayloadSizeField appends the auxiliary field containing the size of payload
-func (msg *sentMessage) addPayloadSizeField(payload []byte) {
- fieldSize := getSizeOfPayloadSizeField(payload)
- field := make([]byte, 4)
- binary.LittleEndian.PutUint32(field, uint32(len(payload)))
- field = field[:fieldSize]
- msg.Raw = append(msg.Raw, field...)
- msg.Raw[0] |= byte(fieldSize)
-}
-
-// getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload
-func getSizeOfPayloadSizeField(payload []byte) int {
- s := 1
- for i := len(payload); i >= 256; i /= 256 {
- s++
- }
- return s
-}
-
-// appendPadding appends the padding specified in params.
-// If no padding is provided in params, then random padding is generated.
-func (msg *sentMessage) appendPadding(params *MessageParams) error {
- if len(params.Padding) != 0 {
- // padding data was provided by the Dapp, just use it as is
- msg.Raw = append(msg.Raw, params.Padding...)
- return nil
- }
-
- rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload)
- if params.Src != nil {
- rawSize += signatureLength
- }
- odd := rawSize % padSizeLimit
- paddingSize := padSizeLimit - odd
- pad := make([]byte, paddingSize)
- _, err := crand.Read(pad)
- if err != nil {
- return err
- }
- if !validateDataIntegrity(pad, paddingSize) {
- return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize))
- }
- msg.Raw = append(msg.Raw, pad...)
- return nil
-}
-
-// sign calculates and sets the cryptographic signature for the message,
-// also setting the sign flag.
-func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error {
- if isMessageSigned(msg.Raw[0]) {
- // this should not happen, but no reason to panic
- log.Error("failed to sign the message: already signed")
- return nil
- }
-
- msg.Raw[0] |= signatureFlag // it is important to set this flag before signing
- hash := crypto.Keccak256(msg.Raw)
- signature, err := crypto.Sign(hash, key)
- if err != nil {
- msg.Raw[0] &= (0xFF ^ signatureFlag) // clear the flag
- return err
- }
- msg.Raw = append(msg.Raw, signature...)
- return nil
-}
-
-// encryptAsymmetric encrypts a message with a public key.
-func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
- if !ValidatePublicKey(key) {
- return errors.New("invalid public key provided for asymmetric encryption")
- }
- encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil)
- if err == nil {
- msg.Raw = encrypted
- }
- return err
-}
-
-// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
-// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
-func (msg *sentMessage) encryptSymmetric(key []byte) (err error) {
- if !validateDataIntegrity(key, aesKeyLength) {
- return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key)))
- }
- block, err := aes.NewCipher(key)
- if err != nil {
- return err
- }
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return err
- }
- salt, err := generateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key
- if err != nil {
- return err
- }
- encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil)
- msg.Raw = append(encrypted, salt...)
- return nil
-}
-
-// generateSecureRandomData generates random data where extra security is required.
-// The purpose of this function is to prevent some bugs in software or in hardware
-// from delivering not-very-random data. This is especially useful for AES nonce,
-// where true randomness does not really matter, but it is very important to have
-// a unique nonce for every message.
-func generateSecureRandomData(length int) ([]byte, error) {
- x := make([]byte, length)
- y := make([]byte, length)
- res := make([]byte, length)
-
- _, err := crand.Read(x)
- if err != nil {
- return nil, err
- } else if !validateDataIntegrity(x, length) {
- return nil, errors.New("crypto/rand failed to generate secure random data")
- }
- _, err = mrand.Read(y)
- if err != nil {
- return nil, err
- } else if !validateDataIntegrity(y, length) {
- return nil, errors.New("math/rand failed to generate secure random data")
- }
- for i := 0; i < length; i++ {
- res[i] = x[i] ^ y[i]
- }
- if !validateDataIntegrity(res, length) {
- return nil, errors.New("failed to generate secure random data")
- }
- return res, nil
-}
-
-// Wrap bundles the message into an Envelope to transmit over the network.
-func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
- if options.TTL == 0 {
- options.TTL = DefaultTTL
- }
- if options.Src != nil {
- if err = msg.sign(options.Src); err != nil {
- return nil, err
- }
- }
- if options.Dst != nil {
- err = msg.encryptAsymmetric(options.Dst)
- } else if options.KeySym != nil {
- err = msg.encryptSymmetric(options.KeySym)
- } else {
- err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
- }
- if err != nil {
- return nil, err
- }
-
- envelope = NewEnvelope(options.TTL, options.Topic, msg)
- if err = envelope.Seal(options); err != nil {
- return nil, err
- }
- return envelope, nil
-}
-
-// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
-// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
-func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
- // symmetric messages are expected to contain the 12-byte nonce at the end of the payload
- if len(msg.Raw) < aesNonceLength {
- return errors.New("missing salt or invalid payload in symmetric message")
- }
- salt := msg.Raw[len(msg.Raw)-aesNonceLength:]
-
- block, err := aes.NewCipher(key)
- if err != nil {
- return err
- }
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- return err
- }
- decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil)
- if err != nil {
- return err
- }
- msg.Raw = decrypted
- msg.Salt = salt
- return nil
-}
-
-// decryptAsymmetric decrypts an encrypted payload with a private key.
-func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error {
- decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil)
- if err == nil {
- msg.Raw = decrypted
- }
- return err
-}
-
-// ValidateAndParse checks the message validity and extracts the fields in case of success.
-func (msg *ReceivedMessage) ValidateAndParse() bool {
- end := len(msg.Raw)
- if end < 1 {
- return false
- }
-
- if isMessageSigned(msg.Raw[0]) {
- end -= signatureLength
- if end <= 1 {
- return false
- }
- msg.Signature = msg.Raw[end : end+signatureLength]
- msg.Src = msg.SigToPubKey()
- if msg.Src == nil {
- return false
- }
- }
-
- beg := 1
- payloadSize := 0
- sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload
- if sizeOfPayloadSizeField != 0 {
- payloadSize = int(bytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField]))
- if payloadSize+1 > end {
- return false
- }
- beg += sizeOfPayloadSizeField
- msg.Payload = msg.Raw[beg : beg+payloadSize]
- }
-
- beg += payloadSize
- msg.Padding = msg.Raw[beg:end]
- return true
-}
-
-// SigToPubKey returns the public key associated to the message's
-// signature.
-func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
- defer func() { recover() }() // in case of invalid signature
-
- pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
- if err != nil {
- log.Error("failed to recover public key from signature", "err", err)
- return nil
- }
- return pub
-}
-
-// hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding.
-func (msg *ReceivedMessage) hash() []byte {
- if isMessageSigned(msg.Raw[0]) {
- sz := len(msg.Raw) - signatureLength
- return crypto.Keccak256(msg.Raw[:sz])
- }
- return crypto.Keccak256(msg.Raw)
-}
diff --git a/whisper/whisperv6/message_test.go b/whisper/whisperv6/message_test.go
deleted file mode 100644
index 8485be63ed..0000000000
--- a/whisper/whisperv6/message_test.go
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- mrand "math/rand"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-func generateMessageParams() (*MessageParams, error) {
- // set all the parameters except p.Dst and p.Padding
-
- buf := make([]byte, 4)
- mrand.Read(buf)
- sz := mrand.Intn(400)
-
- var p MessageParams
- p.PoW = 0.01
- p.WorkTime = 1
- p.TTL = uint32(mrand.Intn(1024))
- p.Payload = make([]byte, sz)
- p.KeySym = make([]byte, aesKeyLength)
- mrand.Read(p.Payload)
- mrand.Read(p.KeySym)
- p.Topic = BytesToTopic(buf)
-
- var err error
- p.Src, err = crypto.GenerateKey()
- if err != nil {
- return nil, err
- }
-
- return &p, nil
-}
-
-func singleMessageTest(t *testing.T, symmetric bool) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
-
- if !symmetric {
- params.KeySym = nil
- params.Dst = &key.PublicKey
- }
-
- text := make([]byte, 0, 512)
- text = append(text, params.Payload...)
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- var decrypted *ReceivedMessage
- if symmetric {
- decrypted, err = env.OpenSymmetric(params.KeySym)
- } else {
- decrypted, err = env.OpenAsymmetric(key)
- }
-
- if err != nil {
- t.Fatalf("failed to encrypt with seed %d: %s.", seed, err)
- }
-
- if !decrypted.ValidateAndParse() {
- t.Fatalf("failed to validate with seed %d, symmetric = %v.", seed, symmetric)
- }
-
- if !bytes.Equal(text, decrypted.Payload) {
- t.Fatalf("failed with seed %d: compare payload.", seed)
- }
- if !isMessageSigned(decrypted.Raw[0]) {
- t.Fatalf("failed with seed %d: unsigned.", seed)
- }
- if len(decrypted.Signature) != signatureLength {
- t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
- }
- if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
- t.Fatalf("failed with seed %d: signature mismatch.", seed)
- }
-}
-
-func TestMessageEncryption(t *testing.T) {
- InitSingleTest()
-
- var symmetric bool
- for i := 0; i < 256; i++ {
- singleMessageTest(t, symmetric)
- symmetric = !symmetric
- }
-}
-
-func TestMessageWrap(t *testing.T) {
- seed = int64(1777444222)
- mrand.Seed(seed)
- target := 128.0
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1
- params.WorkTime = 12
- params.PoW = target
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- pow := env.PoW()
- if pow < target {
- t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target)
- }
-
- // set PoW target too high, expect error
- msg2, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1000000
- params.WorkTime = 1
- params.PoW = 10000000.0
- _, err = msg2.Wrap(params)
- if err == nil {
- t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed)
- }
-}
-
-func TestMessageSeal(t *testing.T) {
- // this test depends on deterministic choice of seed (1976726903)
- seed = int64(1976726903)
- mrand.Seed(seed)
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.TTL = 1
-
- env := NewEnvelope(params.TTL, params.Topic, msg)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- env.Expiry = uint32(seed) // make it deterministic
- target := 32.0
- params.WorkTime = 4
- params.PoW = target
- env.Seal(params)
-
- env.calculatePoW(0)
- pow := env.PoW()
- if pow < target {
- t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target)
- }
-
- params.WorkTime = 1
- params.PoW = 1000000000.0
- env.Seal(params)
- env.calculatePoW(0)
- pow = env.PoW()
- if pow < 2*target {
- t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow)
- }
-}
-
-func TestEnvelopeOpen(t *testing.T) {
- InitSingleTest()
-
- var symmetric bool
- for i := 0; i < 32; i++ {
- singleEnvelopeOpenTest(t, symmetric)
- symmetric = !symmetric
- }
-}
-
-func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- key, err := crypto.GenerateKey()
- if err != nil {
- t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
- }
-
- if !symmetric {
- params.KeySym = nil
- params.Dst = &key.PublicKey
- }
-
- text := make([]byte, 0, 512)
- text = append(text, params.Payload...)
-
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- var f Filter
- if symmetric {
- f = Filter{KeySym: params.KeySym}
- } else {
- f = Filter{KeyAsym: key}
- }
- decrypted := env.Open(&f)
- if decrypted == nil {
- t.Fatalf("failed to open with seed %d.", seed)
- }
-
- if !bytes.Equal(text, decrypted.Payload) {
- t.Fatalf("failed with seed %d: compare payload.", seed)
- }
- if !isMessageSigned(decrypted.Raw[0]) {
- t.Fatalf("failed with seed %d: unsigned.", seed)
- }
- if len(decrypted.Signature) != signatureLength {
- t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature))
- }
- if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) {
- t.Fatalf("failed with seed %d: signature mismatch.", seed)
- }
- if decrypted.isAsymmetricEncryption() == symmetric {
- t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric)
- }
- if decrypted.isSymmetricEncryption() != symmetric {
- t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric)
- }
- if !symmetric {
- if decrypted.Dst == nil {
- t.Fatalf("failed with seed %d: dst is nil.", seed)
- }
- if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) {
- t.Fatalf("failed with seed %d: Dst.", seed)
- }
- }
-}
-
-func TestEncryptWithZeroKey(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = make([]byte, aesKeyLength)
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with zero key, seed: %d.", seed)
- }
-
- params, err = generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = make([]byte, 0)
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with empty key, seed: %d.", seed)
- }
-
- params, err = generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- params.KeySym = nil
- _, err = msg.Wrap(params)
- if err == nil {
- t.Fatalf("wrapped with nil key, seed: %d.", seed)
- }
-}
-
-func TestRlpEncode(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("wrapped with zero key, seed: %d.", seed)
- }
-
- raw, err := rlp.EncodeToBytes(env)
- if err != nil {
- t.Fatalf("RLP encode failed: %s.", err)
- }
-
- var decoded Envelope
- rlp.DecodeBytes(raw, &decoded)
- if err != nil {
- t.Fatalf("RLP decode failed: %s.", err)
- }
-
- he := env.Hash()
- hd := decoded.Hash()
-
- if he != hd {
- t.Fatalf("Hashes are not equal: %x vs. %x", he, hd)
- }
-}
-
-func singlePaddingTest(t *testing.T, padSize int) {
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err)
- }
- params.Padding = make([]byte, padSize)
- params.PoW = 0.0000000001
- pad := make([]byte, padSize)
- _, err = mrand.Read(pad)
- if err != nil {
- t.Fatalf("padding is not generated (seed %d): %s", seed, err)
- }
- n := copy(params.Padding, pad)
- if n != padSize {
- t.Fatalf("padding is not copied (seed %d): %s", seed, err)
- }
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize)
- }
- f := Filter{KeySym: params.KeySym}
- decrypted := env.Open(&f)
- if decrypted == nil {
- t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize)
- }
- if !bytes.Equal(pad, decrypted.Padding) {
- t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding)
- }
-}
-
-func TestPadding(t *testing.T) {
- InitSingleTest()
-
- for i := 1; i < 260; i++ {
- singlePaddingTest(t, i)
- }
-
- lim := 256 * 256
- for i := lim - 5; i < lim+2; i++ {
- singlePaddingTest(t, i)
- }
-
- for i := 0; i < 256; i++ {
- n := mrand.Intn(256*254) + 256
- singlePaddingTest(t, n)
- }
-
- for i := 0; i < 256; i++ {
- n := mrand.Intn(256*1024) + 256*256
- singlePaddingTest(t, n)
- }
-}
-
-func TestPaddingAppendedToSymMessagesWithSignature(t *testing.T) {
- params := &MessageParams{
- Payload: make([]byte, 246),
- KeySym: make([]byte, aesKeyLength),
- }
-
- pSrc, err := crypto.GenerateKey()
-
- if err != nil {
- t.Fatalf("Error creating the signature key %v", err)
- return
- }
- params.Src = pSrc
-
- // Simulate a message with a payload just under 256 so that
- // payload + flag + signature > 256. Check that the result
- // is padded on the next 256 boundary.
- msg := sentMessage{}
- const payloadSizeFieldMinSize = 1
- msg.Raw = make([]byte, flagsLength+payloadSizeFieldMinSize+len(params.Payload))
-
- err = msg.appendPadding(params)
-
- if err != nil {
- t.Fatalf("Error appending padding to message %v", err)
- return
- }
-
- if len(msg.Raw) != 512-signatureLength {
- t.Errorf("Invalid size %d != 512", len(msg.Raw))
- }
-}
-
-func TestAesNonce(t *testing.T) {
- key := hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31")
- block, err := aes.NewCipher(key)
- if err != nil {
- t.Fatalf("NewCipher failed: %s", err)
- }
- aesgcm, err := cipher.NewGCM(block)
- if err != nil {
- t.Fatalf("NewGCM failed: %s", err)
- }
- // This is the most important single test in this package.
- // If it fails, whisper will not be working.
- if aesgcm.NonceSize() != aesNonceLength {
- t.Fatalf("Nonce size is wrong. This is a critical error. Apparently AES nonce size have changed in the new version of AES GCM package. Whisper will not be working until this problem is resolved.")
- }
-}
diff --git a/whisper/whisperv6/peer.go b/whisper/whisperv6/peer.go
deleted file mode 100644
index f5bbf00d5e..0000000000
--- a/whisper/whisperv6/peer.go
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "fmt"
- "math"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- mapset "github.com/deckarep/golang-set"
-)
-
-// Peer represents a whisper protocol peer connection.
-type Peer struct {
- host *Whisper
- peer *p2p.Peer
- ws p2p.MsgReadWriter
-
- trusted bool
- powRequirement float64
- bloomMu sync.Mutex
- bloomFilter []byte
- fullNode bool
-
- known mapset.Set // Messages already known by the peer to avoid wasting bandwidth
-
- quit chan struct{}
-}
-
-// newPeer creates a new whisper peer object, but does not run the handshake itself.
-func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
- return &Peer{
- host: host,
- peer: remote,
- ws: rw,
- trusted: false,
- powRequirement: 0.0,
- known: mapset.NewSet(),
- quit: make(chan struct{}),
- bloomFilter: MakeFullNodeBloom(),
- fullNode: true,
- }
-}
-
-// start initiates the peer updater, periodically broadcasting the whisper packets
-// into the network.
-func (peer *Peer) start() {
- go peer.update()
- log.Trace("start", "peer", peer.ID())
-}
-
-// stop terminates the peer updater, stopping message forwarding to it.
-func (peer *Peer) stop() {
- close(peer.quit)
- log.Trace("stop", "peer", peer.ID())
-}
-
-// handshake sends the protocol initiation status message to the remote peer and
-// verifies the remote status too.
-func (peer *Peer) handshake() error {
- // Send the handshake status message asynchronously
- errc := make(chan error, 1)
- go func() {
- pow := peer.host.MinPow()
- powConverted := math.Float64bits(pow)
- bloom := peer.host.BloomFilter()
- errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom)
- }()
-
- // Fetch the remote status packet and verify protocol match
- packet, err := peer.ws.ReadMsg()
- if err != nil {
- return err
- }
- if packet.Code != statusCode {
- return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code)
- }
- s := rlp.NewStream(packet.Payload, uint64(packet.Size))
- _, err = s.List()
- if err != nil {
- return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err)
- }
- peerVersion, err := s.Uint()
- if err != nil {
- return fmt.Errorf("peer [%x] sent bad status message (unable to decode version): %v", peer.ID(), err)
- }
- if peerVersion != ProtocolVersion {
- return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion)
- }
-
- // only version is mandatory, subsequent parameters are optional
- powRaw, err := s.Uint()
- if err == nil {
- pow := math.Float64frombits(powRaw)
- if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 {
- return fmt.Errorf("peer [%x] sent bad status message: invalid pow", peer.ID())
- }
- peer.powRequirement = pow
-
- var bloom []byte
- err = s.Decode(&bloom)
- if err == nil {
- sz := len(bloom)
- if sz != BloomFilterSize && sz != 0 {
- return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz)
- }
- peer.setBloomFilter(bloom)
- }
- }
-
- if err := <-errc; err != nil {
- return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
- }
- return nil
-}
-
-// update executes periodic operations on the peer, including message transmission
-// and expiration.
-func (peer *Peer) update() {
- // Start the tickers for the updates
- expire := time.NewTicker(expirationCycle)
- transmit := time.NewTicker(transmissionCycle)
-
- // Loop and transmit until termination is requested
- for {
- select {
- case <-expire.C:
- peer.expire()
-
- case <-transmit.C:
- if err := peer.broadcast(); err != nil {
- log.Trace("broadcast failed", "reason", err, "peer", peer.ID())
- return
- }
-
- case <-peer.quit:
- return
- }
- }
-}
-
-// mark marks an envelope known to the peer so that it won't be sent back.
-func (peer *Peer) mark(envelope *Envelope) {
- peer.known.Add(envelope.Hash())
-}
-
-// marked checks if an envelope is already known to the remote peer.
-func (peer *Peer) marked(envelope *Envelope) bool {
- return peer.known.Contains(envelope.Hash())
-}
-
-// expire iterates over all the known envelopes in the host and removes all
-// expired (unknown) ones from the known list.
-func (peer *Peer) expire() {
- unmark := make(map[common.Hash]struct{})
- peer.known.Each(func(v interface{}) bool {
- if !peer.host.isEnvelopeCached(v.(common.Hash)) {
- unmark[v.(common.Hash)] = struct{}{}
- }
- return true
- })
- // Dump all known but no longer cached
- for hash := range unmark {
- peer.known.Remove(hash)
- }
-}
-
-// broadcast iterates over the collection of envelopes and transmits yet unknown
-// ones over the network.
-func (peer *Peer) broadcast() error {
- envelopes := peer.host.Envelopes()
- bundle := make([]*Envelope, 0, len(envelopes))
- for _, envelope := range envelopes {
- if !peer.marked(envelope) && envelope.PoW() >= peer.powRequirement && peer.bloomMatch(envelope) {
- bundle = append(bundle, envelope)
- }
- }
-
- if len(bundle) > 0 {
- // transmit the batch of envelopes
- if err := p2p.Send(peer.ws, messagesCode, bundle); err != nil {
- return err
- }
-
- // mark envelopes only if they were successfully sent
- for _, e := range bundle {
- peer.mark(e)
- }
-
- log.Trace("broadcast", "num. messages", len(bundle))
- }
- return nil
-}
-
-// ID returns a peer's id
-func (peer *Peer) ID() []byte {
- id := peer.peer.ID()
- return id[:]
-}
-
-func (peer *Peer) notifyAboutPowRequirementChange(pow float64) error {
- i := math.Float64bits(pow)
- return p2p.Send(peer.ws, powRequirementCode, i)
-}
-
-func (peer *Peer) notifyAboutBloomFilterChange(bloom []byte) error {
- return p2p.Send(peer.ws, bloomFilterExCode, bloom)
-}
-
-func (peer *Peer) bloomMatch(env *Envelope) bool {
- peer.bloomMu.Lock()
- defer peer.bloomMu.Unlock()
- return peer.fullNode || BloomFilterMatch(peer.bloomFilter, env.Bloom())
-}
-
-func (peer *Peer) setBloomFilter(bloom []byte) {
- peer.bloomMu.Lock()
- defer peer.bloomMu.Unlock()
- peer.bloomFilter = bloom
- peer.fullNode = isFullNode(bloom)
- if peer.fullNode && peer.bloomFilter == nil {
- peer.bloomFilter = MakeFullNodeBloom()
- }
-}
-
-func MakeFullNodeBloom() []byte {
- bloom := make([]byte, BloomFilterSize)
- for i := 0; i < BloomFilterSize; i++ {
- bloom[i] = 0xFF
- }
- return bloom
-}
diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go
deleted file mode 100644
index d5869e180c..0000000000
--- a/whisper/whisperv6/peer_test.go
+++ /dev/null
@@ -1,515 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "bytes"
- "crypto/ecdsa"
- "fmt"
- mrand "math/rand"
- "net"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/p2p/discover"
- "github.com/XinFinOrg/XDPoSChain/p2p/nat"
-)
-
-var keys = []string{
- "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9",
- "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98",
- "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc",
- "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837",
- "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf",
- "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5",
- "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68",
- "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c",
- "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0",
- "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9",
- "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4",
- "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620",
- "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590",
- "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1",
- "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a",
- "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f",
- "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8",
- "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805",
- "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045",
- "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5",
- "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7",
- "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd",
- "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f",
- "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211",
- "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611",
- "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f",
- "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b",
- "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5",
- "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15",
- "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c",
- "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2",
- "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a",
- "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4",
-}
-
-type TestData struct {
- started int64
- counter [NumNodes]int
- mutex sync.RWMutex
-}
-
-type TestNode struct {
- shh *Whisper
- id *ecdsa.PrivateKey
- server *p2p.Server
- filerID string
-}
-
-const NumNodes = 8 // must not exceed the number of keys (32)
-
-var result TestData
-var nodes [NumNodes]*TestNode
-var sharedKey = hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31")
-var wrongKey = hexutil.MustDecode("0xf91156714d7ec88d3edc1c652c2181dbb3044e8771c683f3b30d33c12b986b11")
-var sharedTopic = TopicType{0xF, 0x1, 0x2, 0}
-var wrongTopic = TopicType{0, 0, 0, 0}
-var expectedMessage = []byte("per aspera ad astra")
-var unexpectedMessage = []byte("per rectum ad astra")
-var masterBloomFilter []byte
-var masterPow = 0.00000001
-var round = 1
-var debugMode = false
-var prevTime time.Time
-var cntPrev int
-
-func TestSimulation(t *testing.T) {
- t.Skip("TODO: PR-136 Broken test due to EVM upgrade!")
- // create a chain of whisper nodes,
- // installs the filters with shared (predefined) parameters
- initialize(t)
-
- // each node sends one random (not decryptable) message
- for i := 0; i < NumNodes; i++ {
- sendMsg(t, false, i)
- }
-
- // node #0 sends one expected (decryptable) message
- sendMsg(t, true, 0)
-
- // check if each node have received and decrypted exactly one message
- checkPropagation(t, true)
-
- // check if Status message was correctly decoded
- checkBloomFilterExchange(t)
- checkPowExchange(t)
-
- // send new pow and bloom exchange messages
- resetParams(t)
-
- // node #1 sends one expected (decryptable) message
- sendMsg(t, true, 1)
-
- // check if each node (except node #0) have received and decrypted exactly one message
- checkPropagation(t, false)
-
- // check if corresponding protocol-level messages were correctly decoded
- checkPowExchangeForNodeZero(t)
- checkBloomFilterExchange(t)
-
- stopServers()
-}
-
-func resetParams(t *testing.T) {
- // change pow only for node zero
- masterPow = 7777777.0
- nodes[0].shh.SetMinimumPoW(masterPow)
-
- // change bloom for all nodes
- masterBloomFilter = TopicToBloom(sharedTopic)
- for i := 0; i < NumNodes; i++ {
- nodes[i].shh.SetBloomFilter(masterBloomFilter)
- }
-
- round++
-}
-
-func initBloom(t *testing.T) {
- masterBloomFilter = make([]byte, BloomFilterSize)
- _, err := mrand.Read(masterBloomFilter)
- if err != nil {
- t.Fatalf("rand failed: %s.", err)
- }
-
- msgBloom := TopicToBloom(sharedTopic)
- masterBloomFilter = addBloom(masterBloomFilter, msgBloom)
- for i := 0; i < 32; i++ {
- masterBloomFilter[i] = 0xFF
- }
-
- if !BloomFilterMatch(masterBloomFilter, msgBloom) {
- t.Fatalf("bloom mismatch on initBloom.")
- }
-}
-
-func initialize(t *testing.T) {
- initBloom(t)
-
- var err error
- ip := net.IPv4(127, 0, 0, 1)
- port0 := 30303
-
- for i := 0; i < NumNodes; i++ {
- var node TestNode
- b := make([]byte, BloomFilterSize)
- copy(b, masterBloomFilter)
- node.shh = New(&DefaultConfig)
- node.shh.SetMinimumPoW(masterPow)
- node.shh.SetBloomFilter(b)
- if !bytes.Equal(node.shh.BloomFilter(), masterBloomFilter) {
- t.Fatalf("bloom mismatch on init.")
- }
- node.shh.Start(nil)
- topics := make([]TopicType, 0)
- topics = append(topics, sharedTopic)
- f := Filter{KeySym: sharedKey}
- f.Topics = [][]byte{topics[0][:]}
- node.filerID, err = node.shh.Subscribe(&f)
- if err != nil {
- t.Fatalf("failed to install the filter: %s.", err)
- }
- node.id, err = crypto.HexToECDSA(keys[i])
- if err != nil {
- t.Fatalf("failed convert the key: %s.", keys[i])
- }
- port := port0 + i
- addr := fmt.Sprintf(":%d", port) // e.g. ":30303"
- name := common.MakeName("whisper-go", "2.0")
- var peers []*discover.Node
- if i > 0 {
- peerNodeID := nodes[i-1].id
- peerPort := uint16(port - 1)
- peerNode := discover.PubkeyID(&peerNodeID.PublicKey)
- peer := discover.NewNode(peerNode, ip, peerPort, peerPort)
- peers = append(peers, peer)
- }
-
- node.server = &p2p.Server{
- Config: p2p.Config{
- PrivateKey: node.id,
- MaxPeers: NumNodes/2 + 1,
- Name: name,
- Protocols: node.shh.Protocols(),
- ListenAddr: addr,
- NAT: nat.Any(),
- BootstrapNodes: peers,
- StaticNodes: peers,
- TrustedNodes: peers,
- },
- }
-
- nodes[i] = &node
- }
-
- for i := 0; i < NumNodes; i++ {
- go startServer(t, nodes[i].server)
- }
-
- waitForServersToStart(t)
-}
-
-func startServer(t *testing.T, s *p2p.Server) {
- err := s.Start()
- if err != nil {
- t.Fatalf("failed to start the fisrt server.")
- }
-
- atomic.AddInt64(&result.started, 1)
-}
-
-func stopServers() {
- for i := 0; i < NumNodes; i++ {
- n := nodes[i]
- if n != nil {
- n.shh.Unsubscribe(n.filerID)
- n.shh.Stop()
- n.server.Stop()
- }
- }
-}
-
-func checkPropagation(t *testing.T, includingNodeZero bool) {
- if t.Failed() {
- return
- }
-
- prevTime = time.Now()
- // (cycle * iterations) should not exceed 50 seconds, since TTL=50
- const cycle = 200 // time in milliseconds
- const iterations = 250
-
- first := 0
- if !includingNodeZero {
- first = 1
- }
-
- for j := 0; j < iterations; j++ {
- for i := first; i < NumNodes; i++ {
- f := nodes[i].shh.GetFilter(nodes[i].filerID)
- if f == nil {
- t.Fatalf("failed to get filterId %s from node %d, round %d.", nodes[i].filerID, i, round)
- }
-
- mail := f.Retrieve()
- validateMail(t, i, mail)
-
- if isTestComplete() {
- checkTestStatus()
- return
- }
- }
-
- checkTestStatus()
- time.Sleep(cycle * time.Millisecond)
- }
-
- if !includingNodeZero {
- f := nodes[0].shh.GetFilter(nodes[0].filerID)
- if f != nil {
- t.Fatalf("node zero received a message with low PoW.")
- }
- }
-
- t.Fatalf("Test was not complete (%d round): timeout %d seconds. nodes=%v", round, iterations*cycle/1000, nodes)
-}
-
-func validateMail(t *testing.T, index int, mail []*ReceivedMessage) {
- var cnt int
- for _, m := range mail {
- if bytes.Equal(m.Payload, expectedMessage) {
- cnt++
- }
- }
-
- if cnt == 0 {
- // no messages received yet: nothing is wrong
- return
- }
- if cnt > 1 {
- t.Fatalf("node %d received %d.", index, cnt)
- }
-
- if cnt == 1 {
- result.mutex.Lock()
- defer result.mutex.Unlock()
- result.counter[index] += cnt
- if result.counter[index] > 1 {
- t.Fatalf("node %d accumulated %d.", index, result.counter[index])
- }
- }
-}
-
-func checkTestStatus() {
- var cnt int
- var arr [NumNodes]int
-
- for i := 0; i < NumNodes; i++ {
- arr[i] = nodes[i].server.PeerCount()
- envelopes := nodes[i].shh.Envelopes()
- if len(envelopes) >= NumNodes {
- cnt++
- }
- }
-
- if debugMode {
- if cntPrev != cnt {
- fmt.Printf(" %v \t number of nodes that have received all msgs: %d, number of peers per node: %v \n",
- time.Since(prevTime), cnt, arr)
- prevTime = time.Now()
- cntPrev = cnt
- }
- }
-}
-
-func isTestComplete() bool {
- result.mutex.RLock()
- defer result.mutex.RUnlock()
-
- for i := 0; i < NumNodes; i++ {
- if result.counter[i] < 1 {
- return false
- }
- }
-
- for i := 0; i < NumNodes; i++ {
- envelopes := nodes[i].shh.Envelopes()
- if len(envelopes) < NumNodes+1 {
- return false
- }
- }
-
- return true
-}
-
-func sendMsg(t *testing.T, expected bool, id int) {
- if t.Failed() {
- return
- }
-
- opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1}
- if !expected {
- opt.KeySym = wrongKey
- opt.Topic = wrongTopic
- opt.Payload = unexpectedMessage
- opt.Payload[0] = byte(id)
- }
-
- msg, err := NewSentMessage(&opt)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- envelope, err := msg.Wrap(&opt)
- if err != nil {
- t.Fatalf("failed to seal message: %s", err)
- }
-
- err = nodes[id].shh.Send(envelope)
- if err != nil {
- t.Fatalf("failed to send message: %s", err)
- }
-}
-
-func TestPeerBasic(t *testing.T) {
- InitSingleTest()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d.", seed)
- }
-
- params.PoW = 0.001
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d.", seed)
- }
-
- p := newPeer(nil, nil, nil)
- p.mark(env)
- if !p.marked(env) {
- t.Fatalf("failed mark with seed %d.", seed)
- }
-}
-
-func checkPowExchangeForNodeZero(t *testing.T) {
- const iterations = 200
- for j := 0; j < iterations; j++ {
- lastCycle := (j == iterations-1)
- ok := checkPowExchangeForNodeZeroOnce(t, lastCycle)
- if ok {
- break
- }
- time.Sleep(50 * time.Millisecond)
- }
-}
-
-func checkPowExchangeForNodeZeroOnce(t *testing.T, mustPass bool) bool {
- cnt := 0
- for i, node := range nodes {
- for peer := range node.shh.peers {
- if peer.peer.ID() == discover.PubkeyID(&nodes[0].id.PublicKey) {
- cnt++
- if peer.powRequirement != masterPow {
- if mustPass {
- t.Fatalf("node %d: failed to set the new pow requirement for node zero.", i)
- } else {
- return false
- }
- }
- }
- }
- }
- if cnt == 0 {
- t.Fatalf("looking for node zero: no matching peers found.")
- }
- return true
-}
-
-func checkPowExchange(t *testing.T) {
- for i, node := range nodes {
- for peer := range node.shh.peers {
- if peer.peer.ID() != discover.PubkeyID(&nodes[0].id.PublicKey) {
- if peer.powRequirement != masterPow {
- t.Fatalf("node %d: failed to exchange pow requirement in round %d; expected %f, got %f",
- i, round, masterPow, peer.powRequirement)
- }
- }
- }
- }
-}
-
-func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool {
- for i, node := range nodes {
- for peer := range node.shh.peers {
- peer.bloomMu.Lock()
- equals := bytes.Equal(peer.bloomFilter, masterBloomFilter)
- peer.bloomMu.Unlock()
- if !equals {
- if mustPass {
- t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got",
- i, round, masterBloomFilter, peer.bloomFilter)
- } else {
- return false
- }
- }
- }
- }
-
- return true
-}
-
-func checkBloomFilterExchange(t *testing.T) {
- const iterations = 200
- for j := 0; j < iterations; j++ {
- lastCycle := (j == iterations-1)
- ok := checkBloomFilterExchangeOnce(t, lastCycle)
- if ok {
- break
- }
- time.Sleep(50 * time.Millisecond)
- }
-}
-
-func waitForServersToStart(t *testing.T) {
- const iterations = 200
- var started int64
- for j := 0; j < iterations; j++ {
- time.Sleep(50 * time.Millisecond)
- started = atomic.LoadInt64(&result.started)
- if started == NumNodes {
- return
- }
- }
- t.Fatalf("Failed to start all the servers, running: %d", started)
-}
diff --git a/whisper/whisperv6/topic.go b/whisper/whisperv6/topic.go
deleted file mode 100644
index 21941f69af..0000000000
--- a/whisper/whisperv6/topic.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Contains the Whisper protocol Topic element.
-
-package whisperv6
-
-import (
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/hexutil"
-)
-
-// TopicType represents a cryptographically secure, probabilistic partial
-// classifications of a message, determined as the first (left) 4 bytes of the
-// SHA3 hash of some arbitrary data given by the original author of the message.
-type TopicType [TopicLength]byte
-
-// BytesToTopic converts from the byte array representation of a topic
-// into the TopicType type.
-func BytesToTopic(b []byte) (t TopicType) {
- sz := TopicLength
- if x := len(b); x < TopicLength {
- sz = x
- }
- for i := 0; i < sz; i++ {
- t[i] = b[i]
- }
- return t
-}
-
-// String converts a topic byte array to a string representation.
-func (t *TopicType) String() string {
- return common.ToHex(t[:])
-}
-
-// MarshalText returns the hex representation of t.
-func (t TopicType) MarshalText() ([]byte, error) {
- return hexutil.Bytes(t[:]).MarshalText()
-}
-
-// UnmarshalText parses a hex representation to a topic.
-func (t *TopicType) UnmarshalText(input []byte) error {
- return hexutil.UnmarshalFixedText("Topic", input, t[:])
-}
diff --git a/whisper/whisperv6/topic_test.go b/whisper/whisperv6/topic_test.go
deleted file mode 100644
index 454afe0de1..0000000000
--- a/whisper/whisperv6/topic_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "encoding/json"
- "testing"
-)
-
-var topicStringTests = []struct {
- topic TopicType
- str string
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"},
- {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"},
- {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"},
-}
-
-func TestTopicString(t *testing.T) {
- for i, tst := range topicStringTests {
- s := tst.topic.String()
- if s != tst.str {
- t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str)
- }
- }
-}
-
-var bytesToTopicTests = []struct {
- data []byte
- topic TopicType
-}{
- {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}},
- {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}},
- {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}},
- {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}},
- {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil},
-}
-
-var unmarshalTestsGood = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)},
- {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)},
- {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)},
- {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)},
-}
-
-var unmarshalTestsBad = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000000"`)},
- {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"abcdefg0"`)},
-}
-
-var unmarshalTestsUgly = []struct {
- topic TopicType
- data []byte
-}{
- {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte(`"0x00000001"`)},
-}
-
-func TestBytesToTopic(t *testing.T) {
- for i, tst := range bytesToTopicTests {
- top := BytesToTopic(tst.data)
- if top != tst.topic {
- t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic)
- }
- }
-}
-
-func TestUnmarshalTestsGood(t *testing.T) {
- for i, tst := range unmarshalTestsGood {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err != nil {
- t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err)
- } else if top != tst.topic {
- t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
- }
- }
-}
-
-func TestUnmarshalTestsBad(t *testing.T) {
- // in this test UnmarshalJSON() is supposed to fail
- for i, tst := range unmarshalTestsBad {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err == nil {
- t.Fatalf("failed test %d. input: %v.", i, tst.data)
- }
- }
-}
-
-func TestUnmarshalTestsUgly(t *testing.T) {
- // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong
- for i, tst := range unmarshalTestsUgly {
- var top TopicType
- err := json.Unmarshal(tst.data, &top)
- if err != nil {
- t.Errorf("failed test %d. input: %v.", i, tst.data)
- } else if top == tst.topic {
- t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic)
- }
- }
-}
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
deleted file mode 100644
index 0b83c89d13..0000000000
--- a/whisper/whisperv6/whisper.go
+++ /dev/null
@@ -1,1052 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "bytes"
- "crypto/ecdsa"
- "crypto/sha256"
- "fmt"
- "math"
- "runtime"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/p2p"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- "github.com/XinFinOrg/XDPoSChain/rpc"
- mapset "github.com/deckarep/golang-set"
- "github.com/syndtr/goleveldb/leveldb/errors"
- "golang.org/x/crypto/pbkdf2"
- "golang.org/x/sync/syncmap"
-)
-
-// Statistics holds several message-related counter for analytics
-// purposes.
-type Statistics struct {
- messagesCleared int
- memoryCleared int
- memoryUsed int
- cycles int
- totalMessagesCleared int
-}
-
-const (
- maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
- overflowIdx // Indicator of message queue overflow
- minPowIdx // Minimal PoW required by the whisper node
- minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time
- bloomFilterIdx // Bloom filter for topics of interest for this node
- bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time
-)
-
-// Whisper represents a dark communication interface through the Ethereum
-// network, using its very own P2P communication layer.
-type Whisper struct {
- protocol p2p.Protocol // Protocol description and parameters
- filters *Filters // Message filters installed with Subscribe function
-
- privateKeys map[string]*ecdsa.PrivateKey // Private key storage
- symKeys map[string][]byte // Symmetric key storage
- keyMu sync.RWMutex // Mutex associated with key storages
-
- poolMu sync.RWMutex // Mutex to sync the message and expiration pools
- envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
- expirations map[uint32]mapset.Set // Message expiration pool
-
- peerMu sync.RWMutex // Mutex to sync the active peer set
- peers map[*Peer]struct{} // Set of currently active peers
-
- messageQueue chan *Envelope // Message queue for normal whisper messages
- p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further)
- quit chan struct{} // Channel used for graceful exit
-
- settings syncmap.Map // holds configuration settings that can be dynamically changed
-
- syncAllowance int // maximum time in seconds allowed to process the whisper-related messages
-
- lightClient bool // indicates is this node is pure light client (does not forward any messages)
-
- statsMu sync.Mutex // guard stats
- stats Statistics // Statistics of whisper node
-
- mailServer MailServer // MailServer interface
-}
-
-// New creates a Whisper client ready to communicate through the Ethereum P2P network.
-func New(cfg *Config) *Whisper {
- if cfg == nil {
- cfg = &DefaultConfig
- }
-
- whisper := &Whisper{
- privateKeys: make(map[string]*ecdsa.PrivateKey),
- symKeys: make(map[string][]byte),
- envelopes: make(map[common.Hash]*Envelope),
- expirations: make(map[uint32]mapset.Set),
- peers: make(map[*Peer]struct{}),
- messageQueue: make(chan *Envelope, messageQueueLimit),
- p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
- quit: make(chan struct{}),
- syncAllowance: DefaultSyncAllowance,
- }
-
- whisper.filters = NewFilters(whisper)
-
- whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
- whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
- whisper.settings.Store(overflowIdx, false)
-
- // p2p whisper sub protocol handler
- whisper.protocol = p2p.Protocol{
- Name: ProtocolName,
- Version: uint(ProtocolVersion),
- Length: NumberOfMessageCodes,
- Run: whisper.HandlePeer,
- NodeInfo: func() interface{} {
- return map[string]interface{}{
- "version": ProtocolVersionStr,
- "maxMessageSize": whisper.MaxMessageSize(),
- "minimumPoW": whisper.MinPow(),
- }
- },
- }
-
- return whisper
-}
-
-// MinPow returns the PoW value required by this node.
-func (whisper *Whisper) MinPow() float64 {
- val, exist := whisper.settings.Load(minPowIdx)
- if !exist || val == nil {
- return DefaultMinimumPoW
- }
- v, ok := val.(float64)
- if !ok {
- log.Error("Error loading minPowIdx, using default")
- return DefaultMinimumPoW
- }
- return v
-}
-
-// MinPowTolerance returns the value of minimum PoW which is tolerated for a limited
-// time after PoW was changed. If sufficient time have elapsed or no change of PoW
-// have ever occurred, the return value will be the same as return value of MinPow().
-func (whisper *Whisper) MinPowTolerance() float64 {
- val, exist := whisper.settings.Load(minPowToleranceIdx)
- if !exist || val == nil {
- return DefaultMinimumPoW
- }
- return val.(float64)
-}
-
-// BloomFilter returns the aggregated bloom filter for all the topics of interest.
-// The nodes are required to send only messages that match the advertised bloom filter.
-// If a message does not match the bloom, it will tantamount to spam, and the peer will
-// be disconnected.
-func (whisper *Whisper) BloomFilter() []byte {
- val, exist := whisper.settings.Load(bloomFilterIdx)
- if !exist || val == nil {
- return nil
- }
- return val.([]byte)
-}
-
-// BloomFilterTolerance returns the bloom filter which is tolerated for a limited
-// time after new bloom was advertised to the peers. If sufficient time have elapsed
-// or no change of bloom filter have ever occurred, the return value will be the same
-// as return value of BloomFilter().
-func (whisper *Whisper) BloomFilterTolerance() []byte {
- val, exist := whisper.settings.Load(bloomFilterToleranceIdx)
- if !exist || val == nil {
- return nil
- }
- return val.([]byte)
-}
-
-// MaxMessageSize returns the maximum accepted message size.
-func (whisper *Whisper) MaxMessageSize() uint32 {
- val, _ := whisper.settings.Load(maxMsgSizeIdx)
- return val.(uint32)
-}
-
-// Overflow returns an indication if the message queue is full.
-func (whisper *Whisper) Overflow() bool {
- val, _ := whisper.settings.Load(overflowIdx)
- return val.(bool)
-}
-
-// APIs returns the RPC descriptors the Whisper implementation offers
-func (whisper *Whisper) APIs() []rpc.API {
- return []rpc.API{
- {
- Namespace: ProtocolName,
- Version: ProtocolVersionStr,
- Service: NewPublicWhisperAPI(whisper),
- Public: true,
- },
- }
-}
-
-// RegisterServer registers MailServer interface.
-// MailServer will process all the incoming messages with p2pRequestCode.
-func (whisper *Whisper) RegisterServer(server MailServer) {
- whisper.mailServer = server
-}
-
-// Protocols returns the whisper sub-protocols ran by this particular client.
-func (whisper *Whisper) Protocols() []p2p.Protocol {
- return []p2p.Protocol{whisper.protocol}
-}
-
-// Version returns the whisper sub-protocols version number.
-func (whisper *Whisper) Version() uint {
- return whisper.protocol.Version
-}
-
-// SetMaxMessageSize sets the maximal message size allowed by this node
-func (whisper *Whisper) SetMaxMessageSize(size uint32) error {
- if size > MaxMessageSize {
- return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize)
- }
- whisper.settings.Store(maxMsgSizeIdx, size)
- return nil
-}
-
-// SetBloomFilter sets the new bloom filter
-func (whisper *Whisper) SetBloomFilter(bloom []byte) error {
- if len(bloom) != BloomFilterSize {
- return fmt.Errorf("invalid bloom filter size: %d", len(bloom))
- }
-
- b := make([]byte, BloomFilterSize)
- copy(b, bloom)
-
- whisper.settings.Store(bloomFilterIdx, b)
- whisper.notifyPeersAboutBloomFilterChange(b)
-
- go func() {
- // allow some time before all the peers have processed the notification
- time.Sleep(time.Duration(whisper.syncAllowance) * time.Second)
- whisper.settings.Store(bloomFilterToleranceIdx, b)
- }()
-
- return nil
-}
-
-// SetMinimumPoW sets the minimal PoW required by this node
-func (whisper *Whisper) SetMinimumPoW(val float64) error {
- if val < 0.0 {
- return fmt.Errorf("invalid PoW: %f", val)
- }
-
- whisper.settings.Store(minPowIdx, val)
- whisper.notifyPeersAboutPowRequirementChange(val)
-
- go func() {
- // allow some time before all the peers have processed the notification
- time.Sleep(time.Duration(whisper.syncAllowance) * time.Second)
- whisper.settings.Store(minPowToleranceIdx, val)
- }()
-
- return nil
-}
-
-// SetMinimumPowTest sets the minimal PoW in test environment
-func (whisper *Whisper) SetMinimumPowTest(val float64) {
- whisper.settings.Store(minPowIdx, val)
- whisper.notifyPeersAboutPowRequirementChange(val)
- whisper.settings.Store(minPowToleranceIdx, val)
-}
-
-func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) {
- arr := whisper.getPeers()
- for _, p := range arr {
- err := p.notifyAboutPowRequirementChange(pow)
- if err != nil {
- // allow one retry
- err = p.notifyAboutPowRequirementChange(pow)
- }
- if err != nil {
- log.Warn("failed to notify peer about new pow requirement", "peer", p.ID(), "error", err)
- }
- }
-}
-
-func (whisper *Whisper) notifyPeersAboutBloomFilterChange(bloom []byte) {
- arr := whisper.getPeers()
- for _, p := range arr {
- err := p.notifyAboutBloomFilterChange(bloom)
- if err != nil {
- // allow one retry
- err = p.notifyAboutBloomFilterChange(bloom)
- }
- if err != nil {
- log.Warn("failed to notify peer about new bloom filter", "peer", p.ID(), "error", err)
- }
- }
-}
-
-func (whisper *Whisper) getPeers() []*Peer {
- arr := make([]*Peer, len(whisper.peers))
- i := 0
- whisper.peerMu.Lock()
- for p := range whisper.peers {
- arr[i] = p
- i++
- }
- whisper.peerMu.Unlock()
- return arr
-}
-
-// getPeer retrieves peer by ID
-func (whisper *Whisper) getPeer(peerID []byte) (*Peer, error) {
- whisper.peerMu.Lock()
- defer whisper.peerMu.Unlock()
- for p := range whisper.peers {
- id := p.peer.ID()
- if bytes.Equal(peerID, id[:]) {
- return p, nil
- }
- }
- return nil, fmt.Errorf("Could not find peer with ID: %x", peerID)
-}
-
-// AllowP2PMessagesFromPeer marks specific peer trusted,
-// which will allow it to send historic (expired) messages.
-func (whisper *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error {
- p, err := whisper.getPeer(peerID)
- if err != nil {
- return err
- }
- p.trusted = true
- return nil
-}
-
-// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer,
-// which is known to implement MailServer interface, and is supposed to process this
-// request and respond with a number of peer-to-peer messages (possibly expired),
-// which are not supposed to be forwarded any further.
-// The whisper protocol is agnostic of the format and contents of envelope.
-func (whisper *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
- p, err := whisper.getPeer(peerID)
- if err != nil {
- return err
- }
- p.trusted = true
- return p2p.Send(p.ws, p2pRequestCode, envelope)
-}
-
-// SendP2PMessage sends a peer-to-peer message to a specific peer.
-func (whisper *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
- p, err := whisper.getPeer(peerID)
- if err != nil {
- return err
- }
- return whisper.SendP2PDirect(p, envelope)
-}
-
-// SendP2PDirect sends a peer-to-peer message to a specific peer.
-func (whisper *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
- return p2p.Send(peer.ws, p2pMessageCode, envelope)
-}
-
-// NewKeyPair generates a new cryptographic identity for the client, and injects
-// it into the known identities for message decryption. Returns ID of the new key pair.
-func (whisper *Whisper) NewKeyPair() (string, error) {
- key, err := crypto.GenerateKey()
- if err != nil || !validatePrivateKey(key) {
- key, err = crypto.GenerateKey() // retry once
- }
- if err != nil {
- return "", err
- }
- if !validatePrivateKey(key) {
- return "", errors.New("failed to generate valid key")
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
-
- if whisper.privateKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- whisper.privateKeys[id] = key
- return id, nil
-}
-
-// DeleteKeyPair deletes the specified key if it exists.
-func (whisper *Whisper) DeleteKeyPair(key string) bool {
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
-
- if whisper.privateKeys[key] != nil {
- delete(whisper.privateKeys, key)
- return true
- }
- return false
-}
-
-// AddKeyPair imports a asymmetric private key and returns it identifier.
-func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- whisper.keyMu.Lock()
- whisper.privateKeys[id] = key
- whisper.keyMu.Unlock()
-
- return id, nil
-}
-
-// HasKeyPair checks if the the whisper node is configured with the private key
-// of the specified public pair.
-func (whisper *Whisper) HasKeyPair(id string) bool {
- whisper.keyMu.RLock()
- defer whisper.keyMu.RUnlock()
- return whisper.privateKeys[id] != nil
-}
-
-// GetPrivateKey retrieves the private key of the specified identity.
-func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
- whisper.keyMu.RLock()
- defer whisper.keyMu.RUnlock()
- key := whisper.privateKeys[id]
- if key == nil {
- return nil, errors.New("invalid id")
- }
- return key, nil
-}
-
-// GenerateSymKey generates a random symmetric key and stores it under id,
-// which is then returned. Will be used in the future for session key exchange.
-func (whisper *Whisper) GenerateSymKey() (string, error) {
- key, err := generateSecureRandomData(aesKeyLength)
- if err != nil {
- return "", err
- } else if !validateDataIntegrity(key, aesKeyLength) {
- return "", errors.New("error in GenerateSymKey: crypto/rand failed to generate random data")
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
-
- if whisper.symKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- whisper.symKeys[id] = key
- return id, nil
-}
-
-// AddSymKeyDirect stores the key, and returns its id.
-func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) {
- if len(key) != aesKeyLength {
- return "", fmt.Errorf("wrong key size: %d", len(key))
- }
-
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
-
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
-
- if whisper.symKeys[id] != nil {
- return "", errors.New("failed to generate unique ID")
- }
- whisper.symKeys[id] = key
- return id, nil
-}
-
-// AddSymKeyFromPassword generates the key from password, stores it, and returns its id.
-func (whisper *Whisper) AddSymKeyFromPassword(password string) (string, error) {
- id, err := GenerateRandomID()
- if err != nil {
- return "", fmt.Errorf("failed to generate ID: %s", err)
- }
- if whisper.HasSymKey(id) {
- return "", errors.New("failed to generate unique ID")
- }
-
- // kdf should run no less than 0.1 seconds on an average computer,
- // because it's an once in a session experience
- derived := pbkdf2.Key([]byte(password), nil, 65356, aesKeyLength, sha256.New)
- if err != nil {
- return "", err
- }
-
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
-
- // double check is necessary, because deriveKeyMaterial() is very slow
- if whisper.symKeys[id] != nil {
- return "", errors.New("critical error: failed to generate unique ID")
- }
- whisper.symKeys[id] = derived
- return id, nil
-}
-
-// HasSymKey returns true if there is a key associated with the given id.
-// Otherwise returns false.
-func (whisper *Whisper) HasSymKey(id string) bool {
- whisper.keyMu.RLock()
- defer whisper.keyMu.RUnlock()
- return whisper.symKeys[id] != nil
-}
-
-// DeleteSymKey deletes the key associated with the name string if it exists.
-func (whisper *Whisper) DeleteSymKey(id string) bool {
- whisper.keyMu.Lock()
- defer whisper.keyMu.Unlock()
- if whisper.symKeys[id] != nil {
- delete(whisper.symKeys, id)
- return true
- }
- return false
-}
-
-// GetSymKey returns the symmetric key associated with the given id.
-func (whisper *Whisper) GetSymKey(id string) ([]byte, error) {
- whisper.keyMu.RLock()
- defer whisper.keyMu.RUnlock()
- if whisper.symKeys[id] != nil {
- return whisper.symKeys[id], nil
- }
- return nil, errors.New("non-existent key ID")
-}
-
-// Subscribe installs a new message handler used for filtering, decrypting
-// and subsequent storing of incoming messages.
-func (whisper *Whisper) Subscribe(f *Filter) (string, error) {
- s, err := whisper.filters.Install(f)
- if err == nil {
- whisper.updateBloomFilter(f)
- }
- return s, err
-}
-
-// updateBloomFilter recalculates the new value of bloom filter,
-// and informs the peers if necessary.
-func (whisper *Whisper) updateBloomFilter(f *Filter) {
- aggregate := make([]byte, BloomFilterSize)
- for _, t := range f.Topics {
- top := BytesToTopic(t)
- b := TopicToBloom(top)
- aggregate = addBloom(aggregate, b)
- }
-
- if !BloomFilterMatch(whisper.BloomFilter(), aggregate) {
- // existing bloom filter must be updated
- aggregate = addBloom(whisper.BloomFilter(), aggregate)
- whisper.SetBloomFilter(aggregate)
- }
-}
-
-// GetFilter returns the filter by id.
-func (whisper *Whisper) GetFilter(id string) *Filter {
- return whisper.filters.Get(id)
-}
-
-// Unsubscribe removes an installed message handler.
-func (whisper *Whisper) Unsubscribe(id string) error {
- ok := whisper.filters.Uninstall(id)
- if !ok {
- return errors.New("Unsubscribe: Invalid ID")
- }
- return nil
-}
-
-// Send injects a message into the whisper send queue, to be distributed in the
-// network in the coming cycles.
-func (whisper *Whisper) Send(envelope *Envelope) error {
- ok, err := whisper.add(envelope, false)
- if err == nil && !ok {
- return errors.New("failed to add envelope")
- }
- return err
-}
-
-// Start implements node.Service, starting the background data propagation thread
-// of the Whisper protocol.
-func (whisper *Whisper) Start(*p2p.Server) error {
- log.Info("started whisper v." + ProtocolVersionStr)
- go whisper.update()
-
- numCPU := runtime.NumCPU()
- for i := 0; i < numCPU; i++ {
- go whisper.processQueue()
- }
-
- return nil
-}
-func (whisper *Whisper) SaveData() {
-}
-
-// Stop implements node.Service, stopping the background data propagation thread
-// of the Whisper protocol.
-func (whisper *Whisper) Stop() error {
- close(whisper.quit)
- log.Info("whisper stopped")
- return nil
-}
-
-// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol
-// connection is negotiated.
-func (whisper *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
- // Create the new peer and start tracking it
- whisperPeer := newPeer(whisper, peer, rw)
-
- whisper.peerMu.Lock()
- whisper.peers[whisperPeer] = struct{}{}
- whisper.peerMu.Unlock()
-
- defer func() {
- whisper.peerMu.Lock()
- delete(whisper.peers, whisperPeer)
- whisper.peerMu.Unlock()
- }()
-
- // Run the peer handshake and state updates
- if err := whisperPeer.handshake(); err != nil {
- return err
- }
- whisperPeer.start()
- defer whisperPeer.stop()
-
- return whisper.runMessageLoop(whisperPeer, rw)
-}
-
-// runMessageLoop reads and processes inbound messages directly to merge into client-global state.
-func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
- for {
- // fetch the next packet
- packet, err := rw.ReadMsg()
- if err != nil {
- log.Warn("message loop", "peer", p.peer.ID(), "err", err)
- return err
- }
- if packet.Size > whisper.MaxMessageSize() {
- log.Warn("oversized message received", "peer", p.peer.ID())
- return errors.New("oversized message received")
- }
-
- switch packet.Code {
- case statusCode:
- // this should not happen, but no need to panic; just ignore this message.
- log.Warn("unxepected status message received", "peer", p.peer.ID())
- case messagesCode:
- // decode the contained envelopes
- var envelopes []*Envelope
- if err := packet.Decode(&envelopes); err != nil {
- log.Warn("failed to decode envelopes, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid envelopes")
- }
-
- trouble := false
- for _, env := range envelopes {
- cached, err := whisper.add(env, whisper.lightClient)
- if err != nil {
- trouble = true
- log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- }
- if cached {
- p.mark(env)
- }
- }
-
- if trouble {
- return errors.New("invalid envelope")
- }
- case powRequirementCode:
- s := rlp.NewStream(packet.Payload, uint64(packet.Size))
- i, err := s.Uint()
- if err != nil {
- log.Warn("failed to decode powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid powRequirementCode message")
- }
- f := math.Float64frombits(i)
- if math.IsInf(f, 0) || math.IsNaN(f) || f < 0.0 {
- log.Warn("invalid value in powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid value in powRequirementCode message")
- }
- p.powRequirement = f
- case bloomFilterExCode:
- var bloom []byte
- err := packet.Decode(&bloom)
- if err == nil && len(bloom) != BloomFilterSize {
- err = fmt.Errorf("wrong bloom filter size %d", len(bloom))
- }
-
- if err != nil {
- log.Warn("failed to decode bloom filter exchange message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid bloom filter exchange message")
- }
- p.setBloomFilter(bloom)
- case p2pMessageCode:
- // peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
- // this message is not supposed to be forwarded to other peers, and
- // therefore might not satisfy the PoW, expiry and other requirements.
- // these messages are only accepted from the trusted peer.
- if p.trusted {
- var envelope Envelope
- if err := packet.Decode(&envelope); err != nil {
- log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid direct message")
- }
- whisper.postEvent(&envelope, true)
- }
- case p2pRequestCode:
- // Must be processed if mail server is implemented. Otherwise ignore.
- if whisper.mailServer != nil {
- var request Envelope
- if err := packet.Decode(&request); err != nil {
- log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
- return errors.New("invalid p2p request")
- }
- whisper.mailServer.DeliverMail(p, &request)
- }
- default:
- // New message types might be implemented in the future versions of Whisper.
- // For forward compatibility, just ignore.
- }
-
- packet.Discard()
- }
-}
-
-// add inserts a new envelope into the message pool to be distributed within the
-// whisper network. It also inserts the envelope into the expiration pool at the
-// appropriate time-stamp. In case of error, connection should be dropped.
-// param isP2P indicates whether the message is peer-to-peer (should not be forwarded).
-func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) {
- now := uint32(time.Now().Unix())
- sent := envelope.Expiry - envelope.TTL
-
- if sent > now {
- if sent-DefaultSyncAllowance > now {
- return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash())
- }
- // recalculate PoW, adjusted for the time difference, plus one second for latency
- envelope.calculatePoW(sent - now + 1)
- }
-
- if envelope.Expiry < now {
- if envelope.Expiry+DefaultSyncAllowance*2 < now {
- return false, errors.New("very old message")
- }
- log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex())
- return false, nil // drop envelope without error
- }
-
- if uint32(envelope.size()) > whisper.MaxMessageSize() {
- return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
- }
-
- if envelope.PoW() < whisper.MinPow() {
- // maybe the value was recently changed, and the peers did not adjust yet.
- // in this case the previous value is retrieved by MinPowTolerance()
- // for a short period of peer synchronization.
- if envelope.PoW() < whisper.MinPowTolerance() {
- return false, fmt.Errorf("envelope with low PoW received: PoW=%f, hash=[%v]", envelope.PoW(), envelope.Hash().Hex())
- }
- }
-
- if !BloomFilterMatch(whisper.BloomFilter(), envelope.Bloom()) {
- // maybe the value was recently changed, and the peers did not adjust yet.
- // in this case the previous value is retrieved by BloomFilterTolerance()
- // for a short period of peer synchronization.
- if !BloomFilterMatch(whisper.BloomFilterTolerance(), envelope.Bloom()) {
- return false, fmt.Errorf("envelope does not match bloom filter, hash=[%v], bloom: \n%x \n%x \n%x",
- envelope.Hash().Hex(), whisper.BloomFilter(), envelope.Bloom(), envelope.Topic)
- }
- }
-
- hash := envelope.Hash()
-
- whisper.poolMu.Lock()
- _, alreadyCached := whisper.envelopes[hash]
- if !alreadyCached {
- whisper.envelopes[hash] = envelope
- if whisper.expirations[envelope.Expiry] == nil {
- whisper.expirations[envelope.Expiry] = mapset.NewThreadUnsafeSet()
- }
- if !whisper.expirations[envelope.Expiry].Contains(hash) {
- whisper.expirations[envelope.Expiry].Add(hash)
- }
- }
- whisper.poolMu.Unlock()
-
- if alreadyCached {
- log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex())
- } else {
- log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex())
- whisper.statsMu.Lock()
- whisper.stats.memoryUsed += envelope.size()
- whisper.statsMu.Unlock()
- whisper.postEvent(envelope, isP2P) // notify the local node about the new message
- if whisper.mailServer != nil {
- whisper.mailServer.Archive(envelope)
- }
- }
- return true, nil
-}
-
-// postEvent queues the message for further processing.
-func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) {
- if isP2P {
- whisper.p2pMsgQueue <- envelope
- } else {
- whisper.checkOverflow()
- whisper.messageQueue <- envelope
- }
-}
-
-// checkOverflow checks if message queue overflow occurs and reports it if necessary.
-func (whisper *Whisper) checkOverflow() {
- queueSize := len(whisper.messageQueue)
-
- if queueSize == messageQueueLimit {
- if !whisper.Overflow() {
- whisper.settings.Store(overflowIdx, true)
- log.Warn("message queue overflow")
- }
- } else if queueSize <= messageQueueLimit/2 {
- if whisper.Overflow() {
- whisper.settings.Store(overflowIdx, false)
- log.Warn("message queue overflow fixed (back to normal)")
- }
- }
-}
-
-// processQueue delivers the messages to the watchers during the lifetime of the whisper node.
-func (whisper *Whisper) processQueue() {
- var e *Envelope
- for {
- select {
- case <-whisper.quit:
- return
-
- case e = <-whisper.messageQueue:
- whisper.filters.NotifyWatchers(e, false)
-
- case e = <-whisper.p2pMsgQueue:
- whisper.filters.NotifyWatchers(e, true)
- }
- }
-}
-
-// update loops until the lifetime of the whisper node, updating its internal
-// state by expiring stale messages from the pool.
-func (whisper *Whisper) update() {
- // Start a ticker to check for expirations
- expire := time.NewTicker(expirationCycle)
-
- // Repeat updates until termination is requested
- for {
- select {
- case <-expire.C:
- whisper.expire()
-
- case <-whisper.quit:
- return
- }
- }
-}
-
-// expire iterates over all the expiration timestamps, removing all stale
-// messages from the pools.
-func (whisper *Whisper) expire() {
- whisper.poolMu.Lock()
- defer whisper.poolMu.Unlock()
-
- whisper.statsMu.Lock()
- defer whisper.statsMu.Unlock()
- whisper.stats.reset()
- now := uint32(time.Now().Unix())
- for expiry, hashSet := range whisper.expirations {
- if expiry < now {
- // Dump all expired messages and remove timestamp
- hashSet.Each(func(v interface{}) bool {
- sz := whisper.envelopes[v.(common.Hash)].size()
- delete(whisper.envelopes, v.(common.Hash))
- whisper.stats.messagesCleared++
- whisper.stats.memoryCleared += sz
- whisper.stats.memoryUsed -= sz
- return true
- })
- whisper.expirations[expiry].Clear()
- delete(whisper.expirations, expiry)
- }
- }
-}
-
-// Stats returns the whisper node statistics.
-func (whisper *Whisper) Stats() Statistics {
- whisper.statsMu.Lock()
- defer whisper.statsMu.Unlock()
-
- return whisper.stats
-}
-
-// Envelopes retrieves all the messages currently pooled by the node.
-func (whisper *Whisper) Envelopes() []*Envelope {
- whisper.poolMu.RLock()
- defer whisper.poolMu.RUnlock()
-
- all := make([]*Envelope, 0, len(whisper.envelopes))
- for _, envelope := range whisper.envelopes {
- all = append(all, envelope)
- }
- return all
-}
-
-// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
-func (whisper *Whisper) isEnvelopeCached(hash common.Hash) bool {
- whisper.poolMu.Lock()
- defer whisper.poolMu.Unlock()
-
- _, exist := whisper.envelopes[hash]
- return exist
-}
-
-// reset resets the node's statistics after each expiry cycle.
-func (s *Statistics) reset() {
- s.cycles++
- s.totalMessagesCleared += s.messagesCleared
-
- s.memoryCleared = 0
- s.messagesCleared = 0
-}
-
-// ValidatePublicKey checks the format of the given public key.
-func ValidatePublicKey(k *ecdsa.PublicKey) bool {
- return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0
-}
-
-// validatePrivateKey checks the format of the given private key.
-func validatePrivateKey(k *ecdsa.PrivateKey) bool {
- if k == nil || k.D == nil || k.D.Sign() == 0 {
- return false
- }
- return ValidatePublicKey(&k.PublicKey)
-}
-
-// validateDataIntegrity returns false if the data have the wrong or contains all zeros,
-// which is the simplest and the most common bug.
-func validateDataIntegrity(k []byte, expectedSize int) bool {
- if len(k) != expectedSize {
- return false
- }
- if expectedSize > 3 && containsOnlyZeros(k) {
- return false
- }
- return true
-}
-
-// containsOnlyZeros checks if the data contain only zeros.
-func containsOnlyZeros(data []byte) bool {
- for _, b := range data {
- if b != 0 {
- return false
- }
- }
- return true
-}
-
-// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer.
-func bytesToUintLittleEndian(b []byte) (res uint64) {
- mul := uint64(1)
- for i := 0; i < len(b); i++ {
- res += uint64(b[i]) * mul
- mul *= 256
- }
- return res
-}
-
-// BytesToUintBigEndian converts the slice to 64-bit unsigned integer.
-func BytesToUintBigEndian(b []byte) (res uint64) {
- for i := 0; i < len(b); i++ {
- res *= 256
- res += uint64(b[i])
- }
- return res
-}
-
-// GenerateRandomID generates a random string, which is then returned to be used as a key id
-func GenerateRandomID() (id string, err error) {
- buf, err := generateSecureRandomData(keyIDSize)
- if err != nil {
- return "", err
- }
- if !validateDataIntegrity(buf, keyIDSize) {
- return "", errors.New("error in generateRandomID: crypto/rand failed to generate random data")
- }
- id = common.Bytes2Hex(buf)
- return id, err
-}
-
-func isFullNode(bloom []byte) bool {
- if bloom == nil {
- return true
- }
- for _, b := range bloom {
- if b != 255 {
- return false
- }
- }
- return true
-}
-
-func BloomFilterMatch(filter, sample []byte) bool {
- if filter == nil {
- return true
- }
-
- for i := 0; i < BloomFilterSize; i++ {
- f := filter[i]
- s := sample[i]
- if (f | s) != f {
- return false
- }
- }
-
- return true
-}
-
-func addBloom(a, b []byte) []byte {
- c := make([]byte, BloomFilterSize)
- for i := 0; i < BloomFilterSize; i++ {
- c[i] = a[i] | b[i]
- }
- return c
-}
diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go
deleted file mode 100644
index b7d17a32cb..0000000000
--- a/whisper/whisperv6/whisper_test.go
+++ /dev/null
@@ -1,885 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package whisperv6
-
-import (
- "bytes"
- "crypto/ecdsa"
- "crypto/sha256"
- mrand "math/rand"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "golang.org/x/crypto/pbkdf2"
-)
-
-func TestWhisperBasic(t *testing.T) {
- w := New(&DefaultConfig)
- p := w.Protocols()
- shh := p[0]
- if shh.Name != ProtocolName {
- t.Fatalf("failed Protocol Name: %v.", shh.Name)
- }
- if uint64(shh.Version) != ProtocolVersion {
- t.Fatalf("failed Protocol Version: %v.", shh.Version)
- }
- if shh.Length != NumberOfMessageCodes {
- t.Fatalf("failed Protocol Length: %v.", shh.Length)
- }
- if shh.Run == nil {
- t.Fatalf("failed shh.Run.")
- }
- if uint64(w.Version()) != ProtocolVersion {
- t.Fatalf("failed whisper Version: %v.", shh.Version)
- }
- if w.GetFilter("non-existent") != nil {
- t.Fatalf("failed GetFilter.")
- }
-
- peerID := make([]byte, 64)
- mrand.Read(peerID)
- peer, _ := w.getPeer(peerID)
- if peer != nil {
- t.Fatal("found peer for random key.")
- }
- if err := w.AllowP2PMessagesFromPeer(peerID); err == nil {
- t.Fatalf("failed MarkPeerTrusted.")
- }
- exist := w.HasSymKey("non-existing")
- if exist {
- t.Fatalf("failed HasSymKey.")
- }
- key, err := w.GetSymKey("non-existing")
- if err == nil {
- t.Fatalf("failed GetSymKey(non-existing): false positive.")
- }
- if key != nil {
- t.Fatalf("failed GetSymKey: false positive.")
- }
- mail := w.Envelopes()
- if len(mail) != 0 {
- t.Fatalf("failed w.Envelopes().")
- }
-
- derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
- if !validateDataIntegrity(derived, aesKeyLength) {
- t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
- }
- if containsOnlyZeros(derived) {
- t.Fatalf("failed containsOnlyZeros with param = %v.", derived)
- }
-
- buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0}
- le := bytesToUintLittleEndian(buf)
- be := BytesToUintBigEndian(buf)
- if le != uint64(0x280e5ff) {
- t.Fatalf("failed bytesToIntLittleEndian: %d.", le)
- }
- if be != uint64(0xffe5800200) {
- t.Fatalf("failed BytesToIntBigEndian: %d.", be)
- }
-
- id, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- pk, err := w.GetPrivateKey(id)
- if err != nil {
- t.Fatalf("failed to retrieve new key pair: %s.", err)
- }
- if !validatePrivateKey(pk) {
- t.Fatalf("failed validatePrivateKey: %v.", pk)
- }
- if !ValidatePublicKey(&pk.PublicKey) {
- t.Fatalf("failed ValidatePublicKey: %v.", pk)
- }
-}
-
-func TestWhisperAsymmetricKeyImport(t *testing.T) {
- var (
- w = New(&DefaultConfig)
- privateKeys []*ecdsa.PrivateKey
- )
-
- for i := 0; i < 50; i++ {
- id, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("could not generate key: %v", err)
- }
-
- pk, err := w.GetPrivateKey(id)
- if err != nil {
- t.Fatalf("could not export private key: %v", err)
- }
-
- privateKeys = append(privateKeys, pk)
-
- if !w.DeleteKeyPair(id) {
- t.Fatalf("could not delete private key")
- }
- }
-
- for _, pk := range privateKeys {
- if _, err := w.AddKeyPair(pk); err != nil {
- t.Fatalf("could not import private key: %v", err)
- }
- }
-}
-
-func TestWhisperIdentityManagement(t *testing.T) {
- w := New(&DefaultConfig)
- id1, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- id2, err := w.NewKeyPair()
- if err != nil {
- t.Fatalf("failed to generate new key pair: %s.", err)
- }
- pk1, err := w.GetPrivateKey(id1)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- pk2, err := w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
-
- if !w.HasKeyPair(id1) {
- t.Fatalf("failed HasIdentity(pk1).")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed HasIdentity(pk2).")
- }
- if pk1 == nil {
- t.Fatalf("failed GetIdentity(pk1).")
- }
- if pk2 == nil {
- t.Fatalf("failed GetIdentity(pk2).")
- }
-
- if !validatePrivateKey(pk1) {
- t.Fatalf("pk1 is invalid.")
- }
- if !validatePrivateKey(pk2) {
- t.Fatalf("pk2 is invalid.")
- }
-
- // Delete one identity
- done := w.DeleteKeyPair(id1)
- if !done {
- t.Fatalf("failed to delete id1.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed DeleteIdentity(pub1): still exist.")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed DeleteIdentity(pub1): first key still exist.")
- }
- if pk2 == nil {
- t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.")
- }
-
- // Delete again non-existing identity
- done = w.DeleteKeyPair(id1)
- if done {
- t.Fatalf("delete id1: false positive.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err != nil {
- t.Fatalf("failed to retrieve the key pair: %s.", err)
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed delete non-existing identity: exist.")
- }
- if !w.HasKeyPair(id2) {
- t.Fatalf("failed delete non-existing identity: pub2 does not exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed delete non-existing identity: first key exist.")
- }
- if pk2 == nil {
- t.Fatalf("failed delete non-existing identity: second key does not exist.")
- }
-
- // Delete second identity
- done = w.DeleteKeyPair(id2)
- if !done {
- t.Fatalf("failed to delete id2.")
- }
- pk1, err = w.GetPrivateKey(id1)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- pk2, err = w.GetPrivateKey(id2)
- if err == nil {
- t.Fatalf("retrieve the key pair: false positive.")
- }
- if w.HasKeyPair(id1) {
- t.Fatalf("failed delete second identity: first identity exist.")
- }
- if w.HasKeyPair(id2) {
- t.Fatalf("failed delete second identity: still exist.")
- }
- if pk1 != nil {
- t.Fatalf("failed delete second identity: first key exist.")
- }
- if pk2 != nil {
- t.Fatalf("failed delete second identity: second key exist.")
- }
-}
-
-func TestWhisperSymKeyManagement(t *testing.T) {
- InitSingleTest()
-
- var err error
- var k1, k2 []byte
- w := New(&DefaultConfig)
- id1 := string("arbitrary-string-1")
- id2 := string("arbitrary-string-2")
-
- id1, err = w.GenerateSymKey()
- if err != nil {
- t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
- }
-
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed GetSymKey(id2): false positive.")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("failed HasSymKey(id1).")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed HasSymKey(id2): false positive.")
- }
- if k1 == nil {
- t.Fatalf("first key does not exist.")
- }
- if k2 != nil {
- t.Fatalf("second key still exist.")
- }
-
- // add existing id, nothing should change
- randomKey := make([]byte, aesKeyLength)
- mrand.Read(randomKey)
- id1, err = w.AddSymKeyDirect(randomKey)
- if err != nil {
- t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err)
- }
-
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id2): false positive.")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("failed w.HasSymKey(id1).")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed w.HasSymKey(id2): false positive.")
- }
- if k1 == nil {
- t.Fatalf("first key does not exist.")
- }
- if !bytes.Equal(k1, randomKey) {
- t.Fatalf("k1 != randomKey.")
- }
- if k2 != nil {
- t.Fatalf("second key already exist.")
- }
-
- id2, err = w.AddSymKeyDirect(randomKey)
- if err != nil {
- t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err)
- }
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("HasSymKey(id1) failed.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("HasSymKey(id2) failed.")
- }
- if k1 == nil {
- t.Fatalf("k1 does not exist.")
- }
- if k2 == nil {
- t.Fatalf("k2 does not exist.")
- }
- if !bytes.Equal(k1, k2) {
- t.Fatalf("k1 != k2.")
- }
- if !bytes.Equal(k1, randomKey) {
- t.Fatalf("k1 != randomKey.")
- }
- if len(k1) != aesKeyLength {
- t.Fatalf("wrong length of k1.")
- }
- if len(k2) != aesKeyLength {
- t.Fatalf("wrong length of k2.")
- }
-
- w.DeleteSymKey(id1)
- k1, err = w.GetSymKey(id1)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id1): false positive.")
- }
- if k1 != nil {
- t.Fatalf("failed GetSymKey(id1): false positive.")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if w.HasSymKey(id1) {
- t.Fatalf("failed to delete first key: still exist.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("failed to delete first key: second key does not exist.")
- }
- if k1 != nil {
- t.Fatalf("failed to delete first key.")
- }
- if k2 == nil {
- t.Fatalf("failed to delete first key: second key is nil.")
- }
-
- w.DeleteSymKey(id1)
- w.DeleteSymKey(id2)
- k1, err = w.GetSymKey(id1)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id1): false positive.")
- }
- k2, err = w.GetSymKey(id2)
- if err == nil {
- t.Fatalf("failed w.GetSymKey(id2): false positive.")
- }
- if k1 != nil || k2 != nil {
- t.Fatalf("k1 or k2 is not nil")
- }
- if w.HasSymKey(id1) {
- t.Fatalf("failed to delete second key: first key exist.")
- }
- if w.HasSymKey(id2) {
- t.Fatalf("failed to delete second key: still exist.")
- }
- if k1 != nil {
- t.Fatalf("failed to delete second key: first key is not nil.")
- }
- if k2 != nil {
- t.Fatalf("failed to delete second key: second key is not nil.")
- }
-
- randomKey = make([]byte, aesKeyLength+1)
- mrand.Read(randomKey)
- _, err = w.AddSymKeyDirect(randomKey)
- if err == nil {
- t.Fatalf("added the key with wrong size, seed %d.", seed)
- }
-
- const password = "arbitrary data here"
- id1, err = w.AddSymKeyFromPassword(password)
- if err != nil {
- t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err)
- }
- id2, err = w.AddSymKeyFromPassword(password)
- if err != nil {
- t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err)
- }
- k1, err = w.GetSymKey(id1)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id1).")
- }
- k2, err = w.GetSymKey(id2)
- if err != nil {
- t.Fatalf("failed w.GetSymKey(id2).")
- }
- if !w.HasSymKey(id1) {
- t.Fatalf("HasSymKey(id1) failed.")
- }
- if !w.HasSymKey(id2) {
- t.Fatalf("HasSymKey(id2) failed.")
- }
- if !validateDataIntegrity(k2, aesKeyLength) {
- t.Fatalf("key validation failed.")
- }
- if !bytes.Equal(k1, k2) {
- t.Fatalf("k1 != k2.")
- }
-}
-
-func TestExpiry(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- w.SetMinimumPowTest(0.0000001)
- defer w.SetMinimumPowTest(DefaultMinimumPoW)
- w.Start(nil)
- defer w.Stop()
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.TTL = 1
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
- }
-
- // wait till received or timeout
- var received, expired bool
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // wait till expired or timeout
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) == 0 {
- expired = true
- break
- }
- }
-
- if !expired {
- t.Fatalf("expire failed, seed: %d.", seed)
- }
-}
-
-func TestCustomization(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPowTest(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- const smallPoW = 0.00001
-
- f, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.KeySym = f.KeySym
- params.Topic = BytesToTopic(f.Topics[2])
- params.PoW = smallPoW
- params.TTL = 3600 * 24 // one day
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err == nil {
- t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed)
- }
-
- w.SetMinimumPowTest(smallPoW / 2)
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
- }
-
- params.TTL++
- msg, err = NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err = msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
- w.SetMaxMessageSize(uint32(env.size() - 1))
- err = w.Send(env)
- if err == nil {
- t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed)
- }
-
- w.SetMaxMessageSize(DefaultMaxMessageSize)
- err = w.Send(env)
- if err != nil {
- t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
- if len(w.Envelopes()) > 1 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- _, err = w.Subscribe(f)
- if err != nil {
- t.Fatalf("failed subscribe with seed %d: %s.", seed, err)
- }
- time.Sleep(5 * time.Millisecond)
- mail := f.Retrieve()
- if len(mail) > 0 {
- t.Fatalf("received premature mail")
- }
-}
-
-func TestSymmetricSendCycle(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPowTest(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter1, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter1.PoW = DefaultMinimumPoW
-
- // Copy the first filter since some of its fields
- // are randomly gnerated.
- filter2 := &Filter{
- KeySym: filter1.KeySym,
- Topics: filter1.Topics,
- PoW: filter1.PoW,
- AllowP2P: filter1.AllowP2P,
- Messages: make(map[common.Hash]*ReceivedMessage),
- }
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- filter1.Src = ¶ms.Src.PublicKey
- filter2.Src = ¶ms.Src.PublicKey
-
- params.KeySym = filter1.KeySym
- params.Topic = BytesToTopic(filter1.Topics[2])
- params.PoW = filter1.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter1)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter2)
- if err != nil {
- t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail1 := filter1.Retrieve()
- mail2 := filter2.Retrieve()
- if len(mail2) == 0 {
- t.Fatalf("did not receive any email for filter 2")
- }
- if len(mail1) == 0 {
- t.Fatalf("did not receive any email for filter 1")
- }
-
-}
-
-func TestSymmetricSendWithoutAKey(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPowTest(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter.PoW = DefaultMinimumPoW
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- filter.Src = nil
-
- params.KeySym = filter.KeySym
- params.Topic = BytesToTopic(filter.Topics[2])
- params.PoW = filter.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail := filter.Retrieve()
- if len(mail) == 0 {
- t.Fatalf("did not receive message in spite of not setting a public key")
- }
-}
-
-func TestSymmetricSendKeyMismatch(t *testing.T) {
- InitSingleTest()
-
- w := New(&DefaultConfig)
- defer w.SetMinimumPowTest(DefaultMinimumPoW)
- defer w.SetMaxMessageSize(DefaultMaxMessageSize)
- w.Start(nil)
- defer w.Stop()
-
- filter, err := generateFilter(t, true)
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
- filter.PoW = DefaultMinimumPoW
-
- params, err := generateMessageParams()
- if err != nil {
- t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
- }
-
- params.KeySym = filter.KeySym
- params.Topic = BytesToTopic(filter.Topics[2])
- params.PoW = filter.PoW
- params.WorkTime = 10
- params.TTL = 50
- msg, err := NewSentMessage(params)
- if err != nil {
- t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
- }
- env, err := msg.Wrap(params)
- if err != nil {
- t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
- }
-
- _, err = w.Subscribe(filter)
- if err != nil {
- t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err)
- }
-
- err = w.Send(env)
- if err != nil {
- t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err)
- }
-
- // wait till received or timeout
- var received bool
- for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
- if len(w.Envelopes()) > 0 {
- received = true
- break
- }
- }
-
- if !received {
- t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
- }
-
- // check w.messages()
- time.Sleep(5 * time.Millisecond)
- mail := filter.Retrieve()
- if len(mail) > 0 {
- t.Fatalf("received a message when keys weren't matching")
- }
-}
-
-func TestBloom(t *testing.T) {
- topic := TopicType{0, 0, 255, 6}
- b := TopicToBloom(topic)
- x := make([]byte, BloomFilterSize)
- x[0] = byte(1)
- x[32] = byte(1)
- x[BloomFilterSize-1] = byte(128)
- if !BloomFilterMatch(x, b) || !BloomFilterMatch(b, x) {
- t.Fatalf("bloom filter does not match the mask")
- }
-
- _, err := mrand.Read(b)
- if err != nil {
- t.Fatalf("math rand error")
- }
- _, err = mrand.Read(x)
- if err != nil {
- t.Fatalf("math rand error")
- }
- if !BloomFilterMatch(b, b) {
- t.Fatalf("bloom filter does not match self")
- }
- x = addBloom(x, b)
- if !BloomFilterMatch(x, b) {
- t.Fatalf("bloom filter does not match combined bloom")
- }
- if !isFullNode(nil) {
- t.Fatalf("isFullNode did not recognize nil as full node")
- }
- x[17] = 254
- if isFullNode(x) {
- t.Fatalf("isFullNode false positive")
- }
- for i := 0; i < BloomFilterSize; i++ {
- b[i] = byte(255)
- }
- if !isFullNode(b) {
- t.Fatalf("isFullNode false negative")
- }
- if BloomFilterMatch(x, b) {
- t.Fatalf("bloomFilterMatch false positive")
- }
- if !BloomFilterMatch(b, x) {
- t.Fatalf("bloomFilterMatch false negative")
- }
-
- w := New(&DefaultConfig)
- f := w.BloomFilter()
- if f != nil {
- t.Fatalf("wrong bloom on creation")
- }
- err = w.SetBloomFilter(x)
- if err != nil {
- t.Fatalf("failed to set bloom filter: %s", err)
- }
- f = w.BloomFilter()
- if !BloomFilterMatch(f, x) || !BloomFilterMatch(x, f) {
- t.Fatalf("retireved wrong bloom filter")
- }
-}